first commit

This commit is contained in:
zhouyanli
2026-04-18 08:31:55 +08:00
commit eeb09abc04
989 changed files with 137562 additions and 0 deletions

4
property-uniapp-project/.gitignore vendored Normal file
View File

@@ -0,0 +1,4 @@
node_modules/
unpackage/
.hbuilderx/
.DS_Store

View File

@@ -0,0 +1,34 @@
<script>
export default {
onLaunch: function() {
// console.log('App Launch')
},
onShow: function() {
// console.log('App Show')
},
onHide: function() {
console.log('App Hide')
}
}
</script>
<style lang="scss">
@import "@/uni_modules/uview-plus/index.scss";
/*每个页面公共css */
</style>
<style>
/* 全局给 uni.showToast 加内边距 */
.uni-toast {
padding: 5px !important;
/* 上下20 左右40自己改数值 */
min-height: auto !important;
box-sizing: border-box !important;
}
/* 文字也可以居中、调整行高 */
.uni-toast-content {
/* line-height: 1.6 !important; */
padding: 0 !important;
}
</style>

View File

@@ -0,0 +1,9 @@
<template>
</template>
<script>
</script>
<style>
</style>

View File

@@ -0,0 +1,113 @@
<template>
<canvas v-if="value" :type="type" :style="{width: size + 'px', height: size + 'px'}" :canvas-id="canvasId"
ref="qrcodeCanvas"></canvas>
</template>
<script setup>
import {
ref,
onMounted,
watch
} from 'vue';
import {
uqrcode
} from 'uqrcodejs'
const props = defineProps({
// 二维码内容(必填,如链接/文本)
value: {
type: String,
default: ''
},
// 尺寸默认300px
size: {
type: [Number, String],
default: 300
},
// 二维码颜色
color: {
type: String,
default: '#000000'
},
// 背景色
backgroundColor: {
type: String,
default: '#ffffff'
},
// logo图片地址
logo: {
type: String,
default: ''
},
// logo大小占二维码比例
logoSize: {
type: Number,
default: 0.2
},
// canvas 类型(小程序需指定)
type: {
type: String,
default: '2d'
},
// canvas ID避免重复
canvasId: {
type: String,
default: 'uqrcode'
}
});
const emit = defineEmits(['success', 'fail']);
const qrcodeCanvas = ref(null);
// 生成二维码核心方法
const createQRCode = async () => {
if (!props.value) return;
try {
// 引入uQRCode核心库
const uQRCode = require('uqrcodejs');
// 获取canvas上下文
const query = uni.createSelectorQuery().in(getCurrentInstance());
const res = await query.select(`#${props.canvasId}`).fields({
node: true,
size: true
}).exec();
const canvas = res[0].node;
const ctx = canvas.getContext('2d');
// 设置canvas尺寸
const dpr = uni.getSystemInfoSync().pixelRatio;
canvas.width = props.size * dpr;
canvas.height = props.size * dpr;
ctx.scale(dpr, dpr);
// 生成二维码
await uQRCode.default.draw({
canvas: canvas,
componentInstance: getCurrentInstance(),
text: props.value,
width: props.size,
color: props.color,
background: props.backgroundColor,
logo: props.logo,
logoSize: props.logoSize
});
// 生成临时图片(可选,用于分享/保存)
uni.canvasToTempFilePath({
canvasId: props.canvasId,
success: (res) => emit('success', res.tempFilePath),
fail: (err) => emit('fail', err)
}, getCurrentInstance());
} catch (err) {
emit('fail', err);
}
};
// 监听value变化重新生成
watch(() => props.value, () => createQRCode(), {
immediate: true
});
onMounted(() => createQRCode());
</script>

View File

@@ -0,0 +1,20 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<script>
var coverSupport = 'CSS' in window && typeof CSS.supports === 'function' && (CSS.supports('top: env(a)') ||
CSS.supports('top: constant(a)'))
document.write(
'<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0' +
(coverSupport ? ', viewport-fit=cover' : '') + '" />')
</script>
<title></title>
<!--preload-links-->
<!--app-context-->
</head>
<body>
<div id="app"><!--app-html--></div>
<script type="module" src="/main.js"></script>
</body>
</html>

View File

@@ -0,0 +1,24 @@
import App from './App'
import uviewPlus from '@/uni_modules/uview-plus'
// #ifndef VUE3
import Vue from 'vue'
import './uni.promisify.adaptor'
Vue.config.productionTip = false
App.mpType = 'app'
const app = new Vue({
...App
})
app.$mount()
// #endif
// #ifdef VUE3
import { createSSRApp } from 'vue'
export function createApp() {
const app = createSSRApp(App)
app.use(uviewPlus)
return {
app
}
}
// #endif

View File

@@ -0,0 +1,72 @@
{
"name" : "uniapp-project-test",
"appid" : "",
"description" : "",
"versionName" : "1.0.0",
"versionCode" : "100",
"transformPx" : false,
/* 5+App */
"app-plus" : {
"usingComponents" : true,
"nvueStyleCompiler" : "uni-app",
"compilerVersion" : 3,
"splashscreen" : {
"alwaysShowBeforeRender" : true,
"waiting" : true,
"autoclose" : true,
"delay" : 0
},
/* */
"modules" : {},
/* */
"distribute" : {
/* android */
"android" : {
"permissions" : [
"<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
"<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
"<uses-permission android:name=\"android.permission.VIBRATE\"/>",
"<uses-permission android:name=\"android.permission.READ_LOGS\"/>",
"<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
"<uses-feature android:name=\"android.hardware.camera.autofocus\"/>",
"<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
"<uses-permission android:name=\"android.permission.CAMERA\"/>",
"<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
"<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>",
"<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
"<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
"<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
"<uses-feature android:name=\"android.hardware.camera\"/>",
"<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
]
},
/* ios */
"ios" : {},
/* SDK */
"sdkConfigs" : {}
}
},
/* */
"quickapp" : {},
/* */
"mp-weixin" : {
"appid" : "wx4a49ad94e2b25927",
"setting" : {
"urlCheck" : false
},
"usingComponents" : true
},
"mp-alipay" : {
"usingComponents" : true
},
"mp-baidu" : {
"usingComponents" : true
},
"mp-toutiao" : {
"usingComponents" : true
},
"uniStatistics" : {
"enable" : false
},
"vueVersion" : "3"
}

View File

@@ -0,0 +1,74 @@
{
"name": "property-uniapp-project",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"dependencies": {
"clipboard": "^2.0.11",
"dayjs": "^1.11.20",
"moment": "^2.30.1",
"uqrcodejs": "^4.0.7"
}
},
"node_modules/clipboard": {
"version": "2.0.11",
"resolved": "https://registry.npmmirror.com/clipboard/-/clipboard-2.0.11.tgz",
"integrity": "sha512-C+0bbOqkezLIsmWSvlsXS0Q0bmkugu7jcfMIACB+RDEntIzQIkdr148we28AfSloQLRdZlYL/QYyrq05j/3Faw==",
"license": "MIT",
"dependencies": {
"good-listener": "^1.2.2",
"select": "^1.1.2",
"tiny-emitter": "^2.0.0"
}
},
"node_modules/dayjs": {
"version": "1.11.20",
"resolved": "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.20.tgz",
"integrity": "sha512-YbwwqR/uYpeoP4pu043q+LTDLFBLApUP6VxRihdfNTqu4ubqMlGDLd6ErXhEgsyvY0K6nCs7nggYumAN+9uEuQ==",
"license": "MIT"
},
"node_modules/delegate": {
"version": "3.2.0",
"resolved": "https://registry.npmmirror.com/delegate/-/delegate-3.2.0.tgz",
"integrity": "sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw==",
"license": "MIT"
},
"node_modules/good-listener": {
"version": "1.2.2",
"resolved": "https://registry.npmmirror.com/good-listener/-/good-listener-1.2.2.tgz",
"integrity": "sha512-goW1b+d9q/HIwbVYZzZ6SsTr4IgE+WA44A0GmPIQstuOrgsFcT7VEJ48nmr9GaRtNu0XTKacFLGnBPAM6Afouw==",
"license": "MIT",
"dependencies": {
"delegate": "^3.1.2"
}
},
"node_modules/moment": {
"version": "2.30.1",
"resolved": "https://registry.npmmirror.com/moment/-/moment-2.30.1.tgz",
"integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==",
"license": "MIT",
"engines": {
"node": "*"
}
},
"node_modules/select": {
"version": "1.1.2",
"resolved": "https://registry.npmmirror.com/select/-/select-1.1.2.tgz",
"integrity": "sha512-OwpTSOfy6xSs1+pwcNrv0RBMOzI39Lp3qQKUTPVVPRjCdNa5JH/oPRiqsesIskK8TVgmRiHwO4KXlV2Li9dANA==",
"license": "MIT"
},
"node_modules/tiny-emitter": {
"version": "2.1.0",
"resolved": "https://registry.npmmirror.com/tiny-emitter/-/tiny-emitter-2.1.0.tgz",
"integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==",
"license": "MIT"
},
"node_modules/uqrcodejs": {
"version": "4.0.7",
"resolved": "https://registry.npmmirror.com/uqrcodejs/-/uqrcodejs-4.0.7.tgz",
"integrity": "sha512-84+aZmD2godCVI+93lxE3YUAPNY8zAJvNA7xRS7R7U+q57KzMDepBSfNCwoRUhWOfR6eHFoAOcHRPwsP6ka1cA==",
"license": "Apache-2.0"
}
}
}

View File

@@ -0,0 +1,8 @@
{
"dependencies": {
"clipboard": "^2.0.11",
"dayjs": "^1.11.20",
"moment": "^2.30.1",
"uqrcodejs": "^4.0.7"
}
}

View File

@@ -0,0 +1,478 @@
{
"pages": [
// {
// "path": "pages/navigation/navigation",
// "style": {
// "navigationBarTitleText": "引导页",
// "navigationBarTextStyle": "white",
// "enablePullDownRefresh": false,
// "navigationStyle": "custom"
// }
// },
// {
// "path": "pages/login/pwd-login",
// "style": {
// "navigationBarTitleText": "密码登录",
// "navigationBarTextStyle": "white",
// "enablePullDownRefresh": false,
// "navigationStyle": "custom"
// }
// },
// {
// "path": "pages/login/login",
// "style": {
// "navigationBarTitleText": "验证码登录",
// "navigationBarTextStyle": "white",
// "enablePullDownRefresh": false,
// "navigationStyle": "custom"
// }
// },
{
"path": "pages/index/index",
"style": {
"navigationBarTitleText": "首页",
"navigationStyle": "custom",
"navigationBarBackgroundColor": "#F8F8F8"
}
},
{
"path": "pages/serviceIndex/serviceIndex",
"style": {
"navigationBarTitleText": "服务",
"navigationStyle": "custom"
}
},
{
"path": "pages/myIndex/myIndex",
"style": {
"navigationBarTitleText": "我的",
"navigationStyle": "custom"
}
}
],
"subPackages": [{
"root": "pages/loginSub",
"pages": [{
"path": "register-new-user",
"style": {
"navigationBarTitleText": "注册新用户",
"navigationStyle": "custom"
}
},
{
"path": "forget-pwd",
"style": {
"navigationBarTitleText": "忘记密码",
"navigationStyle": "custom"
}
},
{
"path": "change-pwd",
"style": {
"navigationBarTitleText": "修改密码",
"navigationStyle": "custom"
}
}
]
},
{
"root": "pages/online-repair",
"pages": [{
"path": "onlineRepair",
"style": {
"navigationBarTitleText": "在线报修"
}
},
{
"path": "repairSelect",
"style": {
"navigationBarTitleText": "选择维修项目"
}
},
{
"path": "publicRepair",
"style": {
"navigationBarTitleText": "填写维修信息"
}
},
{
"path": "personalRepair",
"style": {
"navigationBarTitleText": "个人报修"
}
},
{
"path": "selectHouse",
"style": {
"navigationBarTitleText": "选择房屋"
}
},
{
"path": "personalSelectPoject",
"style": {
"navigationBarTitleText": "个人维修项目"
}
},
{
"path": "repairDetail",
"style": {
"navigationBarTitleText": "报修详情"
}
}
]
},
{
"root": "pages/visitor",
"pages": [{
"path": "visitor-list",
"style": {
"navigationBarTitleText": "访客邀请"
}
},
{
"path": "visitor-detail",
"style": {
"navigationBarTitleText": "访客详情"
}
},
{
"path": "qrcode-page",
"style": {
"navigationBarTitleText": "通行二维码"
}
},
{
"path": "visitor-add",
"style": {
"navigationBarTitleText": "新增访客"
}
}
]
},
{
"root": "pages/activity-list",
"pages": [{
"path": "activityList",
"style": {
"navigationBarTitleText": "活动列表"
}
},
{
"path": "live-add",
"style": {
"navigationBarTitleText": "添加实况"
}
},
{
"path": "activityListDetails",
"style": {
"navigationBarTitleText": "报名详情",
"navigationStyle": "custom"
}
}
]
},
{
"root": "pages/notice-list",
"pages": [{
"path": "noticeList",
"style": {
"navigationBarTitleText": "通知公告"
}
},
{
"path": "noticeListDetails",
"style": {
"navigationBarTitleText": "公告详情"
}
},
{
"path": "oneClickOpen",
"style": {
"navigationBarTitleText": "一键开门",
"navigationStyle": "custom"
}
},
{
"path": "selectHome",
"style": {
"navigationBarTitleText": "选择小区"
}
}
]
},
{
"root": "pages/my",
"pages": [{
"path": "settingIndex",
"style": {
"navigationBarTitleText": "",
"navigationStyle": "custom"
}
},
{
"path": "changePhone",
"style": {
"navigationBarTitleText": "",
"navigationStyle": "custom"
}
},
{
"path": "changePwd",
"style": {
"navigationBarTitleText": "",
"navigationStyle": "custom"
}
},
{
"path": "myHouseIndex",
"style": {
"navigationBarTitleText": "",
"navigationStyle": "custom"
}
},
{
"path": "houseDetail",
"style": {
"navigationBarTitleText": "",
"navigationStyle": "custom"
}
}, {
"path": "addHouse",
"style": {
"navigationBarTitleText": "",
"navigationStyle": "custom"
}
}, {
"path": "selectResidentialCommunity",
"style": {
"navigationBarTitleText": "",
"navigationStyle": "custom"
}
}, {
"path": "selectBuilding",
"style": {
"navigationBarTitleText": "",
"navigationStyle": "custom"
}
}, {
"path": "myParkingSpaceIndex",
"style": {
"navigationBarTitleText": "",
"navigationStyle": "custom"
}
},
{
"path": "parkingSpaceDetail",
"style": {
"navigationBarTitleText": "",
"navigationStyle": "custom"
}
},
{
"path": "addParkingSpace",
"style": {
"navigationBarTitleText": "",
"navigationStyle": "custom"
}
}, {
"path": "bindHouse",
"style": {
"navigationBarTitleText": "",
"navigationStyle": "custom"
}
}, {
"path": "selectParingLot",
"style": {
"navigationBarTitleText": "",
"navigationStyle": "custom"
}
}, {
"path": "bindCar",
"style": {
"navigationBarTitleText": "",
"navigationStyle": "custom"
}
},
{
"path": "myCarIndex",
"style": {
"navigationBarTitleText": "",
"navigationStyle": "custom"
}
}, {
"path": "addCar",
"style": {
"navigationBarTitleText": "",
"navigationStyle": "custom"
}
},
{
"path": "selectCarBrand",
"style": {
"navigationBarTitleText": "",
"navigationStyle": "custom"
}
}, {
"path": "carDetail",
"style": {
"navigationBarTitleText": "",
"navigationStyle": "custom"
}
}
]
}, {
"root": "pages/payment-list",
"pages": [{
"path": "addPayment",
"style": {
"navigationBarTitleText": "",
"navigationStyle": "custom"
}
}, {
"path": "addPaymentSuccess",
"style": {
"navigationBarTitleText": "",
"navigationStyle": "custom"
}
}, {
"path": "arrearsPayment",
"style": {
"navigationBarTitleText": "",
"navigationStyle": "custom"
}
}, {
"path": "noArrearsPayment",
"style": {
"navigationBarTitleText": "",
"navigationStyle": "custom"
}
},
{
"path": "paymentRecord",
"style": {
"navigationBarTitleText": "",
"navigationStyle": "custom"
}
},
{
"path": "selectPaymentEntity",
"style": {
"navigationBarTitleText": "",
"navigationStyle": "custom"
}
},
{
"path": "paymentIndex",
"style": {
"navigationBarTitleText": "",
"navigationStyle": "custom"
}
}
]
},
{
"root": "pages/service-list",
"pages": [{
"path": "complaintsSuggestionsIndex",
"style": {
"navigationBarTitleText": "",
"navigationStyle": "custom"
}
}, {
"path": "complaintsDetail",
"style": {
"navigationBarTitleText": "",
"navigationStyle": "custom"
}
}, {
"path": "addComplaints",
"style": {
"navigationBarTitleText": "",
"navigationStyle": "custom"
}
}, {
"path": "questionnaireSurveyIndex",
"style": {
"navigationBarTitleText": "",
"navigationStyle": "custom"
}
},
{
"path": "questionnaireFillout",
"style": {
"navigationBarTitleText": "",
"navigationStyle": "custom"
}
},
{
"path": "questionnaireDetail",
"style": {
"navigationBarTitleText": "",
"navigationStyle": "custom"
}
}, {
"path": "contactProperty",
"style": {
"navigationBarTitleText": "",
"navigationStyle": "custom"
}
}
]
}
],
"easycom": {
"autoscan": true,
"custom": {
"^u--(.*)": "@/uni_modules/uview-plus/components/u-$1/u-$1.vue",
"^up-(.*)": "@/uni_modules/uview-plus/components/u-$1/u-$1.vue",
"^u-([^-].*)": "@/uni_modules/uview-plus/components/u-$1/u-$1.vue"
}
},
"tabBar": {
"color": "#333333",
"backgroundColor": "#fff",
"borderStyle": "black",
"selectedColor": "#666666",
"list": [{
"pagePath": "pages/index/index",
"iconPath": "/static/tabbar/首页.png",
"selectedIconPath": "/static/tabbar/首页蓝.png",
"text": "首页"
},
{
"pagePath": "pages/serviceIndex/serviceIndex",
"iconPath": "/static/tabbar/服务.png",
"selectedIconPath": "/static/tabbar/服务蓝.png",
"text": "服务"
},
{
"pagePath": "pages/myIndex/myIndex",
"iconPath": "/static/tabbar/我的.png",
"selectedIconPath": "/static/tabbar/我的蓝.png",
"text": "我的"
}
]
},
"globalStyle": {
"navigationBarTextStyle": "black",
"navigationBarTitleText": "uni-app",
"navigationBarBackgroundColor": "#F8F8F8",
"backgroundColor": "#F8F8F8"
},
"uniIdRouter": {}
}

View File

@@ -0,0 +1,845 @@
<template>
<view class="container">
<!-- 顶部图片 -->
<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/logo.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 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;
}
.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

@@ -0,0 +1,903 @@
<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

@@ -0,0 +1,285 @@
<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

@@ -0,0 +1,23 @@
import request,{get,post} from "/pages/api/request.js"
// =======账号密码登录========
export const getLoginPassword = (data) =>{
return post('/api/resident/auth/loginByPassword',data)
}
// 注册新用户获取验证码
export const getSendCode = (data) =>{
return post('/api/resident/auth/sendSmsCode',data)
}
// 注册新用户
export const getRegisterUser = (data) =>{
return post('/api/resident/auth/register',data)
}
// 短信验证码登录
export const getLoginBySms = (data) =>{
return post('/api/resident/auth/register',data)
}
// 忘记密码接口
export const getResetPassword = (data) =>{
return post('/api/resident/auth/resetPassword',data)
}

View File

@@ -0,0 +1,75 @@
let baseUrl = '';
if (process.env.NODE_ENV === 'development') {
baseUrl = 'http://192.168.0.224:8080'; // 开发环境
} else {
baseUrl = 'http://192.168.1.224:8080'; // 生产环境
}
export default function request(url, data = {}, method = "GET") {
return new Promise((resolve, reject) => {
// 加载中提示(提升用户体验)
uni.showLoading({ title: '请求中...', mask: true });
uni.request({
url: baseUrl + url,
data: data,
method: method,
header: {
"token": uni.getStorageSync("token") || '',
'content-type': 'application/json'
},
success: (res) => {
uni.hideLoading(); // 关闭加载提示
// 处理HTTP状态码网络层面
if (res.statusCode !== 200) {
const errMsg = `请求失败(${res.statusCode}`;
uni.showToast({ title: errMsg, icon: "none", duration: 2000 });
reject(errMsg);
return;
}
// 处理业务状态码后端返回的data里
const resData = res.data || {}; // 兼容后端返回空的情况
// 优先处理token过期401
if (resData.code === 401) {
uni.showToast({ title: '登录过期,请重新登录', icon: 'none', duration: 2000 });
uni.clearStorageSync('token');
setTimeout(() => {
uni.reLaunch({ url: '/pages/login/login' });
}, 1500);
reject('token 过期');
return;
}
// 业务成功code=1
if (resData.code === 200) {
resolve(resData.data); // 只返回核心数据,简化页面调用
}
// 业务失败code=2
else if (resData.code === 500) {
const errMsg = resData.msg || '请求失败';
uni.showToast({ title: errMsg, icon: "none", duration: 2000 });
resolve(null)
// reject(errMsg);
}
else {
const errMsg = resData.msg || '业务异常';
uni.showToast({ title: errMsg, icon: "none", duration: 2000 });
reject(errMsg);
}
},
fail: (err) => {
uni.hideLoading(); // 关闭加载提示
uni.showToast({ title: '网络连接失败', icon: "none", duration: 2000 });
reject(err);
}
});
});
}
export const get = (url, data) => request(url, data, 'GET');
export const post = (url, data) => request(url, data, 'POST');

View File

@@ -0,0 +1,470 @@
<template>
<view class="content-class">
<view class="nar-top">
<!-- 这里是状态栏占位 -->
<view class="status_bar"></view>
<!-- 导航栏 -->
<view class="nav-bar">
<text class="nav-title">首页</text>
</view>
<view class="tabbarClass">
<view class="header-container" @click="navigateToPage">
<!-- 左侧小区选择区域 -->
<view class="location-area">
<!-- 定位图标 -->
<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>
</view>
<!-- 小区名称和下拉箭头 -->
<view class="community-selector">
<text class="community-name">{{ currentCommunity }}</text>
<u-icon
name="arrow-down-fill"
size="16"
:color="isDropdownOpen ? '#2979ff' : '#222222'"
:class="['dropdown-arrow', { 'rotate': isDropdownOpen }]"
></u-icon>
</view>
</view>
</view>
</view>
</view>
<!-- banner -->
<view class="bannarClass">
<up-swiper :list="bannerList" indicator indicatorMode="dot" ></up-swiper>
</view>
<view class="noticeClass">
<image src="/static/首页/公告.png" style="width: 81rpx;height: 81rpx;position: absolute;top: 10px;left: 16px;"></image>
<uni-notice-bar show-icon :text="noticeText" type="line"
:showIcon="true" show-get-more scrollable color="#000" background-color="#fff" moreColor="#000" @getmore="getNoticeMore"/>
<!-- <up-notice-bar class="notice-bar" :text="noticeText" mode="link" speed="100" bgColor="#FFF" color="#000" url="pages/notice-list/noticeList"></up-notice-bar> -->
</view>
<!-- 一键开门 -->
<view class="u-demo-block__content bg-card-open">
<up-row justify="space-between" gutter="10">
<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>
</view>
<view class="title-dome-calss">一键开门</view>
</view>
</up-col>
<up-col span="3">
<view class="demo-layout bg-purple-light openClass">
<view class="iconClass">
<image src="/static/首页/生活缴费.png" mode="" style="width: 70px;height: 70px;border-radius: 8px;"></image>
</view>
<view class="title-dome-calss">生活缴费</view>
</view>
</up-col>
<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>
</view>
<view class="title-dome-calss">在线报修</view>
</view>
</up-col>
<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>
</view>
<view class="title-dome-calss">访客邀请</view>
</view>
</up-col>
</up-row>
</view>
<!-- 广告卡片 -->
<view class="adCardClass">
<!-- <uni-card :is-shadow="false" > -->
<view class="u-demo-block__content">
<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>
</view>
</up-col>
<up-col span="8">
<view class="demo-layout bg-purple-light titleLeft">
<text style="color: #fff;font-size: 18px;font-weight: 700;">便民服务</text>
<view style="color: #fff;font-size: 16px;">邻里便民服务 全新上线</view>
</view>
</up-col>
</up-row>
</view>
<!-- </uni-card> -->
</view>
<!-- 活动列表 -->
<uni-card class="activity-card">
<view class="eventListClass">
<view>
<text class="activity-name">活动列表</text>
<!-- <u-title>活动列表</u-title> -->
</view>
<view class="nav-more">
<!-- <up-button type="primary" shape="circle" size="small" text="查看更多" @click="moreClick"></up-button> -->
<text class="look-more-title" @click="moreClick">查看更多</text>
</view>
</view>
<scroll-view :scroll-top="scrollTop" scroll-y="true" class="scroll-Y">
<view class="eventListCard" v-for="(activity, index) in activityList" :key="activity.id">
<!-- 状态标签 -->
<view class="status-tag" :class="{}">
{{ activity.statusText }}
<!-- 进行中 -->
</view>
<view class="u-demo-block__content ">
<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/logo.png" style="width: 100%;height: 100%;"></image>
</view>
</up-col>
<up-col span="10">
<view class="demo-layout bg-purple activity-info">
<view class="activity-title">{{activity.title}}</view>
<view class="activity-time">{{activity.time}}</view>
<view class="activity-organizer">
<text>{{activity.organizer}}</text>
</view>
<view class="activity-enrollment">
<text>活动名额100人已报名{{activity.enrollment}}</text>
</view>
</view>
</up-col>
</up-row>
</view>
</view>
</scroll-view>
</uni-card>
</view>
</template>
<script setup>
import { ref } from 'vue'
import {onLoad,onUnload} from '@dcloudio/uni-app'
// 列表数据状态
const isDropdownOpen = ref(false)
const scrollTop = ref(0)
const activityList = ref([
{
id:'0',
statusText:'进行中',
img:'/static/logo.png',
title:'社区活动节活动',
time:'2026.3.5-2026.4.30',
organizer:'物业中心',
enrollment:'87'
},
// {
// id:'1',
// statusText:'进行中',
// img:'',
// title:'222',
// time:'2026.3.5-2026.4.30',
// organizer:'物业中心',
// enrollment:'87'
// },
])
const currentCommunity = ref('碧桂园小区')
const bannerList = ref([
'/static/首页/banner.png',
'/static/bannar2.png'
])
const noticeText = ref('公告公告本小区今天安装了摄像头')
// =======方法=======
const getNoticeMore = () =>{
uni.navigateTo({
url:"/pages/notice-list/noticeList"
})
}
// 接收选择小区返回的参数
const slelctHomeData = (data) =>{
// console.log('收到B页面返回的数据', data)
currentCommunity.value = data.name
}
onLoad(() => {
// 绑定全局事件监听
uni.$on('confirm', slelctHomeData)
})
// 页面销毁时移除监听(防止内存泄漏)
onUnload(() => {
uni.$off('confirm', slelctHomeData)
})
// 方法逻辑
const openDoorClick = () =>{
uni.navigateTo({
url:"/pages/notice-list/oneClickOpen"
})
}
const navigateToPage = () => {
uni.navigateTo({
url: '/pages/notice-list/selectHome'
})
}
const onlineRepairClick = () =>{
uni.navigateTo({
url:'/pages/online-repair/onlineRepair'
})
}
// 访客邀请
const visitorClick = () =>{
uni.navigateTo({
url:"/pages/visitor/visitor-list"
})
}
// 活动列表的查看更多
const moreClick = () =>{
uni.navigateTo({
url:"/pages/activity-list/activityList"
})
}
</script>
<style lang="scss" scoped>
.content-class{
// margin: 0 20rpx;
// padding: 2rpx;
background-color: #f9f9f9;
padding-bottom: 100rpx;
box-sizing: border-box; /* 保证padding不会撑大容器 */
min-height: 110vh;
overflow-y: auto;
.nar-top{
height: 400rpx;
width: 100%;
// background-color: #C0E4FB;
background: repeating-linear-gradient(to right, #EDF4FA , #C0E4FB);
position: absolute;
}
.status_bar {
height: 46px;
width: 100%;
}
/* 导航栏 */
.nav-bar {
height: 44px;
line-height: 44px;
text-align: center;
}
.nav-title {
font-size: 14px;
font-weight: 400;
}
.header-container{
display: flex;
align-items: center;
justify-content: space-between;
padding: 4rpx 30rpx;
box-sizing: border-box;
// border-bottom: 1rpx solid #f0f0f0;
position:absolute;
top: 85px;
left: 0;
z-index: 9999;
}
// 左侧区域
.location-area {
display: flex;
align-items: center;
position: relative;
flex: 1;
min-width: 0;
.location-icon {
width: 45rpx;
height: 45rpx;
margin-right: 12rpx;
flex-shrink: 0;
}
.community-selector {
display: flex;
align-items: center;
max-width: 300rpx;
cursor: pointer;
.community-name {
font-size: 26rpx;
font-weight: 500;
color: #000;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 200rpx;
}
}
}
.bannarClass{
position: relative;
top: 110px;
left: 0;
padding: 20rpx;
}
.noticeClass{
position: relative;
top: 205rpx;
left: 0;
padding: 20rpx;
}
.noticeClass.uni-noticebar{
padding: 10px 15px 10px 45px;
}
.notice-bar::after {
color: #3E8EFF;
}
.bg-card-open{
position: relative;
top: 180rpx;
left: 0;
padding: 20rpx;
background-color: #fff;
border-radius: 8rpx;
margin: 9px;
}
.openClass{
display: flex;
flex-direction: column;
align-items: center;
margin-top: 20rpx;
.iconClass{
width: 130rpx;
height: 130rpx;
border-radius: 10rpx;
// opacity: 0.4;
}
.title-dome-calss{
margin-top: 10rpx;
font-size: 13px;
color: #000;
}
}
.adCardClass{
// width: 100%;
height: 200rpx;
margin: 17rpx;
background-color: #3E8EFF;
// margin-top: 30rpx;
border-radius: 20rpx;
position: relative;
top:99px;
left: 0;
.titleLeft{
text-align: left;
line-height: 2;
// margin-top: 32rpx;
// margin-left: 30rpx;
}
.iconRightClass{
width: 120rpx;
height: 120rpx;
border-radius: 50%;
// margin-top: 20rpx;
margin: 36rpx;
}
}
.activity-card{
position: relative;
top: 102px;
left: 0;
bottom: 20;
}
.eventListClass{
margin: 20rpx;
display: flex;
justify-content: space-between;
align-items: center;
}
.activity-name{
font-size: 16px;
font-weight: 600;
color: #222;
}
.eventListCard{
// min-height:200rpx ;
position: relative;
margin: 30rpx 0rpx;
// overflow: hidden;
.status-tag {
position: absolute;
top: 0;
left: 0;
// padding: 6px 18px;
padding: 0px 9px 10px 11px;
border-radius: 10px 0px 10px 0px;
font-size: 12px;
color: #fff;
font-weight: 500;
width: 80rpx;
height: 20rpx;
// background-color: #FD7373 ;
background: repeating-linear-gradient(to right, #FD7373 , #FB3F3F);
}
.activity-img{
width: 170rpx;
height: 230rpx;
// background-color: #333333;
}
.activity-title{
font-size: 16px;
font-weight: bold;
color: #333;
margin-bottom: 12px;
line-height: 1.4;
}
.activity-time,
.activity-organizer {
display: flex;
align-items: center;
font-size: 14px;
color: #999999;
margin-bottom: 8px;
}
.activity-enrollment{
display: flex;
align-items: center;
font-size: 15px;
margin-top: 20rpx;
color: #666666;
}
}
.look-more-title{
color: #3E8EFF;
font-size: 14px;
}
}
</style>

View File

@@ -0,0 +1,325 @@
<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/login/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(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);
}
/* 验证码输入项(特殊布局) */
.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;
}
/* 协议勾选项 */
.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;
}
/* 底部链接 */
.bottom-link {
display: flex;
justify-content: space-between;
padding: 0 20rpx;
}
/* 链接项 */
.link-item {
font-size: 28rpx;
color: #007aff;
text-decoration: none;
}
</style>

View File

@@ -0,0 +1,260 @@
<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/login/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 = async () => {
if (!canLogin.value) return;
// 防止多次点击触发登录
canLogin.value = false;
// // 2. 空值校验
// if (!phoneNumber.value) {
// uni.showToast({ title: '请输入手机号', icon: 'none' });
// canLogin.value = true; // 解锁
// return;
// }
// if (!password.value) {
// uni.showToast({ title: '请输入密码', icon: 'none' });
// canLogin.value = true; // 解锁
// return;
// }
const params = {
username: phoneNumber.value,
password: password.value
};
try {
// 3. 调用登录接口
const res = await getLoginPassword(params);
// 登录成功逻辑
// 如果后端返回token这里保存
// uni.setStorageSync('token', res.token || '');
uni.showToast({ title: '登录成功', icon: 'success' });
// 登录成功后再跳转
setTimeout(() => {
uni.switchTab({ url: '/pages/index/index' });
}, 1500);
} catch (err) {
// 登录失败逻辑(所有错误:账号不存在/密码错/网络异常都走这里)
uni.showToast({ title: `${err || '登录失败'}`, icon: 'none' });
} finally {
// 无论成功/失败,解锁按钮
canLogin.value = true;
}
};
// 登录逻辑
// const handleLogin = () => {
// if (!canLogin.value) return
// let params = {
// username:phoneNumber.value,
// password:password.value
// }
// 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' });
// }
// })
// };
</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;
}
.bottom-link {
display: flex;
justify-content: space-between;
padding: 0 20rpx;
}
.link-item {
font-size: 28rpx;
color: #007aff;
text-decoration: none;
}
</style>

View File

@@ -0,0 +1,185 @@
<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;
}
</style>

View File

@@ -0,0 +1,219 @@
<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;
}
</style>

View File

@@ -0,0 +1,365 @@
<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/login/pwd-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;
}
.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

@@ -0,0 +1,738 @@
<template>
<view class="contentStyle" style="">
<view class="status-bar"></view>
<uni-nav-bar :title="type+'车辆'" left-icon="left" @clickLeft="goBack" backgroundColor="transparent"
:border="false">
</uni-nav-bar>
<scroll-view class="page" scroll-y="true">
<!-- 表单区域 -->
<view class="form-container">
<!-- 车牌号 -->
<view class="form-item">
<view class="item-label">车牌号</view>
<input class="item-input" v-model="formData.plateNo" placeholder="请输入"
placeholder-class="input-placeholder" @blur="inputChange('车牌号')" />
</view>
<!-- 车辆品牌选择 -->
<view class="form-item" @click="selectItem('车辆品牌')">
<text class="item-label">车辆品牌</text>
<text class="item-placeholder"
:class="{selected:formData.carBrandName}">{{ formData.carBrandName || '请选择' }}</text>
<text class="item-arrow"></text>
</view>
<!-- 车辆型号 -->
<view class="form-item">
<view class="item-label">车辆型号</view>
<input class="item-input" v-model="formData.model" placeholder="请输入"
placeholder-class="input-placeholder" @blur="inputChange('车辆型号')" />
</view>
<!-- 车辆颜色 -->
<view class="form-item">
<view class="item-label">车辆颜色</view>
<input class="item-input" v-model="formData.color" placeholder="请输入"
placeholder-class="input-placeholder" @blur="inputChange('车辆颜色')" />
</view>
<!-- 车辆图片上传 -->
<view class="form-item upload-item">
<view class="item-label">车辆图片</view>
<view class="upload-box" @click="chooseImage">
<!-- 未上传显示加号+文字 -->
<view v-if="!formData.carImg" class="upload-tip">
<text class="plus-icon">+</text>
<text class="upload-text">上传照片</text>
</view>
<!-- 已上传显示图片 -->
<image v-else :src="formData.carImg" class="car-image" mode="aspectFill" />
</view>
</view>
</view>
</scroll-view>
<!-- 底部下一步按钮 -->
<view class="bottom-btn-wrap">
<button class="next-btn" @click="saveCar">保存</button>
</view>
</view>
</template>
<script>
export default {
onReady: function(e) {},
components: {
},
computed: {
},
created() {
},
onShow() {
},
onLoad(option) {
if (uni.getStorageSync('operationType') == 'update') {
this.id = option.id;
this.type = '修改';
//根据id获取回显数据
this.formData.plateNo = '京A88888';
this.formData.carBrandName = '宝马';
this.formData.model = 'X5';
this.formData.color = '白色';
this.formData.carImg = '/static/propertyImgs/carImg.png';
this.formData.carBrandId = '1';
} else if (uni.getStorageSync('operationType') == 'add') {
this.id = undefined;
this.type = '添加';
}
},
onUnload() {},
mounted() {},
data() {
return {
/* 设定状态栏默认高度 */
statusBarHeight: 20,
id: undefined,
type: '',
// 表单数据
formData: {
plateNo: uni.getStorageSync('plateNo'),
carBrandName: uni.getStorageSync('carBrandName'),
model: uni.getStorageSync('model'),
color: uni.getStorageSync('color'),
carImg: uni.getStorageSync('carImg') // 车辆图片路径
}
}
},
methods: {
inputChange(type) {
if (type == '车牌号') {
uni.setStorageSync('plateNo', this.formData.plateNo)
} else if (type == '车辆型号') {
uni.setStorageSync('model', this.formData.model)
} else if (type == '车辆颜色') {
uni.setStorageSync('color', this.formData.color)
}
},
// 选择图片(上传照片)
chooseImage() {
uni.chooseImage({
count: 1, // 只选1张
sizeType: ["original", "compressed"],
sourceType: ["album", "camera"], // 相册/相机
success: (res) => {
// 赋值图片路径
uni.setStorageSync('carImg', res.tempFilePaths[0]);
this.formData.carImg = res.tempFilePaths[0];
}
});
},
// 保存车辆信息
saveCar() {
// 表单校验
if (!this.formData.plateNo) {
return uni.showToast({
title: "请输入车牌号",
icon: "none"
});
}
if (!this.formData.carBrandName) {
return uni.showToast({
title: "请输入车辆品牌",
icon: "none"
});
}
if (!this.formData.model) {
return uni.showToast({
title: "请输入车辆型号",
icon: "none"
});
}
if (!this.formData.color) {
return uni.showToast({
title: "请输入车辆颜色",
icon: "none"
});
}
uni.showModal({
title: '确认提交',
content: '保存后进入审核',
success: (res) => {
uni.showLoading({
title: '提交中...',
mask: true
});
setTimeout(() => {
uni.showToast({
title: '提交成功',
icon: 'success'
});
}, 1000);
setTimeout(() => {
// 提交成功后跳转列表页
uni.hideLoading();
this.goBack();
}, 3000);
}
})
},
// 选择项点击事件可替换为picker/弹窗选择)
selectItem(type) {
if (type === '车辆品牌') {
uni.redirectTo({
url: '/pages/my/selectCarBrand',
success: res => {},
fail: () => {},
complete: () => {}
});
}
},
goBack() {
uni.removeStorageSync('plateNo');
uni.removeStorageSync('carBrandName');
uni.removeStorageSync('model');
uni.removeStorageSync('color');
uni.removeStorageSync('carImg');
uni.removeStorageSync('carBrandId');
uni.removeStorageSync('checkType');
// if (uni.getStorageSync('operationType') == 'update') {
// uni.redirectTo({
// url: '/pages/my/houseDetail?id=' + this.id,
// success: res => {},
// fail: () => {},
// complete: () => {}
// });
// } else if (uni.getStorageSync('operationType') == 'add') {
uni.redirectTo({
url: '/pages/my/myCarIndex',
success: res => {},
fail: () => {},
complete: () => {}
});
// }
},
},
}
</script>
<style lang="scss">
page {
background: #f9f9f9;
}
</style>
<style>
/* 全局样式去掉switch的×保证开关纯净 */
.my-switch::before {
display: none !important;
}
.my-switch {
background-color: #e5e5e5 !important;
border: none !important;
width: 90rpx !important;
height: 48rpx !important;
border-radius: 50rpx !important;
}
.my-switch[checked="true"] {
background-color: #1677ff !important;
}
.my-switch::after {
width: 44rpx !important;
height: 44rpx !important;
border-radius: 50% !important;
margin: 2rpx !important;
}
</style>
<style scoped>
.status-bar {
width: 100vw;
height: 46px;
}
.contentStyle {
display: flex;
flex-direction: column;
height: 100vh;
/* 关键:占满屏幕高度 */
background-color: #f9f9f9;
}
.page {
flex: 1;
overflow-y: auto;
padding: 0px 20px;
box-sizing: border-box;
background-color: #fff;
}
/* 步骤条 */
.step-bar {
display: flex;
align-items: center;
justify-content: center;
padding: 40rpx 15px 20rpx;
}
.step-item {
display: flex;
flex-direction: column;
align-items: center;
}
.step-circle {
width: 80rpx;
height: 80rpx;
border-radius: 50%;
background-color: #ccc;
color: #fff;
display: flex;
align-items: center;
justify-content: center;
font-size: 14px;
margin-bottom: 16rpx;
}
.step-item.active .step-circle {
background-color: #1677ff;
}
.step-text {
font-size: 14px;
color: #999;
}
.step-item.active .step-text {
color: #1677ff;
font-weight: 500;
}
.step-line {
width: 150rpx;
height: 4rpx;
background-color: #ccc;
margin: 0 20rpx;
margin-bottom: 40rpx;
}
/* 表单容器 */
.form-container {
padding: 0 0px;
/* height: calc(100vh - 220px); */
/* overflow-y: scroll; */
}
/* 表单项 */
.form-item {
display: flex;
align-items: center;
padding: 15px 0;
border-bottom: 1rpx solid #f0f0f0;
}
.item-label {
font-size: 14px;
color: #000;
width: 90px;
font-weight: 600;
}
.item-placeholder {
flex: 1;
font-size: 14px;
color: #999;
}
.item-placeholder.selected {
flex: 1;
font-size: 14px;
color: #333;
}
.item-arrow {
display: inline-block;
width: 5px;
height: 5px;
border-top: 2rpx solid #999;
border-right: 2rpx solid #999;
transform: rotate(45deg);
margin-left: 10rpx;
margin-right: 5px;
}
/* 开关项 */
.switch-item {
justify-content: space-between;
}
/* 底部按钮 */
.bottom-btn-wrap {
padding: 10px 15px 20px;
display: flex;
gap: 20rpx;
background-color: #fff;
}
.next-btn {
width: 100%;
height: 96rpx;
line-height: 96rpx;
background-color: #1677ff;
color: #fff;
font-size: 36rpx;
border-radius: 12rpx;
border: none;
}
</style>
<style>
/* 注意:这里不能加 scoped必须写全局样式 */
/* 隐藏 switch 关闭状态的 × 号 */
.no-cross-switch::before {
display: none !important;
}
.no-cross-switch {
transform: scale(0.85);
border-radius: 50rpx !important;
}
.no-cross-switch>>>.uni-switch-input-checked {
background-color: #007aff !important;
}
</style>
<style scoped>
/* 表单项 —— 和你页面保持一致 */
.form-item {
display: flex;
align-items: center;
padding: 15px 0;
border-bottom: 1rpx solid #f0f0f0;
}
.label {
font-size: 14px;
color: #000;
width: 90px;
font-weight: 600;
}
.value {
flex: 1;
text-align: left;
font-size: 14px;
color: #999;
margin-right: 10rpx;
}
.arrow {
font-size: 28rpx;
color: #ccc;
}
/* 遮罩 */
.mask {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
z-index: 99;
}
/* 底部弹出框 */
.popup {
position: fixed;
left: 0;
right: 0;
bottom: 0;
background: #fff;
border-radius: 20rpx 20rpx 0 0;
z-index: 100;
max-height: 60vh;
}
.popup-header {
padding: 15px;
text-align: center;
border-bottom: 1rpx solid #f0f0f0;
position: relative;
}
.title {
font-size: 32rpx;
font-weight: 500;
}
.close {
position: absolute;
right: 15px;
top: 50%;
transform: translateY(-50%);
font-size: 36rpx;
color: #666;
}
/* 选项列表 */
.list {
padding: 0 15px;
}
.item {
padding: 15px 0;
text-align: center;
font-size: 14px;
border-bottom: 1rpx solid #f5f5f5;
}
.item:last-child {
border-bottom: none;
}
</style>
<style scoped>
/* 表单 */
.form-item {
display: flex;
align-items: center;
padding: 28rpx 0;
border-bottom: 1rpx solid #f2f2f2;
position: relative;
}
.label {
width: 180rpx;
font-size: 14px;
color: #333;
}
.input {
flex: 1;
font-size: 14px;
text-align: left;
color: #333;
}
.ph {
color: #999 !important;
}
.arrow-item::after {
content: '>';
position: absolute;
right: 0;
color: #ccc;
font-size: 28rpx;
}
.value {
flex: 1;
text-align: left;
font-size: 14px;
color: #999;
padding-right: 40rpx;
}
.item-input {
flex: 1;
height: 40rpx;
font-size: 14px;
color: #333;
border: none;
outline: none;
background: transparent;
}
.input-placeholder {
color: #999 !important;
font-size: 14px !important;
}
/* ===================== 图片上传区域 ===================== */
.upload-item {
align-items: flex-start;
padding-top: 40rpx;
padding-bottom: 40rpx;
border-bottom: none;
}
.upload-box {
width: 280rpx;
height: 280rpx;
border: 2rpx solid #e5e5e5;
border-radius: 8rpx;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
background-color: #f9f9f9;
}
.upload-tip {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.plus-icon {
font-size: 80rpx;
color: #999;
line-height: 1;
margin-bottom: 10rpx;
}
.upload-text {
font-size: 28rpx;
color: #999;
}
.car-image {
width: 100%;
height: 100%;
border-radius: 8rpx;
}
/* 弹窗遮罩 */
.mask {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
z-index: 99;
}
.popup {
position: fixed;
left: 0;
right: 0;
bottom: 0;
background: #fff;
border-radius: 20rpx 20rpx 0 0;
z-index: 100;
max-height: 60vh;
}
.popup-header {
padding: 15px;
text-align: center;
border-bottom: 1rpx solid #eee;
font-size: 32rpx;
font-weight: 500;
position: relative;
}
.close {
position: absolute;
right: 15px;
top: 50%;
transform: translateY(-50%);
font-size: 36rpx;
color: #666;
}
.popup-list {
padding: 0 15px;
max-height: 40vh;
overflow-y: auto;
}
.item {
padding: 15px 0;
text-align: center;
font-size: 14px;
border-bottom: 1rpx solid #f5f5f5;
}
/* 成功提示弹窗 */
.toast-mask {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
display: flex;
align-items: center;
justify-content: center;
background-color: transparent;
z-index: 9999;
}
.toast-content {
background-color: rgba(0, 0, 0, 0.8);
padding: 40rpx 60rpx;
border-radius: 8rpx;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.toast-icon {
width: 60rpx;
height: 60rpx;
line-height: 60rpx;
border-radius: 50%;
background-color: #fff;
color: #1677ff;
font-size: 40rpx;
font-weight: bold;
margin-bottom: 20rpx;
display: flex;
align-items: center;
justify-content: center;
}
.toast-text {
color: #fff;
font-size: 28rpx;
}
</style>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,913 @@
<template>
<view class="contentStyle" style="">
<view class="status-bar"></view>
<uni-nav-bar :title="type+'车位'" left-icon="left" @clickLeft="goBack" backgroundColor="transparent"
:border="false">
</uni-nav-bar>
<scroll-view class="page" scroll-y="true">
<!-- 表单区域 -->
<view class="form-container">
<!-- 第一步 -->
<view>
<!-- 小区选择 -->
<view class="form-item" @click="selectItem('小区')">
<text class="item-label">小区</text>
<text class="item-placeholder"
:class="{selected:formData.community}">{{ formData.community || '请选择' }}</text>
<text class="item-arrow"></text>
</view>
<!-- 房屋选择 -->
<view class="form-item" @click="selectItem('绑定房屋')">
<text class="item-label">绑定房屋</text>
<text class="item-placeholder"
:class="{selected:formData.bindHouse}">{{ formData.bindHouse || '请选择' }}</text>
<text class="item-arrow"></text>
</view>
<!-- 停车场选择 -->
<view class="form-item" @click="selectItem('停车场')">
<text class="item-label">停车场</text>
<text class="item-placeholder"
:class="{selected:formData.parkingLot}">{{ formData.parkingLot || '请选择' }}</text>
<text class="item-arrow"></text>
</view>
<!-- 车位编号选择 -->
<view class="form-item" @click="selectItem('车位编号')">
<text class="item-label">车位编号</text>
<text class="item-placeholder"
:class="{selected:formData.parkingSpaceNum}">{{ formData.parkingSpaceNum || '请选择' }}</text>
<text class="item-arrow"></text>
</view>
<!-- 车位状态选择 -->
<view class="form-item">
<text class="label">车位状态</text>
<view class="parkingSpaceStatus-group">
<view class="parkingSpaceStatus-item"
:class="{ active: formData.parkingSpaceStatus == '自用' }"
@click="formData.parkingSpaceStatus = '自用'">
<view class="radio"></view>
<text>自用</text>
</view>
<view class="parkingSpaceStatus-item"
:class="{ active: formData.parkingSpaceStatus == '租赁' }"
@click="formData.parkingSpaceStatus = '租赁'">
<view class="radio"></view>
<text>租赁</text>
</view>
</view>
</view>
<!-- 绑定车辆选择 -->
<view class="form-item" @click="bindCarClick('绑定车辆')">
<text class="item-label">绑定车辆</text>
<text class="item-placeholder"
:class="{selected:formData.bindCar}">{{ formData.bindCar || '请选择' }}</text>
<text class="item-arrow"></text>
</view>
</view>
</view>
</scroll-view>
<!-- 底部下一步按钮 -->
<view class="bottom-btn-wrap">
<button class="next-btn" @click="goSubmit">提交审核</button>
</view>
</view>
</template>
<script>
export default {
onReady: function(e) {},
components: {
},
computed: {
},
created() {
},
onShow() {
},
onLoad(option) {
if (uni.getStorageSync('operationType') == 'update') {
this.id = option.id;
this.type = '修改';
//根据id获取回显数据
this.formData.community = '北境碧桂园小区';
this.formData.bindHouse = '01栋';
this.formData.parkingLot = '地下停车场';
this.formData.parkingSpaceNum = 'B1-02';
this.formData.bindCar = '宝马';
this.formData.communityId = '1';
this.formData.bindHouseId = '2';
this.formData.parkingLotId = '3';
this.formData.parkingSpaceId = '4';
this.formData.bindCarId = '自用';
this.formData.parkingSpaceStatus = '租赁';
} else if (uni.getStorageSync('operationType') == 'add') {
this.id = undefined;
this.type = '添加';
}
},
onUnload() {},
mounted() {},
data() {
return {
/* 设定状态栏默认高度 */
statusBarHeight: 20,
id: undefined,
type: '',
formData: {
community: uni.getStorageSync('communityName'),
communityId: uni.getStorageSync('communityId'),
bindHouse: uni.getStorageSync('bindHouseName'),
bindHouseId: uni.getStorageSync('bindHouseId'),
parkingLot: uni.getStorageSync('parkingLotName'),
parkingLotId: uni.getStorageSync('parkingLotId'),
parkingSpaceNum: uni.getStorageSync('parkingSpaceName'),
parkingSpaceId: uni.getStorageSync('parkingSpaceId'),
parkingSpaceStatus: '自用',
bindCar: uni.getStorageSync('bindCarName'),
bindCarId: uni.getStorageSync('bindCarId'),
},
}
},
methods: {
// 完整校验
goSubmit() {
if (!this.formData.community) return uni.showToast({
title: '请选择小区',
icon: 'none'
})
if (!this.formData.bindHouse) return uni.showToast({
title: '请绑定房屋',
icon: 'none'
})
if (!this.formData.parkingLot) return uni.showToast({
title: '请选择停车场',
icon: 'none'
})
if (!this.formData.parkingSpaceNum) return uni.showToast({
title: '请选择车位编号',
icon: 'none'
})
if (!this.formData.bindCar) return uni.showToast({
title: '请选择绑定车辆',
icon: 'none'
})
uni.showModal({
title: '确认提交',
content: '提交后进入审核',
success: (res) => {
if (res.confirm) {
uni.showLoading({
title: '提交中...',
mask: true
});
setTimeout(() => {
uni.showToast({
title: '提交成功',
icon: 'success'
});
}, 1000);
setTimeout(() => {
// 提交成功后跳转列表页
uni.hideLoading();
this.goBack();
}, 3000);
}
}
})
},
selectClick(item) {
this.formData.status = item;
uni.setStorageSync('houseStatus', item)
this.showPopup = false;
},
bindCarClick() {
uni.redirectTo({
url: '/pages/my/bindCar',
success: res => {},
fail: () => {},
complete: () => {}
});
},
// 选择项点击事件可替换为picker/弹窗选择)
selectItem(type) {
if (type === '小区') {
uni.redirectTo({
url: '/pages/my/selectResidentialCommunity?sourcePage=' + 'addParkingSpace',
success: res => {},
fail: () => {},
complete: () => {}
});
}
if (type === '绑定房屋') {
if (!this.formData.community) {
uni.showToast({
title: '请先选择上级',
icon: 'none'
})
} else {
uni.redirectTo({
url: '/pages/my/bindHouse',
success: res => {},
fail: () => {},
complete: () => {}
});
}
}
if (type === '停车场') {
if (!this.formData.community) {
uni.showToast({
title: '请先选择上级',
icon: 'none'
})
} else {
uni.setStorageSync('checkType', '停车场')
uni.redirectTo({
url: '/pages/my/selectParingLot',
success: res => {},
fail: () => {},
complete: () => {}
});
}
}
if (type === '车位编号') {
if (!this.formData.community) {
uni.showToast({
title: '请先选择上级',
icon: 'none'
})
} else {
uni.setStorageSync('checkType', '车位编号')
uni.redirectTo({
url: '/pages/my/selectParingLot',
success: res => {},
fail: () => {},
complete: () => {}
});
}
}
},
// 开关切换
onSwitchChange(e) {
this.formData.isDefault = e.detail.value
uni.setStorageSync('houseIsDefault', e.detail.value)
},
// 下一步
goNext() {
// 基础校验
if (!this.formData.community) {
uni.showToast({
title: '请选择小区',
icon: 'none'
})
return
}
if (!this.formData.building) {
uni.showToast({
title: '请选择楼栋',
icon: 'none'
})
return
}
if (!this.formData.unit) {
uni.showToast({
title: '请选择单元',
icon: 'none'
})
return
}
if (!this.formData.floor) {
uni.showToast({
title: '请选择楼层',
icon: 'none'
})
return
}
if (!this.formData.houseNumber) {
uni.showToast({
title: '请选择户号',
icon: 'none'
})
return
}
if (!this.formData.status) {
uni.showToast({
title: '请选择房屋状态',
icon: 'none'
})
return
}
this.currentStep = 1
//跳转
},
goBack() {
uni.removeStorageSync('communityId');
uni.removeStorageSync('communityName');
uni.removeStorageSync('bindHouseId');
uni.removeStorageSync('bindHouseName');
uni.removeStorageSync('checkType');
uni.removeStorageSync('parkingLotId');
uni.removeStorageSync('parkingLotName');
uni.removeStorageSync('parkingSpaceId');
uni.removeStorageSync('parkingSpaceName');
uni.removeStorageSync('bindCarId');
uni.removeStorageSync('bindCarName');
uni.redirectTo({
url: '/pages/my/myParkingSpaceIndex',
success: res => {},
fail: () => {},
complete: () => {}
});
},
},
}
</script>
<style lang="scss">
page {
background: #f9f9f9;
}
</style>
<style>
/* 全局样式去掉switch的×保证开关纯净 */
.my-switch::before {
display: none !important;
}
.my-switch {
background-color: #e5e5e5 !important;
border: none !important;
width: 90rpx !important;
height: 48rpx !important;
border-radius: 50rpx !important;
}
.my-switch[checked="true"] {
background-color: #1677ff !important;
}
.my-switch::after {
width: 44rpx !important;
height: 44rpx !important;
border-radius: 50% !important;
margin: 2rpx !important;
}
</style>
<style scoped>
/* 页面整体 */
.contentStyle {
display: flex;
flex-direction: column;
height: 100vh;
/* 关键:占满屏幕高度 */
background-color: #f9f9f9;
}
.page {
flex: 1;
overflow-y: auto;
padding: 0px 20px;
box-sizing: border-box;
background-color: #fff;
}
/* 步骤条 */
.step-bar {
display: flex;
align-items: center;
justify-content: center;
padding: 40rpx 15px 20rpx;
}
.step-item {
display: flex;
flex-direction: column;
align-items: center;
}
.step-circle {
width: 80rpx;
height: 80rpx;
border-radius: 50%;
background-color: #ccc;
color: #fff;
display: flex;
align-items: center;
justify-content: center;
font-size: 14px;
margin-bottom: 16rpx;
}
.step-item.active .step-circle {
background-color: #1677ff;
}
.step-text {
font-size: 14px;
color: #999;
}
.step-item.active .step-text {
color: #1677ff;
font-weight: 500;
}
.step-line {
width: 150rpx;
height: 4rpx;
background-color: #ccc;
margin: 0 20rpx;
margin-bottom: 40rpx;
}
/* 表单容器 */
.form-container {}
/* 表单项 */
.form-item {
display: flex;
align-items: center;
padding: 15px 0;
border-bottom: 1rpx solid #f0f0f0;
}
.item-label {
font-size: 14px;
color: #000;
width: 90px;
font-weight: 600;
}
.item-placeholder {
flex: 1;
font-size: 14px;
color: #999;
}
.item-placeholder.selected {
flex: 1;
font-size: 14px;
color: #333;
}
.item-arrow {
display: inline-block;
width: 5px;
height: 5px;
border-top: 2rpx solid #999;
border-right: 2rpx solid #999;
transform: rotate(45deg);
margin-left: 10rpx;
margin-right: 5px;
}
/* 开关项 */
.switch-item {
justify-content: space-between;
}
/* 底部按钮 */
.bottom-btn-wrap {
padding: 10px 15px 20px;
display: flex;
gap: 20rpx;
background-color: #fff;
}
.next-btn {
width: 100%;
height: 96rpx;
line-height: 96rpx;
background-color: #1677ff;
color: #fff;
font-size: 36rpx;
border-radius: 12rpx;
border: none;
}
</style>
<style>
/* 注意:这里不能加 scoped必须写全局样式 */
::v-deep .uni-switch-input:before {
background-color: #cccccd !important;
}
/* 隐藏 switch 关闭状态的 × 号 */
.no-cross-switch::before {
display: none !important;
}
.no-cross-switch {
transform: scale(0.85);
border-radius: 50rpx !important;
}
.no-cross-switch>>>.uni-switch-input-checked {
background-color: #007aff !important;
}
</style>
<style scoped>
/* 表单项 —— 和你页面保持一致 */
.form-item {
display: flex;
align-items: center;
padding: 15px 0;
border-bottom: 1rpx solid #f0f0f0;
}
.label {
font-size: 14px;
color: #000;
width: 90px;
font-weight: 600;
}
.value {
flex: 1;
text-align: left;
font-size: 14px;
color: #999;
margin-right: 10rpx;
}
.value.selected {
font-size: 14px;
color: #333;
}
.arrow {
font-size: 28rpx;
color: #ccc;
}
/* 遮罩 */
.mask {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
z-index: 99;
}
/* 底部弹出框 */
.popup {
position: fixed;
left: 0;
right: 0;
bottom: 0;
background: #fff;
border-radius: 20rpx 20rpx 0 0;
z-index: 100;
max-height: 60vh;
}
.popup-header {
padding: 15px;
text-align: center;
border-bottom: 1rpx solid #f0f0f0;
position: relative;
}
.title {
font-size: 32rpx;
font-weight: 500;
}
.close {
position: absolute;
right: 15px;
top: 50%;
transform: translateY(-50%);
font-size: 36rpx;
color: #666;
}
/* 选项列表 */
.list {
padding: 0 15px;
}
.item {
padding: 15px 0;
text-align: center;
font-size: 14px;
border-bottom: 1rpx solid #f5f5f5;
}
.item:last-child {
border-bottom: none;
}
</style>
<style scoped>
/* 表单 */
.form-item {
display: flex;
align-items: center;
padding: 28rpx 0;
border-bottom: 1rpx solid #f2f2f2;
position: relative;
}
.label {
width: 180rpx;
font-size: 14px;
color: #333;
}
.input {
flex: 1;
font-size: 14px;
text-align: left;
color: #333;
}
.ph {
color: #999 !important;
}
.arrow-item::after {
content: '>';
position: absolute;
right: 0;
color: #ccc;
font-size: 28rpx;
}
.value {
flex: 1;
text-align: left;
font-size: 14px;
color: #999;
padding-right: 40rpx;
}
/* 性别 */
.parkingSpaceStatus-group {
flex: 1;
display: flex;
justify-content: flex-start;
gap: 40rpx;
}
.parkingSpaceStatus-item {
display: flex;
align-items: center;
gap: 12rpx;
font-size: 14px;
color: #666;
}
.parkingSpaceStatus-item.active {
color: #1677ff;
}
.radio {
width: 32rpx;
height: 32rpx;
border-radius: 50%;
border: 2rpx solid #ddd;
position: relative;
}
.parkingSpaceStatus-item.active .radio::after {
content: '';
width: 18rpx;
height: 18rpx;
background: #1677ff;
border-radius: 50%;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
/* 手机验证码 */
.phone-item .input {
padding-right: 200rpx;
}
.code-btn {
position: absolute;
right: 0;
/* background: #f5f7fa; */
color: #1677ff;
font-size: 26rpx;
padding: 10rpx 20rpx;
border-radius: 8rpx;
}
/* 上传区域 */
.upload-section {
margin-top: 15px;
}
.upload-title {
font-size: 14px;
font-weight: 500;
color: #333;
margin-bottom: 12rpx;
display: block;
font-weight: 600;
}
.upload-desc {
font-size: 12px;
color: #666666;
line-height: 1.5;
margin-bottom: 15px;
}
/* 两列布局 */
.upload-wrap {
margin-top: 15px;
display: flex;
flex-direction: column;
gap: 15px;
}
.upload-item {
/* flex: 1; */
height: 200px;
border-radius: 16rpx;
overflow: hidden;
background: #eff0ff;
position: relative;
}
/* 上传按钮 */
.upload-box {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.plus {
font-size: 50rpx;
color: #ccc;
margin-bottom: 12rpx;
}
.txt {
font-size: 26rpx;
color: #999;
}
/* 预览图 */
.preview-img {
width: 100%;
height: 100%;
object-fit: cover;
}
/* 弹窗遮罩 */
.mask {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
z-index: 99;
}
.popup {
position: fixed;
left: 0;
right: 0;
bottom: 0;
background: #fff;
border-radius: 20rpx 20rpx 0 0;
z-index: 100;
max-height: 60vh;
}
.popup-header {
padding: 15px;
text-align: center;
border-bottom: 1rpx solid #eee;
font-size: 32rpx;
font-weight: 500;
position: relative;
}
.close {
position: absolute;
right: 15px;
top: 50%;
transform: translateY(-50%);
font-size: 36rpx;
color: #666;
}
.popup-list {
padding: 0 15px;
max-height: 40vh;
overflow-y: auto;
}
.item {
padding: 15px 0;
text-align: center;
font-size: 14px;
border-bottom: 1rpx solid #f5f5f5;
}
/* 成功提示弹窗 */
.toast-mask {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
display: flex;
align-items: center;
justify-content: center;
background-color: transparent;
z-index: 9999;
}
.toast-content {
background-color: rgba(0, 0, 0, 0.8);
padding: 40rpx 60rpx;
border-radius: 8rpx;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.toast-icon {
width: 60rpx;
height: 60rpx;
line-height: 60rpx;
border-radius: 50%;
background-color: #fff;
color: #1677ff;
font-size: 40rpx;
font-weight: bold;
margin-bottom: 20rpx;
display: flex;
align-items: center;
justify-content: center;
}
.toast-text {
color: #fff;
font-size: 28rpx;
}
.status-bar {
width: 100vw;
height: 46px;
}
</style>

View File

@@ -0,0 +1,378 @@
<template>
<view class="contentStyle" style="">
<view class="status-bar"></view>
<uni-nav-bar title="绑定车辆" left-icon="left" @clickLeft="goBack" backgroundColor="transparent" :border="false">
</uni-nav-bar>
<!-- 表单区域 -->
<scroll-view class="page" scroll-y>
<view class="car-item" v-for="(item, index) in carList" :key="index" @click="handleItemClick(item,index)"
:class="{ checked: selectedIndex === index }">
<!-- 左侧圆形占位 -->
<image :src="item.imgSrc" mode="aspectFill" class="car-avatar" @error="item.imgSrc = defaultCarImg" />
<!-- 中间车辆信息 -->
<view class="car-info">
<view class="car-plate">{{ item.plate }}</view>
<view class="car-model">{{ item.model }}·{{ item.color }}</view>
</view>
<!-- 右侧状态标签 -->
<view class="status-tag" :class="{ bound: item.status === '已绑定', unbound: item.status === '未绑定' }">
{{ item.status }}
</view>
</view>
</scroll-view>
<!-- 底部确定绑定按钮 -->
<view class="bottom-btn">
<button class="confirm-btn" @click="confirmBind">确定绑定</button>
</view>
</view>
</template>
<script>
export default {
onReady: function(e) {},
components: {
},
computed: {
},
created() {
},
onShow() {
},
onUnload() {
},
mounted() {},
data() {
return {
/* 设定状态栏默认高度 */
statusBarHeight: 20,
defaultCarImg: "/static/propertyImgs/defaultCarImg.png",
searchKey: "",
selectedId: '',
selectedName: '',
selectedIndex: -1, // 当前选中的索引
// 模拟车辆数据
carList: [{
id: '1',
imgSrc: '/static/propertyImgs/carImg.png',
plate: "京A88888",
model: "宝马X5",
color: "白色",
status: "已绑定"
},
{
id: '2',
imgSrc: 'https://错误地址2.png',
plate: "京A88888",
model: "宝马X5",
color: "白色",
status: "未绑定"
},
{
id: '3',
imgSrc: 'https://错误地址3.png',
plate: "京A88888",
model: "宝马X5",
color: "白色",
status: "未绑定"
}
]
}
},
methods: {
// 选择绑定车辆
handleItemClick(item, index) {
this.selectedIndex = index;
this.selectedId = item.id;
this.selectedName = item.plate;
},
// 确定绑定
confirmBind() {
if (this.selectedIndex === -1) {
return uni.showToast({
title: "请先选择绑定车辆",
icon: "none",
});
}
uni.showModal({
title: "确认选择",
content: `确定绑定该车辆吗?`,
success: (res) => {
if (res.confirm) {
uni.showLoading({
title: '绑定中...',
mask: true
});
setTimeout(() => {
uni.showToast({
title: '绑定成功',
icon: 'success'
});
}, 1000);
setTimeout(() => {
// 提交成功后跳转列表页
uni.hideLoading();
uni.setStorageSync('bindCarId', this.selectedId);
uni.setStorageSync('bindCarName', this.selectedName);
this.goBack();
}, 3000);
}
},
});
},
goBack() {
uni.redirectTo({
url: '/pages/my/addParkingSpace',
success: res => {},
fail: () => {},
complete: () => {}
});
},
},
}
</script>
<style lang="scss">
page {
background: #f9f9f9;
overflow: hidden;
// height: 100vh;
}
</style>
<style scoped>
.contentStyle {
display: flex;
flex-direction: column;
height: 100vh;
/* 关键:占满屏幕高度 */
/* background-color: #fff; */
}
.page {
flex: 1;
overflow-y: auto;
padding: 0px 20px;
box-sizing: border-box;
}
/* 底部按钮 */
.bottom-btn {
padding: 10px 15px 20px;
}
/* 通用输入框样式 */
.input-item {
font-size: 30rpx;
color: #333;
}
.input-placeholder {
color: #999;
font-size: 30rpx;
}
/* 确定按钮 */
.submit-btn {
width: 100%;
height: 88rpx;
line-height: 88rpx;
background-color: #1677ff;
color: #fff;
font-size: 14px;
border-radius: 8rpx;
border: none;
margin-top: 20rpx;
}
/* 成功提示弹窗 */
.toast-mask {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
display: flex;
align-items: center;
justify-content: center;
background-color: transparent;
z-index: 9999;
}
.toast-content {
background-color: rgba(0, 0, 0, 0.8);
padding: 40rpx 60rpx;
border-radius: 8rpx;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.toast-icon {
width: 60rpx;
height: 60rpx;
line-height: 60rpx;
border-radius: 50%;
background-color: #fff;
color: #1677ff;
font-size: 16px;
font-weight: bold;
margin-bottom: 20rpx;
display: flex;
align-items: center;
justify-content: center;
}
.toast-text {
color: #fff;
font-size: 28rpx;
}
</style>
<style scoped>
/* 页面整体 */
/* ====================== 列表容器 ====================== */
.list-container {
/* flex: 1; */
padding: 20px;
min-height: calc(100vh - 105px);
overflow-y: scroll;
}
/* ====================== 绑定车辆卡片 ====================== */
/* 单个车辆项 */
.car-item {
display: flex;
align-items: center;
padding: 10px;
margin-bottom: 20rpx;
border: 1rpx solid #e6e9ff;
border-radius: 16rpx;
background-color: #fff;
position: relative;
}
.car-item.checked {
background: #f4f7ff;
/* 浅蓝色背景 */
border-color: #1677ff;
}
/* 左侧圆形头像占位 */
.car-avatar {
width: 100rpx;
height: 100rpx;
border-radius: 50%;
background-color: #f0f4ff;
margin-right: 24rpx;
flex-shrink: 0;
}
/* 中间信息区域 */
.car-info {
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
}
/* 车牌号 */
.car-plate {
font-size: 16px;
font-weight: bold;
color: #000;
line-height: 1.4;
margin-bottom: 8rpx;
}
/* 车型+颜色 */
.car-model {
font-size: 14px;
color: #666;
line-height: 1.4;
}
/* 状态标签 */
.status-tag {
font-size: 12px;
padding: 3px 7px;
border-radius: 3px;
font-weight: 500;
flex-shrink: 0;
position: absolute;
right: 15px;
top: 15px;
}
/* 未绑定状态(浅绿色背景+绿色文字) */
.status-tag.unbound {
/* background-color: #e6f7e6; */
color: #999999;
}
/* 已绑定状态(可扩展,比如蓝色) */
.status-tag.bound {
/* background-color: #e6f0ff; */
color: #1677ff;
}
/* ====================== 底部按钮 ====================== */
.confirm-btn {
width: 100%;
height: 88rpx;
background-color: #1677ff;
color: #fff;
border-radius: 12rpx;
font-size: 34rpx;
border: none;
line-height: 88rpx;
}
.status-bar {
width: 100vw;
height: 46px;
}
</style>

View File

@@ -0,0 +1,466 @@
<template>
<view class="contentStyle" style="">
<view class="status-bar"></view>
<uni-nav-bar title="绑定房屋" left-icon="left" @clickLeft="goBack" :border="false" backgroundColor="transparent">
</uni-nav-bar>
<!-- 表单区域 -->
<scroll-view class="page" scroll-y="true">
<!-- 搜索框 -->
<view class="search-box">
<input class="search-input" v-model="searchKey" placeholder="请输入名称"
placeholder-class="input-placeholder" @input="onSearch" />
<uni-icons type="search" size="20" color="#999" />
</view>
<!-- 房屋列表 -->
<scroll-view class="list-container" scroll>
<view class="house-card" v-for="(item, index) in filteredList" :key="index"
@click="selectHouse(item,index)" :class="{ checked: selectedIndex === index }">
<!-- 小区名称 -->
<view class="community-name">
<text class="title-line"></text>
<text class="title-text">{{ item.community }}</text>
</view>
<!-- 选中对勾 -->
<view class="check-icon" :class="{ checked: selectedIndex === index }"></view>
<!-- 房屋信息 -->
<view class="info-row">
<text class="info-label">房间号</text>
<text class="info-value">{{ item.roomNo }}</text>
</view>
<view class="info-row">
<text class="info-label">业主</text>
<text class="info-value">{{ item.owner }}</text>
</view>
<view class="info-row">
<text class="info-label">房屋状态</text>
<text class="info-value status-text" :style="{ color: statusColor[item.status] }">
{{ item.status }}
</text>
</view>
</view>
</scroll-view>
</scroll-view>
<!-- 底部确定绑定按钮 -->
<view class="bottom-btn">
<button class="confirm-btn" @click="confirmBind">确定绑定</button>
</view>
</view>
</template>
<script>
export default {
onReady: function(e) {},
components: {
},
computed: {
// 过滤后的列表(搜索功能)
filteredList() {
this.selectedIndex = -1
if (!this.searchKey.trim()) {
return this.houseList
}
return this.houseList.filter(item =>
item.community.includes(this.searchKey.trim())
)
},
},
created() {
},
onShow() {
},
onUnload() {
},
mounted() {},
data() {
return {
/* 设定状态栏默认高度 */
statusBarHeight: 20,
searchKey: "",
selectedId: '',
selectedName: '',
selectedIndex: -1, // 当前选中的索引
// 房屋状态颜色映射
statusColor: {
自住: "#409EFF",
闲置: "#67C23A",
租赁: "#E6A23C",
其他: "#909399",
},
// 模拟房屋数据
houseList: [{
id: 1,
community: "北境碧桂园小区(别墅)",
roomNo: "2号楼2层",
owner: "张三",
status: "自住",
},
{
id: 1,
community: "北境碧桂园小区(别墅)",
roomNo: "2号楼1层",
owner: "张三",
status: "闲置",
},
],
}
},
methods: {
// 搜索输入事件
onSearch() {
// 实时过滤computed 自动处理
},
// 选择房屋
selectHouse(item, index) {
this.selectedIndex = index;
this.selectedId = item.id;
this.selectedName = item.community + item.roomNo;
},
// 确定绑定
confirmBind() {
if (this.selectedIndex === -1) {
return uni.showToast({
title: "请先选择房屋",
icon: "none",
});
}
uni.showModal({
title: "确认选择",
content: `确定绑定 ${this.selectedName} 吗?`,
success: (res) => {
if (res.confirm) {
uni.showLoading({
title: '绑定中...',
mask: true
});
setTimeout(() => {
uni.showToast({
title: '绑定成功',
icon: 'success'
});
}, 1000);
setTimeout(() => {
// 提交成功后跳转列表页
uni.hideLoading();
uni.setStorageSync('bindHouseId', this.selectedId);
uni.setStorageSync('bindHouseName', this.selectedName);
uni.setStorageSync('checkType', '停车场');
uni.removeStorageSync('paringLotId');
uni.removeStorageSync('paringLotName');
uni.removeStorageSync('parkingSpaceId');
uni.removeStorageSync('parkingSpaceName');
this.goNext();
}, 3000);
}
},
});
},
goNext() {
uni.redirectTo({
url: '/pages/my/selectParingLot',
success: res => {},
fail: () => {},
complete: () => {}
});
},
goBack() {
uni.redirectTo({
url: '/pages/my/addParkingSpace',
success: res => {},
fail: () => {},
complete: () => {}
});
},
},
}
</script>
<style lang="scss">
page {
background: #f9f9f9;
overflow: hidden;
// height: 100vh;
}
</style>
<style scoped>
.contentStyle {
display: flex;
flex-direction: column;
height: 100vh;
/* 关键:占满屏幕高度 */
/* background-color: #fff; */
}
.page {
flex: 1;
overflow-y: auto;
padding: 0px 20px;
box-sizing: border-box;
}
/* 底部按钮 */
.bottom-btn {
padding: 10px 15px 20px;
}
/* 通用输入框样式 */
.input-item {
font-size: 30rpx;
color: #333;
}
.input-placeholder {
color: #999;
font-size: 30rpx;
}
/* 确定按钮 */
.submit-btn {
width: 100%;
height: 88rpx;
line-height: 88rpx;
background-color: #1677ff;
color: #fff;
font-size: 32rpx;
border-radius: 8rpx;
border: none;
margin-top: 20rpx;
}
/* 成功提示弹窗 */
.toast-mask {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
display: flex;
align-items: center;
justify-content: center;
background-color: transparent;
z-index: 9999;
}
.toast-content {
background-color: rgba(0, 0, 0, 0.8);
padding: 40rpx 60rpx;
border-radius: 8rpx;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.toast-icon {
width: 60rpx;
height: 60rpx;
line-height: 60rpx;
border-radius: 50%;
background-color: #fff;
color: #1677ff;
font-size: 40rpx;
font-weight: bold;
margin-bottom: 20rpx;
display: flex;
align-items: center;
justify-content: center;
}
.toast-text {
color: #fff;
font-size: 28rpx;
}
</style>
<style scoped>
/* 页面整体 */
/* ====================== 搜索框样式1:1还原 ====================== */
/* 搜索框 */
.search-box {
display: flex;
align-items: center;
background-color: #F3F3F3;
border-radius: 10px;
margin: 0px 0px 10px 0rpx;
padding: 0 30rpx;
height: 70rpx;
}
.search-input {
flex: 1;
height: 100%;
font-size: 30rpx;
border: none;
background: transparent;
}
.input-placeholder {
color: #999;
font-size: 30rpx;
}
.search-icon {
font-size: 36rpx;
color: #999;
}
/* ====================== 列表容器 ====================== */
.list-container {}
/* ====================== 房屋卡片 ====================== */
.house-card {
border: 1rpx solid #e5e5e5;
border-radius: 12rpx;
padding: 10px;
margin-bottom: 15px;
position: relative;
background-color: #fff;
}
.house-card.checked {
background: #f4f7ff;
border-color: #1677ff;
}
.community-name {
font-size: 16px;
font-weight: 600;
color: #333;
margin-bottom: 20rpx;
padding-bottom: 20rpx;
border-bottom: 1rpx solid #f0f0f0;
display: flex;
align-items: center;
}
.title-line {
width: 8rpx;
height: 36rpx;
background-color: #007AFF;
margin-right: 12rpx;
border-radius: 4rpx;
}
.title-text {
font-size: 16px;
font-weight: bold;
color: #333333;
}
.info-row {
display: flex;
align-items: center;
margin-bottom: 16rpx;
}
.info-label {
width: 160rpx;
font-size: 14px;
/* font-weight: 600; */
color: #000;
}
.info-value {
font-size: 14px;
color: #333;
/* font-weight: 600; */
}
.status-text {
font-weight: 500;
}
/* ====================== 选中对勾 ====================== */
.check-icon {
position: absolute;
right: 10px;
top: 23px;
transform: translateY(-50%);
width: 26px;
height: 26px;
border-radius: 50%;
border: 2rpx solid #ccc;
/* background-color: #ccc; */
}
.check-icon::after {
content: "✓";
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
color: #fff;
font-size: 30rpx;
font-weight: bold;
}
/* 选中状态:蓝色背景 */
.check-icon.checked {
background-color: #1677ff;
border-color: #1677ff;
}
.confirm-btn {
width: 100%;
height: 96rpx;
line-height: 96rpx;
background-color: #1677ff;
color: #fff;
font-size: 36rpx;
border-radius: 12rpx;
border: none;
}
.title-line {
width: 8rpx;
height: 36rpx;
background-color: #007AFF;
margin-right: 12rpx;
border-radius: 4rpx;
}
.status-bar {
width: 100vw;
height: 46px;
}
</style>

View File

@@ -0,0 +1,457 @@
<template>
<view class="contentStyle" style="">
<view class="status-bar"></view>
<uni-nav-bar title="车辆详情" left-icon="left" @clickLeft="goBack" backgroundColor="transparent" :border="false">
</uni-nav-bar>
<scroll-view class="page" scroll-y="true">
<!-- 车辆信息 -->
<view class="info-card">
<view class="card-title">
<view class="title-line"></view>
车辆信息
</view>
<view class="info-row">
<text class="info-label">车牌号</text>
<text class="info-value">{{form.plateNo}}</text>
</view>
<view class="info-row">
<text class="info-label">车辆品牌</text>
<text class="info-value">{{form.carBrandName}}</text>
</view>
<view class="info-row">
<text class="info-label">车辆型号</text>
<text class="info-value">{{form.model}}</text>
</view>
<view class="info-row">
<text class="info-label">车身颜色</text>
<text class="info-value">{{form.color}}</text>
</view>
<!-- 车辆图片区-->
<view class="photo-row">
<text class="info-label" style="width: 200rpx;">车辆图片</text>
<view class="photo-list">
<image class="photo-item" :src="form.carImg"></image>
</view>
</view>
</view>
</scroll-view>
<!-- 底部操作按钮 -->
<view class="bottom-btn-wrap">
<!-- 仅认证失败显示修改按钮 -->
<button class="btn-modify" @click="clickUpdate()">
修改
</button>
<button class="btn-delete" @click="onDelete">
删除
</button>
</view>
</view>
</template>
<script>
export default {
onReady: function(e) {},
components: {
},
computed: {
},
created() {
},
onShow() {
},
onLoad(option) {
this.id = option.id;
this.currentStatus = option.status;
},
mounted() {},
data() {
return {
/* 设定状态栏默认高度 */
statusBarHeight: 20,
currentStatus: '',
// 可通过页面传参动态修改状态success / pending / fail
id: undefined,
form: {
plateNo: '北境碧桂园小区小区',
carBrandName: '101栋2单元402',
model: '张三',
color: '租赁',
carImg: '/static/propertyImgs/carImg.png',
},
}
},
methods: {
// 修改按钮事件(仅认证失败显示)
clickUpdate() {
uni.setStorageSync('operationType', 'update')
uni.redirectTo({
url: '/pages/my/addCar?id=' + this.id,
success: res => {},
fail: () => {},
complete: () => {}
});
},
// 删除按钮事件
onDelete() {
uni.showModal({
title: "确认删除",
content: "确认删除此车辆信息吗?",
success: (res) => {
if (res.confirm) {
uni.showLoading({
title: '删除中...',
mask: true
});
setTimeout(() => {
uni.showToast({
title: '删除成功',
icon: 'success'
});
}, 1000);
setTimeout(() => {
// 提交成功后跳转列表页
uni.hideLoading();
this.goBack();
}, 3000);
}
},
});
},
goBack() {
uni.redirectTo({
url: '/pages/my/myCarIndex',
success: res => {},
fail: () => {},
complete: () => {}
});
},
},
}
</script>
<style lang="scss">
page {
background: #f9f9f9;
}
</style>
<style scoped>
.contentStyle {
display: flex;
flex-direction: column;
height: 100vh;
/* 关键:占满屏幕高度 */
background-color: #f5f7fa;
}
.page {
flex: 1;
overflow-y: auto;
padding: 0px 20px;
box-sizing: border-box;
}
/* 顶部状态区 */
.status-header {
display: flex;
flex-direction: column;
align-items: center;
padding: 40rpx 30rpx 30rpx;
}
.status-icon {
width: 80rpx;
height: 80rpx;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 20rpx;
}
.icon-success {
background-color: #e6f7ee;
border: 4rpx solid #00b42a;
}
.icon-pending {
background-color: #e6f0ff;
border: 4rpx solid #1677ff;
}
.icon-fail {
background-color: #ffe7e7;
border: 4rpx solid #f53f3f;
}
.icon-text {
font-size: 40rpx;
font-weight: bold;
color: inherit;
}
.icon-success .icon-text {
color: #00b42a;
}
.icon-pending .icon-text {
color: #1677ff;
}
.icon-fail .icon-text {
color: #f53f3f;
}
.status-title {
font-size: 30rpx;
font-weight: 500;
color: #333;
margin-bottom: 40rpx;
}
/* 步骤条 */
.step-bar {
display: flex;
align-items: center;
width: 100%;
justify-content: space-between;
padding: 0 20rpx;
}
.step-item {
display: flex;
flex-direction: column;
align-items: center;
position: relative;
flex: 1;
}
.step-circle {
width: 40rpx;
height: 40rpx;
border-radius: 50%;
background-color: #1677ff;
color: #fff;
display: flex;
align-items: center;
justify-content: center;
font-size: 24rpx;
z-index: 2;
}
.step-item.active .step-circle {
background-color: #1677ff;
}
.step-item:not(.active) .step-circle {
background-color: #e5e5e5;
color: #999;
}
.step-text {
font-size: 24rpx;
color: #666;
margin-top: 10rpx;
text-align: center;
}
.step-line {
position: absolute;
top: 20rpx;
left: 50%;
width: 100%;
height: 4rpx;
background-color: #e5e5e5;
z-index: 1;
}
.step-line.active {
background-color: #1677ff;
}
/* 信息卡片 */
.info-card {
background-color: #fff;
padding: 10px;
}
.card-title {
display: flex;
align-items: center;
font-size: 16px;
font-weight: 600;
color: #000;
margin-bottom: 30rpx;
}
.title-line {
width: 8rpx;
height: 28rpx;
background-color: #1677ff;
margin-right: 10rpx;
border-radius: 4rpx;
}
.info-row {
display: flex;
margin-bottom: 24rpx;
align-items: center;
}
.info-row:last-child {
margin-bottom: 0;
}
.info-label {
width: 180rpx;
font-size: 28rpx;
color: #000;
}
.info-value {
font-size: 28rpx;
color: #333;
flex: 1;
}
.status-tag {
color: #ff7d00;
font-weight: 500;
}
/* 身份证照片区 */
.photo-row {
margin-top: 30rpx;
display: flex;
flex-direction: column;
}
.photo-list {
display: flex;
flex-direction: column;
gap: 20rpx;
margin-top: 20rpx;
}
.photo-item {
width: 100%;
/* height: 170px; */
background-color: #f0f4ff;
border-radius: 8rpx;
}
/* 底部按钮区 */
.bottom-btn-wrap {
padding: 10px 15px 20px;
display: flex;
gap: 20rpx;
/* background-color: #fff; */
}
.btn-modify {
flex: 1;
height: 88rpx;
line-height: 88rpx;
background-color: #1677ff;
color: #fff;
font-size: 30rpx;
border-radius: 8rpx;
border: none;
}
.btn-delete {
flex: 1;
height: 88rpx;
line-height: 88rpx;
background-color: #fff;
color: #f53f3f;
border: 1rpx solid #f53f3f;
font-size: 30rpx;
border-radius: 8rpx;
}
.btn-delete.delete-only {
flex: 1;
}
/* 成功提示弹窗 */
.toast-mask {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
display: flex;
align-items: center;
justify-content: center;
background-color: transparent;
z-index: 9999;
}
.toast-content {
background-color: rgba(0, 0, 0, 0.8);
padding: 40rpx 60rpx;
border-radius: 8rpx;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.toast-icon {
width: 60rpx;
height: 60rpx;
line-height: 60rpx;
border-radius: 50%;
background-color: #fff;
color: #1677ff;
font-size: 40rpx;
font-weight: bold;
margin-bottom: 20rpx;
display: flex;
align-items: center;
justify-content: center;
}
.toast-text {
color: #fff;
font-size: 28rpx;
}
.status-bar {
width: 100vw;
height: 46px;
}
</style>

View File

@@ -0,0 +1,296 @@
<template>
<view class="contentStyle" style="">
<view class="status-bar"></view>
<uni-nav-bar title="更改手机号码" left-icon="left" @clickLeft="goBack" backgroundColor="transparent" :border="false">
</uni-nav-bar>
<scroll-view class="page" scroll-y>
<!-- 表单区域 -->
<view class="form-container">
<!-- 手机号输入框+86前缀 -->
<view class="input-group phone-input">
<image class="iconStyle" src="/static/propertyImgs/phoneIcon.png">📱</image>
<input class="input-item" type="number" v-model="phone" placeholder="请输入手机号码"
placeholder-class="input-placeholder" maxlength="11" />
</view>
<!-- 验证码输入框带获取验证码按钮 -->
<view class="input-group code-input" style="padding: 0 0 0 20rpx;">
<image class="iconStyle" src="/static/propertyImgs/codeIcon.png"></image>
<input class="input-item code-item" type="number" v-model="code" placeholder="请输入验证码"
placeholder-class="input-placeholder" maxlength="6" />
<button class="code-btn" :disabled="countdown > 0" @click="getCode">
{{ countdown > 0 ? `${countdown}s后重发` : '获取验证码' }}
</button>
</view>
</view>
</scroll-view>
<view class="bottom-btn">
<button class="submit-btn" @click="onSubmit">确定</button>
</view>
</view>
</template>
<script>
export default {
onReady: function(e) {},
components: {
},
computed: {
},
created() {
},
onShow() {
},
onUnload() {
// 页面卸载时清除定时器
if (this.timer) clearInterval(this.timer)
},
mounted() {},
data() {
return {
/* 设定状态栏默认高度 */
statusBarHeight: 20,
phone: '',
code: '',
countdown: 0,
timer: null,
}
},
methods: {
// 获取验证码
getCode() {
// 1. 手机号校验
const phoneReg = /^1[3-9]\d{9}$/
if (!this.phone) {
uni.showToast({
title: '请输入手机号',
icon: 'none'
})
return
}
if (!phoneReg.test(this.phone)) {
uni.showToast({
title: '手机号格式不正确',
icon: 'none'
})
return
}
// 2. 模拟发送验证码(替换为实际接口)
uni.showLoading({
title: '发送中...'
})
setTimeout(() => {
uni.hideLoading()
uni.showToast({
title: '验证码已发送',
icon: 'none'
})
// 3. 开启60秒倒计时
this.countdown = 60
this.timer = setInterval(() => {
if (this.countdown <= 1) {
clearInterval(this.timer)
this.countdown = 0
return
}
this.countdown--
}, 1000)
}, 800)
},
// 提交修改
onSubmit() {
// 1. 基础校验
const phoneReg = /^1[3-9]\d{9}$/
if (!this.phone) {
uni.showToast({
title: '请输入手机号',
icon: 'none'
})
return
}
if (!phoneReg.test(this.phone)) {
uni.showToast({
title: '手机号格式不正确',
icon: 'none'
})
return
}
if (!this.code) {
uni.showToast({
title: '请输入验证码',
icon: 'none'
})
return
}
uni.showLoading({
title: '更换中...',
mask: true
});
setTimeout(() => {
uni.showToast({
title: '更换成功',
icon: 'success'
});
}, 1000);
setTimeout(() => {
// 提交成功后跳转列表页
uni.hideLoading();
this.goBack();
}, 3000);
},
goBack() {
uni.redirectTo({
url: '/pages/my/settingIndex',
success: res => {},
fail: () => {},
complete: () => {}
});
},
},
}
</script>
<style lang="scss">
page {
background: #f9f9f9;
}
</style>
<style scoped>
.contentStyle {
display: flex;
flex-direction: column;
height: 100vh;
/* 关键:占满屏幕高度 */
background-color: #f9f9f9;
}
.page {
flex: 1;
overflow-y: auto;
padding: 0px 20px;
box-sizing: border-box;
/* background-color: #fff; */
}
/* 底部按钮 */
.bottom-btn {
padding: 10px 15px 20px;
}
/* 表单容器 */
.form-container {
/* padding: 60rpx 30rpx; */
}
/* 输入框组(手机号+验证码行) */
.input-group {
display: flex;
align-items: center;
height: 88rpx;
/* border: 1rpx solid #dcdcdc; */
border-radius: 8rpx;
margin-bottom: 30rpx;
padding: 0 20rpx;
box-sizing: border-box;
background-color: #fff;
}
.iconStyle {
width:25px;
height: 25px;
}
.phone-input .input-item {
flex: 1;
height: 100%;
line-height: 88rpx;
border: none;
padding-left: 20rpx;
}
/* 验证码输入框 */
.code-input .code-item {
flex: 1;
height: 100%;
line-height: 88rpx;
border: none;
font-size: 30rpx;
}
.code-btn {
font-size: 12px;
color: #fff;
background: #2F77FD;
border: none;
padding: 0;
line-height: 88rpx;
width: 100px;
}
.code-btn[disabled] {
color: #999;
}
/* 通用输入框样式 */
.input-item {
font-size: 30rpx;
color: #333;
background-color: #fff;
padding-left: 20rpx;
}
.input-placeholder {
color: #999;
font-size: 30rpx;
}
/* 确定按钮 */
.submit-btn {
width: 100%;
height: 88rpx;
line-height: 88rpx;
background-color: #1677ff;
color: #fff;
font-size: 32rpx;
border-radius: 8rpx;
border: none;
margin-top: 20rpx;
}
.status-bar {
width: 100vw;
height: 46px;
}
</style>

View File

@@ -0,0 +1,244 @@
<template>
<view class="contentStyle" style="">
<view class="status-bar"></view>
<uni-nav-bar title="修改密码" left-icon="left" @clickLeft="goBack" backgroundColor="transparent" :border="false">
</uni-nav-bar>
<scroll-view class="page" scroll-y>
<!-- 表单区域 -->
<view class="form-container">
<!-- 原密码 -->
<input class="input-item" type="password" v-model="oldPwd" placeholder="请输入原密码"
placeholder-class="input-placeholder" />
<!-- 新密码 -->
<input class="input-item" type="password" v-model="newPwd" placeholder="请输入新密码"
placeholder-class="input-placeholder" />
<!-- 确认新密码 -->
<input class="input-item" type="password" v-model="confirmPwd" placeholder="请再次输入新密码"
placeholder-class="input-placeholder" />
</view>
</scroll-view>
<view class="bottom-btn">
<button class="submit-btn" @click="onSubmit">确定</button>
</view>
</view>
</template>
<script>
export default {
onReady: function(e) {},
components: {
},
computed: {
},
created() {
},
onShow() {
},
onLoad() {
},
mounted() {},
data() {
return {
/* 设定状态栏默认高度 */
statusBarHeight: 20,
oldPwd: '',
newPwd: '',
confirmPwd: '',
}
},
methods: {
// 提交修改
onSubmit() {
// 1. 基础校验
if (!this.oldPwd) {
uni.showToast({
title: '请输入原密码',
icon: 'none'
})
return
}
if (!this.newPwd) {
uni.showToast({
title: '请输入新密码',
icon: 'none'
})
return
}
if (this.newPwd !== this.confirmPwd) {
uni.showToast({
title: '两次密码不一致',
icon: 'none'
})
return
}
uni.showLoading({
title: '修改中...',
mask: true
});
setTimeout(() => {
uni.showToast({
title: '修改成功',
icon: 'success'
});
}, 1000);
setTimeout(() => {
// 提交成功后跳转列表页
uni.hideLoading();
this.goBack();
}, 3000);
},
goBack() {
uni.redirectTo({
url: '/pages/my/settingIndex',
success: res => {},
fail: () => {},
complete: () => {}
});
},
},
}
</script>
<style lang="scss">
page {
background: #f9f9f9;
}
</style>
<style scoped>
.contentStyle {
display: flex;
flex-direction: column;
height: 100vh;
/* 关键:占满屏幕高度 */
background-color: #f9f9f9;
}
.page {
flex: 1;
overflow-y: auto;
padding: 0px 20px;
box-sizing: border-box;
/* background-color: #fff; */
}
/* 底部按钮 */
.bottom-btn {
padding: 10px 15px 20px;
}
/* 表单容器 */
.form-container {
/* padding: 40rpx 30rpx; */
}
/* 输入框样式 */
.input-item {
width: 100%;
height: 88rpx;
line-height: 88rpx;
padding: 0 20rpx;
margin-bottom: 30rpx;
/* border: 1rpx solid #dcdcdc; */
border-radius: 8rpx;
font-size: 30rpx;
color: #333;
box-sizing: border-box;
background-color: #fff;
}
/* 占位符样式 */
.input-placeholder {
color: #999;
font-size: 30rpx;
}
/* 确定按钮 */
.submit-btn {
width: 100%;
height: 88rpx;
line-height: 88rpx;
background-color: #1677ff;
color: #fff;
font-size: 32rpx;
border-radius: 8rpx;
border: none;
}
/* 成功提示弹窗 */
.toast-mask {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
display: flex;
align-items: center;
justify-content: center;
background-color: transparent;
z-index: 9999;
}
.toast-content {
background-color: rgba(0, 0, 0, 0.8);
padding: 40rpx 60rpx;
border-radius: 8rpx;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.toast-icon {
width: 60rpx;
height: 60rpx;
line-height: 60rpx;
border-radius: 50%;
background-color: #fff;
color: #1677ff;
font-size: 40rpx;
font-weight: bold;
margin-bottom: 20rpx;
display: flex;
align-items: center;
justify-content: center;
}
.toast-text {
color: #fff;
font-size: 28rpx;
}
.status-bar {
width: 100vw;
height: 46px;
}
</style>

View File

@@ -0,0 +1,542 @@
<template>
<view class="contentStyle" style="">
<view class="status-bar"></view>
<uni-nav-bar title="房屋详情" left-icon="left" @clickLeft="goBack" :border="false" backgroundColor="transparent">
</uni-nav-bar>
<scroll-view class="page" scroll-y="true">
<!-- 顶部状态区 -->
<view class="status-header" v-if="statusConfig">
<view class="status-icon" :class="statusConfig.iconClass">
<text class="icon-text">{{ statusConfig.iconText }}</text>
</view>
<text class="status-title">{{ statusConfig.title }}</text>
<!-- 步骤条 -->
<view class="step-bar">
<view class="step-item" v-for="(step, index) in stepList" :key="index"
:class="{ active: index < currentStep, done: index < currentStep - 1 }">
<view class="step-circle">{{ index + 1 }}</view>
<text class="step-text">{{ step }}</text>
<view class="step-line" v-if="index < stepList.length - 1"
:class="{ active: index < currentStep - 1 }"></view>
</view>
</view>
</view>
<!-- 房屋信息 -->
<view class="info-card">
<view class="card-title">
<view class="title-line"></view>
房屋信息
</view>
<view class="info-row">
<text class="info-label">小区</text>
<text class="info-value">{{form.community}}}</text>
</view>
<view class="info-row">
<text class="info-label">房间号</text>
<text class="info-value">{{form.houseNumber}}</text>
</view>
<view class="info-row">
<text class="info-label">业主</text>
<text class="info-value">{{form.owner}}</text>
</view>
<view class="info-row">
<text class="info-label">房间状态</text>
<text class="info-value"
:style="{color: form.status === '自住' ? '#00aaff' : form.status === '闲置' ?
'#FF700A' : form.status === '租赁' ? '#00aa00' : form.status === '其他' ? '#00557f' : '#999' }">{{form.status}}</text>
</view>
</view>
<!-- 住户信息 -->
<view class="info-card">
<view class="card-title">
<view class="title-line"></view>
住户信息
</view>
<view class="info-row">
<text class="info-label">住户</text>
<text class="info-value">{{form.resident}}</text>
</view>
<view class="info-row">
<text class="info-label">房间号</text>
<text class="info-value">{{form.gender}}</text>
</view>
<view class="info-row">
<text class="info-label">证件类型</text>
<text class="info-value">{{form.documentType}}</text>
</view>
<view class="info-row">
<text class="info-label">证件号码</text>
<text class="info-value">{{form.documentNumber}}</text>
</view>
<view class="info-row">
<text class="info-label">与业主关系</text>
<text class="info-value">{{form.relationship}}</text>
</view>
<view class="info-row">
<text class="info-label">手机号码</text>
<text class="info-value">{{form.phoneNum}}</text>
</view>
<!-- 身份证照片区 -->
<view class="photo-row">
<text class="info-label" style="width: 200rpx;">本人身份证照片</text>
<view class="photo-list">
<view class="photo-item"></view>
<view class="photo-item"></view>
</view>
</view>
</view>
</scroll-view>
<!-- 底部操作按钮 -->
<view class="bottom-btn-wrap">
<!-- 仅认证失败显示修改按钮 -->
<button v-if="currentStatus === 'fail'" class="btn-modify" @click="clickUpdate()">
修改
</button>
<button class="btn-delete" :class="{ 'delete-only': currentStatus !== 'fail' }" @click="onDelete">
删除
</button>
</view>
</view>
</template>
<script>
export default {
onReady: function(e) {},
components: {
},
computed: {
// 动态计算当前状态配置
statusConfig() {
return this.statusMap[this.currentStatus];
},
// 动态计算当前步骤
currentStep() {
return this.statusConfig.currentStep;
},
},
created() {
},
onShow() {
},
onLoad(option) {
this.id = option.id;
this.currentStatus = option.statusClass;
},
mounted() {},
data() {
return {
/* 设定状态栏默认高度 */
statusBarHeight: 20,
// 可通过页面传参动态修改状态success / pending / fail
id: undefined,
currentStatus: "",
stepList: ["房屋信息", "住户信息", "物业审核", "认证成功"],
form: {
community: '北境碧桂园小区小区',
houseNumber: '101栋2单元402',
owner: '张三',
status: '租赁',
resident: '蒋依依',
gender: '女',
documentType: '身份证',
documentNumber: '371100200001010888',
relationship: '亲属',
phoneNum: '16599999999',
},
// 状态配置表
statusMap: {
success: {
title: "房屋认证成功",
iconClass: "icon-success",
iconText: "✓",
currentStep: 4,
},
pending: {
title: "房屋审核中,请耐心等待",
iconClass: "icon-pending",
iconText: "⏳",
currentStep: 3,
},
fail: {
title: "房屋认证失败,请重新提交",
iconClass: "icon-fail",
iconText: "✕",
currentStep: 3,
},
},
}
},
methods: {
// 修改按钮事件(仅认证失败显示)
clickUpdate() {
uni.setStorageSync('operationType', 'update')
uni.redirectTo({
url: '/pages/my/addHouse?id=' + this.id + '&sourcePage=' + 'addHouse',
success: res => {},
fail: () => {},
complete: () => {}
});
},
// 删除按钮事件
onDelete() {
uni.showModal({
title: "确认删除",
content: "确认删除此房屋信息吗?",
success: (res) => {
if (res.confirm) {
uni.showLoading({
title: '提删除中...',
mask: true
});
setTimeout(() => {
uni.showToast({
title: '提删除成功',
icon: 'success'
});
}, 1000);
setTimeout(() => {
// 提交成功后跳转列表页
uni.hideLoading();
this.goBack();
}, 3000);
}
},
});
},
goBack() {
uni.redirectTo({
url: '/pages/my/myHouseIndex',
success: res => {},
fail: () => {},
complete: () => {}
});
},
},
}
</script>
<style lang="scss">
page {
background: #fff;
}
</style>
<style scoped>
.contentStyle {
display: flex;
flex-direction: column;
height: 100vh;
/* 关键:占满屏幕高度 */
background-color: #f5f7fa;
}
.page {
flex: 1;
overflow-y: auto;
padding: 0px 20px;
box-sizing: border-box;
}
/* 顶部状态区 */
.status-header {
display: flex;
flex-direction: column;
align-items: center;
padding: 10px 0px 10px;
}
.status-icon {
width: 80rpx;
height: 80rpx;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 20rpx;
}
.icon-success {
background-color: #2F77FD;
/* border: 4rpx solid #00b42a; */
}
.icon-pending {
background-color: #2F77FD;
/* border: 4rpx solid #1677ff; */
}
.icon-fail {
background-color: #E34D59;
/* border: 4rpx solid #f53f3f; */
}
.icon-text {
font-size: 40rpx;
font-weight: bold;
color: inherit;
}
.icon-success .icon-text {
color: #fff;
}
.icon-pending .icon-text {
color: #fff;
}
.icon-fail .icon-text {
color: #fff;
}
.status-title {
font-size: 30rpx;
font-weight: 500;
color: #333;
margin-bottom: 40rpx;
}
/* 步骤条 */
.step-bar {
display: flex;
align-items: center;
width: 100%;
justify-content: space-between;
padding: 0 20rpx;
}
.step-item {
display: flex;
flex-direction: column;
align-items: center;
position: relative;
flex: 1;
}
.step-circle {
width: 40rpx;
height: 40rpx;
border-radius: 50%;
background-color: #1677ff;
color: #fff;
display: flex;
align-items: center;
justify-content: center;
font-size: 24rpx;
z-index: 2;
}
.step-item.active .step-circle {
background-color: #1677ff;
}
.step-item:not(.active) .step-circle {
background-color: #e5e5e5;
color: #999;
}
.step-text {
font-size: 24rpx;
color: #666;
margin-top: 10rpx;
text-align: center;
}
.step-line {
position: absolute;
top: 20rpx;
left: 50%;
width: 100%;
height: 4rpx;
background-color: #e5e5e5;
z-index: 1;
}
.step-line.active {
background-color: #1677ff;
}
/* 信息卡片 */
.info-card {
padding: 20rpx;
border: 1rpx solid #f0f0f0;
border-radius: 8rpx;
background-color: #fff;
margin-bottom: 10px;
}
.card-title {
display: flex;
align-items: center;
font-size: 30rpx;
font-weight: 500;
color: #333;
margin-bottom: 30rpx;
}
.title-line {
width: 8rpx;
height: 28rpx;
background-color: #1677ff;
margin-right: 10rpx;
border-radius: 4rpx;
}
.info-row {
display: flex;
margin-bottom: 24rpx;
align-items: center;
}
.info-row:last-child {
margin-bottom: 0;
}
.info-label {
width: 180rpx;
font-size: 28rpx;
color: #666;
}
.info-value {
font-size: 28rpx;
color: #333;
flex: 1;
}
.status-tag {
color: #ff7d00;
font-weight: 500;
}
/* 身份证照片区 */
.photo-row {
margin-top: 30rpx;
display: flex;
flex-direction: column;
}
.photo-list {
display: flex;
flex-direction: column;
gap: 20rpx;
margin-top: 20rpx;
}
.photo-item {
width: 100%;
height: 170px;
background-color: #f6f6f6;
border-radius: 8rpx;
}
/* 底部按钮区 */
.bottom-btn-wrap {
padding: 10px 15px 20px;
display: flex;
gap: 20rpx;
/* background-color: #fff; */
}
.btn-modify {
flex: 1;
height: 88rpx;
line-height: 88rpx;
background-color: #1677ff;
color: #fff;
font-size: 30rpx;
border-radius: 8rpx;
border: none;
}
.btn-delete {
flex: 1;
height: 88rpx;
line-height: 88rpx;
background-color: #fff;
color: #f53f3f;
border: 1rpx solid #f53f3f;
font-size: 30rpx;
border-radius: 8rpx;
}
.btn-delete.delete-only {
flex: 1;
}
/* 成功提示弹窗 */
.toast-mask {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
display: flex;
align-items: center;
justify-content: center;
background-color: transparent;
z-index: 9999;
}
.toast-content {
background-color: rgba(0, 0, 0, 0.8);
padding: 40rpx 60rpx;
border-radius: 8rpx;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.toast-icon {
width: 60rpx;
height: 60rpx;
line-height: 60rpx;
border-radius: 50%;
background-color: #fff;
color: #1677ff;
font-size: 40rpx;
font-weight: bold;
margin-bottom: 20rpx;
display: flex;
align-items: center;
justify-content: center;
}
.toast-text {
color: #fff;
font-size: 28rpx;
}
.status-bar {
width: 100vw;
height: 46px;
}
</style>

View File

@@ -0,0 +1,377 @@
<template>
<view class="contentStyle" style="">
<view class="status-bar"></view>
<uni-nav-bar title="我的车辆" left-icon="left" @clickLeft="goBack" backgroundColor="transparent" :border="false">
</uni-nav-bar>
<!-- 表单区域 -->
<scroll-view class="page" scroll-y="true">
<!-- 我的车辆列表 -->
<view class="car-item" v-for="(item, index) in carList" :key="index" @click="handleItemClick(item,index)">
<!-- 左侧圆形占位 -->
<image :src="item.imgSrc" mode="aspectFill" class="car-avatar" @error="item.imgSrc = defaultCarImg" />
<!-- 中间车辆信息 -->
<view class="car-info">
<view>
<view class="car-plate">{{ item.plate }}</view>
<view class="car-model">{{ item.model }}·{{ item.color }}</view>
</view>
<view class="car-bindParkingSpace">{{ item.bindParkingSpace }}
</view>
</view>
<!-- 右侧状态标签 -->
<view class="status-tag"
:class="{ underReview: item.status === '审核中', unbound: item.status === '未绑定',bounded: item.status === '已绑定' }">
{{ item.status }}
</view>
</view>
</scroll-view>
<!-- 底部添加车辆按钮 -->
<view class="bottom-btn">
<button class="confirm-btn" @click="addCar">添加车辆</button>
</view>
</view>
</template>
<script>
export default {
onReady: function(e) {},
components: {
},
computed: {
},
created() {
},
onShow() {
},
onUnload() {
},
mounted() {},
data() {
return {
/* 设定状态栏默认高度 */
statusBarHeight: 20,
defaultCarImg: "/static/propertyImgs/defaultCarImg.png",
searchKey: "",
selectedId: '',
selectedName: '',
selectedIndex: -1, // 当前选中的索引
// 模拟车辆数据
carList: [{
id: '1',
imgSrc: '/static/propertyImgs/carImg.png',
plate: "京A88888",
model: "宝马X5",
color: "白色",
status: "未绑定",
bindParkingSpace: "",
},
{
id: '2',
imgSrc: 'https://错误地址2.png',
plate: "京A88888",
model: "宝马X5",
color: "白色",
status: "已绑定",
bindParkingSpace: "北境碧桂园小区小区地下停车场B2A区202",
},
{
id: '3',
imgSrc: 'https://错误地址3.png',
plate: "京A88888",
model: "宝马X5",
color: "白色",
status: "审核中",
bindParkingSpace: "",
}
]
}
},
methods: {
// 选择我的车辆
handleItemClick(item, index) {
uni.setStorageSync('operationType', 'update');
uni.redirectTo({
url: '/pages/my/carDetail?id=' + item.id + '&status=' + item.status,
success: res => {},
fail: () => {},
complete: () => {}
});
},
// 添加车辆
addCar() {
uni.setStorageSync('operationType', 'add');
uni.redirectTo({
url: '/pages/my/addCar',
success: res => {},
fail: () => {},
complete: () => {}
});
},
goBack() {
uni.switchTab({
url: '/pages/myIndex/myIndex',
success: res => {},
fail: () => {},
complete: () => {}
});
},
},
}
</script>
<style lang="scss">
page {
background: #F3F8FD;
overflow: hidden;
// height: 100vh;
}
</style>
<style scoped>
.contentStyle {
display: flex;
flex-direction: column;
height: 100vh;
/* 关键:占满屏幕高度 */
background-color: #f5f7fa;
}
.page {
flex: 1;
overflow-y: auto;
padding: 0px 20px;
box-sizing: border-box;
}
/* 底部按钮 */
.bottom-btn {
padding: 10px 15px 20px;
}
/* 表单容器 */
.form-container {
padding: 60rpx 30rpx;
}
/* 通用输入框样式 */
.input-item {
font-size: 30rpx;
color: #333;
}
.input-placeholder {
color: #999;
font-size: 30rpx;
}
/* 确定按钮 */
.submit-btn {
width: 100%;
height: 88rpx;
line-height: 88rpx;
background-color: #1677ff;
color: #fff;
font-size: 14px;
border-radius: 8rpx;
border: none;
margin-top: 20rpx;
}
/* 成功提示弹窗 */
.toast-mask {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
display: flex;
align-items: center;
justify-content: center;
background-color: transparent;
z-index: 9999;
}
.toast-content {
background-color: rgba(0, 0, 0, 0.8);
padding: 40rpx 60rpx;
border-radius: 8rpx;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.toast-icon {
width: 60rpx;
height: 60rpx;
line-height: 60rpx;
border-radius: 50%;
background-color: #fff;
color: #1677ff;
font-size: 16px;
font-weight: bold;
margin-bottom: 20rpx;
display: flex;
align-items: center;
justify-content: center;
}
.toast-text {
color: #fff;
font-size: 28rpx;
}
</style>
<style scoped>
/* 页面整体 */
/* ====================== 列表容器 ====================== */
/* ====================== 我的车辆卡片 ====================== */
/* 单个车辆项 */
.car-item {
display: flex;
align-items: center;
padding: 10px;
margin-bottom: 20rpx;
border: 1rpx solid #e6e9ff;
border-radius: 16rpx;
background-color: #fff;
position: relative;
width: calc(100% - 22px);
}
.car-item.checked {
background: #f4f7ff;
/* 浅蓝色背景 */
border-color: #1677ff;
}
/* 左侧圆形头像占位 */
.car-avatar {
width: 100rpx;
height: 100rpx;
border-radius: 50%;
background-color: #f0f4ff;
margin-right: 24rpx;
flex-shrink: 0;
}
/* 中间信息区域 */
.car-info {
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
}
/* 车牌号 */
.car-plate {
font-size: 16px;
font-weight: bold;
color: #000;
line-height: 1.4;
margin-bottom: 8rpx;
}
/* 车型+颜色 */
.car-model {
font-size: 14px;
color: #666;
line-height: 1.4;
}
.car-bindParkingSpace {
font-size: 14px;
color: #666;
white-space: nowrap;
/* 强制不换行 */
overflow: hidden;
/* 超出部分隐藏 */
text-overflow: ellipsis;
/* 超出显示省略号 */
width: 90%;
}
/* 状态标签 */
.status-tag {
font-size: 12px;
padding: 3px 7px;
border-radius: 3px;
font-weight: 500;
flex-shrink: 0;
position: absolute;
right: 15px;
top: 15px;
}
/* 未绑定状态 */
.status-tag.unbound {
/* background-color: #e6f7e6; */
color: #999;
}
/* 已绑定状态) */
.status-tag.bounded {
color: #2F77FD;
}
/* 审核中 */
.status-tag.underReview {
/* background-color: #F8DDBD; */
color: #FF8F40;
}
/* ====================== 底部按钮 ====================== */
.confirm-btn {
width: 100%;
height: 88rpx;
background-color: #1677ff;
color: #fff;
border-radius: 12rpx;
font-size: 34rpx;
border: none;
line-height: 88rpx;
}
.status-bar {
width: 100vw;
height: 46px;
}
</style>

View File

@@ -0,0 +1,396 @@
<template>
<view class="contentStyle" style="">
<view class="status-bar"></view>
<uni-nav-bar title="我的房屋" left-icon="left" @clickLeft="goBack" backgroundColor="transparent" :border="false">
</uni-nav-bar>
<!-- -->
<scroll-view class="page" scroll-y="true">
<!-- 房屋列表 v-for 渲染 -->
<view class="house-list">
<view class="house-card" v-for="(item, index) in houseList" :key="index" @click="tapHouse(item)">
<!-- 标题 + 状态 -->
<view class="card-header">
<view class="title-box">
<text class="tag-default" v-if="item.isDefault">默认</text>
<text class="house-name">{{ item.houseName }}</text>
</view>
<text class="status-tag" :class="item.statusClass">{{ item.statusText }}</text>
</view>
<!-- 房屋信息 -->
<view class="card-body">
<view class="row">
<text class="label">房间号</text>
<view class="value-box">
<text class="value">{{ item.roomNo }}</text>
</view>
</view>
<view class="row">
<text class="label">业主</text>
<text class="value">{{ item.owner }}</text>
</view>
<view class="row">
<text class="label">房屋状态</text>
<text class="value">{{ item.houseStatus }}</text>
</view>
<view class="defaultBox" v-if="item.statusText=='认证成功'&&!item.isDefault">
<text class="set-default">设为默认</text>
<switch class="no-cross-switch" :checked="item.isDefault" color="#1677ff"
@change="onSwitchChange($event,index)" @click.stop />
</view>
<!-- <text v-if="item.showSetDefault" class="set-default"
@click="setDefault(item.id)">设为默认</text> -->
</view>
</view>
</view>
</scroll-view>
<!-- 底部添加按钮 -->
<view class="bottom-btn">
<button class="add-btn" @click="addHouse">添加房屋</button>
</view>
</view>
</template>
<script>
export default {
onReady: function(e) {},
components: {
},
computed: {
},
created() {
},
onShow() {
},
onUnload() {
// 页面卸载时清除定时器
if (this.timer) clearInterval(this.timer)
},
mounted() {},
data() {
return {
/* 设定状态栏默认高度 */
statusBarHeight: 20,
houseList: [{
id: 1,
houseName: "北境碧桂园小区(别墅)",
roomNo: "2号楼2层",
owner: "张三",
houseStatus: "自住",
isDefault: true,
statusText: "认证成功",
statusClass: "success",
showSetDefault: false,
},
{
id: 2,
houseName: "北境碧桂园小区(别墅)",
roomNo: "3号楼2层",
owner: "张三",
houseStatus: "自住",
isDefault: false,
statusText: "认证成功",
statusClass: "success",
showSetDefault: true,
},
{
id: 3,
houseName: "北境碧桂园小区(别墅)",
roomNo: "2号楼1层",
owner: "张三",
houseStatus: "闲置",
isDefault: false,
statusText: "认证失败",
statusClass: "fail",
showSetDefault: false,
},
{
id: 4,
houseName: "北境碧桂园小区(别墅)",
roomNo: "2号楼1层",
owner: "张三",
houseStatus: "闲置",
isDefault: false,
statusText: "认证中",
statusClass: "pending",
showSetDefault: false,
},
]
}
},
methods: {
// 开关切换
onSwitchChange(e, i) {
uni.showLoading({
title: '设置中...',
mask: true
});
setTimeout(() => {
uni.showToast({
title: '设置默认房屋成功',
icon: 'success'
});
}, 1000);
setTimeout(() => {
this.houseList.forEach((item, index) => {
if (index != i) {
item.isDefault = false
} else {
this.houseList[i].isDefault = e.detail.value
}
})
uni.setStorageSync('houseIsDefault', e.detail.value)
}, 3000);
},
tapHouse(item) {
uni.redirectTo({
url: '/pages/my/houseDetail?id=' + item.id + '&statusClass=' + item.statusClass,
success: res => {},
fail: () => {},
complete: () => {}
});
},
addHouse() {
uni.setStorageSync('operationType', 'add');
uni.redirectTo({
url: '/pages/my/addHouse',
success: res => {},
fail: () => {},
complete: () => {}
});
},
goBack() {
uni.switchTab({
url: '/pages/myIndex/myIndex',
success: res => {},
fail: () => {},
complete: () => {}
});
},
},
}
</script>
<style lang="scss">
page {
// background: #F3F8FD;
background: linear-gradient(to left bottom, #e2efff 0%, #f9f9f9 50%);
}
</style>
<style scoped>
.contentStyle {
display: flex;
flex-direction: column;
height: 100vh;
/* 关键:占满屏幕高度 */
background-color: #f5f7fa;
}
.page {
flex: 1;
overflow-y: auto;
padding: 0px 20px;
box-sizing: border-box;
}
/* 底部按钮 */
.bottom-btn {
padding: 10px 15px 20px;
}
/* 列表 */
.house-list {
height: 100%;
overflow-y: scroll;
}
.house-card {
background: #fff;
border-radius: 12rpx;
margin-bottom: 20rpx;
overflow: hidden;
}
/* 头部 */
.card-header {
padding: 30rpx;
border-bottom: 1rpx solid #f0f0f0;
display: flex;
justify-content: space-between;
align-items: center;
}
.title-box {
display: flex;
align-items: center;
gap: 16rpx;
}
.house-name {
font-size: 16px;
font-weight: 600;
}
.tag-default {
background: #007aff;
color: #fff;
font-size: 24rpx;
padding: 1px 8px;
border-radius: 15px;
}
/* 状态标签 */
.status-tag {
font-size: 14px;
padding: 6rpx 14rpx;
font-weight: 600;
border-radius: 6rpx;
}
.success {
/* background: #e6f7ee; */
color: #2F77FD;
}
.pending {
/* background: #fff4e4; */
color: #09C2BD;
}
.fail {
/* background: #ffe7e7; */
color: #E34D59;
}
/* 内容行 */
.card-body {
padding: 30rpx;
}
.row {
display: flex;
margin-bottom: 24rpx;
}
.row:last-child {
margin-bottom: 0;
}
.label {
width: 100px;
font-size: 14px;
/* color: #666; */
}
.value-box {
flex: 1;
display: flex;
justify-content: space-between;
}
.value {
font-size: 14px;
font-weight: 600;
color: #333;
}
.defaultBox {
margin-top: 5px;
display: flex;
justify-content: space-between;
z-index: 99 !important;
}
.set-default {
color: #222222;
/* font-weight: 600; */
font-size: 16px;
}
.add-btn {
width: 100%;
height: 88rpx;
line-height: 88rpx;
background: #007aff;
color: #fff;
border-radius: 12rpx;
font-size: 16px;
border: none;
}
.status-bar {
width: 100vw;
height: 46px;
}
</style>
<style>
/* 去掉 switch 内部的 × 和 √ 图标 */
uni-switch::before,
uni-switch::after {
display: none !important;
}
/* 微信小程序专属去除 */
wx-switch::before,
wx-switch::after {
display: none !important;
}
/* 支付宝/百度/抖音小程序兼容 */
my-switch::before,
swan-switch::before,
tt-switch::before {
display: none !important;
}
</style>
<style>
::v-deep .uni-switch-input:before {
background-color: #cccccd !important;
}
/* 隐藏 switch 关闭状态的 × 号 */
.no-cross-switch::before {
display: none !important;
}
.no-cross-switch {
transform: scale(0.85);
border-radius: 50rpx !important;
}
.no-cross-switch>>>.uni-switch-input-checked {
background-color: #007aff !important;
}
</style>

View File

@@ -0,0 +1,333 @@
<template>
<view class="contentStyle" style="">
<view class="status-bar"></view>
<uni-nav-bar title="我的车位" left-icon="left" @clickLeft="goBack" backgroundColor="transparent" :border="false">
</uni-nav-bar>
<scroll-view class="page" scroll-y="true">
<!-- 车位列表 v-for 渲染 -->
<view class="parkingSpace-list">
<view class="parkingSpace-card" v-for="(item, index) in parkingSpaceList" :key="index"
@click="tapParkingSpace(item)">
<!-- 标题 + 状态 -->
<view class="card-header">
<view class="title-box">
<text class="parkingSpace-name">{{ item.parkingSpaceName }}</text>
</view>
<text class="status-tag" :class="item.statusClass">{{ item.statusText }}</text>
</view>
<!-- 车位信息 -->
<view class="card-body">
<view class="left">
<view class="line">
<view class="row">
<text class="label">车位楼层</text>
<view class="value-box">
<text class="value">{{ item.floorNo }}</text>
</view>
</view>
</view>
<view class="line">
<view class="row">
<text class="label">车位编号</text>
<text class="value">{{ item.parkingSpaceNumber }}</text>
</view>
</view>
<view class="line">
<view class="row">
<text class="label">车位状态</text>
<text class="value">{{ item.parkingSpaceStatus }}</text>
</view>
</view>
<view class="line">
<view class="row">
<text class="label">绑定车辆</text>
<text class="value">{{ item.bindCarNum }}</text>
</view>
</view>
</view>
<view class="right">
<image src="/static/propertyImgs/myParkingSpace.png" class="imgItem" mode="widthFix">
</image>
</view>
</view>
</view>
</view>
</scroll-view>
<!-- 底部添加按钮 -->
<view class="bottom-btn">
<button class="add-btn" @click="addParkingSpace">添加车位</button>
</view>
</view>
</template>
<script>
export default {
onReady: function(e) {},
components: {
},
computed: {
},
created() {
},
onShow() {
},
onUnload() {
// 页面卸载时清除定时器
if (this.timer) clearInterval(this.timer)
},
mounted() {},
data() {
return {
/* 设定状态栏默认高度 */
statusBarHeight: 20,
parkingSpaceList: [{
id: 1,
parkingSpaceName: "北境碧桂园小区地下停车场",
floorNo: "B2",
parkingSpaceNumber: "202",
parkingSpaceStatus: "租赁",
bindCarNum: '京A88888',
statusText: "认证成功",
statusClass: "success",
},
{
id: 2,
parkingSpaceName: "北境碧桂园小区停车场",
floorNo: "B2",
parkingSpaceNumber: "202",
parkingSpaceStatus: "自用",
bindCarNum: '京A88888',
statusText: "认证中",
statusClass: "pending",
},
{
id: 3,
parkingSpaceName: "北境碧桂园小区停车场",
floorNo: "2号楼1层",
floorNo: "B2",
parkingSpaceNumber: "202",
parkingSpaceStatus: "闲置",
bindCarNum: '京A88888',
statusText: "认证失败",
statusClass: "fail",
},
]
}
},
methods: {
tapParkingSpace(item) {
uni.redirectTo({
url: '/pages/my/parkingSpaceDetail?id=' + item.id + '&statusClass=' + item.statusClass,
success: res => {},
fail: () => {},
complete: () => {}
});
},
addParkingSpace() {
uni.setStorageSync('operationType', 'add');
uni.redirectTo({
url: '/pages/my/addParkingSpace',
success: res => {},
fail: () => {},
complete: () => {}
});
},
goBack() {
uni.switchTab({
url: '/pages/myIndex/myIndex',
success: res => {},
fail: () => {},
complete: () => {}
});
},
},
}
</script>
<style lang="scss">
page {
background: #F9F9F9;
}
</style>
<style scoped>
.contentStyle {
display: flex;
flex-direction: column;
height: 100vh;
/* 关键:占满屏幕高度 */
background-color: #f5f7fa;
}
.page {
flex: 1;
overflow-y: auto;
padding: 0px 20px;
box-sizing: border-box;
}
/* 底部按钮 */
.bottom-btn {
padding: 10px 15px 20px;
}
/* 列表 */
.parkingSpace-list {}
.parkingSpace-card {
background: #fff;
border-radius: 12rpx;
margin-bottom: 20rpx;
overflow: hidden;
}
/* 头部 */
.card-header {
padding: 30rpx;
/* border-bottom: 1rpx solid #f0f0f0; */
display: flex;
justify-content: space-between;
align-items: center;
}
.title-box {
display: flex;
align-items: center;
gap: 16rpx;
}
.parkingSpace-name {
font-size: 16px;
font-weight: 600;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
/* 状态标签 */
.status-tag {
font-size: 12px;
padding: 2px 8px;
border-radius: 15px;
margin-left: 10px;
white-space: nowrap;
}
.success {
background: #2F77FD;
color: #fff;
}
.pending {
background: #09C2BD;
color: #fff;
}
.fail {
background: #E34D59;
color: #fff;
}
/* 内容行 */
.card-body {
padding: 0rpx 30rpx 30rpx 30rpx;
display: flex;
justify-content: space-between;
}
.line {
display: flex;
margin-bottom: 10px;
}
.row {
display: flex;
width: 100%;
}
.label {
width: 80px;
font-size: 14px;
font-weight: 600;
color: #333;
}
.value-box {
flex: 1;
display: flex;
justify-content: space-between;
}
.value {
font-size: 14px;
color: #666;
}
.set-default {
color: #007aff;
font-weight: 600;
font-size: 14px;
}
.add-btn {
width: 100%;
height: 88rpx;
line-height: 88rpx;
background: #007aff;
color: #fff;
border-radius: 12rpx;
font-size: 16px;
border: none;
}
.left {}
.right {
width: 42%;
}
.imgItem {
width: 100%;
}
.status-bar {
width: 100vw;
height: 46px;
}
</style>

View File

@@ -0,0 +1,523 @@
<template>
<view class="contentStyle" style="">
<view class="status-bar"></view>
<uni-nav-bar title="车位详情" left-icon="left" @clickLeft="goBack" :border="false" backgroundColor="transparent">
</uni-nav-bar>
<scroll-view class="page" scroll-y="true">
<!-- 顶部状态区 -->
<view class="status-header">
<view class="status-icon" :class="statusConfig.iconClass">
<text class="icon-text">{{ statusConfig.iconText }}</text>
</view>
<text class="status-title">{{ statusConfig.title }}</text>
<!-- 步骤条 -->
<view class="step-bar">
<view class="step-item" v-for="(step, index) in stepList" :key="index"
:class="{ active: index < currentStep, done: index < currentStep - 1 }">
<view class="step-circle">{{ index + 1 }}</view>
<text class="step-text">{{ step }}</text>
<view class="step-line" v-if="index < stepList.length - 1"
:class="{ active: index < currentStep - 1 }"></view>
</view>
</view>
</view>
<!-- 房屋信息 -->
<view class="info-card">
<view class="card-title">
<view class="title-line"></view>
房屋信息
</view>
<view class="info-row">
<text class="info-label">小区</text>
<text class="info-value">{{form.community}}</text>
</view>
<view class="info-row">
<text class="info-label">停车场</text>
<text class="info-value">{{form.parkingSpace}}</text>
</view>
<view class="info-row">
<text class="info-label">车位编号</text>
<text class="info-value">{{form.parkingSpaceNum}}</text>
</view>
<view class="info-row">
<text class="info-label">车位状态</text>
<text class="info-value"
:style="{color: form.status === '自用' ? '#FF8F40' : form.status === '租赁' ? '#00aa00' : '#999' }">{{form.status}}</text>
</view>
</view>
<!-- 车辆信息 -->
<view class="info-card">
<view class="card-title">
<view class="title-line"></view>
车辆信息
</view>
<text class="unbind" @click="clickUnbind(item.id)">取消绑定</text>
<view class="info-row">
<text class="info-label">车牌号</text>
<text class="info-value">{{form.carNum}}</text>
</view>
<view class="info-row">
<text class="info-label">车辆品牌</text>
<text class="info-value">{{form.carBrand}}</text>
</view>
<view class="info-row">
<text class="info-label">车辆型号</text>
<text class="info-value">{{form.carModel}}</text>
</view>
<view class="info-row">
<text class="info-label">车辆颜色</text>
<text class="info-value">{{form.carColor}}</text>
</view>
</view>
</scroll-view>
<!-- 底部操作按钮 -->
<view class="bottom-btn-wrap">
<!-- 仅认证失败显示修改按钮 -->
<button class="btn-modify" @click="onModify">
修改
</button>
<button class="btn-delete" :class="{ 'delete-only': currentStatus !== 'fail' }" @click="onDelete">
删除
</button>
</view>
</view>
</template>
<script>
export default {
onReady: function(e) {},
components: {
},
computed: {
// 动态计算当前状态配置
statusConfig() {
return this.statusMap[this.currentStatus];
},
// 动态计算当前步骤
currentStep() {
return this.statusConfig.currentStep;
},
},
created() {
},
onShow() {
},
onLoad(option) {
this.id = option.id;
this.currentStatus = option.statusClass;
// this.currentStatus = 'success';
},
mounted() {},
data() {
return {
/* 设定状态栏默认高度 */
statusBarHeight: 20,
// 可通过页面传参动态修改状态success / pending / fail
id: undefined,
form: {
community: "北境碧桂园小区小区",
parkingSpace: "地下停车场",
parkingSpaceNum: "02",
status: '自用',
carNum: "京A88888",
carBrand: "宝马",
carModel: "X5",
carColor: "白色",
},
currentStatus: "",
stepList: ["车位信息", "物业审核", "认证成功"],
// 状态配置表
statusMap: {
success: {
title: "车位认证成功",
iconClass: "icon-success",
iconText: "✓",
currentStep: 3,
},
pending: {
title: "车位审核中,请耐心等待",
iconClass: "icon-pending",
iconText: "⏳",
currentStep: 2,
},
fail: {
title: "车位认证失败,请重新提交",
iconClass: "icon-fail",
iconText: "✕",
currentStep: 2,
},
},
}
},
methods: {
clickUnbind(id) {
},
// 修改按钮事件(仅认证失败显示)
onModify() {
uni.setStorageSync('operationType', 'update');
uni.redirectTo({
url: '/pages/my/addParkingSpace?id=' + this.id + '&sourcePage=' + 'addParkingSpace',
success: res => {},
fail: () => {},
complete: () => {}
});
},
// 删除按钮事件
onDelete() {
uni.showModal({
title: "确认删除",
content: "确认删除此车位信息吗?",
success: (res) => {
if (res.confirm) {
uni.showLoading({
title: '删除中...',
mask: true
});
setTimeout(() => {
uni.showToast({
title: '删除成功',
icon: 'success'
});
}, 1000);
setTimeout(() => {
// 提交成功后跳转列表页
uni.hideLoading();
this.goBack();
}, 3000);
}
},
});
},
goBack() {
uni.redirectTo({
url: '/pages/my/myParkingSpaceIndex',
success: res => {},
fail: () => {},
complete: () => {}
});
},
},
}
</script>
<style lang="scss">
page {
background: #f9f9f9;
}
</style>
<style scoped>
.contentStyle {
display: flex;
flex-direction: column;
height: 100vh;
/* 关键:占满屏幕高度 */
background-color: #f9f9f9;
}
.page {
flex: 1;
overflow-y: auto;
padding: 0px 20px;
box-sizing: border-box;
}
/* 顶部状态区 */
.status-header {
display: flex;
flex-direction: column;
align-items: center;
padding: 40rpx 30rpx 30rpx;
}
.status-icon {
width: 80rpx;
height: 80rpx;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 20rpx;
}
.icon-success {
background-color: #2F77FD;
}
.icon-pending {
background-color: #2F77FD;
}
.icon-fail {
background-color: #E34D59;
}
.icon-text {
font-size: 40rpx;
font-weight: bold;
color: inherit;
}
.icon-success .icon-text {
color: #fff;
}
.icon-pending .icon-text {
color: #fff;
}
.icon-fail .icon-text {
color: #fff;
}
.status-title {
font-size: 30rpx;
font-weight: 500;
color: #333;
margin-bottom: 40rpx;
}
/* 步骤条 */
.step-bar {
display: flex;
align-items: center;
width: 100%;
justify-content: space-between;
padding: 0 20rpx;
}
.step-item {
display: flex;
flex-direction: column;
align-items: center;
position: relative;
flex: 1;
}
.step-circle {
width: 40rpx;
height: 40rpx;
border-radius: 50%;
background-color: #1677ff;
color: #fff;
display: flex;
align-items: center;
justify-content: center;
font-size: 24rpx;
z-index: 2;
}
.step-item.active .step-circle {
background-color: #1677ff;
}
.step-item:not(.active) .step-circle {
background-color: #e5e5e5;
color: #999;
}
.step-text {
font-size: 24rpx;
color: #666;
margin-top: 10rpx;
text-align: center;
}
.step-line {
position: absolute;
top: 20rpx;
left: 50%;
width: 100%;
height: 4rpx;
background-color: #e5e5e5;
z-index: 1;
}
.step-line.active {
background-color: #1677ff;
}
/* 信息卡片 */
.info-card {
/* margin: 0 30rpx 30rpx; */
padding: 20rpx;
border: 1rpx solid #f0f0f0;
background-color: #fff;
border-radius: 8rpx;
margin-bottom: 10px;
}
.card-title {
display: flex;
align-items: center;
font-size: 30rpx;
font-weight: 500;
color: #333;
margin-bottom: 30rpx;
}
.title-line {
width: 8rpx;
height: 28rpx;
background-color: #1677ff;
margin-right: 10rpx;
border-radius: 4rpx;
}
.info-row {
display: flex;
margin-bottom: 24rpx;
align-items: center;
}
.info-row:last-child {
margin-bottom: 0;
}
.info-label {
width: 180rpx;
font-size: 28rpx;
color: #666;
}
.info-value {
font-size: 28rpx;
color: #333;
flex: 1;
}
.status-tag {
color: #ff7d00;
font-weight: 500;
}
/* 身份证照片区 */
.photo-row {
margin-top: 30rpx;
display: flex;
flex-direction: column;
}
.photo-list {
display: flex;
flex-direction: column;
gap: 20rpx;
margin-top: 20rpx;
}
.photo-item {
width: 100%;
height: 170px;
background-color: #f0f4ff;
border-radius: 8rpx;
}
/* 底部按钮区 */
.bottom-btn-wrap {
padding: 10px 15px 20px;
display: flex;
gap: 20rpx;
}
.btn-modify {
flex: 1;
height: 88rpx;
line-height: 88rpx;
background-color: #1677ff;
color: #fff;
font-size: 30rpx;
border-radius: 8rpx;
border: none;
}
.btn-delete {
flex: 1;
height: 88rpx;
line-height: 88rpx;
background-color: #fff;
color: #f53f3f;
border: 1rpx solid #f53f3f;
font-size: 30rpx;
border-radius: 8rpx;
}
.btn-delete.delete-only {
flex: 1;
}
/* 成功提示弹窗 */
.toast-mask {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
display: flex;
align-items: center;
justify-content: center;
background-color: transparent;
z-index: 9999;
}
.toast-content {
background-color: rgba(0, 0, 0, 0.8);
padding: 40rpx 60rpx;
border-radius: 8rpx;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.toast-icon {
width: 60rpx;
height: 60rpx;
line-height: 60rpx;
border-radius: 50%;
background-color: #fff;
color: #1677ff;
font-size: 40rpx;
font-weight: bold;
margin-bottom: 20rpx;
display: flex;
align-items: center;
justify-content: center;
}
.toast-text {
color: #fff;
font-size: 28rpx;
}
.status-bar {
width: 100vw;
height: 46px;
}
</style>

View File

@@ -0,0 +1,475 @@
<template>
<view class="contentStyle" style="">
<view class="status-bar"></view>
<uni-nav-bar :title="'选择'+checkType" left-icon="left" @clickLeft="goBackAdd" backgroundColor="transparent"
:border="false">
</uni-nav-bar>
<scroll-view class="page" scroll-y>
<!-- 楼栋网格列表 -->
<view class="building-grid">
<view class="building-item"
:class="{ active:(checkType=='楼栋'&& selectedBuilding === item.id)||(checkType=='单元'&& selectedUnit === item.id)||(checkType=='楼层'&&selectedFloor === item.id) ||(checkType=='户号'&&selectedHouseNumber === item.id) }"
v-for="(item, index) in dataList" :key="index" @click="selectBuilding(item)">
<text class="building-text">{{ item.name }}</text>
</view>
</view>
</scroll-view>
</view>
</template>
<script>
export default {
onReady: function(e) {},
components: {
},
computed: {
},
created() {
},
onShow() {
},
onUnload() {},
mounted() {
if (this.checkType == '楼栋') {
this.dataList = this.buildingList
} else if (this.checkType == '单元') {
this.dataList = this.unitList
} else if (this.checkType == '楼层') {
this.dataList = this.floorList
} else if (this.checkType == '户号') {
this.dataList = this.houseNumberList
}
},
data() {
return {
/* 设定状态栏默认高度 */
statusBarHeight: 20,
checkType: uni.getStorageSync('checkType'),
dataList: [],
// 楼栋列表(可动态生成/接口返回)
buildingList: [{
id: 1,
name: '01栋'
},
{
id: 2,
name: '02栋'
},
{
id: 3,
name: '03栋'
},
{
id: 4,
name: '04栋'
},
{
id: 5,
name: '05栋'
},
{
id: 6,
name: '06栋'
},
{
id: 7,
name: '07栋'
},
{
id: 8,
name: '08栋'
},
{
id: 9,
name: '09栋'
},
{
id: 10,
name: '10栋'
},
{
id: 11,
name: '11栋'
},
{
id: 12,
name: '12栋'
}
],
floorList: [{
id: 13,
name: '01层'
},
{
id: 14,
name: '02层'
},
{
id: 15,
name: '03层'
},
{
id: 16,
name: '04层'
}, {
id: 17,
name: '05层'
},
{
id: 18,
name: '06层'
},
{
id: 19,
name: '07层'
},
{
id: 20,
name: '08层'
}, {
id: 21,
name: '09层'
},
{
id: 22,
name: '10层'
},
{
id: 23,
name: '11层'
},
{
id: 24,
name: '12层'
}, {
id: 25,
name: '13层'
},
{
id: 26,
name: '14层'
},
{
id: 27,
name: '15层'
},
{
id: 28,
name: '16层'
},
{
id: 29,
name: '17层'
},
{
id: 30,
name: '18层'
},
{
id: 31,
name: '19层'
},
{
id: 32,
name: '20层'
},
],
unitList: [{
id: 33,
name: '01单元'
},
{
id: 34,
name: '02单元'
},
{
id: 35,
name: '03单元'
},
{
id: 36,
name: '04单元'
},
],
houseNumberList: [{
id: 37,
name: '01'
},
{
id: 38,
name: '02'
},
{
id: 39,
name: '03'
},
{
id: 40,
name: '04'
},
],
// 选中的楼栋
selectedBuilding: uni.getStorageSync('buildingId'),
// 选中的单元
selectedUnit: uni.getStorageSync('unitId'),
// 选中的楼层
selectedFloor: uni.getStorageSync('floorId'),
// 选中的户号
selectedHouseNumber: uni.getStorageSync('houseNumberId'),
}
},
methods: {
selectBuilding(item) {
let modalContent = '确认选择此' + this.checkType + '吗?'
uni.showModal({
title: "确认选择",
content: modalContent,
success: (res) => {
if (res.confirm) {
uni.showLoading({
title: '选择中...',
mask: true
});
if (this.checkType == '楼栋') {
this.selectedBuilding = item.id
uni.setStorageSync('buildingId', item.id);
uni.setStorageSync('buildingName', item.name);
uni.removeStorageSync('unitId');
uni.removeStorageSync('unitName');
uni.removeStorageSync('floorId');
uni.removeStorageSync('floorName');
uni.removeStorageSync('houseNumberId');
uni.removeStorageSync('houseNumberName');
uni.setStorageSync('checkType', '单元');
} else if (this.checkType == '单元') {
this.selectedUnit = item.id
uni.setStorageSync('unitId', item.id);
uni.setStorageSync('unitName', item.name);
uni.removeStorageSync('floorId');
uni.removeStorageSync('floorName');
uni.removeStorageSync('houseNumberId');
uni.removeStorageSync('houseNumberName');
uni.setStorageSync('checkType', '楼层');
} else if (this.checkType == '楼层') {
this.selectedFloor = item.id
uni.setStorageSync('floorId', item.id);
uni.setStorageSync('floorName', item.name);
uni.removeStorageSync('houseNumberId');
uni.removeStorageSync('houseNumberName');
uni.setStorageSync('checkType', '户号');
} else if (this.checkType == '户号') {
this.selectedHouseNumber = item.id
uni.setStorageSync('houseNumberId', item.id);
uni.setStorageSync('houseNumberName', item.name);
}
setTimeout(() => {
uni.showToast({
title: '选择成功',
icon: 'success'
});
}, 500);
setTimeout(() => {
// 提交成功后跳转列表页
uni.hideLoading();
this.goFreash();
}, 2000);
}
},
});
},
goFreash() {
console.log(this.checkType, '刷新页面');
if (this.checkType != '户号') {
uni.redirectTo({
url: '/pages/my/selectBuilding',
success: res => {},
fail: () => {},
complete: () => {}
});
} else {
uni.redirectTo({
url: '/pages/my/addHouse',
success: res => {},
fail: () => {},
complete: () => {}
});
}
},
goBackAdd() {
if (this.checkType == '楼栋') {
uni.removeStorageSync('buildingId');
uni.removeStorageSync('buildingName');
} else if (this.checkType == '单元') {
uni.removeStorageSync('unitId');
uni.removeStorageSync('unitName');
} else if (this.checkType == '楼层') {
uni.removeStorageSync('floorId');
uni.removeStorageSync('floorName');
} else if (this.checkType == '户号') {
uni.removeStorageSync('houseNumberId');
uni.removeStorageSync('houseNumberName');
}
uni.redirectTo({
url: '/pages/my/addHouse',
success: res => {},
fail: () => {},
complete: () => {}
});
},
},
}
</script>
<style lang="scss">
page {
background: linear-gradient(to left bottom, #e2efff 0%, #f9f9f9 50%);
overflow: hidden;
height: 100vh;
}
</style>
<style scoped>
.contentStyle {
display: flex;
flex-direction: column;
height: 100vh;
/* 关键:占满屏幕高度 */
/* background-color: #fff; */
}
.page {
flex: 1;
overflow-y: auto;
padding: 0px 20px;
box-sizing: border-box;
}
/* 底部按钮 */
.bottom-btn {
padding: 10px 15px 20px;
}
/* 网格布局3列 */
.building-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 30rpx;
}
/* 楼栋按钮 */
.building-item {
height: 100rpx;
display: flex;
align-items: center;
justify-content: center;
background-color: #f9f9f9;
/* border: 1rpx solid #e5e5e5; */
border-radius: 20rpx;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.05);
transition: all 0.2s ease;
}
/* 选中状态 */
.building-item.active {
background-color: #2F77FD;
/* border-color: #1677ff; */
}
.building-item.active .building-text {
color: #fff;
}
/* 楼栋文字 */
.building-text {
font-size: 32rpx;
color: #333;
font-weight: 500;
}
/* 成功提示弹窗 */
.toast-mask {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
display: flex;
align-items: center;
justify-content: center;
background-color: transparent;
z-index: 9999;
}
.toast-content {
background-color: rgba(0, 0, 0, 0.8);
padding: 40rpx 60rpx;
border-radius: 8rpx;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.toast-icon {
width: 60rpx;
height: 60rpx;
line-height: 60rpx;
border-radius: 50%;
background-color: #fff;
color: #1677ff;
font-size: 40rpx;
font-weight: bold;
margin-bottom: 20rpx;
display: flex;
align-items: center;
justify-content: center;
}
.toast-text {
color: #fff;
font-size: 28rpx;
}
.status-bar {
width: 100vw;
height: 46px;
}
</style>

View File

@@ -0,0 +1,213 @@
<template>
<view class="contentStyle" style="">
<view class="status-bar"></view>
<uni-nav-bar title="车辆品牌" left-icon="left" @clickLeft="goBackAdd" backgroundColor="transparent" :border="false">
</uni-nav-bar>
<scroll-view class="page" scroll-y>
<!-- 搜索框 -->
<view class="search-box">
<input class="search-input" v-model="searchKey" placeholder="请输入名称"
placeholder-class="input-placeholder" @input="onSearch" />
<uni-icons type="search" size="20" color="#999" />
</view>
<!-- 小区列表 -->
<view class="carBrand-list">
<view class="carBrand-item" v-for="(item, index) in filteredList" :key="index"
@click="selectCarBrand(item)">
<text class="carBrand-name">{{ item.name }}</text>
<text class="item-arrow"></text>
</view>
</view>
</scroll-view>
</view>
</template>
<script>
export default {
onReady: function(e) {},
components: {
},
computed: {
// 过滤后的列表(搜索功能)
filteredList() {
if (!this.searchKey.trim()) {
return this.carBrandList
}
return this.carBrandList.filter(item =>
item.name.includes(this.searchKey.trim())
)
},
},
created() {
},
onShow() {
},
onUnload() {
},
onLoad(option) {
},
mounted() {},
data() {
return {
/* 设定状态栏默认高度 */
statusBarHeight: 20,
searchKey: '',
carBrandList: [{
id: 1,
name: '宝马'
},
{
id: 2,
name: '奥迪'
},
]
}
},
methods: {
// 搜索输入事件
onSearch() {
// 实时过滤computed 自动处理
},
// 选择小区
selectCarBrand(item) {
uni.setStorageSync('carBrandId', item.id);
uni.setStorageSync('carBrandName', item.name);
uni.redirectTo({
url: '/pages/my/addCar',
success: res => {},
fail: () => {},
complete: () => {}
});
},
goBackAdd() {
uni.removeStorageSync('carBrandId');
uni.removeStorageSync('carBrandName');
uni.redirectTo({
url: '/pages/my/addCar',
success: res => {},
fail: () => {},
complete: () => {}
});
},
},
}
</script>
<style lang="scss">
page {
background: #fff;
overflow: hidden;
height: 100vh;
}
</style>
<style scoped>
.contentStyle {
display: flex;
flex-direction: column;
height: 100vh;
/* 关键:占满屏幕高度 */
/* background-color: #fff; */
}
.page {
flex: 1;
overflow-y: auto;
padding: 0px 20px;
box-sizing: border-box;
}
/* 底部按钮 */
.bottom-btn {
padding: 10px 15px 20px;
}
/* 搜索框 */
.search-box {
display: flex;
align-items: center;
background-color: #F3F3F3;
border-radius: 10px;
margin: 0rpx 0px 20rpx 0px;
padding: 0 30rpx;
height: 70rpx;
}
.search-input {
flex: 1;
height: 100%;
font-size: 30rpx;
border: none;
background: transparent;
}
.input-placeholder {
color: #999;
font-size: 30rpx;
}
.search-icon {
font-size: 36rpx;
color: #999;
}
/* 小区列表 */
.carBrand-list {
padding: 0 30rpx;
}
.carBrand-item {
display: flex;
align-items: center;
justify-content: space-between;
padding: 30rpx 0;
border-bottom: 1rpx solid #f0f0f0;
}
.carBrand-name {
font-size: 32rpx;
color: #000;
font-weight: 500;
}
.item-arrow {
display: inline-block;
width: 5px;
height: 5px;
border-top: 2rpx solid #999;
border-right: 2rpx solid #999;
transform: rotate(45deg);
margin-left: 10rpx;
margin-right: 5px;
}
.status-bar {
width: 100vw;
height: 46px;
}
</style>

View File

@@ -0,0 +1,360 @@
<template>
<view class="contentStyle" style="">
<view class="status-bar"></view>
<uni-nav-bar :title="'选择'+checkType" left-icon="left" @clickLeft="goBackAdd" backgroundColor="transparent" :border="false">
</uni-nav-bar>
<scroll-view class="page" scroll-y>
<!-- 停车场网格列表 -->
<view class="parkingLot-grid">
<view class="parkingLot-item"
:class="{ active:(checkType=='停车场'&& selectedParkingLot === item.id)||(checkType=='车位编号'&& selectedParkingSpace === item.id)}"
v-for="(item, index) in dataList" :key="index" @click="selectedParking(item)">
<text class="parkingLot-text">{{ item.name }}</text>
</view>
</view>
</scroll-view>
</view>
</template>
<script>
export default {
onReady: function(e) {},
components: {
},
computed: {
},
created() {
},
onShow() {
},
onUnload() {},
mounted() {
if (this.checkType == '停车场') {
this.dataList = this.parkingLotList
} else if (this.checkType == '车位编号') {
this.dataList = this.parkingSpaceList
}
},
data() {
return {
/* 设定状态栏默认高度 */
statusBarHeight: 20,
checkType: uni.getStorageSync('checkType'),
dataList: [],
// 停车场列表(可动态生成/接口返回)
parkingLotList: [{
id: 1,
name: '地上停车场'
},
{
id: 2,
name: '地下停车场'
},
],
parkingSpaceList: [{
id: 13,
name: '01'
},
{
id: 14,
name: '02'
},
{
id: 15,
name: '03'
},
{
id: 16,
name: '04'
}, {
id: 17,
name: '05'
},
{
id: 18,
name: '06'
},
{
id: 19,
name: '07'
},
{
id: 20,
name: '08'
}, {
id: 21,
name: '09'
},
{
id: 22,
name: '10'
},
{
id: 23,
name: '11'
},
{
id: 24,
name: '12'
}, {
id: 25,
name: '13'
},
{
id: 26,
name: '14'
},
{
id: 27,
name: '15'
},
{
id: 28,
name: '16'
},
{
id: 29,
name: '17'
},
{
id: 30,
name: '18'
},
{
id: 31,
name: '19'
},
{
id: 32,
name: '20'
},
],
// 选中的停车场
selectedParkingLot: uni.getStorageSync('parkingLotId'),
// 选中的车位编号
selectedParkingSpace: uni.getStorageSync('parkingSpaceId'),
}
},
methods: {
selectedParking(item) {
if (this.checkType == '停车场') {
this.selectedParkingLot = item.id
} else if (this.checkType == '车位编号') {
this.selectedParkingSpace = item.id
}
let modalContent = '确认选择此' + this.checkType + '吗?'
uni.showModal({
title: "确认选择",
content: modalContent,
success: (res) => {
if (res.confirm) {
uni.showLoading({
title: '选择中...',
mask: true
});
setTimeout(() => {
uni.showToast({
title: '选择成功',
icon: 'success'
});
if (this.checkType == '停车场') {
uni.setStorageSync('parkingLotId', item.id);
uni.setStorageSync('parkingLotName', item.name);
uni.removeStorageSync('parkingSpaceId');
uni.removeStorageSync('parkingSpaceName');
uni.setStorageSync('checkType', '车位编号');
} else if (this.checkType == '车位编号') {
uni.setStorageSync('parkingSpaceId', item.id);
uni.setStorageSync('parkingSpaceName', item.name);
}
}, 1000);
setTimeout(() => {
// 提交成功后跳转列表页
uni.hideLoading();
this.goFreash()
}, 3000);
}
},
});
},
goFreash() {
if (this.checkType != '车位编号') {
uni.redirectTo({
url: '/pages/my/selectParingLot',
success: res => {},
fail: () => {},
complete: () => {}
});
} else {
uni.redirectTo({
url: '/pages/my/addParkingSpace',
success: res => {},
fail: () => {},
complete: () => {}
});
}
},
goBackAdd() {
if (this.checkType == '停车场') {
uni.removeStorageSync('parkingLotId');
uni.removeStorageSync('parkingLotName');
} else if (this.checkType == '车位编号') {
uni.removeStorageSync('parkingSpaceId');
uni.removeStorageSync('parkingSpaceName');
}
uni.redirectTo({
url: '/pages/my/addParkingSpace',
success: res => {},
fail: () => {},
complete: () => {}
});
},
},
}
</script>
<style lang="scss">
page {
background: linear-gradient(to left bottom, #e2efff 0%, #f9f9f9 50%);
overflow: hidden;
height: 100vh;
}
</style>
<style scoped>
.contentStyle {
display: flex;
flex-direction: column;
height: 100vh;
/* 关键:占满屏幕高度 */
/* background-color: #fff; */
}
.page {
flex: 1;
overflow-y: auto;
padding: 0px 20px;
box-sizing: border-box;
}
/* 底部按钮 */
.bottom-btn {
padding: 10px 15px 20px;
}
/* 网格布局3列 */
.parkingLot-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 30rpx;
}
/* 停车场按钮 */
.parkingLot-item {
height: 100rpx;
display: flex;
align-items: center;
justify-content: center;
background-color: #fff;
border: 1rpx solid #e5e5e5;
border-radius: 20rpx;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.05);
transition: all 0.2s ease;
}
/* 选中状态 */
.parkingLot-item.active {
background-color: #1677ff;
border-color: #1677ff;
}
.parkingLot-item.active .parkingLot-text {
color: #fff;
}
/* 停车场文字 */
.parkingLot-text {
font-size: 32rpx;
color: #333;
font-weight: 500;
}
/* 成功提示弹窗 */
.toast-mask {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
display: flex;
align-items: center;
justify-content: center;
background-color: transparent;
z-index: 9999;
}
.toast-content {
background-color: rgba(0, 0, 0, 0.8);
padding: 40rpx 60rpx;
border-radius: 8rpx;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.toast-icon {
width: 60rpx;
height: 60rpx;
line-height: 60rpx;
border-radius: 50%;
background-color: #fff;
color: #1677ff;
font-size: 40rpx;
font-weight: bold;
margin-bottom: 20rpx;
display: flex;
align-items: center;
justify-content: center;
}
.toast-text {
color: #fff;
font-size: 28rpx;
}.status-bar{
width: 100vw;
height: 46px;
}
</style>

View File

@@ -0,0 +1,267 @@
<template>
<view class="contentStyle" style="">
<view class="status-bar"></view>
<uni-nav-bar title="选择小区" left-icon="left" @clickLeft="goBackAdd" backgroundColor="transparent" :border="false">
</uni-nav-bar>
<scroll-view class="page" scroll-y>
<!-- 搜索框 -->
<view class="search-box">
<input class="search-input" v-model="searchKey" placeholder="请输入名称"
placeholder-class="input-placeholder" @input="onSearch" />
<uni-icons type="search" size="20" color="#999" />
</view>
<!-- 小区列表 -->
<view class="community-list">
<view class="community-item" v-for="(item, index) in filteredList" :key="index"
@click="selectCommunity(item)">
<text class="community-name">{{ item.name }}</text>
<text class="item-arrow"></text>
</view>
</view>
</scroll-view>
</view>
</template>
<script>
export default {
onReady: function(e) {},
components: {
},
computed: {
// 过滤后的列表(搜索功能)
filteredList() {
if (!this.searchKey.trim()) {
return this.communityList
}
return this.communityList.filter(item =>
item.name.includes(this.searchKey.trim())
)
},
},
created() {
},
onShow() {
},
onUnload() {
},
onLoad(option) {
// addHouse
// addParkingSpace
this.sourcePage = option.sourcePage;
},
mounted() {},
data() {
return {
/* 设定状态栏默认高度 */
statusBarHeight: 20,
sourcePage: '',
searchKey: '',
communityList: [{
id: 1,
name: '北境碧桂园小区'
},
{
id: 2,
name: '东境碧桂园小区'
},
{
id: 3,
name: '南境碧桂园小区'
},
{
id: 4,
name: '西境碧桂园小区'
},
]
}
},
methods: {
// 搜索输入事件
onSearch() {
// 实时过滤computed 自动处理
},
// 选择小区
selectCommunity(item) {
uni.setStorageSync('communityId', item.id);
uni.setStorageSync('communityName', item.name);
uni.setStorageSync('checkType', '楼栋');
uni.removeStorageSync('buildingId');
uni.removeStorageSync('buildingName');
uni.removeStorageSync('unitId');
uni.removeStorageSync('unitName');
uni.removeStorageSync('floorId');
uni.removeStorageSync('floorName');
uni.removeStorageSync('houseNumberId');
uni.removeStorageSync('houseNumberName');
uni.removeStorageSync('bindHouseId');
uni.removeStorageSync('bindHouseName');
uni.removeStorageSync('parkingLotId');
uni.removeStorageSync('parkingLotName');
uni.removeStorageSync('parkingSpaceId');
uni.removeStorageSync('parkingSpaceName');
if (this.sourcePage == 'addHouse') {
uni.redirectTo({
url: '/pages/my/selectBuilding',
success: res => {},
fail: () => {},
complete: () => {}
});
} else if (this.sourcePage == 'addParkingSpace') {
uni.redirectTo({
url: '/pages/my/bindHouse',
success: res => {},
fail: () => {},
complete: () => {}
});
} else {
}
},
goBackAdd() {
uni.removeStorageSync('communityId');
uni.removeStorageSync('communityName');
if (this.sourcePage == 'addHouse') {
uni.redirectTo({
url: '/pages/my/addHouse',
success: res => {},
fail: () => {},
complete: () => {}
});
} else if (this.sourcePage == 'addParkingSpace') {
uni.redirectTo({
url: '/pages/my/addParkingSpace',
success: res => {},
fail: () => {},
complete: () => {}
});
} else {
}
},
},
}
</script>
<style lang="scss">
page {
background: #f6f6f6;
overflow: hidden;
height: 100vh;
}
</style>
<style scoped>
.contentStyle {
display: flex;
flex-direction: column;
height: 100vh;
/* 关键:占满屏幕高度 */
background-color: #fff;
}
.page {
flex: 1;
overflow-y: auto;
padding: 0px 20px;
box-sizing: border-box;
}
/* 底部按钮 */
/* .bottom-btn {
padding: 10px 15px 20px;
} */
/* 搜索框 */
.search-box {
display: flex;
align-items: center;
background-color: #F3F3F3;
border-radius: 10px;
margin: 0rpx 0px 20rpx 0px;
padding: 0 30rpx;
height: 70rpx;
}
.search-input {
flex: 1;
height: 100%;
font-size: 30rpx;
border: none;
background: transparent;
}
.input-placeholder {
color: #999;
font-size: 30rpx;
}
.search-icon {
font-size: 36rpx;
color: #999;
}
/* 小区列表 */
.community-list {
/* padding: 0 30rpx; */
}
.community-item {
display: flex;
align-items: center;
justify-content: space-between;
padding: 30rpx 0;
border-bottom: 1rpx solid #f0f0f0;
}
.community-name {
font-size: 32rpx;
color: #000;
font-weight: 500;
}
.item-arrow {
display: inline-block;
width: 5px;
height: 5px;
border-top: 2rpx solid #999;
border-right: 2rpx solid #999;
transform: rotate(45deg);
margin-left: 10rpx;
margin-right: 5px;
}
.status-bar {
width: 100vw;
height: 46px;
}
</style>

View File

@@ -0,0 +1,306 @@
<template>
<view class="contentStyle" style="">
<view class="status-bar"></view>
<uni-nav-bar title="设置" left-icon="left" @clickLeft="goBack" backgroundColor="transparent" :border="false">
</uni-nav-bar>
<scroll-view class="page" scroll-y>
<view class="setting-item" v-for="(item, index) in settingList" :key="index" @click="onItemClick(item)">
<text class="item-title">{{ item.title }}</text>
<!-- 不同类型的右侧内容 -->
<view class="item-right" v-if="item.type === 'text'">
<text :class="item.valueClass || 'item-value'">{{ item.value }}</text>
<view class="item-arrow"></view>
</view>
<switch class="no-cross-switch" v-else-if="item.type === 'switch'" :checked="item.checked"
@change="onSwitchChange(item, $event)" />
<view class="item-arrow" v-else></view>
</view>
</scroll-view>
<!-- 底部下一步按钮 -->
<view class="bottom-btn">
<button class="logout-btn" @click="logout">退出登录</button>
</view>
</view>
</template>
<script>
export default {
onReady: function(e) {},
components: {
},
computed: {
},
created() {
},
onShow() {
},
onLoad() {
},
mounted() {},
data() {
return {
message: '',
/* 设定状态栏默认高度 */
statusBarHeight: 20,
cacheSize: 0,
settingList: [{
title: '更改手机号码',
type: 'arrow'
},
{
title: '修改密码',
type: 'arrow'
},
{
title: '消息通知',
type: 'switch',
checked: false
},
{
title: '清除缓存',
type: 'text',
value: '9.2M'
},
{
title: '升级版本',
type: 'text',
value: '当前版本 2.9.8',
valueClass: 'item-version'
}
]
}
},
methods: {
onItemClick(item) {
// console.log('点击了:', item.title,item.title == '更改手机号码')
if (item.title == '更改手机号码') {
uni.redirectTo({
url: '/pages/my/changePhone',
success: res => {},
fail: () => {},
complete: () => {}
});
} else if (item.title == '修改密码') {
uni.redirectTo({
url: '/pages/my/changePwd',
success: res => {},
fail: () => {},
complete: () => {}
});
} else if (item.title == '清除缓存') {
uni.clearStorageSync();
this.message = '清除成功'
this.settingList[3].value = '0M'
uni.showLoading({
title: '清除中...',
mask: true
});
setTimeout(() => {
uni.showToast({
title: '清除成功',
icon: 'success'
});
}, 1000);
setTimeout(() => {
// 提交成功后跳转列表页
uni.hideLoading();
}, 3000);
}
},
logout() {
uni.showModal({
title: '提示',
content: '确定要退出当前账号吗?',
success: (res) => {
if (res.confirm) {
this.message = '退出成功'
uni.showLoading({
title: '退出中...',
mask: true
});
setTimeout(() => {
uni.showToast({
title: '退出成功',
icon: 'success'
});
}, 1000);
setTimeout(() => {
// 登出成功后跳转回登录页
uni.hideLoading();
}, 3000);
}
}
})
},
onSwitchChange(item, e) {
item.checked = e.detail.value
},
goBack() {
uni.switchTab({
url: '/pages/myIndex/myIndex',
success: res => {},
fail: () => {},
complete: () => {}
});
},
},
}
</script>
<style lang="scss">
page {
background: #f9f9f9;
}
</style>
<style lang="scss" scoped>
.contentStyle {
display: flex;
flex-direction: column;
height: 100vh;
/* 关键:占满屏幕高度 */
background-color: #f9f9f9;
}
.page {
flex: 1;
overflow-y: auto;
padding: 0px 20px;
box-sizing: border-box;
background-color: #fff;
}
/* 底部按钮 */
.bottom-btn {
padding: 10px 15px 20px;
background-color: #fff;
}
</style>
<style scoped>
/* 列表项 */
.setting-item {
display: flex;
align-items: center;
justify-content: space-between;
background-color: #ffffff;
padding: 15px 15px;
border-bottom: 1px solid #f0f0f0;
}
/* 标题文字 */
.item-title {
font-size: 16px;
color: #333333;
line-height: 1.4;
}
/* 右侧区域(文字+箭头) */
.item-right {
display: flex;
align-items: center;
gap: 10rpx;
}
/* 右侧数值/版本文字 */
.item-value {
font-size: 14px;
color: #333333;
}
.item-version {
font-size: 14px;
color: #999999;
}
/* 右侧箭头 */
.item-arrow {
display: inline-block;
width: 5px;
height: 5px;
border-top: 2rpx solid #999;
border-right: 2rpx solid #999;
transform: rotate(45deg);
margin-left: 10rpx;
margin-right: 5px;
}
/* 开关样式适配 */
/* switch {
transform: scale(0.8);
} */
/* 底部按钮 */
.logout-btn {
width: 100%;
height: 96rpx;
line-height: 96rpx;
text-align: center;
font-size: 36rpx;
border-radius: 12rpx;
color: #666;
background-color: #F3F3F3;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.06);
}
/* 按钮点击态 */
.logout-btn:active {
background-color: #f5f5f5;
opacity: 0.8;
}
.status-bar {
width: 100vw;
height: 46px;
}
</style>
<style>
::v-deep .uni-switch-input:before {
background-color: #cccccd !important;
}
/* 隐藏 switch 关闭状态的 × 号 */
.no-cross-switch::before {
display: none !important;
}
.no-cross-switch {
transform: scale(0.85);
border-radius: 50rpx !important;
}
.no-cross-switch>>>.uni-switch-input-checked {
background-color: #007aff !important;
}
</style>

View File

@@ -0,0 +1,277 @@
<template>
<view class="contentStyle" style="">
<view class="status-bar"></view>
<uni-nav-bar title="我的" backgroundColor="transparent" :border="false">
</uni-nav-bar>
<scroll-view class="page" scroll-y>
<view class="topBox">
<view class="name">
<image src="/static/propertyImgs/touxiang.jpg" class="img" />
<span style="padding: 5rpx 5rpx;display: flex;flex-direction: column;line-height: 30px;">
<span style="font-size: 18px;font-weight: 600;">
{{loginName}}
</span>
<span style="font-size: 13px;color:#666666;">
{{phoneNum}}
</span>
</span>
</view>
</view>
<view class="contentBox">
<div class="page-container">
<view class="list-container">
<view class="list-item" v-for="(item, index) in list" :key="index" @click="onItemClick(item)">
<image :src="item.icon" class="item-icon" />
<view class="item-content">
<text class="item-title">{{ item.title }}</text>
<span class="item-desc" v-if="item.desc"><span
style="align-self: flex-end;font-size: 12px;vertical-align: baseline;">{{ item.desc }}</span></span>
</view>
<view class="item-arrow"></view>
<!-- <image src="/static/propertyImgs/arrow-right.png" class="arrow-icon" /> -->
</view>
</view>
</div>
</view>
</scroll-view>
</view>
</template>
<script>
export default {
onReady: function(e) {},
components: {
},
computed: {
},
created() {
},
onShow() {
},
onLoad() {
},
mounted() {},
data() {
return {
loginName: "wangh",
phoneNum: '156*****1212',
list: [{
icon: "/static/propertyImgs/house.png",
title: "我的房屋"
},
{
icon: "/static/propertyImgs/user.png",
title: "住户管理",
// desc: "您可以在这里添加家人、朋友、租客~"
},
{
icon: "/static/propertyImgs/parking.png",
title: "我的车位"
},
{
icon: "/static/propertyImgs/car.png",
title: "我的车辆"
},
{
icon: "/static/propertyImgs/setting.png",
title: "设置"
}
]
}
},
methods: {
onItemClick(item) {
if (item.title == '我的房屋') {
uni.redirectTo({
url: '/pages/my/myHouseIndex',
success: res => {},
fail: () => {},
complete: () => {}
});
} else if (item.title == '住户管理') {
// uni.redirectTo({
// url: '/pages/my/changePwd',
// success: res => {},
// fail: () => {},
// complete: () => {}
// });
} else if (item.title == '我的车位') {
uni.redirectTo({
url: '/pages/my/myParkingSpaceIndex',
success: res => {},
fail: () => {},
complete: () => {}
});
} else if (item.title == '我的车辆') {
uni.redirectTo({
url: '/pages/my/myCarIndex',
success: res => {},
fail: () => {},
complete: () => {}
});
} else if (item.title == '设置') {
this.goSetting()
}
},
goSetting() {
uni.redirectTo({
url: '/pages/my/settingIndex',
success: res => {},
fail: () => {},
complete: () => {}
});
},
},
}
</script>
<style lang="scss">
page {
// background: #fff;
background: linear-gradient(to left bottom, #e2efff 0%, #f9f9f9 50%);
}
</style>
<style lang="scss" scoped>
.contentStyle {
display: flex;
flex-direction: column;
height: calc(100vh - 50px);
.topBox {
width: 100%;
padding: 10px 0px;
position: relative;
// background-color: #1A75FF;
.titleBox {
width: 100%;
position: relative;
.title {
font-size: 18px;
line-height: 40px;
text-align: center;
font-weight: 500;
}
.settingImg {
width: 24px;
height: 24px;
top: 8px;
right: 0px;
}
}
.img {
width: 60px;
height: 60px;
margin-right: 10rpx;
border-radius: 50%;
}
.name {
bottom: 15px;
left: 15px;
display: flex;
align-items: center;
}
}
.contentBox {
position: relative;
}
}
.contentStyle {}
.page {
flex: 1;
overflow-y: auto;
padding: 0px 20px;
box-sizing: border-box;
}
</style>
<style scoped>
.list-container {}
.status-bar {
width: 100vw;
height: 46px;
}
.list-item {
display: flex;
align-items: center;
/* background: #fff; */
padding: 10px 3px;
border-radius: 8rpx;
/* border-bottom: 1px solid #f2f2f2; */
/* margin-bottom: 10rpx; */
}
.item-icon {
width: 42px;
height: 42px;
/* margin-right: 20rpx; */
}
.item-content {
flex: 1;
display: flex;
flex-direction: row;
align-items: center;
}
.item-title {
font-size: 16px;
/* color: #333; */
font-weight: 400;
margin: 5px 10px;
/* margin-bottom: 6rpx; */
}
.item-desc {
font-size: 12px;
color: #b5b5b5;
white-space: nowrap;
/* 强制不换行 */
overflow: hidden;
/* 超出隐藏 */
text-overflow: ellipsis;
}
.item-arrow {
width: 8px;
height: 8px;
border-right: 3rpx solid #999999;
border-bottom: 3rpx solid #999999;
transform: rotate(-45deg);
margin-right: 5px;
}
</style>

View File

@@ -0,0 +1,209 @@
<template>
<view class="guide-page">
<view class="status_bar"></view>
<!-- 引导内容滑动容器 -->
<swiper
class="guide-swiper"
:current="currentStep"
@change="handleSwiperChange"
:disable-scroll="false"
:indicator-dots="false"
:mode="mode"
>
<!-- 便捷服务 -->
<swiper-item>
<view class="guide-item">
<image class="guide-img" src="/static/引导1.png" ></image>
<view class="guide-title">便捷服务</view>
<view class="guide-subtitle">报修缴费预约</view>
</view>
</swiper-item>
<!-- 高效管理 -->
<swiper-item>
<view class="guide-item">
<image class="guide-img" src="/static/引导2.png" ></image>
<view class="guide-title">高效管理</view>
<view class="guide-subtitle">自动化流程和智能设备</view>
</view>
</swiper-item>
<!--安全保障 -->
<swiper-item>
<view class="guide-item">
<image class="guide-img" src="/static/引导3.png"></image>
<view class="guide-title">安全保障</view>
<view class="guide-subtitle">智能巡检和智慧访客</view>
</view>
</swiper-item>
</swiper>
<!-- 进度点 -->
<view class="progress-dots">
<view class="dot" :class="{ active: currentStep === 0 }"></view>
<view class="dot" :class="{ active: currentStep === 1 }"></view>
<view class="dot" :class="{ active: currentStep === 2 }"></view>
</view>
<!-- 操作按钮 -->
<view class="guide-btn-wrap">
<button
class="guide-btn"
:class="{ primary: currentStep === 2 }"
@click="handleBtnClick"
>
{{ currentStep === 2 ? '立即体验' : '下一步' }}
</button>
</view>
</view>
</template>
<script setup>
import { ref } from 'vue';
// ========== 响应式数据 ==========
// 当前引导步骤0/1/2
const currentStep = ref(0);
const mode = ref("default")
// ========== 方法 ==========
// 滑动swiper切换步骤
const handleSwiperChange = (e) => {
currentStep.value = e.detail.current;
};
// 点击按钮操作
const handleBtnClick = () => {
// 前两步:点击下一步切换
if (currentStep.value < 2) {
currentStep.value++;
} else {
// 立即体验,跳转首页
// 1. 记录引导已完成
uni.setStorageSync('hasShowGuide', true);
// 2. 跳转首页
uni.navigateTo({
url:"/pages/login/pwd-login"
})
// uni.switchTab({
// url: '/pages/index/index'
// });
}
};
</script>
<style scoped>
/* 页面容器 */
.guide-page {
width: 100%;
height: 100vh;
background-color: #f8f9ff;
display: flex;
flex-direction: column;
align-items: center;
justify-content: space-between;
padding: 120rpx 40rpx 80rpx;
box-sizing: border-box;
}
.status_bar {
height: 115px;
width: 100%;
}
/* 滑动容器 */
.guide-swiper {
flex: 1;
width: 100%;
}
/* 单个引导项 */
.guide-item {
width: 360px;
height: 360px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
/* 引导插画 */
.guide-img {
width: 100%;
height: 100%;
margin-bottom: 80rpx;
}
/* 引导标题 */
.guide-title {
font-size: 48rpx;
font-weight: 600;
color: #333;
margin-bottom: 20rpx;
}
/* 引导副标题 */
.guide-subtitle {
font-size: 28rpx;
color: #666;
}
/* 进度点容器 */
.progress-dots {
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 60rpx;
}
/* 单个进度点 */
.dot {
width: 16rpx;
height: 16rpx;
border-radius: 8rpx;
background-color: #ddd;
margin: 0 8rpx;
}
/* 激活的进度点 */
.dot.active {
background-color: #007aff;
width: 40rpx;
}
/* 按钮容器 */
.guide-btn-wrap {
width: 80%;
}
/* 引导按钮 */
.guide-btn {
width: 100%;
height: 88rpx;
line-height: 88rpx;
border-radius: 44rpx;
font-size: 32rpx;
padding: 0;
margin: 0;
box-sizing: border-box;
}
/* 默认按钮样式(下一步) */
.guide-btn:not(.primary) {
background-color: #fff;
color: #007aff;
border: 2rpx solid #007aff;
}
/* 主按钮样式(立即体验) */
.guide-btn.primary {
background-color: #007aff;
color: #fff;
border: none;
}
/* 按钮禁用态(可选) */
.guide-btn:disabled {
background-color: #ccc;
color: #fff;
border: none;
}
</style>

View File

@@ -0,0 +1,356 @@
<template>
<!-- 通知列表 -->
<scroll-view
class="notice-list"
scroll-y
:refresher-enabled="true"
:refresher-triggered="refreshing"
:style="{ height: `calc(100vh - ${statusBarHeight + navBarHeight}px)` }"
>
<!-- 下拉刷新 -->
<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'
})
}
</script>
<style lang="scss">
/* 通知列表容器 */
.notice-list {
padding: 12px 12px 20px;
box-sizing: border-box;
background-color: #f9f9f9;
}
/* 下拉刷新 */
.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

@@ -0,0 +1,494 @@
<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/bannar1.png" mode="" style="width: 100%;height: 100%;border-radius: 20rpx;"></image>
<image src="/static/bannar1.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;
// 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: 18px;
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

@@ -0,0 +1,241 @@
<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 = () =>{
// uni.navigateBack({
// delta: 1
// })
uni.switchTab({
url:"/pages/index/index"
})
}
// 确认开门方法
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, #4a90e2, #F6FAFF);
min-height: 100vh;
padding: 30rpx 26rpx;
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 #e5e5e5;
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: #ffffff;
color: #2c6bc7;
font-size: 32rpx;
border-radius: 20rpx;
border: none;
}
.confirm-btn::after {
border: none; /* 去除uni-app默认按钮边框 */
}
</style>

View File

@@ -0,0 +1,545 @@
<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'
// 响应式数据
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号' },
// { id: 12, name: '华润幸福里', address: '华润市萧山区市心北路200号' },
// { id: 13, name: '融创壹号院', address: '融创市余杭区文二西路300号' },
// { id: 14, name: '中海国际社区', address: '中海市西湖区天目山路400号' },
// { id: 15, name: '世茂江滨花园', address: '世茂市江干区钱江路500号' },
// { id: 16, name: '恒大御景湾', address: '恒大市拱墅区湖墅南路600号' },
// { id: 17, name: '碧桂园凤凰城', address: '碧桂园市滨江区滨康路700号' },
// { id: 18, name: '万科金色家园', address: '万科市萧山区金城路800号' },
// { id: 19, name: '绿城桂花城', address: '绿城市西湖区文三路900号' },
// { id: 20, name: '滨江金色海岸', address: '滨江市江干区下沙路1000号' },
// { id: 21, name: '远洋公馆', address: '远洋市拱墅区莫干山路1100号' },
// { id: 22, name: '龙湖春江彼岸', address: '龙湖市滨江区闻涛路1200号' },
// { id: 23, name: '融信蓝孔雀', address: '融信市西湖区学院路1300号' },
// { id: 24, name: '德信早安', address: '德信市余杭区良睦路1400号' },
// { id: 25, name: '宋都阳光国际', address: '宋都市江干区下沙路1500号' }
])
// 计算属性
const totalPages = computed(() => {
return Math.ceil(totalCommunities.value.length / pageSize)
})
// 方法
// 加载分页数据
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: 20rpx; */
}
/* 搜索区域 */
.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

@@ -0,0 +1,337 @@
<template>
<view class="repair-page">
<!-- 顶部导航栏 -->
<!-- <view class="nav-bar">
<text class="nav-title">在线报修</text>
</view> -->
<!-- 报修类型Tab -->
<view class="repair-type-tabs">
<view
class="type-tab"
:class="{ active: activeType === 'public' }"
@click="publicClick"
>
<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: 10px 13px;">
<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 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 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-color: #f5f5f5;
min-height: 100vh;
}
/* 报修类型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;
margin-right: 20rpx;
}
.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: 10px;
}
.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;
}
/* 内容 */
.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;
}
/* 底部时间和详情 */
.item-footer {
display: flex;
justify-content: space-between;
align-items: center;
font-size: 12px;
}
.item-time {
color: #999;
}
.detail-btn {
color: #1E88E5;
}
</style>

View File

@@ -0,0 +1,639 @@
<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: #007aff;
}
.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

@@ -0,0 +1,179 @@
<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

@@ -0,0 +1,245 @@
<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

@@ -0,0 +1,263 @@
<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

@@ -0,0 +1,141 @@
<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

@@ -0,0 +1,114 @@
<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

@@ -0,0 +1,536 @@
<template>
<view class="contentStyle" style="">
<view class="status-bar"></view>
<uni-nav-bar :title="'新增'+paymentType+'缴费'" left-icon="left" @clickLeft="goBack" backgroundColor="transparent"
:border="false">
</uni-nav-bar>
<Loading mask="true" click="true" ref="loading"></Loading>
<!-- 表单区域 -->
<scroll-view class="page" scroll-y="true">
<view class="container">
<!-- 顶部蓝色标题栏 -->
<view class="header-card">
<view class="header-icon">
<text class="icon-fire">🔥</text>
</view>
<text class="header-title">燃气费</text>
</view>
<!-- 缴费信息卡片 -->
<view class="info-card">
<view class="form-item">
<text class="label">缴费单位</text>
<text class="company-name">XXXXXXXX燃气有限公司</text>
</view>
<view class="form-item">
<text class="label">缴费单位</text>
<view class="input-wrapper">
<input type="number" placeholder="请输入户号" class="input-field" v-model="userNo" />
<text class="help-link" @click="showPopup = true">如何获取户号?</text>
</view>
</view>
</view>
<!-- 底部协议和提交按钮 -->
<view class="bottom-area">
<view class="agreement">
<checkbox-group @change="handleAgreeChange">
<checkbox :checked="isAgree" />
</checkbox-group>
<text class="agreement-text">已阅并同意</text>
<text class="agreement-link" @click="handleProtocol">协议链接</text>
</view>
<button class="submit-btn" @click="handleSubmit">
提交
</button>
</view>
</view>
<!-- 快速获取户号 底部弹窗 -->
<view v-if="showPopup" class="popup-mask" @click="showPopup = false">
<view class="popup-content" @click.stop>
<view class="popup-header">
<text class="popup-title">快速获取户号</text>
<text class="popup-close" @click="showPopup = false">×</text>
</view>
<view class="popup-list">
<view class="popup-item">
<view class="item-number">1</view>
<view class="item-content">
<text class="item-title">查看缴费通知单</text>
<text class="item-desc">在纸质缴费/催费通知单上查找户号</text>
</view>
</view>
<view class="popup-item">
<view class="item-number">2</view>
<view class="item-content">
<text class="item-title">查询通知短信</text>
<text class="item-desc">搜索手机由缴费单位发送的催缴短信短信中包含户号</text>
</view>
</view>
</view>
</view>
</view>
</scroll-view>
</view>
</template>
<script>
export default {
onReady: function(e) {},
components: {
},
computed: {
style() {
var statusBarHeight = this.statusBarHeight;
return statusBarHeight;
},
},
created() {
let statusBarObj = this.getPhoneInfo()
this.statusBarHeight = statusBarObj.statusBarHeight
},
onShow() {
},
mounted() {},
onReady() {
this.$nextTick(() => {
})
},
data() {
return {
/* 设定状态栏默认高度 */
statusBarHeight: 20,
paymentType: uni.getStorageSync('addPaymentType'),
userNo: '',
isAgree: false,
showPopup: false, // 控制弹窗显示
}
},
methods: {
handleAgreeChange(e) {
// e.detail.value 是数组,有值 = 选中,空 = 未选中
this.isAgree = e.detail.value.length > 0
},
// 处理协议链接点击
handleProtocol() {
uni.navigateTo({
})
},
// 处理提交
handleSubmit() {
if (!this.userNo) {
uni.showToast({
title: '请输入户号',
icon: 'none'
})
return
}
if (!this.isAgree) {
uni.showToast({
title: '请先同意协议',
icon: 'none'
})
return
}
uni.showModal({
title: '确认提交',
content: '确认要提交吗?',
success: (res) => {
if (res.confirm) {
uni.showLoading({
title: '提交中...',
mask: true
});
setTimeout(() => {
uni.showToast({
title: '提交成功',
icon: 'success'
});
}, 1000);
setTimeout(() => {
// 提交成功后跳转列表页
uni.hideLoading();
this.goNext()
}, 3000);
}
}
})
},
goNext() {
uni.redirectTo({
url: '/pages/payment-list/addPaymentSuccess',
success: res => {},
fail: () => {},
complete: () => {}
});
},
goBack() {
uni.redirectTo({
url: '/pages/payment-list/paymentIndex',
success: res => {},
fail: () => {},
complete: () => {}
});
},
},
}
</script>
<style lang="scss">
page {
background: #f2f1f6;
}
</style>
<style scoped>
.contentStyle {
display: flex;
flex-direction: column;
height: 100vh;
/* 关键:占满屏幕高度 */
background-color: #f9f9f9;
}
.page {
flex: 1;
overflow-y: auto;
padding: 0px 20px;
box-sizing: border-box;
background-color: #fff;
}
.container {
min-height: 100vh;
background-color: #f5f5f5;
padding: 20rpx;
box-sizing: border-box;
}
/* 顶部标题栏 */
.header-card {
background: linear-gradient(135deg, #66b3ff 0%, #4080ff 100%);
border-radius: 20rpx 20rpx 0 0;
padding: 30rpx 40rpx;
display: flex;
align-items: center;
color: #fff;
}
.header-icon {
width: 60rpx;
height: 60rpx;
background-color: #fff;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin-right: 20rpx;
}
.icon-fire {
font-size: 32rpx;
color: #4080ff;
}
.header-title {
font-size: 16px;
font-weight: bold;
}
/* 信息卡片 */
.info-card {
background-color: #fff;
border-radius: 0 0 20rpx 20rpx;
padding: 40rpx;
margin-bottom: 40rpx;
}
.form-item {
margin-bottom: 40rpx;
}
.form-item:last-child {
margin-bottom: 0;
}
.label {
display: block;
font-size: 14px;
color: #565656;
margin-bottom: 20rpx;
}
.company-name {
display: block;
font-size: 16px;
color: #333;
font-weight: 600;
}
.input-wrapper {
display: flex;
align-items: center;
justify-content: space-between;
}
.input-field {
flex: 1;
font-size: 16px;
color: #333;
padding: 10rpx 0;
border: none;
outline: none;
}
.input-field::placeholder {
color: #ccc;
}
.help-link {
font-size: 14px;
color: #4080ff;
white-space: nowrap;
margin-left: 20rpx;
}
/* 底部区域 */
.bottom-area {
position: fixed;
bottom: 0;
left: 0;
right: 0;
background-color: #fff;
padding: 15px;
box-sizing: border-box;
}
.agreement {
display: flex;
align-items: center;
margin-bottom: 30rpx;
}
.agreement checkbox {
transform: scale(0.8);
margin-right: 10rpx;
}
.agreement-text {
font-size: 14px;
color: #333;
}
.agreement-link {
font-size: 14px;
color: #4080ff;
margin-left: 10rpx;
}
.submit-btn {
width: 100%;
height: 90rpx;
line-height: 90rpx;
background-color: #4080ff;
color: #fff;
border-radius: 10rpx;
font-size: 36rpx;
font-weight: 500;
border: none;
outline: none;
}
.submit-btn[disabled] {
background-color: #a0c4ff;
color: #fff;
opacity: 0.7;
}
/* 弹窗遮罩层 */
.popup-mask {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.5);
z-index: 999;
display: flex;
align-items: flex-end;
}
/* 弹窗内容 */
.popup-content {
width: 100%;
background-color: #fff;
border-radius: 20rpx 20rpx 0 0;
padding: 30rpx 40rpx 60rpx;
box-sizing: border-box;
animation: slideUp 0.3s ease-out;
}
/* 弹窗标题栏 */
.popup-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 40rpx;
}
.popup-title {
font-size: 16px;
line-height: 30px;
font-weight: bold;
color: #333;
width: 100%;
display: flex;
/* align-items: center; */
justify-content: space-evenly;
border-bottom: 1px solid #ebebeb;
}
.popup-close {
font-size: 48rpx;
color: #999;
line-height: 1;
}
/* 弹窗列表 */
.popup-list {
padding: 0 10rpx;
}
.popup-item {
display: flex;
align-items: flex-start;
margin-bottom: 40rpx;
}
.popup-item:last-child {
margin-bottom: 0;
}
.item-number {
width: 40rpx;
height: 40rpx;
line-height: 40rpx;
text-align: center;
border: 2rpx solid #4080ff;
border-radius: 6rpx;
font-size: 28rpx;
font-weight: bold;
color: #4080ff;
margin-right: 20rpx;
flex-shrink: 0;
}
.item-content {
flex: 1;
display: flex;
flex-direction: column;
}
.item-title {
font-size: 16px;
font-weight: bold;
color: #333;
margin-bottom: 16rpx;
}
.item-desc {
font-size: 14px;
color: #666;
line-height: 1.5;
}
/* 弹窗动画 */
@keyframes slideUp {
from {
transform: translateY(100%);
}
to {
transform: translateY(0);
}
}
/* 成功提示弹窗 */
.toast-mask {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
display: flex;
align-items: center;
justify-content: center;
background-color: transparent;
z-index: 9999;
}
.toast-content {
background-color: rgba(0, 0, 0, 0.8);
padding: 40rpx 60rpx;
border-radius: 8rpx;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.toast-icon {
width: 60rpx;
height: 60rpx;
line-height: 60rpx;
border-radius: 50%;
background-color: #fff;
color: #1677ff;
font-size: 40rpx;
font-weight: bold;
margin-bottom: 20rpx;
display: flex;
align-items: center;
justify-content: center;
}
.toast-text {
color: #fff;
font-size: 28rpx;
}.status-bar{
width: 100vw;
height: 46px;
}
</style>

View File

@@ -0,0 +1,193 @@
<template>
<view class="contentStyle" style="">
<view class="status-bar"></view>
<uni-nav-bar :title="'新增'+paymentType+'缴费'" left-icon="left" @clickLeft="goBack" backgroundColor="transparent"
:border="false">
</uni-nav-bar>
<Loading mask="true" click="true" ref="loading"></Loading>
<!-- 表单区域 -->
<scroll-view class="page" scroll-y="true">
<view class="container">
<!-- 成功图标 -->
<view class="success-icon">
<text class="icon-check"></text>
</view>
<!-- 主标题 -->
<view class="title">绑定成功</view>
<!-- 提示文案 -->
<view class="desc">
恭喜您绑定成功费用将在24小时内更新<br>
请您按时查看避免造成费用欠费
</view>
<!-- 操作按钮 -->
<view class="btn-wrap">
<button class="btn-back" @click="goBack">返回生活缴费</button>
</view>
</view>
</scroll-view>
</view>
</template>
<script>
export default {
onReady: function(e) {},
components: {
},
computed: {
},
created() {
},
onShow() {
},
mounted() {},
onReady() {
this.$nextTick(() => {
})
},
data() {
return {
/* 设定状态栏默认高度 */
statusBarHeight: 20,
paymentType: uni.getStorageSync('addPaymentType'),
}
},
methods: {
goBack() {
uni.redirectTo({
url: '/pages/payment-list/paymentIndex',
success: res => {},
fail: () => {},
complete: () => {}
});
},
},
}
</script>
<style lang="scss">
page {
background: #f2f1f6;
}
</style>
<style scoped>
.contentStyle {
display: flex;
flex-direction: column;
height: 100vh;
/* 关键:占满屏幕高度 */
background-color: #f9f9f9;
}
.page {
flex: 1;
overflow-y: auto;
padding: 0px 20px;
box-sizing: border-box;
background-color: #fff;
}
</style>
<style scoped>
/* 全局容器 */
.container {
min-height: 100vh;
background-color: #f5f5f7;
display: flex;
flex-direction: column;
align-items: center;
padding: 0 30rpx;
box-sizing: border-box;
}
/* 成功图标 */
.success-icon {
width: 180rpx;
height: 180rpx;
border-radius: 50%;
background-color: #1677ff;
display: flex;
align-items: center;
justify-content: center;
margin-top: 200rpx;
margin-bottom: 30rpx;
}
.icon-check {
color: #fff;
font-size: 100rpx;
font-weight: bold;
line-height: 1;
}
/* 主标题 */
.title {
font-size: 44rpx;
font-weight: bold;
color: #000;
margin-bottom: 40rpx;
}
/* 提示文案 */
.desc {
font-size: 32rpx;
color: #666;
line-height: 1.6;
text-align: center;
margin-bottom: 150rpx;
}
/* 按钮容器 */
.btn-wrap {
width: 100%;
}
/* 返回按钮 */
.btn-back {
width: 100%;
height: 96rpx;
line-height: 96rpx;
background-color: #1677ff;
color: #fff;
font-size: 36rpx;
border-radius: 8rpx;
border: none;
padding: 0;
margin: 0;
}
/* 按钮默认样式重置解决uni-app button默认样式问题 */
.btn-back::after {
border: none;
}.status-bar{
width: 100vw;
height: 46px;
}
</style>

View File

@@ -0,0 +1,720 @@
<template>
<view class="contentStyle" style="">
<view class="status-bar"></view>
<uni-nav-bar :title="arrearsPayment" left-icon="left" @clickLeft="goBack" backgroundColor="transparent"
:border="false">
</uni-nav-bar>
<Loading mask="true" click="true" ref="loading"></Loading>
<!-- 表单区域 -->
<scroll-view class="page" scroll-y="true">
<view class="container">
<!-- 1. 缴费详情 -->
<view v-if="currentStep === 1" class="pay-page">
<!-- 物业费缴费 -->
<!-- 金额展示 -->
<view class="amount-section">
<text class="amount-text">- ¥1680.00</text>
</view>
<!-- 缴费信息 -->
<view class="info-section" style="">
<view v-if="arrearsPayment=='物业费'">
<view class="info-item">
<text class="info-label">户名</text>
<text class="info-value">桂花园别墅2号楼2层</text>
</view>
<view class="info-item">
<text class="info-label">户主</text>
<text class="info-value">周大声</text>
</view>
<view class="info-item">
<text class="info-label">缴费单位</text>
<text class="info-value">CT物业</text>
</view>
<view class="info-item">
<text class="info-label">可用余额</text>
<text class="info-value">0.00</text>
</view>
<view style="border-bottom: 1px solid #c7c7c7;padding-bottom: 10px;"></view>
<view class="info-payAmountItem">
<text class="info-payAmount">{{'¥'+payAmount}}</text>
</view>
</view>
<view v-else>
<view class="info-item" v-if="arrearsPayment=='暖气费'">
<text class="info-label">房屋</text>
<text class="info-value">桂花园别墅2号楼2层</text>
</view>
<view class="info-item">
<text class="info-label">户号</text>
<text class="info-value">101123456</text>
</view>
<view class="info-item">
<text class="info-label">户主</text>
<text class="info-value">周大声</text>
</view>
<view class="info-item">
<text class="info-label">缴费单位</text>
<text class="info-value">HN热力有限公司</text>
</view>
<view style="border-bottom: 1px solid #c7c7c7;padding-bottom: 10px;"></view>
<!-- 缴费金额输入 -->
<view class="input-section" style="display: flex;">
<text class="amount-input" style="width: auto;">¥</text>
<input type="number" v-model="payAmount" class="amount-input" placeholder="点击输入缴费金额">
</input>
</view>
<view class="arrears-row">
<text class="arrears-text">当前欠费金额¥{{ arrearsAmount }}</text>
<text class="auto-fill-btn" @click="handleAutoFill">点击自动填入</text>
</view>
</view>
</view>
<!-- 立即缴费按钮 -->
<view class="btn-section">
<button class="pay-btn" @click="handlePay">立即缴费</button>
</view>
</view>
<!-- 2. 支付密码弹窗 -->
<view v-if="currentStep === 2" class="modal-overlay" @click="closePasswordModal">
<view class="password-modal" @click.stop>
<!-- 弹窗头部 -->
<view class="modal-header">
<view class="close-icon" @click="closePasswordModal">
<text class="close-text">×</text>
</view>
<view class="modal-title">请输入支付密码</view>
<view class="header-right"></view>
</view>
<!-- 商户信息 & 金额 -->
<view class="pay-info">
<text class="merchant-name">智享日照洗护</text>
<text class="pay-amount">¥{{ payAmount }}</text>
</view>
<!-- 支付方式选择 -->
<view class="pay-way">
<text class="way-label">支付方式</text>
<view class="way-content">
<image class="bank-icon" src="/static/propertyImgs/bank-logo.png" mode="aspectFit">
</image>
<text class="bank-name">建设银行储蓄卡(8888)</text>
<text class="arrow-icon">></text>
</view>
</view>
<!-- 银行卡选择列表 -->
<!-- <view v-if="showBankList" class="bank-list">
<view v-for="(item, index) in bankList" :key="index" class="bank-item"
@click="selectBank(item)">
<image class="bank-item-icon" :src="item.icon" mode="aspectFit"></image>
<text class="bank-item-name">{{ item.name }}</text>
<text v-if="selectedBank.id === item.id" class="check-icon">&#xe60c;</text>
</view>
</view> -->
<!-- 密码输入框 -->
<view class="password-input">
<view v-for="(item, index) in 6" :key="index" class="password-item">
<text v-if="password.length > index" class="password-dot"></text>
</view>
</view>
<!-- 数字键盘 -->
<view class="keyboard">
<view class="keyboard-row">
<view class="keyboard-item" @click="inputPassword(1)">1</view>
<view class="keyboard-item" @click="inputPassword(2)">2</view>
<view class="keyboard-item" @click="inputPassword(3)">3</view>
</view>
<view class="keyboard-row">
<view class="keyboard-item" @click="inputPassword(4)">4</view>
<view class="keyboard-item" @click="inputPassword(5)">5</view>
<view class="keyboard-item" @click="inputPassword(6)">6</view>
</view>
<view class="keyboard-row">
<view class="keyboard-item" @click="inputPassword(7)">7</view>
<view class="keyboard-item" @click="inputPassword(8)">8</view>
<view class="keyboard-item" @click="inputPassword(9)">9</view>
</view>
<view class="keyboard-row">
<view class="keyboard-item empty"></view>
<view class="keyboard-item" @click="inputPassword(0)">0</view>
<view class="keyboard-item delete" @click="deletePassword">
<text class="icon"></text>
</view>
</view>
</view>
</view>
</view>
<!-- 3. 缴费成功页 -->
<view v-if="currentStep === 3" class="success-page">
<view class="icon-box">
<!-- 成功图标 -->
<view class="success-icon">
<text class="icon"></text>
</view>
</view>
<!-- 成功文字 -->
<view class="success-text">缴费成功</view>
<view class="success-desc">成功缴纳物业费</view>
<!-- 返回按钮 -->
<view class="btn-section">
<button class="pay-btn" @click="handleBackToList">返回生活缴费</button>
</view>
</view>
</view>
</scroll-view>
</view>
</template>
<script>
export default {
onReady: function(e) {},
components: {
},
computed: {
},
created() {
},
onShow() {
},
mounted() {},
onReady() {
this.$nextTick(() => {
})
},
data() {
return {
/* 设定状态栏默认高度 */
statusBarHeight: 20,
arrearsPayment: uni.getStorageSync('arrearsPayment'),
currentStep: 1, // 1: 详情页 2: 密码弹窗 3: 成功页
payAmount: "1680.00", // 缴费金额
password: "", // 输入的密码
showBankList: false, // 是否显示银行卡列表
arrearsAmount: "21.21",
// 银行卡列表
bankList: [{
id: 1,
name: "建设银行储蓄卡(8888)",
icon: "/static/bank-logo.png"
},
{
id: 2,
name: "工商银行储蓄卡(6666)",
icon: "/static/icbc-logo.png"
},
{
id: 3,
name: "招商银行储蓄卡(9999)",
icon: "/static/cmb-logo.png"
}
],
// 默认选中的银行卡
selectedBank: {
id: 1,
name: "建设银行储蓄卡(8888)",
icon: "/static/bank-logo.png"
}
}
},
methods: {
handleAutoFill() {
this.payAmount = this.arrearsAmount;
uni.vibrateShort({
type: "light"
});
},
goBack() {
uni.redirectTo({
url: '/pages/payment-list/paymentIndex',
success: res => {},
fail: () => {},
complete: () => {}
});
},
// 点击立即缴费
handlePay() {
if (!this.payAmount || Number(this.payAmount) <= 0) {
uni.showToast({
title: "请输入正确的缴费金额",
icon: "none"
});
return;
}
this.currentStep = 2;
},
// 关闭密码弹窗
closePasswordModal() {
this.currentStep = 1;
this.password = "";
this.showBankList = false;
},
// 选择银行卡
selectBank(item) {
this.selectedBank = item;
this.showBankList = false;
},
// 输入密码
inputPassword(num) {
if (this.password.length >= 6) return;
this.password += num.toString();
// 密码满6位自动提交
if (this.password.length === 6) {
this.handleConfirmPay();
}
},
// 删除密码
deletePassword() {
this.password = this.password.slice(0, -1);
},
// 确认支付
handleConfirmPay() {
// 这里模拟支付请求,实际项目中调用后端接口
uni.showLoading({
title: "支付中...",
mask: true
});
setTimeout(() => {
uni.hideLoading();
// 支付成功,跳转到成功页
this.currentStep = 3;
this.password = "";
this.showBankList = false;
}, 1500);
},
// 返回生活缴费列表
handleBackToList() {
uni.redirectTo({
url: '/pages/payment-list/paymentIndex',
success: res => {},
fail: () => {},
complete: () => {}
});
}
},
}
</script>
<style lang="scss">
page {
background: #f2f1f6;
}
</style>
<style scoped>
.contentStyle {
display: flex;
flex-direction: column;
height: 100vh;
/* 关键:占满屏幕高度 */
background-color: #f9f9f9;
}
.page {
flex: 1;
overflow-y: auto;
padding: 0px 20px;
box-sizing: border-box;
background-color: #fff;
}
/* 1. 物业费详情页 */
.pay-page {
width: 100%;
min-height: 100vh;
background-color: #fff;
}
.amount-section {
padding: 30px 0;
text-align: center;
}
.amount-text {
font-size: 28px;
font-weight: 600;
color: #bd3124;
}
.info-section {
padding: 0 15px 15px 15px;
/* margin-bottom: 20px; */
}
.info-item {
display: flex;
justify-content: space-between;
align-items: center;
height: 40px;
}
.info-label {
font-size: 15px;
color: #666;
}
.info-value {
font-size: 15px;
color: #333;
font-weight: 600;
}
.info-payAmountItem {
height: 50px;
line-height: 50px;
margin-bottom: 30px;
}
.info-payAmount {
font-size: 25px;
color: #333;
font-weight: 600;
}
.input-section {
/* padding: 0 15px; */
}
.amount-input {
width: 100%;
height: 50px;
font-size: 24px;
line-height: 50px;
border: none;
outline: none;
margin-right: 5px;
}
.btn-section {
padding: 0 15px;
}
.pay-btn {
width: 100%;
height: 50px;
line-height: 50px;
background-color: #007aff;
color: #fff;
border-radius: 8px;
font-size: 16px;
border: none;
outline: none;
}
/* 2. 支付密码弹窗 */
.modal-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.5);
z-index: 999;
display: flex;
align-items: flex-end;
}
.password-modal {
width: 100%;
background-color: #fff;
border-radius: 12px 12px 0 0;
padding-bottom: 20px;
}
.modal-header {
display: flex;
align-items: center;
justify-content: space-between;
height: 50px;
padding: 0 15px;
border-bottom: 1px solid #f0f0f0;
}
.close-icon {
font-size: 22px;
width: 40px;
height: 50px;
display: flex;
align-items: center;
}
.modal-title {
font-size: 17px;
font-weight: 500;
color: #000;
}
.header-right {
width: 40px;
}
.pay-info {
padding: 20px 15px;
text-align: center;
}
.merchant-name {
display: block;
font-size: 16px;
color: #333;
margin-bottom: 10px;
}
.pay-amount {
font-size: 32px;
font-weight: 500;
color: #000;
}
.pay-way {
display: flex;
align-items: center;
justify-content: space-between;
padding: 15px;
border-bottom: 1px solid #f0f0f0;
}
.way-label {
font-size: 15px;
color: #666;
}
.way-content {
display: flex;
align-items: center;
}
.bank-icon {
width: 16px;
height: 16px;
margin-right: 3px;
}
.bank-name {
font-size: 15px;
color: #333;
margin-right: 8px;
}
.arrow-icon {
font-size: 14px;
color: #999;
}
/* 银行卡列表 */
.bank-list {
padding: 0 15px;
border-bottom: 1px solid #f0f0f0;
}
.bank-item {
display: flex;
align-items: center;
height: 50px;
border-bottom: 1px solid #f5f5f5;
}
.bank-item:last-child {
border-bottom: none;
}
.bank-item-icon {
width: 24px;
height: 24px;
margin-right: 10px;
}
.bank-item-name {
flex: 1;
font-size: 15px;
color: #333;
}
.check-icon {
font-size: 18px;
color: #007aff;
}
/* 密码输入框 */
.password-input {
display: flex;
justify-content: space-between;
padding: 20px 15px;
}
.password-item {
width: 50px;
height: 50px;
/* border: 1px solid #dcdcdc; */
background-color: #efefef;
border-radius: 4px;
display: flex;
align-items: center;
justify-content: center;
}
.password-dot {
width: 12px;
height: 12px;
background-color: #000;
border-radius: 50%;
}
/* 数字键盘 */
.keyboard {
width: 100%;
}
.keyboard-row {
display: flex;
}
.keyboard-item {
flex: 1;
height: 54px;
line-height: 54px;
text-align: center;
font-size: 24px;
color: #000;
border: 1px solid #f0f0f0;
background-color: #fff;
}
.keyboard-item:active {
background-color: #e5e5e5;
}
.keyboard-item.empty {
background-color: #f5f5f5;
border: none;
}
.keyboard-item.delete {
background-color: #f5f5f5;
border: none;
}
/* 3. 缴费成功页 */
.success-page {
width: 100%;
min-height: 100vh;
background-color: #fff;
text-align: center;
}
.icon-box {
display: flex;
justify-content: center;
width: 100vw;
height: 150px;
padding: 30px 0px;
}
.success-icon {
/* margin-top: 60px; */
margin-bottom: 20px;
width: 100px;
height: 100px;
font-size: 80px;
color: #007aff;
background-color: #007aff;
color: #fff;
border-radius: 50%;
}
.success-icon .iconfont {}
.success-text {
font-size: 20px;
font-weight: 600;
color: #000;
margin-bottom: 10px;
}
.success-desc {
font-size: 15px;
color: #999;
margin-bottom: 50px;
}
/* 核心:欠费金额行样式 */
.arrears-row {
display: flex;
align-items: center;
justify-content: space-between;
/* padding: 15px; */
margin-bottom: 30px;
}
.arrears-text {
font-size: 14px;
color: #333;
font-weight: 600;
}
.auto-fill-btn {
font-size: 14px;
color: #007aff;
/* 蓝色,匹配设计图 */
font-weight: 500;
padding: 5px 0;
}
.status-bar {
width: 100vw;
height: 46px;
}
</style>

View File

@@ -0,0 +1,659 @@
<template>
<view class="contentStyle" style="">
<view class="status-bar"></view>
<uni-nav-bar :title="arrearsPayment" left-icon="left" @clickLeft="goBack" backgroundColor="transparent"
:border="false">
</uni-nav-bar>
<Loading mask="true" click="true" ref="loading"></Loading>
<!-- 表单区域 -->
<scroll-view class="page" scroll-y="true">
<view class="container">
<!-- 1. 物业费详情页 -->
<view v-if="currentStep === 1" class="pay-page">
<!-- 金额展示 -->
<view class="amount-section">
<text class="amount-text">- ¥1680.00</text>
</view>
<!-- 缴费信息 -->
<view class="info-section" style="">
<view class="info-item">
<text class="info-label">户名</text>
<text class="info-value">桂花园别墅2号楼2层</text>
</view>
<view class="info-item">
<text class="info-label">户主</text>
<text class="info-value">周大声</text>
</view>
<view class="info-item">
<text class="info-label">缴费单位</text>
<text class="info-value">CT物业</text>
</view>
<view class="info-item" style="border-bottom: 1px solid #c7c7c7;padding-bottom: 10px;">
<text class="info-label">可用余额</text>
<text class="info-value">0.00</text>
</view>
<view class="info-payAmountItem">
<text class="info-payAmount">{{'¥'+payAmount}}</text>
</view>
</view>
<!-- 缴费金额输入 -->
<!-- <view class="input-section">
<input type="number" v-model="payAmount" class="amount-input" placeholder="请输入缴费金额" />
</view> -->
<!-- 立即缴费按钮 -->
<view class="btn-section">
<button class="pay-btn" @click="handlePay">立即缴费</button>
</view>
</view>
<!-- 2. 支付密码弹窗 -->
<view v-if="currentStep === 2" class="modal-overlay" @click="closePasswordModal">
<view class="password-modal" @click.stop>
<!-- 弹窗头部 -->
<view class="modal-header">
<view class="close-icon" @click="closePasswordModal">
<text class="close-text">×</text>
</view>
<view class="modal-title">请输入支付密码</view>
<view class="header-right"></view>
</view>
<!-- 商户信息 & 金额 -->
<view class="pay-info">
<text class="merchant-name">智享日照洗护</text>
<text class="pay-amount">¥{{ payAmount }}</text>
</view>
<!-- 支付方式选择 -->
<view class="pay-way">
<text class="way-label">支付方式</text>
<view class="way-content">
<image class="bank-icon" src="/static/propertyImgs/bank-logo.png" mode="aspectFit">
</image>
<text class="bank-name">建设银行储蓄卡(8888)</text>
<text class="arrow-icon">></text>
</view>
</view>
<!-- 银行卡选择列表 -->
<!-- <view v-if="showBankList" class="bank-list">
<view v-for="(item, index) in bankList" :key="index" class="bank-item"
@click="selectBank(item)">
<image class="bank-item-icon" :src="item.icon" mode="aspectFit"></image>
<text class="bank-item-name">{{ item.name }}</text>
<text v-if="selectedBank.id === item.id" class="check-icon">&#xe60c;</text>
</view>
</view> -->
<!-- 密码输入框 -->
<view class="password-input">
<view v-for="(item, index) in 6" :key="index" class="password-item">
<text v-if="password.length > index" class="password-dot"></text>
</view>
</view>
<!-- 数字键盘 -->
<view class="keyboard">
<view class="keyboard-row">
<view class="keyboard-item" @click="inputPassword(1)">1</view>
<view class="keyboard-item" @click="inputPassword(2)">2</view>
<view class="keyboard-item" @click="inputPassword(3)">3</view>
</view>
<view class="keyboard-row">
<view class="keyboard-item" @click="inputPassword(4)">4</view>
<view class="keyboard-item" @click="inputPassword(5)">5</view>
<view class="keyboard-item" @click="inputPassword(6)">6</view>
</view>
<view class="keyboard-row">
<view class="keyboard-item" @click="inputPassword(7)">7</view>
<view class="keyboard-item" @click="inputPassword(8)">8</view>
<view class="keyboard-item" @click="inputPassword(9)">9</view>
</view>
<view class="keyboard-row">
<view class="keyboard-item empty"></view>
<view class="keyboard-item" @click="inputPassword(0)">0</view>
<view class="keyboard-item delete" @click="deletePassword">
<text class="icon"></text>
</view>
</view>
</view>
</view>
</view>
<!-- 3. 缴费成功页 -->
<view v-if="currentStep === 3" class="success-page">
<view class="icon-box">
<!-- 成功图标 -->
<view class="success-icon">
<text class="icon"></text>
</view>
</view>
<!-- 成功文字 -->
<view class="success-text">缴费成功</view>
<view class="success-desc">成功缴纳物业费</view>
<!-- 返回按钮 -->
<view class="btn-section">
<button class="pay-btn" @click="handleBackToList">返回生活缴费</button>
</view>
</view>
</view>
</scroll-view>
</view>
</template>
<script>
export default {
onReady: function(e) {},
components: {
},
computed: {
},
created() {
},
onShow() {
},
mounted() {},
onReady() {
this.$nextTick(() => {
})
},
data() {
return {
/* 设定状态栏默认高度 */
statusBarHeight: 20,
arrearsPayment: uni.getStorageSync('arrearsPayment'),
currentStep: 1, // 1: 详情页 2: 密码弹窗 3: 成功页
payAmount: "1680.00", // 缴费金额
password: "", // 输入的密码
showBankList: false, // 是否显示银行卡列表
// 银行卡列表
bankList: [{
id: 1,
name: "建设银行储蓄卡(8888)",
icon: "/static/bank-logo.png"
},
{
id: 2,
name: "工商银行储蓄卡(6666)",
icon: "/static/icbc-logo.png"
},
{
id: 3,
name: "招商银行储蓄卡(9999)",
icon: "/static/cmb-logo.png"
}
],
// 默认选中的银行卡
selectedBank: {
id: 1,
name: "建设银行储蓄卡(8888)",
icon: "/static/bank-logo.png"
}
}
},
methods: {
goBack() {
uni.redirectTo({
url: '/pages/payment-list/paymentIndex',
success: res => {},
fail: () => {},
complete: () => {}
});
},
// 点击立即缴费
handlePay() {
if (!this.payAmount || Number(this.payAmount) <= 0) {
uni.showToast({
title: "请输入正确的缴费金额",
icon: "none"
});
return;
}
this.currentStep = 2;
},
// 关闭密码弹窗
closePasswordModal() {
this.currentStep = 1;
this.password = "";
this.showBankList = false;
},
// 选择银行卡
selectBank(item) {
this.selectedBank = item;
this.showBankList = false;
},
// 输入密码
inputPassword(num) {
if (this.password.length >= 6) return;
this.password += num.toString();
// 密码满6位自动提交
if (this.password.length === 6) {
this.handleConfirmPay();
}
},
// 删除密码
deletePassword() {
this.password = this.password.slice(0, -1);
},
// 确认支付
handleConfirmPay() {
// 这里模拟支付请求,实际项目中调用后端接口
uni.showLoading({
title: "支付中...",
mask: true
});
setTimeout(() => {
uni.hideLoading();
// 支付成功,跳转到成功页
this.currentStep = 3;
this.password = "";
this.showBankList = false;
}, 1500);
},
// 返回生活缴费列表
handleBackToList() {
uni.redirectTo({
url: '/pages/payment-list/paymentIndex',
success: res => {},
fail: () => {},
complete: () => {}
});
}
},
}
</script>
<style lang="scss">
page {
background: #f2f1f6;
}
</style>
<style scoped>
.contentStyle {
display: flex;
flex-direction: column;
height: 100vh;
/* 关键:占满屏幕高度 */
background-color: #f9f9f9;
}
.page {
flex: 1;
overflow-y: auto;
padding: 0px 20px;
box-sizing: border-box;
background-color: #fff;
}
/* 1. 物业费详情页 */
.pay-page {
width: 100%;
min-height: 100vh;
background-color: #fff;
}
.amount-section {
padding: 30px 0;
text-align: center;
}
.amount-text {
font-size: 28px;
font-weight: 600;
color: #bd3124;
}
.info-section {
padding: 0 15px 15px 15px;
/* margin-bottom: 20px; */
}
.info-item {
display: flex;
justify-content: space-between;
align-items: center;
height: 40px;
}
.info-label {
font-size: 15px;
color: #666;
}
.info-value {
font-size: 15px;
color: #333;
font-weight: 600;
}
.info-payAmountItem {
height: 50px;
line-height: 50px;
margin-bottom: 30px;
}
.info-payAmount {
font-size: 25px;
color: #333;
font-weight: 600;
}
.input-section {
padding: 0 15px;
margin-bottom: 30px;
}
.amount-input {
width: 100%;
height: 50px;
font-size: 24px;
border: none;
outline: none;
}
.btn-section {
padding: 0 15px;
}
.pay-btn {
width: 100%;
height: 50px;
line-height: 50px;
background-color: #007aff;
color: #fff;
border-radius: 8px;
font-size: 16px;
border: none;
outline: none;
}
/* 2. 支付密码弹窗 */
.modal-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.5);
z-index: 999;
display: flex;
align-items: flex-end;
}
.password-modal {
width: 100%;
background-color: #fff;
border-radius: 12px 12px 0 0;
padding-bottom: 20px;
}
.modal-header {
display: flex;
align-items: center;
justify-content: space-between;
height: 50px;
padding: 0 15px;
border-bottom: 1px solid #f0f0f0;
}
.close-icon {
font-size: 22px;
width: 40px;
height: 50px;
display: flex;
align-items: center;
}
.modal-title {
font-size: 17px;
font-weight: 500;
color: #000;
}
.header-right {
width: 40px;
}
.pay-info {
padding: 20px 15px;
text-align: center;
}
.merchant-name {
display: block;
font-size: 16px;
color: #333;
margin-bottom: 10px;
}
.pay-amount {
font-size: 32px;
font-weight: 500;
color: #000;
}
.pay-way {
display: flex;
align-items: center;
justify-content: space-between;
padding: 15px;
border-bottom: 1px solid #f0f0f0;
}
.way-label {
font-size: 15px;
color: #666;
}
.way-content {
display: flex;
align-items: center;
}
.bank-icon {
width: 16px;
height: 16px;
margin-right: 3px;
}
.bank-name {
font-size: 15px;
color: #333;
margin-right: 8px;
}
.arrow-icon {
font-size: 14px;
color: #999;
}
/* 银行卡列表 */
.bank-list {
padding: 0 15px;
border-bottom: 1px solid #f0f0f0;
}
.bank-item {
display: flex;
align-items: center;
height: 50px;
border-bottom: 1px solid #f5f5f5;
}
.bank-item:last-child {
border-bottom: none;
}
.bank-item-icon {
width: 24px;
height: 24px;
margin-right: 10px;
}
.bank-item-name {
flex: 1;
font-size: 15px;
color: #333;
}
.check-icon {
font-size: 18px;
color: #007aff;
}
/* 密码输入框 */
.password-input {
display: flex;
justify-content: space-between;
padding: 20px 15px;
}
.password-item {
width: 50px;
height: 50px;
/* border: 1px solid #dcdcdc; */
background-color: #efefef;
border-radius: 4px;
display: flex;
align-items: center;
justify-content: center;
}
.password-dot {
width: 12px;
height: 12px;
background-color: #000;
border-radius: 50%;
}
/* 数字键盘 */
.keyboard {
width: 100%;
}
.keyboard-row {
display: flex;
}
.keyboard-item {
flex: 1;
height: 54px;
line-height: 54px;
text-align: center;
font-size: 24px;
color: #000;
border: 1px solid #f0f0f0;
background-color: #fff;
}
.keyboard-item:active {
background-color: #e5e5e5;
}
.keyboard-item.empty {
background-color: #f5f5f5;
border: none;
}
.keyboard-item.delete {
background-color: #f5f5f5;
border: none;
}
/* 3. 缴费成功页 */
.success-page {
width: 100%;
min-height: 100vh;
background-color: #fff;
text-align: center;
}
.icon-box {
display: flex;
justify-content: center;
width: 100vw;
height: 150px;
padding: 30px 0px;
}
.success-icon {
/* margin-top: 60px; */
margin-bottom: 20px;
width: 100px;
height: 100px;
font-size: 80px;
color: #007aff;
background-color: #007aff;
color: #fff;
border-radius: 50%;
}
.success-icon .iconfont {}
.success-text {
font-size: 20px;
font-weight: 600;
color: #000;
margin-bottom: 10px;
}
.success-desc {
font-size: 15px;
color: #999;
margin-bottom: 50px;
}.status-bar{
width: 100vw;
height: 46px;
}
</style>

View File

@@ -0,0 +1,387 @@
<template>
<view class="contentStyle" style="">
<view :style="{height:statusBarHeight+'px'}"></view>
<uni-nav-bar title="生活缴费" left-icon="left" @clickLeft="goBack" backgroundColor="transparent" :border="false">
</uni-nav-bar>
<view class="topbox">
<image class="titleImg" src="/static/propertyImgs/payTitle.png"></image>
<image class="payImg" src="/static/propertyImgs/payImg.png"></image>
</view>
<scroll-view class="page" scroll-y="true">
<view class="container">
<!-- 已缴费/欠费列表 -->
<view class="bill-list">
<view class="bill-item" @click="goToBill(item)" v-for="item,index in paymentList" :key="index">
<image class="billIcon" :src="item.icon"></image>
<view class="bill-info">
<view class="bill-title">{{item.name}}
<view class="bill-status debt" v-if="item.status=='已欠费'">已欠费</view>
</view>
<view class="bill-desc">{{item.address}}</view>
</view>
<view class="arrow"></view>
</view>
</view>
<!-- 新增缴费区域 -->
<view class="add-section">
<view class="section-header">
<text class="section-title">新增缴费</text>
<text class="record-link" @click="goToRecord">缴费记录</text>
</view>
<view class="grid-list">
<view class="grid-item" v-for="(item, index) in addList" :key="index" @click="goToAdd(item)">
<view class="icon-box" :class="item.iconClass">
<image class="iconStyle" :src="item.icon"></image>
</view>
<text class="item-name">{{ item.name +'费'}}</text>
</view>
</view>
</view>
</view>
</scroll-view>
</view>
</template>
<script>
export default {
onReady: function(e) {
// console.log('onReady')
},
components: {
},
computed: {
},
created() {
},
onShow() {
},
onLoad() {
},
mounted() {},
data() {
return {
/* 设定状态栏默认高度 */
statusBarHeight: 20,
paymentList: [{
id: 1,
name: '物业费',
status: '已欠费',
address: '桂花园别墅2号楼2层',
icon: "/static/propertyImgs/property.png",
},
{
id: 2,
name: '暖气费',
status: '',
address: '山东省日照市东港区桂花园别墅2号楼2层',
icon: "/static/propertyImgs/heating.png",
},
{
id: 3,
name: '电费',
status: '已欠费',
address: '山东省日照市东港区桂花园别墅2号楼2层',
icon: "/static/propertyImgs/electric.png",
}
],
// 新增缴费列表数据
addList: [{
name: "燃气",
type: "gas",
icon: "/static/propertyImgs/gas.png",
iconClass: "gas"
},
{
name: "水",
type: "water",
icon: "/static/propertyImgs/water.png",
iconClass: "water"
},
{
name: "物业",
type: "property",
icon: "/static/propertyImgs/property.png",
iconClass: "property"
},
{
name: "电",
type: "electric",
icon: "/static/propertyImgs/electric.png",
iconClass: "electric"
},
{
name: "暖气",
type: "heating",
icon: "/static/propertyImgs/heating.png",
iconClass: "heating"
},
{
name: "车位",
type: "parking",
icon: "/static/propertyImgs/parkingIcon.png",
iconClass: "parking"
}
]
}
},
methods: {
// 跳转到缴费
goToBill(item) {
uni.setStorageSync('arrearsPayment', item.name)
if (item.status == '已欠费') {
uni.redirectTo({
url: '/pages/payment-list/arrearsPayment',
success: res => {},
fail: () => {},
complete: () => {}
});
} else {
uni.redirectTo({
url: '/pages/payment-list/noArrearsPayment',
success: res => {},
fail: () => {},
complete: () => {}
});
}
},
// 跳转到缴费记录
goToRecord() {
uni.redirectTo({
url: '/pages/payment-list/paymentRecord',
success: res => {},
fail: () => {},
complete: () => {}
});
},
// 跳转到新增缴费
goToAdd(type) {
uni.setStorageSync('addPaymentType', type.name)
uni.redirectTo({
url: '/pages/payment-list/addPayment',
success: res => {},
fail: () => {},
complete: () => {}
});
},
goBack() {
uni.switchTab({
url: '/pages/serviceIndex/serviceIndex',
success: res => {},
fail: () => {},
complete: () => {}
});
},
},
}
</script>
<style lang="scss">
page {
background: linear-gradient(180deg, #dfedfd, #F6FAFF);
}
</style>
<style scoped>
.contentStyle {
display: flex;
flex-direction: column;
height: 100vh;
/* 关键:占满屏幕高度 */
background: linear-gradient(180deg, #dfedfd, #F6FAFF);
}
.page {
flex: 1;
overflow-y: auto;
padding: 0px 20px;
box-sizing: border-box;
/* background-color: #fff; */
}
.topbox {
height: 80px;
width: 100%;
position: relative;
}
.titleImg {
width: 131px;
height: 37px;
position: absolute;
left: 20px;
bottom: 10px;
}
.payImg {
width: 250px;
height: 230px;
position: absolute;
right: -60px;
bottom: -100px;
}
.container {
/* padding: 15px; */
}
/* ===================== 欠费列表 ===================== */
.bill-list {
margin-bottom: 40rpx;
background-color: #fff;
padding: 15px;
border-radius: 5px;
}
.bill-item {
display: flex;
align-items: center;
background-color: #F6F9FE;
border-radius: 12rpx;
padding: 10px 10px 10px 0px;
margin-bottom: 20rpx;
}
.bill-info {
width: calc(90% - 50px);
flex: 1;
}
.billIcon {
width: 30px;
height: 30px;
margin: 10px;
}
.bill-title {
font-size: 16px;
font-weight: 600;
color: #333;
margin-bottom: 10rpx;
display: flex;
}
.bill-desc {
font-size: 14px;
font-weight: 600;
color: #999;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
/* 已欠费标签 */
.bill-status {
padding: 1px 8px;
border-radius: 15px;
font-size: 12px;
font-weight: 500;
color: #fff;
background-color: #E34D59;
margin-left: 5px;
}
/* 右侧箭头 */
.arrow {
width: 12rpx;
height: 12rpx;
border-right: 4rpx solid #ccc;
border-bottom: 4rpx solid #ccc;
transform: rotate(-45deg);
}
/* ===================== 新增缴费区域 ===================== */
.add-section {
background-color: #fff;
padding: 15px;
border-radius: 5px;
}
.section-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 30rpx;
}
.section-title {
font-size: 16px;
font-weight: 600;
color: #333;
}
.record-link {
font-size: 14px;
color: #1677ff;
}
/* 网格列表 */
.grid-list {
display: flex;
flex-wrap: wrap;
justify-content: flex-start;
gap: 13px;
}
.grid-item {
width: calc(25% - 30px);
background-color: #F6F9FE;
border-radius: 12rpx;
padding: 10px;
margin-bottom: 20rpx;
display: flex;
align-items: center;
flex-direction: column;
}
/* 图标容器 */
.icon-box {
width: 80rpx;
height: 80rpx;
border-radius: 50%;
background-color: #e6f0ff;
display: flex;
align-items: center;
justify-content: center;
/* margin-right: 24rpx; */
font-size: 40rpx;
}
.iconStyle {
width: 100%;
height: 100%;
}
.item-name {
font-size: 14px;
font-weight: 600;
color: #333;
}
</style>

View File

@@ -0,0 +1,351 @@
<template>
<view class="contentStyle" style="">
<view class="status-bar"></view>
<uni-nav-bar title="缴费记录" left-icon="left" @clickLeft="goBack" backgroundColor="transparent"
:border="false">
</uni-nav-bar>
<Loading mask="true" click="true" ref="loading"></Loading>
<!-- 表单区域 -->
<scroll-view class="page" scroll-y="true">
<!-- 累计缴费 -->
<view class="total-box">
<view class="total-title">累计缴费</view>
<view class="total-price">¥800.00</view>
<!-- 折线图 Canvas -->
<canvas canvas-id="chargeChart" class="chart-canvas"></canvas>
</view>
<!-- 缴费明细 -->
<view class="list-wrapper">
<!-- 按月份分组渲染 -->
<view class="month-group" v-for="(item, idx) in monthGroups" :key="idx">
<view class="month-title">
{{ item.month }} 缴费 ¥{{ item.total }}
</view>
<view class="item-list">
<view class="bill-item" v-for="bill in item.list" :key="bill.id">
<view class="bill-icon">
<text class="icon">{{ bill.icon }}</text>
</view>
<view class="bill-info">
<view class="name">{{ bill.name }}</view>
<view class="account">{{ bill.account }}</view>
</view>
<view class="bill-price">-{{ bill.amount }}</view>
</view>
</view>
</view>
</view>
</scroll-view>
</view>
</template>
<script>
export default {
onReady: function(e) {},
components: {
},
computed: {
// 自动按月份分组
monthGroups() {
let map = {}
this.billList.forEach(bill => {
if (!map[bill.month]) {
map[bill.month] = {
month: bill.month,
total: 0,
list: []
}
}
map[bill.month].list.push(bill)
map[bill.month].total += bill.amount
})
return Object.values(map).sort((a, b) => b.month - a.month)
}
},
created() {
},
onShow() {
},
mounted() {},
onReady() {
this.$nextTick(() => {
this.drawLineChart();
})
},
data() {
return {
/* 设定状态栏默认高度 */
statusBarHeight: 20,
// 折线图数据
chart: {
months: ['1月', '2月', '3月', '4月', '5月', '6月'],
values: [100, 140, 230, 100, 130, 100]
},
// ====================== 核心5月6月放在一个list里 ======================
billList: [
// 5月数据
{
id: 1,
month: 5,
name: '燃气费',
account: '123123123123',
amount: 25,
icon: ''
},
{
id: 2,
month: 5,
name: '水费',
account: '123123123123',
amount: 25,
icon: ''
},
// 6月数据
{
id: 3,
month: 6,
name: '燃气费',
account: '123123123123',
amount: 30,
icon: ''
},
{
id: 4,
month: 6,
name: '水费',
account: '123123123123',
amount: 10,
icon: ''
},
{
id: 5,
month: 6,
name: '电费',
account: '123123123123',
amount: 30,
icon: '⚡'
}
]
}
},
methods: {
drawLineChart() {
const ctx = uni.createCanvasContext('chargeChart', this)
const canvasPage = uni.getSystemInfoSync();
const w = canvasPage.windowWidth - 30;
const h = canvasPage.windowHeight * 0.3;
const padding = 40
const stepX = (w - 60) / (this.chart.months.length - 1)
const maxVal = 250
ctx.setFillStyle('#999')
ctx.setFontSize(12)
ctx.setTextAlign('center')
// 绘制网格
ctx.beginPath()
ctx.setStrokeStyle('#d8d8d8')
ctx.setLineWidth(1)
for (let i = 0; i <= 5; i++) {
let y = padding + (h - padding * 2) * (1 - i / 5)
ctx.moveTo(40, y)
ctx.lineTo(w - 20, y)
ctx.fillText(i * 50 + '元', 20, y + 4)
}
ctx.stroke()
// 绘制X轴月份
this.chart.months.forEach((m, i) => {
let x = 40 + i * stepX
ctx.fillText(m, x, h - 20)
})
// 绘制折线
let points = []
this.chart.values.forEach((val, i) => {
let x = 40 + i * stepX
let y = padding + (h - padding * 2) * (1 - val / maxVal)
points.push({
x,
y
})
})
ctx.beginPath()
ctx.setStrokeStyle('#4080FF')
ctx.setLineWidth(2)
points.forEach((p, i) => {
if (i === 0) ctx.moveTo(p.x, p.y)
else ctx.lineTo(p.x, p.y)
})
ctx.stroke()
// 绘制圆点
points.forEach((p, i) => {
ctx.beginPath()
ctx.arc(p.x, p.y, 3, 0, 2 * Math.PI)
ctx.setFillStyle('#fff')
ctx.fill()
ctx.setStrokeStyle('#4080FF')
ctx.setLineWidth(2)
ctx.stroke()
ctx.setFillStyle('#4080FF')
ctx.setFontSize(13)
ctx.fillText(this.chart.values[i] + '元', p.x, p.y - 10)
})
ctx.draw()
},
goBack() {
uni.redirectTo({
url: '/pages/payment-list/paymentIndex',
success: res => {},
fail: () => {},
complete: () => {}
});
},
},
}
</script>
<style lang="scss">
page {
background: #f2f1f6;
}
</style>
<style scoped>
.contentStyle {
display: flex;
flex-direction: column;
height: 100vh;
/* 关键:占满屏幕高度 */
background-color: #f9f9f9;
}
.page {
flex: 1;
overflow-y: auto;
padding: 0px 20px;
box-sizing: border-box;
background-color: #fff;
}
.total-box {
padding: 15px;
/* margin-bottom: 10px; */
}
.total-title {
font-size: 16px;
font-weight: 600;
color: #333;
}
.total-price {
font-size: 24px;
font-weight: bold;
margin-top: 6px;
margin-bottom: 10px;
}
.chart-canvas {
width: calc(100vw - 30px);
height: 30vh;
}
.list-wrapper {
padding: 0 15px;
}
.month-group {
padding-bottom: 15px;
border-bottom: 1px solid #f2f2f2;
}
.month-group:last-child {
border-bottom: none;
}
.month-title {
font-size: 16px;
font-weight: 600;
margin-bottom: 15px;
}
.bill-item {
display: flex;
align-items: center;
margin-bottom: 15px;
}
.bill-icon {
width: 40px;
height: 40px;
background: #e6efff;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
color: #4080FF;
margin-right: 12px;
font-size: 18px;
}
.bill-info {
flex: 1;
}
.name {
font-size: 14px;
color: #333;
font-weight: 600;
}
.account {
font-size: 12px;
color: #999;
margin-top: 4px;
}
.bill-price {
font-size: 16px;
color: #333;
}.status-bar{
width: 100vw;
height: 46px;
}
</style>

View File

@@ -0,0 +1,346 @@
<template>
<view class="contentStyle" style="">
<view class="status-bar"></view>
<uni-nav-bar title="选择缴费单位" left-icon="left" @clickLeft="goBack" backgroundColor="transparent" :border="false">
</uni-nav-bar>
<Loading mask="true" click="true" ref="loading"></Loading>
<!-- 表单区域 -->
<scroll-view class="page" scroll-y="true">
<!-- 城市 + 搜索 -->
<view class="search-section">
<!-- 城市选择器 -->
<uni-data-picker placeholder="选择省市" popup-title="选择省市" :localdata="alladdress"
field="code as value, value as text" orderby="value asc" :step-searh="true" self-field="code"
parent-field="code" @change="onchange" v-model="cityValue" class="city-picker">
<view class="city-select">
<text class="city-text ellipsis">{{ selectedCity }}</text>
<text class="arrow-icon"></text>
</view>
</uni-data-picker>
<!-- 搜索框 -->
<!-- <view class="search-input">
<input v-model="keyword" placeholder="请输入缴费单位名称" placeholder-class="ph" />
</view> -->
<view class="search-box">
<input class="search-input" v-model="searchKey" placeholder="请输入缴费单位名称"
placeholder-class="input-placeholder" @input="onSearch" />
<uni-icons type="search" size="20" color="#999" />
</view>
</view>
<!-- <scroll-view scroll-y class="list-scroll"> -->
<view v-for="item in filterList">
<view class="group-title">{{item.name}}</view>
<view class="item" v-for="itemChild in item.children" :key="itemChild.id"
@click="selectItem(itemChild)">
{{ itemChild.name }}
</view>
</view>
<!-- </scroll-view> -->
</scroll-view>
</view>
</template>
<script>
// 引入外部城市数据
import Address from '@/static/js/chinaRegions/picker-region.js'
export default {
onReady: function(e) {},
components: {
},
computed: {
filterList() {
// 没有关键词直接返回全部
if (!this.searchKey) return this.companyList;
const key = this.searchKey.trim().toLowerCase();
// 过滤第一层 + 第二层
return this.companyList.filter(group => {
// 过滤子项:只要子项包含关键词就保留
const hasChild = group.children.some(child =>
child.name.toLowerCase().includes(key)
);
// 同时支持 分组名 搜索(不需要可删除)
const groupMatch = group.name.toLowerCase().includes(key);
return hasChild || groupMatch;
}).map(group => {
// 返回过滤后的子项
return {
...group,
children: group.children.filter(child =>
child.name.toLowerCase().includes(key)
)
};
});
}
},
created() {
},
onShow() {
},
onLoad() {
},
mounted() {},
onReady() {
this.$nextTick(() => {
})
},
data() {
return {
selectedCity: '选择城市',
cityValue: '',
alladdress: Address,
searchKey: '',
/* 设定状态栏默认高度 */
statusBarHeight: 20,
showPicker: false,
keyword: '',
// 列表:一个大数组
companyList: [{
id: 7,
name: '官方机构',
children: [{
id: 1,
name: "XX市燃气集团有限公司"
},
{
id: 2,
name: "XX市供水有限公司"
},
{
id: 3,
name: "XX供电服务中心"
},
{
id: 4,
name: "XX新能源服务公司"
},
{
id: 5,
name: "XX公共事业缴费中心"
},
{
id: 6,
name: "XX综合能源服务站"
}
]
},
{
id: 8,
name: '非智享日照提供的服务单位',
children: [{
id: 9,
name: "XX市燃气集团有限公司"
},
{
id: 10,
name: "XX能源集团"
},
]
}
]
}
},
methods: {
onchange(e) {
console.log('选中text数组:', e.detail.value)
let arr = e.detail.value
let selectedCity = ''
// 拼接省市区
// arr.forEach((item, index) => {
// if (index != arr.length - 1) {
// selectedCity = selectedCity + item.text + '/'
// } else {
// selectedCity = selectedCity + item.text
// }
// })
// 只显示城市判断
if (arr[1].text == '市辖区') {
selectedCity = arr[0].text
} else {
selectedCity = arr[1].text
}
this.selectedCity = selectedCity
},
// 搜索输入事件
onSearch() {
// 实时过滤computed 自动处理
},
goBack() {
uni.redirectTo({
url: '/pages/payment-list/paymentIndex',
success: res => {},
fail: () => {},
complete: () => {}
});
},
},
}
</script>
<style lang="scss">
page {
background: #f2f1f6;
}
</style>
<style scoped>
.contentStyle {
display: flex;
flex-direction: column;
height: 100vh;
/* 关键:占满屏幕高度 */
background-color: #f9f9f9;
}
.page {
flex: 1;
overflow-y: auto;
padding: 0px 20px;
box-sizing: border-box;
background-color: #fff;
}
.search-section {
background-color: #fff;
padding: 10px;
display: flex;
}
/* 隐藏 uni-data-picker 原生样式,只保留点击功能 */
.city-picker {
display: block;
width: auto;
}
/* 自定义城市选择区域,完美还原「日照市 ▼」 */
.city-select {
display: flex;
align-items: center;
padding: 4px 0;
cursor: pointer;
width: 80px;
height: 35px;
}
.ellipsis {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.city-text {
font-size: 14px;
/* 匹配截图字体大小 */
font-weight: 600;
color: #000000;
line-height: 1;
}
/* 自定义下拉箭头1:1 还原截图样式 */
.arrow-icon {
width: 0;
height: 0;
border-left: 5px solid transparent;
border-right: 5px solid transparent;
border-top: 8px solid #333333;
/* 纯黑实心三角,和截图一致 */
margin-top: 2px;
}
/* 搜索框 */
.search-box {
display: flex;
width: 100%;
align-items: center;
background-color: #f3f4f6;
border-radius: 50rpx;
/* margin: 0rpx 30rpx 20rpx 30rpx; */
padding: 0 15px;
margin: 0px 0px 0px 5px;
height: 70rpx;
}
.search-input {
flex: 1;
height: 100%;
font-size: 30rpx;
border: none;
background: transparent;
}
.input-placeholder {
color: #999;
font-size: 30rpx;
}
.search-icon {
font-size: 36rpx;
color: #999;
}
/* 列表 */
.list-scroll {
height: calc(100vh - 44px - 70px);
}
.group-title {
padding: 10px 15px;
font-size: 14px;
color: #666;
}
.item {
background: #fff;
padding: 10px 15px;
font-size: 14px;
border-bottom: 1px solid #f2f2f2;
}
.item:active {
background: #f2f2f2;
}
::v-deep .selected-item {
margin-left: 5px !important;
margin-right: 5px !important;
}
.status-bar {
width: 100vw;
height: 46px;
}
</style>

View File

@@ -0,0 +1,579 @@
<template>
<view class="contentStyle" style="">
<view class="status-bar"></view>
<uni-nav-bar title="投诉建议" backgroundColor="transparent" :border="false" left-icon="left" @clickLeft="goBack">
</uni-nav-bar>
<!-- 表单区域 -->
<scroll-view class="page" scroll-y="true">
<!-- 表单主体 -->
<view class="form-content">
<text class="form-tip">请填写反馈信息</text>
<!-- 1. 投诉类型点击弹出popup -->
<view class="form-row" @click="showTypePopup = true">
<view class="row-left">
<text class="required-star">*</text>
<text class="label-text">投诉类型</text>
</view>
<view class="row-right">
<text class="value-text"
:class="{placeholderStyle:selectedType}">{{ selectedType || '请选择投诉类型' }}</text>
<text class="arrow-icon">></text>
</view>
</view>
<!-- 2. 投诉物业选中物业服务时显示 -->
<view class="form-row" v-if="selectedType === '物业服务'" @click="showPropertyPopup = true">
<view class="row-left">
<text class="required-star">*</text>
<text class="label-text">投诉物业</text>
</view>
<view class="row-right">
<text class="value-text"
:class="{placeholderStyle:selectedProperty}">{{ selectedProperty || '请选择' }}</text>
<text class="arrow-icon">></text>
</view>
</view>
<!-- 3. 问题描述 -->
<view class="form-row desc-row" style="flex-direction: column;">
<view class="row-left">
<text class="required-star">*</text>
<text class="label-text">问题描述</text>
</view>
<view class="row-right desc-right">
<textarea v-model="description" placeholder="请描述您的问题" class="desc-input" auto-height
maxlength="500"></textarea>
</view>
</view>
<!-- 4. 投诉人姓名 -->
<view class="form-row">
<view class="row-left">
<text class="label-text">投诉人姓名</text>
</view>
<view class="row-right">
<input v-model="name" placeholder="请输入姓名" class="text-input" type="text" />
</view>
</view>
<!-- 5. 手机号码 -->
<view class="form-row">
<view class="row-left">
<text class="label-text">手机号码</text>
</view>
<view class="row-right">
<input v-model="phone" placeholder="请输入手机号" class="text-input" type="number" maxlength="11" />
</view>
</view>
<!-- 6. 相关照片多图上传+删除 -->
<view class="form-row photo-row">
<view class="row-left">
<text class="label-text">相关照片</text>
</view>
<view class="row-right photo-right">
<view class="photo-grid">
<!-- 已上传图片 -->
<view v-for="(img, index) in tempFilePaths" :key="index" class="photo-item">
<image :src="img" mode="aspectFill" class="photo-img"></image>
<view class="delete-btn" @click="deletePhoto(index)">
<text class="delete-icon">×</text>
</view>
</view>
<!-- 上传加号按钮 -->
<view class="add-btn" v-if="tempFilePaths.length < maxCount" @click="chooseImages">
<text class="add-icon">+</text>
</view>
</view>
</view>
</view>
</view>
</scroll-view>
<!-- 底部下一步按钮 -->
<view class="bottom-btn-wrap">
<button class="next-btn" @click="submitForm">提交</button>
</view>
<!-- ================== 自定义Popup弹窗纯view实现 ================== -->
<!-- 蒙层 -->
<view class="mask" v-if="showTypePopup || showPropertyPopup" @click="closeAllPopup"></view>
<!-- 投诉类型弹窗 -->
<view v-if="showTypePopup" class="mask" @click="showTypePopup=false"></view>
<view v-if="showTypePopup" class="popup">
<view class="popup-header">
<text class="title">选择投诉类型</text>
<text class="close" @click="showTypePopup = false"></text>
</view>
<view class="list">
<view :class="{ active: selectedType === item }" class="item" v-for="(item, index) in typeOptions"
:key="index" @click="selectType(item)">
{{ item }}
</view>
</view>
</view>
<!-- 投诉物业弹窗 -->
<view v-if="showPropertyPopup" class="mask" @click="showPropertyPopup=false"></view>
<view v-if="showPropertyPopup" class="popup">
<view class="popup-header">
<text class="title">选择投诉类型</text>
<text class="close" @click="showPropertyPopup = false"></text>
</view>
<view class="list">
<view :class="{ active: selectedProperty === item }" class="item"
v-for="(item, index) in propertyOptions" :key="index" @click="selectProperty(item)">
{{ item }}
</view>
</view>
</view>
</view>
</template>
<script>
export default {
onReady: function(e) {},
components: {
},
computed: {
},
created() {
},
onShow() {
},
mounted() {},
onReady() {
this.$nextTick(() => {
})
},
data() {
return {
/* 设定状态栏默认高度 */
statusBarHeight: 20,
// 弹窗控制
showTypePopup: false,
showPropertyPopup: false,
// 下拉选项
typeOptions: ['物业服务', '社区服务', '其他'],
propertyOptions: ['XX物业A', 'XX物业B', 'XX物业C'], // 替换为实际数据
// 表单数据
selectedType: '',
selectedProperty: '',
description: '',
name: '',
phone: '',
// 多图上传
tempFilePaths: [],
maxCount: 9 // 最大上传9张
}
},
methods: {
// 返回上一页
navigateBack() {
uni.navigateBack();
},
// 关闭所有弹窗
closeAllPopup() {
this.showTypePopup = false;
this.showPropertyPopup = false;
},
// 选择投诉类型
selectType(item) {
this.selectedType = item;
this.showTypePopup = false;
// 切换类型时清空物业选择
if (item !== '物业服务') {
this.selectedProperty = '';
}
},
// 选择物业
selectProperty(item) {
this.selectedProperty = item;
this.showPropertyPopup = false;
},
// 多图选择
chooseImages() {
uni.chooseImage({
count: this.maxCount - this.tempFilePaths.length,
sizeType: ['compressed'],
sourceType: ['album', 'camera'],
success: (res) => {
this.tempFilePaths = [...this.tempFilePaths, ...res.tempFilePaths];
}
});
},
// 删除图片
deletePhoto(index) {
this.tempFilePaths.splice(index, 1);
},
// 表单提交
submitForm() {
// 基础校验
if (!this.selectedType) {
return uni.showToast({
title: '请选择投诉类型',
icon: 'none'
});
}
if (this.selectedType === '物业服务' && !this.selectedProperty) {
return uni.showToast({
title: '请选择物业',
icon: 'none'
});
}
if (!this.description.trim()) {
return uni.showToast({
title: '请输入问题描述',
icon: 'none'
});
}
uni.showLoading({
title: '提交中...',
mask: true
});
setTimeout(() => {
uni.showToast({
title: '提交成功',
icon: 'success'
});
}, 1000);
setTimeout(() => {
// 提交成功后跳转列表页
uni.hideLoading();
this.goBack();
}, 3000);
},
goBack() {
uni.redirectTo({
url: '/pages/service-list/complaintsSuggestionsIndex',
success: res => {},
fail: () => {},
complete: () => {}
});
},
},
}
</script>
<style lang="scss">
page {
background: #fff;
}
</style>
<style scoped>
.contentStyle {
display: flex;
flex-direction: column;
height: 100vh;
/* 关键:占满屏幕高度 */
/* background: linear-gradient(to left bottom, #e2efff 0%, #f9f9f9 50%); */
}
.page {
flex: 1;
overflow-y: auto;
padding: 0px 20px;
box-sizing: border-box;
background-color: #fff;
}
/* 表单主体 */
.form-content {
}
.form-tip {
display: block;
font-size: 16px;
color: #1D1D1D;
margin-bottom: 10px;
}
/* 表单项:一行布局,不换行 */
.form-row {
display: flex;
align-items: center;
padding: 16px 0;
border-bottom: 1rpx solid #f0f0f0;
}
.row-left {
display: flex;
align-items: center;
width: 180rpx;
flex-shrink: 0;
}
.required-star {
color: #ff3b30;
font-size: 15px;
margin-right: 4px;
}
.label-text {
font-size: 14px;
color: #333;
}
.row-right {
flex: 1;
display: flex;
align-items: center;
justify-content: space-between;
}
.value-text {
font-size: 15px;
color: grey;
}
.value-text.placeholderStyle {
color: #333;
}
.arrow-icon {
font-size: 28rpx;
color: #999;
}
/* 输入框样式 */
.text-input {
width: 100%;
font-size: 15px;
color: #333;
border: none;
outline: none;
background: transparent;
}
/* 问题描述行 */
.desc-row {
align-items: flex-start;
padding-top: 16px;
padding-bottom: 16px;
}
.desc-right {
flex: 1;
}
.desc-input {
width: calc(100vw - 42px);
min-height: 80px;
margin-top: 10px;
padding: 12px;
border: 1rpx solid #f0f0f0;
border-radius: 8rpx;
font-size: 15px;
color: #333;
line-height: 1.5;
box-sizing: border-box;
background: transparent;
}
/* 照片上传区域 */
.photo-row {
align-items: flex-start;
padding-top: 16px;
padding-bottom: 16px;
border-bottom: none;
}
.photo-right {
flex: 1;
}
.photo-grid {
display: flex;
flex-wrap: wrap;
gap: 12px;
}
.photo-item {
position: relative;
width: 120rpx;
height: 120rpx;
border-radius: 8rpx;
overflow: hidden;
}
.photo-img {
width: 100%;
height: 100%;
}
.delete-btn {
position: absolute;
top: -4rpx;
right: -4rpx;
width: 32rpx;
height: 32rpx;
background-color: rgba(0, 0, 0, 0.6);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
}
.delete-icon {
color: #fff;
font-size: 24rpx;
line-height: 1;
}
.add-btn {
width: 120rpx;
height: 120rpx;
border: 1rpx solid #e5e5e5;
border-radius: 8rpx;
display: flex;
align-items: center;
justify-content: center;
}
.add-icon {
font-size: 32rpx;
color: #999;
line-height: 1;
}
/* 底部按钮 */
.bottom-btn-wrap {
padding: 10px 15px 20px;
display: flex;
gap: 20rpx;
background-color: #fff;
}
.next-btn {
width: 100%;
height: 96rpx;
line-height: 96rpx;
background-color: #1677ff;
color: #fff;
font-size: 36rpx;
border-radius: 12rpx;
border: none;
}
/* 遮罩 */
.mask {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
z-index: 99;
}
/* 底部弹出框 */
.popup {
position: fixed;
left: 0;
right: 0;
bottom: 0;
background: #fff;
border-radius: 20rpx 20rpx 0 0;
z-index: 100;
max-height: 60vh;
}
.popup-header {
padding: 15px;
text-align: center;
/* border-bottom: 1rpx solid #f0f0f0; */
position: relative;
}
.title {
font-size: 32rpx;
font-weight: 500;
}
.close {
position: absolute;
right: 15px;
top: 50%;
transform: translateY(-50%);
font-size: 36rpx;
color: #666;
}
/* 选项列表 */
.list {
padding: 0 15px;
}
.item {
padding: 15px 0;
text-align: center;
font-size: 14px;
border-bottom: 1rpx solid #f5f5f5;
}
.item:last-child {
border-bottom: none;
}
.item.active {
color: #337eff;
font-weight: 600;
background-color: #f8f9fa;
}
.status-bar {
width: 100vw;
height: 46px;
}
</style>

View File

@@ -0,0 +1,289 @@
<template>
<view class="contentStyle" style="">
<view class="status-bar"></view>
<uni-nav-bar title="投诉详情" backgroundColor="transparent" :border="false" left-icon="left" @clickLeft="goBack">
</uni-nav-bar>
<!-- 表单区域 -->
<scroll-view class="page" scroll-y="true">
<view class="page-container">
<!-- 投诉状态 -->
<view class="status-section">
<view class="section-title">
<text class="title-line"></text>
<text class="title-text">投诉状态</text>
</view>
<text class="status-value"
:class="status=='pending'?'pending':status=='processing'?'processing':status=='completed'?'completed':''">{{status=='pending'?'待处理':status=='processing'?'处理中':status=='completed'?'已完成':''}}</text>
</view>
<view class="completedDesc" v-if="status=='completed'">已完成处理已联系工作人员粘贴标语提醒并派人定时巡视</view>
<!-- 投诉详情 -->
<view class="detail-section">
<view class="section-title">
<text class="title-line"></text>
<text class="title-text">投诉详情</text>
</view>
<!-- 投诉类型 -->
<view class="info-item">
<text class="info-label">投诉类型</text>
<text class="info-value">社区服务</text>
</view>
<!-- 问题描述 -->
<view class="info-item desc-item">
<text class="info-label">问题描述</text>
<text class="info-value desc-value">
投诉内容投诉内容投诉内容投诉内容投诉内容投诉内容投诉内容投诉内容投诉内容投诉内容
</text>
</view>
<!-- 投诉人姓名 -->
<view class="info-item">
<text class="info-label">投诉人姓名</text>
<text class="info-value">匿名</text>
</view>
<!-- 手机号码 -->
<view class="info-item">
<text class="info-label">手机号码</text>
<text class="info-value"></text>
</view>
<!-- 问题照片 -->
<view class="photo-section">
<text class="photo-label">问题照片</text>
<view class="photo-list">
<image v-for="(img, index) in photoList" :key="index" :src="img" mode="aspectFill"
class="photo-item"></image>
</view>
</view>
</view>
</view>
</scroll-view>
</view>
</template>
<script>
export default {
onReady: function(e) {},
components: {
},
computed: {
},
created() {
},
onShow() {
},
onLoad(option) {
this.id = option.id;
this.status = option.status;
},
mounted() {},
onReady() {
this.$nextTick(() => {
})
},
data() {
return {
id: '',
status: '',
/* 设定状态栏默认高度 */
statusBarHeight: 20,
// 问题照片列表,替换为你的实际图片路径
photoList: [
"/static/propertyImgs/community1.png",
"/static/propertyImgs/community2.png",
"/static/propertyImgs/community3.png"
]
}
},
methods: {
goBack() {
uni.redirectTo({
url: '/pages/service-list/complaintsSuggestionsIndex',
success: res => {},
fail: () => {},
complete: () => {}
});
},
},
}
</script>
<style lang="scss">
page {
background: #f2f1f6;
}
</style>
<style scoped>
.contentStyle {
display: flex;
flex-direction: column;
height: 100vh;
/* 关键:占满屏幕高度 */
/* background: linear-gradient(to left bottom, #e2efff 0%, #f9f9f9 50%); */
}
.page {
flex: 1;
overflow-y: auto;
padding: 0px 20px;
box-sizing: border-box;
/* background-color: #fff; */
}
.page-container {
}
/* 通用标题样式(带蓝色竖线) */
.section-title {
display: flex;
align-items: center;
margin-bottom: 20rpx;
}
.title-line {
width: 8rpx;
height: 36rpx;
background-color: #007AFF;
margin-right: 12rpx;
border-radius: 4rpx;
}
.title-text {
font-size: 16px;
font-weight: bold;
color: #333333;
}
/* 投诉状态区域 */
.status-section {
display: flex;
justify-content: space-between;
align-items: center;
padding-bottom: 20rpx;
border-bottom: 1rpx solid #eeeeee;
margin-bottom: 30rpx;
}
.completedDesc {
color: #2F77FD;
font-size: 14px;
margin-bottom: 15px;
}
.status-value {
font-size: 32rpx;
color: #007AFF;
font-weight: 500;
}
/* 详情信息项通用样式 */
.info-item {
display: flex;
padding: 24rpx 0;
border-bottom: 1rpx solid #eeeeee;
align-items: flex-start;
}
.info-label {
width: 220rpx;
font-size: 14px;
color: #333333;
flex-shrink: 0;
line-height: 1.5;
}
.info-value {
flex: 1;
font-size: 14px;
color: #000;
line-height: 1.5;
}
/* 问题描述特殊样式(多行文本) */
.desc-item {
align-items: flex-start;
}
.desc-value {
white-space: pre-line;
word-break: break-all;
}
/* 问题照片区域 */
.photo-section {
padding: 24rpx 0;
}
.photo-label {
display: block;
font-size: 30rpx;
color: #666666;
margin-bottom: 20rpx;
}
.photo-list {
display: flex;
flex-wrap: wrap;
/* 自动换行 */
gap: 10px;
/* 图片之间的间距(可改) */
margin: 5px 0px 10px 0px;
}
.photo-item {
width: calc(33.333% - 7px);
height: 66px;
object-fit: cover;
}
.pending {
color: #2F77FD;
}
.processing {
color: #2F77FD;
}
.completed {
color: #999999;
}.status-bar{
width: 100vw;
height: 46px;
}
</style>

View File

@@ -0,0 +1,380 @@
<template>
<view class="contentStyle" style="">
<view class="status-bar"></view>
<uni-nav-bar title="投诉建议" backgroundColor="transparent" :border="false" left-icon="left" @clickLeft="goBack">
</uni-nav-bar>
<!-- 1. 顶部分段控制器 -->
<view style="width: 60vw;">
<uni-segmented-control :current="currentTab" :values="tabs" @clickItem="onTabClick" style-type="text"
active-color="#007AFF" inactive-color="#999" class="seg-control"></uni-segmented-control>
</view>
<!-- 表单区域 -->
<scroll-view class="page" scroll-y="true">
<view class="page-container">
<!-- 2. 投诉卡片主体 -->
<view class="card-wrapper" v-for="item,index in dataList" :key="index">
<!-- 卡片头部标题 + 标签 -->
<view class="card-header">
<text class="card-title">{{item.title}}</text>
<text class="card-tag"
:class="item.status=='pending'?'pending':item.status=='processing'?'processing':item.status=='completed'?'completed':''">{{item.status=='pending'?'待处理':item.status=='processing'?'处理中':item.status=='completed'?'已完成':''}}</text>
</view>
<!-- 卡片内容描述 + 横向图片 -->
<view class="card-body">
<text class="card-desc">
{{item.desc}}
</text>
<!-- 横向图片滚动区 -->
<view class="img-scroll-container">
<view v-for="(img, imgIndex) in item.imgList" :key="imgIndex" class="img-item">
<image :src="img" mode="widthFix" class="card-img"></image>
</view>
</view>
<!-- 时间 & 详情 -->
<view class="card-footer">
<text class="card-time">{{item.time}}</text>
<text class="card-detail" @click="turnToDetail(item)">投诉详情</text>
</view>
</view>
</view>
</view>
</scroll-view>
<view class="bottom-btn-wrap">
<button class="next-btn" @click="addComplaintsSuggestions">投诉建议</button>
</view>
</view>
</template>
<script>
export default {
onReady: function(e) {},
components: {
},
computed: {
},
created() {
},
onShow() {
},
mounted() {
this.getDataList()
},
onReady() {
this.$nextTick(() => {
})
},
data() {
return {
/* 设定状态栏默认高度 */
statusBarHeight: 20,
// 分段器数据
tabs: ["待处理", "处理中", "已完成"],
currentTab: 0,
dataList: [],
pendingList: [{
id: 1,
title: '社区服务',
status: 'pending',
desc: '1栋楼下道路路灯不亮并且有的亮度不够请物业公司及时更换防止发生意外事故。',
time: '2026-08-01 10:05:46',
imgList: [
"/static/propertyImgs/community1.png",
"/static/propertyImgs/community2.png",
"/static/propertyImgs/community3.png"
]
},
{
id: 1,
title: '社区服务',
status: 'pending',
desc: '1栋楼下道路路灯不亮并且有的亮度不够请物业公司及时更换防止发生意外事故。',
time: '2026-08-01 10:05:46',
imgList: [
"/static/propertyImgs/community1.png",
"/static/propertyImgs/community2.png",
"/static/propertyImgs/community3.png"
]
},
{
id: 1,
title: '社区服务',
status: 'pending',
desc: '1栋楼下道路路灯不亮并且有的亮度不够请物业公司及时更换防止发生意外事故。',
time: '2026-08-01 10:05:46',
imgList: [
"/static/propertyImgs/community1.png",
"/static/propertyImgs/community2.png",
"/static/propertyImgs/community3.png"
]
},
{
id: 1,
title: '社区服务',
status: 'pending',
desc: '1栋楼下道路路灯不亮并且有的亮度不够请物业公司及时更换防止发生意外事故。',
time: '2026-08-01 10:05:46',
imgList: [
"/static/propertyImgs/community1.png",
"/static/propertyImgs/community2.png",
"/static/propertyImgs/community3.png"
]
}
],
processingList: [{
id: 2,
title: '社区服务',
status: 'processing',
desc: '1栋楼下道路路灯不亮并且有的亮度不够请物业公司及时更换防止发生意外事故。',
time: '2026-08-01 10:05:46',
imgList: []
}],
completedList: [{
id: 3,
title: '社区服务',
status: 'completed',
desc: '1栋楼下道路路灯不亮并且有的亮度不够请物业公司及时更换防止发生意外事故。',
time: '2026-08-01 10:05:46',
imgList: [
"/static/propertyImgs/community1.png",
"/static/propertyImgs/community2.png",
"/static/propertyImgs/community3.png"
]
}],
}
},
methods: {
turnToDetail(item) {
uni.redirectTo({
url: '/pages/service-list/complaintsDetail?id=' + item.id + '&status=' + item.status,
success: res => {},
fail: () => {},
complete: () => {}
});
},
getDataList() {
if (this.currentTab == 0) {
this.dataList = this.pendingList
} else if (this.currentTab == 1) {
this.dataList = this.processingList
} else if (this.currentTab == 2) {
this.dataList = this.completedList
}
},
addComplaintsSuggestions() {
uni.redirectTo({
url: '/pages/service-list/addComplaints',
success: res => {},
fail: () => {},
complete: () => {}
});
},
onTabClick(e) {
this.currentTab = e.currentIndex;
this.getDataList()
},
goBack() {
uni.switchTab({
url: '/pages/serviceIndex/serviceIndex',
success: res => {},
fail: () => {},
complete: () => {}
});
},
},
}
</script>
<style lang="scss">
page {
background: #f2f1f6;
}
</style>
<style scoped>
.contentStyle {
display: flex;
flex-direction: column;
height: 100vh;
/* 关键:占满屏幕高度 */
background: linear-gradient(to left bottom, #e2efff 0%, #f9f9f9 50%);
}
.page {
flex: 1;
overflow-y: auto;
padding: 0px 20px;
box-sizing: border-box;
/* background-color: #fff; */
}
.page-container {}
/* --- 分段控制器样式 --- */
.seg-control {
margin-bottom: 20rpx;
}
.cardScroll {
padding: 0rpx 0rpx 0px 0rpx;
overflow-y: scroll;
}
/* --- 卡片主体样式 --- */
.card-wrapper {
background: #ffffff;
border-radius: 16rpx;
padding: 30rpx;
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.05);
margin-bottom: 10px;
}
/* 卡片头部:标题 & 标签 */
.card-header {
display: flex;
align-items: center;
margin-bottom: 20rpx;
}
.card-title {
font-size: 16px;
font-weight: 400;
color: #222222;
margin-right: 20rpx;
}
.card-tag {
font-size: 12px;
padding: 1px 8px;
border-radius: 20px;
}
.pending {
background: #E34D59;
color: #FFFFFF;
}
.processing {
background: #09C2BD;
color: #FFFFFF;
}
.completed {
background: #2F77FD;
color: #FFFFFF;
}
/* 卡片内容区 */
.card-body {
display: flex;
flex-direction: column;
}
/* 描述文本 */
.card-desc {
font-size: 14px;
color: #666666;
line-height: 20px;
margin-bottom: 20rpx;
}
/* 图片容器 */
.img-scroll-container {
display: flex;
flex-wrap: wrap;
/* 自动换行 */
gap: 10px;
/* 图片之间的间距(可改) */
margin: 5px 0px 10px 0px;
}
/* 单个图片项 */
.img-item {
width: calc(33.333% - 7px);
height: 52px;
}
/* 图片样式 */
.card-img {
width: 100%;
height: 52px;
object-fit: cover;
}
/* 底部栏:时间 & 详情 */
.card-footer {
display: flex;
justify-content: space-between;
align-items: center;
}
.card-time {
font-size: 12px;
color: #999999;
}
.card-detail {
font-size: 26rpx;
color: #2F77FD;
}
/* 底部按钮 */
.bottom-btn-wrap {
padding: 10px 15px 20px;
display: flex;
gap: 20rpx;
/* background-color: #fff; */
}
.next-btn {
width: 100%;
height: 96rpx;
line-height: 96rpx;
background-color: #1677ff;
color: #fff;
font-size: 36rpx;
border-radius: 12rpx;
border: none;
}
.status-bar {
width: 100vw;
height: 46px;
}
</style>

View File

@@ -0,0 +1,210 @@
<template>
<view class="contentStyle" style="">
<view class="status-bar"></view>
<uni-nav-bar title="联系物业" backgroundColor="transparent" :border="false" left-icon="left" @clickLeft="goBack">
</uni-nav-bar>
<!-- 表单区域 -->
<scroll-view class="page" scroll-y="true">
<view class="property-card" v-for="item,index in dataList" :key="index">
<view class="topBox">
<!-- 左侧头像 -->
<image class="avatar" :src="item.src" mode="aspectFill"></image>
<!-- 右侧信息区 -->
<view class="info-content">
<!-- 物业名称 -->
<view class="property-name">{{item.propertyName}}</view>
<!-- 物业地址 -->
<view class="property-address">{{item.propertyAddress}}</view>
</view>
</view>
<!-- 值班电话 -->
<view class="phone-item" v-for="phoneItems,phoneIndex in item.phoneList" :key="phoneIndex">
<text class="phone-label">值班电话</text>
<text class="phone-num">{{phoneItems.phoneNum}}</text>
<!-- <text class="phone-icon"></text> -->
<uni-icons type="phone" size="16" color="#2F77FD"></uni-icons>
</view>
</view>
</scroll-view>
</view>
</template>
<script>
export default {
onReady: function(e) {},
components: {
},
computed: {
},
created() {
},
onShow() {
},
mounted() {},
onReady() {
this.$nextTick(() => {
})
},
data() {
return {
/* 设定状态栏默认高度 */
statusBarHeight: 20,
dataList: [{
src: '/static/propertyImgs/community1.png',
propertyName: '北境碧桂园小区物业中心',
propertyAddress: 'A栋3单元1801',
phoneList: [{
phoneNum: '080-12312312312',
},
{
phoneNum: '080-12312312312',
}
],
}],
}
},
methods: {
// 电话点击拨号
callPhone(phone) {
uni.makePhoneCall({
phoneNumber: phone.replace(/-/g, "")
});
},
goBack() {
uni.switchTab({
url: '/pages/serviceIndex/serviceIndex',
success: res => {},
fail: () => {},
complete: () => {}
});
},
},
}
</script>
<style lang="scss">
page {
background: #f9f9f9;
}
</style>
<style scoped>
.contentStyle {
display: flex;
flex-direction: column;
height: 100vh;
/* 关键:占满屏幕高度 */
/* background: linear-gradient(to left bottom, #e2efff 0%, #f9f9f9 50%); */
}
.page {
flex: 1;
overflow-y: auto;
padding: 0px 20px;
box-sizing: border-box;
/* background-color: #fff; */
}
/* 卡片容器 */
.property-card {
padding: 15px;
/* margin: 0px 20px 20px 20px; */
background: #ffffff;
border-radius: 16rpx;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.05);
}
.topBox {
display: flex;
align-items: flex-start;
margin-bottom: 10px;
}
/* 头像 */
.avatar {
width: 60px;
height: 60px;
border-radius: 8rpx;
margin-right: 24rpx;
flex-shrink: 0;
}
/* 信息内容区 */
.info-content {
flex: 1;
display: flex;
flex-direction: column;
gap: 12rpx;
}
/* 物业名称 */
.property-name {
font-size: 16px;
font-weight: 600;
color: #333333;
line-height: 35px;
}
/* 物业地址 */
.property-address {
font-size: 14px;
color: #666666;
line-height: 12px;
}
/* 电话行 */
.phone-item {
display: flex;
align-items: center;
gap: 8rpx;
line-height: 30px;
}
/* 电话标签 */
.phone-label {
font-size: 14px;
color: #999999;
}
/* 电话号码 */
.phone-num {
font-size: 14px;
color: #666666;
}
/* 电话图标 */
.phone-icon {
font-size: 12px;
color: #409eff !important;
/* 蓝色,和原图一致 */
margin-left: 8rpx;
}
.status-bar{
width: 100vw;
height: 46px;
}
</style>

View File

@@ -0,0 +1,238 @@
<template>
<view class="contentStyle" style="">
<view class="status-bar"></view>
<uni-nav-bar title="问卷调查" backgroundColor="transparent" :border="false" left-icon="left" @clickLeft="goBack">
</uni-nav-bar>
<!-- 表单区域 -->
<scroll-view class="page" scroll-y="true">
<!-- 问卷标题 -->
<view class="survey-title">关于用户体验反馈的调查</view>
<!-- 问卷说明 -->
<view class="survey-desc">
为了给您提供更好的服务希望您能抽出几分钟时间将进的感受和建议告诉我们我们非常重视每位用户的宝贵意见期待您的参与
</view>
<!-- 问题1社区大门数量 -->
<view class="question-item">
<view class="question-title">
<text class="required">*</text>1.您社区的大门数量
</view>
<view class="answer-text">1</view>
</view>
<!-- 问题2消防演练符合项 -->
<view class="question-item">
<view class="question-title">
<text class="required">*</text>2.您所在的社区关于消防演练下列符合几项
</view>
<view class="answer-text">一周2~3</view>
</view>
<!-- 问题3健康医疗服务 -->
<view class="question-item">
<view class="question-title">
<text class="required">*</text>3.您所在的社区是否有健康医疗服务
</view>
<view class="answer-text"></view>
</view>
<!-- 问题4综合评分星级 -->
<view class="question-item">
<view class="question-title">
<text class="required">*</text>4.您对所在社区的综合评分
</view>
<view class="star-container">
<view v-for="(item, index) in 5" :key="index" class="star-item" :class="{ active: index < score }">
<text class="star-icon"></text>
</view>
</view>
</view>
<!-- 问题5建议 -->
<view class="question-item">
<view class="question-title">
<text class="required">*</text>5.您还有哪些建议
</view>
<view class="answer-text">没有其他建议</view>
<!-- 如需可编辑输入框替换为下方textarea -->
<!-- <textarea class="suggest-input" placeholder="请输入您的建议" v-model="suggestion" /> -->
</view>
</scroll-view>
</view>
</template>
<script>
export default {
onReady: function(e) {},
components: {
},
computed: {
},
created() {
},
onShow() {
},
mounted() {},
onReady() {
this.$nextTick(() => {
})
},
data() {
return {
/* 设定状态栏默认高度 */
statusBarHeight: 20,
score: 3, // 默认3星对应原图
suggestion: ''
}
},
methods: {
goBack() {
uni.redirectTo({
url: '/pages/service-list/questionnaireSurveyIndex',
success: res => {},
fail: () => {},
complete: () => {}
});
},
},
}
</script>
<style lang="scss">
page {
background: #fff;
}
</style>
<style scoped>
.contentStyle {
display: flex;
flex-direction: column;
height: 100vh;
/* 关键:占满屏幕高度 */
/* background: linear-gradient(to left bottom, #e2efff 0%, #f9f9f9 50%); */
}
.page {
flex: 1;
overflow-y: auto;
padding: 0px 20px;
box-sizing: border-box;
/* background-color: #fff; */
}
/* 滚动内容区 */
.content-scroll {
flex: 1;
padding: 0 30rpx;
}
/* 标题与描述 */
.survey-title {
font-size: 16px;
font-weight: 600;
color: #333;
text-align: center;
margin: 0px 0 10px 0;
;
}
.survey-desc {
font-size: 12px;
color: #4A4A4A;
line-height: 1.6;
margin-bottom: 10px;
text-align: justify;
}
.question-item {
margin-bottom: 50rpx;
}
.question-title {
font-size: 16px;
color: #000;
margin-bottom: 5px;
font-weight: 600;
}
.required {
color: #ff0000;
font-size: 34rpx;
margin-right: 8rpx;
}
.answer-text {
font-size: 32rpx;
color: #333;
line-height: 1.5;
padding-left: 15px;
}
/* 星级评分样式 */
.star-container {
display: flex;
align-items: center;
gap: 20rpx;
}
.star-item {
font-size: 48rpx;
color: #dcdcdc;
cursor: pointer;
transition: color 0.2s;
}
.star-item.active {
color: #409eff;
/* 蓝色,和原图一致 */
}
.star-icon {
font-family: monospace;
}
/* 建议输入框样式(如需可编辑) */
.suggest-input {
width: 100%;
min-height: 200rpx;
padding: 20rpx;
border: 1rpx solid #e5e5e5;
border-radius: 8rpx;
background: #fff;
font-size: 32rpx;
color: #333;
box-sizing: border-box;
}
.status-bar {
width: 100vw;
height: 46px;
}
</style>

View File

@@ -0,0 +1,498 @@
<template>
<view class="contentStyle" style="">
<view class="status-bar"></view>
<uni-nav-bar title="问卷调查" backgroundColor="transparent" :border="false" left-icon="left" @clickLeft="goBack">
</uni-nav-bar>
<!-- 表单区域 -->
<scroll-view class="page" scroll-y="true">
<view class="content-wrap">
<!-- 问卷标题 -->
<view class="survey-title">关于用户体验反馈的调查</view>
<!-- 问卷说明 -->
<view class="survey-desc">
为了给您提供更好的服务希望您能抽出几分钟时间将进的感受和建议告诉我们我们非常重视每位用户的宝贵意见期待您的参与
</view>
<!-- 问题1单选 -->
<view class="question-item">
<view class="question-title">
<text class="required">*</text>1.您社区的大门数量
</view>
<view class="radio-group">
<view class="radio-item" v-for="(item, index) in q1Options" :key="index"
@click="selectRadio(1, index)">
<view class="radio-circle" :class="{ checked: q1Selected === index }">
<text v-if="q1Selected === index" class="check-icon"></text>
</view>
<text class="radio-label">{{ item }}</text>
</view>
</view>
</view>
<!-- 问题2单选 -->
<view class="question-item">
<view class="question-title">
<text class="required">*</text>2.您所在的社区关于消防演练下列符合几项
</view>
<view class="radio-group">
<view class="radio-item" v-for="(item, index) in q2Options" :key="index"
@click="selectRadio(2, index)">
<view class="radio-circle" :class="{ checked: q2Selected === index }">
<text v-if="q2Selected === index" class="check-icon"></text>
</view>
<text class="radio-label">{{ item }}</text>
</view>
</view>
</view>
<!-- 问题3自定义 Select (核心部分不使用 picker) -->
<view class="question-item">
<view class="question-title">
<text class="required">*</text>3.您所在的社区是否有健康医疗服务
</view>
<!-- 模拟输入框点击展开下拉 -->
<view class="select-wrapper" @click="toggleDropdown">
<text class="select-text" v-if="q3Selected">
{{ q3Selected }}
</text>
<text class="select-placeholder" v-else>
请选择是否有该项服务
</text>
<text class="select-arrow"></text>
</view>
<!-- 下拉面板 (遮罩 + 列表) -->
<view class="dropdown-mask" v-if="showDropdown" @click="hideDropdown"></view>
<view class="dropdown-panel" v-if="showDropdown">
<view class="dropdown-item" v-for="(item, index) in q3Options" :key="index"
@click="selectQ3(index)" :class="{ active: index === q3Index }">
{{ item }}
</view>
</view>
</view>
<!-- 问题4星级评分 -->
<view class="question-item">
<view class="question-title">
<text class="required">*</text>4.您对所在社区的综合评分
</view>
<view class="star-group">
<view class="star-item" v-for="(item, index) in 5" :key="index" @click="selectStar(index + 1)">
<text class="star-icon" :class="{ active: index < q4Score }"></text>
</view>
</view>
</view>
<!-- 问题5文本输入 -->
<view class="question-item">
<view class="question-title">
<text class="required">*</text>5.您还有哪些建议
</view>
<textarea class="textarea-input" v-model="q5Content" placeholder="请输入您还有哪些建议" maxlength="500" />
</view>
</view>
</scroll-view>
<!-- 固定底部提交按钮 -->
<view class="submit-btn-wrap">
<button class="submit-btn" @click="handleSubmit">提交</button>
</view>
</view>
</template>
<script>
export default {
onReady: function(e) {},
components: {
},
computed: {
},
created() {
},
onShow() {
},
mounted() {},
onReady() {
this.$nextTick(() => {
})
},
data() {
return {
/* 设定状态栏默认高度 */
statusBarHeight: 20,
// 问题1
q1Options: ['1', '2', '3', '4'],
q1Selected: -1,
// 问题2
q2Options: ['一周2~3次', '一月2~3次', '一年2~3次', '基本不买'],
q2Selected: -1,
// 问题3 (自定义Select)
q3Options: ['有', '无'], // 选项列表
q3Selected: '', // 绑定显示值
q3Index: -1, // 绑定索引
showDropdown: false, // 控制下拉面板显示
// 问题4
q4Score: -1,
// 问题5
q5Content: ''
}
},
methods: {
selectRadio(type, index) {
if (type === 1) this.q1Selected = index
else this.q2Selected = index
},
// 问题3切换下拉显示
toggleDropdown() {
this.showDropdown = !this.showDropdown
},
// 问题3隐藏下拉
hideDropdown() {
this.showDropdown = false
},
// 问题3选择选项
selectQ3(index) {
this.q3Index = index
this.q3Selected = this.q3Options[index]
this.hideDropdown() // 选择后自动关闭
},
// 问题4
selectStar(score) {
this.q4Score = score
},
// 提交
handleSubmit() {
if (this.q1Selected === -1) {
uni.showToast({
title: '请选择社区大门数量',
icon: 'none'
})
return
}
if (this.q2Selected === -1) {
uni.showToast({
title: '请选择社区消防演练频率',
icon: 'none'
})
return
}
if (this.q3Index === -1) {
uni.showToast({
title: '请选择社区是否有健康医疗服务',
icon: 'none'
})
return
}
if (this.q4Score === -1) {
uni.showToast({
title: '请选择对社区的综合评分',
icon: 'none'
})
return
}
if (!this.q5Content.trim()) {
uni.showToast({
title: '请输入建议',
icon: 'none'
})
return
}
uni.showLoading({
title: '提交中...',
mask: true
});
setTimeout(() => {
uni.showToast({
title: '提交成功',
icon: 'success'
});
}, 1000);
setTimeout(() => {
// 提交成功后跳转列表页
uni.hideLoading();
this.goBack();
}, 3000);
},
goBack() {
uni.redirectTo({
url: '/pages/service-list/questionnaireSurveyIndex',
success: res => {},
fail: () => {},
complete: () => {}
});
},
},
}
</script>
<style lang="scss">
page {
background: #f2f1f6;
}
</style>
<style scoped lang="scss">
.contentStyle {
display: flex;
flex-direction: column;
height: 100vh;
/* 关键:占满屏幕高度 */
background: linear-gradient(to left bottom, #e2efff 0%, #f9f9f9 50%);
}
.page {
flex: 1;
overflow-y: auto;
padding: 0px 20px;
box-sizing: border-box;
/* background-color: #fff; */
}
/* 滚动内容区 */
.content-scroll {}
.content-wrap {
// padding-bottom: 40rpx;
}
/* 标题与描述 */
.survey-title {
font-size: 16px;
font-weight: 600;
color: #333;
text-align: center;
margin: 0px 0 10px 0;
;
}
.survey-desc {
font-size: 12px;
color: #4A4A4A;
line-height: 1.6;
margin-bottom: 10px;
text-align: justify;
}
/* 问题项 */
.question-item {
margin-bottom: 25px;
.question-title {
font-size: 16px;
font-weight: 600;
color: #333;
margin-bottom: 5px;
line-height: 1.4;
.required {
color: #ff3b30;
font-size: 36rpx;
margin-right: 8rpx;
}
}
}
/* 单选按钮 */
.radio-group {
display: flex;
flex-direction: column;
gap: 30rpx;
padding-left: 15px;
}
.radio-item {
display: flex;
align-items: center;
gap: 20rpx;
.radio-circle {
width: 40rpx;
height: 40rpx;
border-radius: 50%;
border: 2rpx solid #ccc;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
&.checked {
background-color: #007aff;
border-color: #007aff;
.check-icon {
color: #fff;
font-size: 24rpx;
font-weight: bold;
}
}
}
.radio-label {
font-size: 34rpx;
color: #333;
}
}
/* 核心:自定义 Select 样式 (模拟输入框) */
.select-wrapper {
position: relative;
display: flex;
align-items: center;
justify-content: space-between;
height: 80rpx;
padding: 0 20rpx;
border: 2rpx solid #e5e5e5;
border-radius: 8rpx;
background-color: #fff;
font-size: 32rpx;
color: #333;
margin-left: 15px;
}
.select-placeholder {
color: #999;
}
.select-arrow {
font-size: 24rpx;
color: #999;
margin-left: 10rpx;
}
/* 下拉遮罩层 */
.dropdown-mask {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.1);
z-index: 99;
}
/* 下拉面板 */
.dropdown-panel {
position: absolute;
left: 0;
right: 0;
max-height: 400rpx;
background-color: #fff;
border: 2rpx solid #e5e5e5;
border-radius: 8rpx;
z-index: 100;
margin-top: 10rpx;
overflow: hidden;
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.08);
}
.dropdown-item {
height: 80rpx;
line-height: 80rpx;
padding: 0 20rpx;
font-size: 32rpx;
color: #333;
&:active {
background-color: #f5f5f5;
}
&.active {
color: #007aff;
font-weight: 500;
}
}
/* 星级评分 */
.star-group {
display: flex;
align-items: center;
gap: 16rpx;
margin-left: 15px;
.star-icon {
font-size: 56rpx;
color: #e5e5e5;
transition: all 0.2s ease;
&.active {
color: #007aff;
}
}
}
/* 文本输入框 */
.textarea-input {
width: calc(100% - 15px);
min-height: 240rpx;
padding: 20rpx;
border: 2rpx solid #e5e5e5;
border-radius: 8rpx;
background-color: #fff;
font-size: 32rpx;
color: #333;
box-sizing: border-box;
margin-left: 15px;
}
/* 底部占位 & 提交按钮 */
.bottom-placeholder {
height: 120rpx; // 高度要大于等于按钮高度
}
.submit-btn-wrap {
padding: 10px 15px 20px;
display: flex;
gap: 20rpx;
// background-color: #fff;
.submit-btn {
width: 100%;
height: 96rpx;
line-height: 96rpx;
background-color: #007aff;
color: #fff;
font-size: 36rpx;
font-weight: 500;
border-radius: 12rpx;
border: none;
padding: 0;
margin: 0;
&::after {
border: none;
}
}
}
.status-bar {
width: 100vw;
height: 46px;
}
</style>

View File

@@ -0,0 +1,355 @@
<template>
<view class="contentStyle" style="">
<view class="status-bar"></view>
<uni-nav-bar title="问卷调查" backgroundColor="transparent" :border="false" left-icon="left" @clickLeft="goBack">
</uni-nav-bar>
<view style="width: 40vw;">
<uni-segmented-control :current="currentTab" :values="tabs" @clickItem="switchTab" style-type="text"
active-color="#007AFF" inactive-color="#999" class="seg-control"></uni-segmented-control>
</view>
<!-- 表单区域 -->
<scroll-view class="page" scroll-y="true">
<view class="list-container">
<view v-for="(item, index) in currentList" :key="index" class="list-item" :class="{
disabled: currentTab === 1,
selected: currentTab === 0 && item.status === '已参与'
}" @click="handleItemClick(item)">
<view class="item-content">
<view class="item-title">{{ item.title }}</view>
<view class="item-info">
<text class="info-time">{{ item.time }}</text>
<text class="info-community">{{ item.community }}</text>
</view>
</view>
<view class="item-status"
:class="{joined: currentTab === 0 &&item.status === '已参与',unjoined: item.status === '未参与'||currentTab === 1, }">
{{ item.status }}
</view>
</view>
</view>
<!-- 占位 -->
<view style="width: 100%;height: 1px;"> </view>
</scroll-view>
</view>
</template>
<script>
export default {
onReady: function(e) {},
components: {
},
computed: {
// 计算当前显示的列表
currentList() {
return this.currentTab === 0 ? this.collectingList : this.endedList;
},
},
created() {
},
onShow() {
},
mounted() {
this.getDataList()
},
onReady() {
this.$nextTick(() => {
})
},
data() {
return {
/* 设定状态栏默认高度 */
statusBarHeight: 20,
currentTab: 0, // 0=收集中1=已结束
tabs: ["收集中", "已结束"],
// 收集中列表数据
collectingList: [{
title: '问卷调查名称问卷调查名称问卷调查...',
time: '2026-09-01 11:00:56',
community: '阳光海岸社区',
status: '已参与',
},
{
title: '问卷调查名称问卷调查名称问卷调查...',
time: '2026-09-01 11:00:56',
community: '阳光海岸社区',
status: '未参与',
},
{
title: '问卷调查名称问卷调查名称问卷调查...',
time: '2026-09-01 11:00:56',
community: '阳光海岸社区',
status: '未参与',
},
{
title: '问卷调查名称问卷调查名称问卷调查...',
time: '2026-09-01 11:00:56',
community: '阳光海岸社区',
status: '未参与',
}, {
title: '问卷调查名称问卷调查名称问卷调查...',
time: '2026-09-01 11:00:56',
community: '阳光海岸社区',
status: '未参与',
},
{
title: '问卷调查名称问卷调查名称问卷调查...',
time: '2026-09-01 11:00:56',
community: '阳光海岸社区',
status: '未参与',
},
{
title: '问卷调查名称问卷调查名称问卷调查...',
time: '2026-09-01 11:00:56',
community: '阳光海岸社区',
status: '未参与',
}, {
title: '问卷调查名称问卷调查名称问卷调查...',
time: '2026-09-01 11:00:56',
community: '阳光海岸社区',
status: '未参与',
},
{
title: '问卷调查名称问卷调查名称问卷调查...',
time: '2026-09-01 11:00:56',
community: '阳光海岸社区',
status: '未参与',
},
{
title: '问卷调查名称问卷调查名称问卷调查...',
time: '2026-09-01 11:00:56',
community: '阳光海岸社区',
status: '未参与',
},
],
// 已结束列表数据
endedList: [{
title: '问卷调查名称问卷调查名称问卷调查...',
time: '2023-09-01 11:00:56',
community: '阳光海岸社区',
status: '已参与',
},
{
title: '问卷调查名称问卷调查名称调查...',
time: '2023-09-01 11:00:56',
community: '阳光海岸社区',
status: '未参与',
},
{
title: '问卷调查名称问卷调查名称调查...',
time: '2023-09-01 11:00:56',
community: '阳光海岸社区',
status: '未参与',
},
],
}
},
methods: {
// Tab切换
switchTab(e) {
this.currentTab = e.currentIndex;
this.getDataList()
},
// 列表项点击
handleItemClick(item) {
if (item.status == '已参与') {
this.turnToDetail(item)
} else {
if (this.currentTab === 0) {
this.turnToFillout(item)
} else if (this.currentTab === 1) {
// 未参与的已结束问卷
}
}
},
turnToFillout(item) {
// 跳转到问卷填写
uni.redirectTo({
url: `/pages/service-list/questionnaireFillout`,
success: res => {},
fail: () => {},
complete: () => {}
});
},
turnToDetail(item) {
// 跳转到问卷详情
uni.redirectTo({
url: `/pages/service-list/questionnaireDetail`,
success: res => {},
fail: () => {},
complete: () => {}
});
},
getDataList() {
if (this.currentTab == 0) {
this.dataList = this.collectingList
} else if (this.currentTab == 1) {
this.dataList = this.endedList
}
},
onTabClick(e) {
this.currentTab = e.currentIndex;
this.getDataList()
},
goBack() {
uni.switchTab({
url: '/pages/serviceIndex/serviceIndex',
success: res => {},
fail: () => {},
complete: () => {}
});
},
},
}
</script>
<style lang="scss">
page {
background: #f2f1f6;
}
</style>
<style lang="scss" scoped>
.contentStyle {
display: flex;
flex-direction: column;
height: 100vh;
/* 关键:占满屏幕高度 */
background: linear-gradient(to left bottom, #e2efff 0%, #f9f9f9 50%);
}
.page {
flex: 1;
overflow-y: auto;
padding: 0px 20px;
box-sizing: border-box;
/* background-color: #fff; */
}
.page-container {
padding: 20px;
}
/* --- 分段控制器样式 --- */
.seg-control {
margin-bottom: 20rpx;
}
.cardScroll {
padding: 0rpx 0rpx 0px 0rpx;
overflow-y: scroll;
}
/* 列表区域 */
.list-scroll {
flex: 1;
}
.list-container {}
.list-item {
padding: 15px;
margin-bottom: 10px;
display: flex;
align-items: center;
justify-content: space-between;
border: 1px solid #E6E6E6;
background-color: #fff;
&.disabled {
opacity: 0.6;
.item-title,
.item-info {}
}
&.selected {
border: 1px solid #2F77FD;
background-color: #F5FAFF;
}
}
.item-content {
// flex: 1;
width: calc(100% - 50px);
// margin-right: 10px;
}
.item-title {
font-size: 16px;
font-weight: 600;
color: #222222;
line-height: 22px;
margin-bottom: 5px;
/* 超长文本省略 */
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
width: 100%;
}
.item-info {
display: flex;
align-items: center;
gap: 10px;
.info-time,
.info-community {
font-size: 14px;
color: #666;
line-height: 16px;
/* 超长文本省略 */
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
.item-status {
font-size: 14px;
font-weight: 500;
padding: 0px 0px;
border-radius: 4px;
&.joined {
color: #2F77FD;
}
&.unjoined {
color: #999;
}
}
.status-bar {
width: 100vw;
height: 46px;
}
</style>

View File

@@ -0,0 +1,319 @@
<template>
<view class="contentStyle" style="">
<view class="status-bar"></view>
<uni-nav-bar title="服务" backgroundColor="transparent" :border="false">
</uni-nav-bar>
<!-- 表单区域 -->
<view class="page" :style="{ height: `calc(100vh - ${statusBarHeight}px)` }">
<view class="container">
<!-- 顶部 Banner 轮播 -->
<view class="banner-box" style="overflow-y: hidden;">
<image class="banner-img" src="/static/propertyImgs/serviceImg.png" />
</view>
<!-- 常用功能区 -->
<view class="func-section">
<view class="section-title">
<text class="title-text">常用功能</text>
</view>
<view class="func-grid">
<view class="func-item" v-for="(item, index) in commonFuncList" :key="index"
@click="handleFuncClick(item)">
<view class="func-icon"
:style="{'background-image': 'url(' + (item.icon) + ')', 'background-repeat': 'no-repeat','background-size': '100% 100%','background-position': 'center',}">
<!-- <image :src="item.icon" mode="aspectFit"></image> -->
</view>
<text class="func-name">{{ item.name }}</text>
</view>
</view>
</view>
<!-- 智慧社区功能区 -->
<view class="func-section" style="margin-bottom: 0px;">
<view class="section-title">
<text class="title-text">智慧社区</text>
</view>
<view class="func-grid community-grid">
<view class="func-item" v-for="(item, index) in communityFuncList" :key="index"
@click="handleFuncClick(item)">
<view class="func-icon">
<image :src="item.icon" mode="aspectFit"></image>
</view>
<text class="func-name">{{ item.name }}</text>
</view>
</view>
</view>
</view>
</view>
</view>
</template>
<script>
export default {
onReady: function(e) {},
components: {
},
computed: {
},
created() {
},
onShow() {
},
mounted() {},
onReady() {
this.$nextTick(() => {
})
},
data() {
return {
/* 设定状态栏默认高度 */
statusBarHeight: 20,
// 常用功能列表
commonFuncList: [{
name: "一键开门",
icon: "/static/propertyImgs/icon-door.png",
path: "/pages/door/index"
},
{
name: "生活缴费",
icon: "/static/propertyImgs/icon-pay.png",
path: "/pages/pay/index"
},
{
name: "在线报修",
icon: "/static/propertyImgs/icon-repair.png",
path: "/pages/repair/index"
},
{
name: "访客邀请",
icon: "/static/propertyImgs/icon-visitor.png",
path: "/pages/visitor/index"
}
],
// 智慧社区功能列表
communityFuncList: [{
name: "一键开门",
icon: "/static/propertyImgs/icon-door.png",
path: "/pages/door/index"
},
{
name: "生活缴费",
icon: "/static/propertyImgs/icon-pay.png",
path: "/pages/pay/index"
},
{
name: "在线报修",
icon: "/static/propertyImgs/icon-repair.png",
path: "/pages/repair/index"
},
{
name: "访客邀请",
icon: "/static/propertyImgs/icon-visitor.png",
path: "/pages/visitor/index"
},
{
name: "投诉意见",
icon: "/static/propertyImgs/icon-complaint.png",
path: "/subpkg/service/complaintsSuggestionsIndex"
},
{
name: "问卷调查",
icon: "/static/propertyImgs/icon-survey.png",
path: "/subpkg/service/questionnaireSurveyIndex"
},
{
name: "社区活动",
icon: "/static/propertyImgs/icon-activity.png",
path: "/pages/activity/index"
},
{
name: "公告公示",
icon: "/static/propertyImgs/icon-notice.png",
path: "/pages/notice/index"
},
{
name: "联系物业",
icon: "/static/propertyImgs/icon-contact.png",
path: "/subpkg/service/contactProperty"
}
]
}
},
methods: {
// 功能点击事件
handleFuncClick(item) {
console.log(item.path);
uni.redirectTo({
url: item.path,
success: res => {},
fail: () => {},
complete: () => {}
});
},
},
}
</script>
<style lang="scss">
page {
background: #f2f1f6;
}
</style>
<style scoped>
.status-bar {
width: 100vw;
height: 46px;
}
.contentStyle {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
display: flex;
flex-direction: column;
padding-bottom: 30px;
background: linear-gradient(to left bottom, #e2efff 0%, #f9f9f9 50%);
/* background: #fff; */
}
/* 页面整体 */
.page {
/* min-height: 100vh; */
/* background-color: #fff; */
flex: 1;
height: 0;
/* 必须加!小程序必加 */
overflow-y: auto;
}
.container {
padding: 20px;
}
/* Banner 轮播 */
.banner-box {
width: 100%;
border-radius: 10px;
overflow: hidden;
margin-bottom: 20px;
height: 111px;
}
.banner-img {
width: 100%;
height: 111px;
}
/* 功能区通用样式 */
.func-section {
width: 100%;
background-color: #ffffff;
border-radius: 10px;
padding: 10px;
box-sizing: border-box;
margin-bottom: 20px;
}
.section-title {
display: flex;
align-items: center;
margin-bottom: 25px;
}
.title-line {
width: 6px;
height: 24px;
background-color: #007aff;
border-radius: 3px;
margin-right: 10px;
}
.title-text {
font-size: 16px;
font-weight: 600;
color: #000000;
}
/* 功能网格 */
.func-grid {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
}
.func-item {
width: 22%;
display: flex;
flex-direction: column;
align-items: center;
margin-bottom: 20px;
}
.func-icon {
width: 42px;
height: 42px;
border-radius: 50%;
background-color: #f0f5ff;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 10px;
position: relative;
}
.func-icon image {
max-height: 100%;
/* 限制最大高度不超出 */
object-fit: cover;
/* 保持比例 */
/* width: 200px; */
/* height: 200px; */
}
.func-name {
font-size: 14px;
color: #000000;
font-weight: 400;
text-align: center;
line-height: 1.4;
}
/* 智慧社区网格适配9个图标最后一个左对齐 */
.community-grid .func-item {
width: 22%;
}
.community-grid .func-item:nth-child(9) {
margin-right: auto;
margin-left: 0;
}
</style>

View File

@@ -0,0 +1,313 @@
<template>
<view class="contentStyle" style="">
<view class="status-bar"></view>
<uni-nav-bar title="服务" backgroundColor="transparent" :border="false">
</uni-nav-bar>
<!-- 表单区域 -->
<scroll-view class="page" scroll-y>
<view class="container">
<!-- 顶部 Banner 轮播 -->
<view class="banner-box" style="overflow-y: hidden;">
<image class="banner-img" src="/static/propertyImgs/serviceImg.png" />
</view>
<!-- 常用功能区 -->
<view class="func-section">
<view class="section-title">
<text class="title-text">常用功能</text>
</view>
<view class="func-grid">
<view class="func-item" v-for="(item, index) in commonFuncList" :key="index"
@click="handleFuncClick(item)">
<view class="func-icon"
:style="{'background-image': 'url(' + (item.icon) + ')', 'background-repeat': 'no-repeat','background-size': '100% 100%','background-position': 'center',}">
<!-- <image :src="item.icon" mode="aspectFit"></image> -->
</view>
<text class="func-name">{{ item.name }}</text>
</view>
</view>
</view>
<!-- 智慧社区功能区 -->
<view class="func-section" style="margin-bottom: 0px;">
<view class="section-title">
<text class="title-text">智慧社区</text>
</view>
<view class="func-grid community-grid">
<view class="func-item" v-for="(item, index) in communityFuncList" :key="index"
@click="handleFuncClick(item)">
<view class="func-icon">
<image :src="item.icon" mode="aspectFit"></image>
</view>
<text class="func-name">{{ item.name }}</text>
</view>
</view>
</view>
</view>
</scroll-view>
</view>
</template>
<script>
export default {
onReady: function(e) {},
components: {
},
computed: {
},
created() {
},
onShow() {
},
mounted() {},
onReady() {
this.$nextTick(() => {
})
},
data() {
return {
/* 设定状态栏默认高度 */
statusBarHeight: 20,
// 常用功能列表
commonFuncList: [{
name: "一键开门",
icon: "/static/propertyImgs/icon-door.png",
path: "/pages/notice-list/oneClickOpen"
},
{
name: "生活缴费",
icon: "/static/propertyImgs/icon-pay.png",
path: "/pages/payment-list/paymentIndex"
},
{
name: "在线报修",
icon: "/static/propertyImgs/icon-repair.png",
path: "/pages/online-repair/onlineRepair"
},
{
name: "访客邀请",
icon: "/static/propertyImgs/icon-visitor.png",
path: "/pages/visitor/visitor-list"
}
],
// 智慧社区功能列表
communityFuncList: [{
name: "一键开门",
icon: "/static/propertyImgs/icon-door.png",
path: "/pages/notice-list/oneClickOpen"
},
{
name: "生活缴费",
icon: "/static/propertyImgs/icon-pay.png",
path: "/pages/payment-list/paymentIndex"
},
{
name: "在线报修",
icon: "/static/propertyImgs/icon-repair.png",
path: "/pages/online-repair/onlineRepair"
},
{
name: "访客邀请",
icon: "/static/propertyImgs/icon-visitor.png",
path: "/pages/visitor/visitor-list"
},
{
name: "投诉意见",
icon: "/static/propertyImgs/icon-complaint.png",
path: "/pages/service-list/complaintsSuggestionsIndex"
},
{
name: "问卷调查",
icon: "/static/propertyImgs/icon-survey.png",
path: "/pages/service-list/questionnaireSurveyIndex"
},
{
name: "社区活动",
icon: "/static/propertyImgs/icon-activity.png",
path: "/pages/activity-list/activityList"
},
{
name: "公告公示",
icon: "/static/propertyImgs/icon-notice.png",
path: "/pages/notice-list/noticeList"
},
{
name: "联系物业",
icon: "/static/propertyImgs/icon-contact.png",
path: "/pages/service-list/contactProperty"
}
]
}
},
methods: {
// 功能点击事件
handleFuncClick(item) {
console.log(item.path);
uni.redirectTo({
url: item.path,
success: res => {},
fail: () => {},
complete: () => {}
});
},
},
}
</script>
<style lang="scss">
page {
background: #f2f1f6;
}
</style>
<style scoped>
.status-bar {
width: 100vw;
height: 46px;
}
.contentStyle {
display: flex;
flex-direction: column;
height: 100vh;
/* 关键:占满屏幕高度 */
background: linear-gradient(to left bottom, #e2efff 0%, #f9f9f9 50%);
}
.page {
flex: 1;
overflow-y: auto;
padding: 0px 20px;
box-sizing: border-box;
/* background-color: #fff; */
}
.container {
/* padding: 20px; */
}
/* Banner 轮播 */
.banner-box {
width: 100%;
border-radius: 10px;
overflow: hidden;
margin-bottom: 20px;
height: 111px;
}
.banner-img {
width: 100%;
height: 111px;
}
/* 功能区通用样式 */
.func-section {
width: 100%;
background-color: #ffffff;
border-radius: 10px;
padding: 10px;
box-sizing: border-box;
margin-bottom: 20px;
}
.section-title {
display: flex;
align-items: center;
margin-bottom: 10px;
}
.title-line {
width: 6px;
height: 24px;
background-color: #007aff;
border-radius: 3px;
margin-right: 10px;
}
.title-text {
font-size: 16px;
font-weight: 600;
color: #000000;
}
/* 功能网格 */
.func-grid {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
}
.func-item {
width: 22%;
display: flex;
flex-direction: column;
align-items: center;
margin-bottom: 20px;
}
.func-icon {
width: 42px;
height: 42px;
border-radius: 50%;
background-color: #f0f5ff;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 10px;
position: relative;
}
.func-icon image {
max-height: 100%;
/* 限制最大高度不超出 */
object-fit: cover;
/* 保持比例 */
/* width: 200px; */
/* height: 200px; */
}
.func-name {
font-size: 14px;
color: #000000;
font-weight: 400;
text-align: center;
line-height: 1.4;
}
/* 智慧社区网格适配9个图标最后一个左对齐 */
.community-grid .func-item {
width: 22%;
}
.community-grid .func-item:nth-child(9) {
margin-right: auto;
margin-left: 0;
}
</style>

View File

@@ -0,0 +1,117 @@
<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

@@ -0,0 +1,496 @@
<template>
<view class="visitor-add-page">
<!-- Tab 切换车辆来访/人员来访 -->
<view class="tab-bar">
<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>
<!-- 表单区域 -->
<scroll-view class="form-scroll" scroll-y>
<view class="form-section">
<uni-forms ref="formRef" :modelValue="formData" :rules="formRules" label-width="105px" label-align="left">
<!-- 来访地址 -->
<uni-forms-item label="来访地址:" name="address" required>
<uni-data-select v-model="formData.address" :localdata="addressList" @change="changeAddress" :clear="false"></uni-data-select>
</uni-forms-item>
<!-- 访客姓名 -->
<uni-forms-item label="访客姓名" name="name" required>
<uni-easyinput type="text" v-model="formData.name" placeholder="请输入姓名" />
</uni-forms-item>
<!-- 访客性别 -->
<uni-forms-item label="访客性别" name="gender" required>
<uni-data-select v-model="formData.gender" :localdata="sexList" @change="changeSex" :clear="false"></uni-data-select>
</uni-forms-item>
<!-- 访客联系方式 -->
<uni-forms-item label="访客联系方式" name="phone" required>
<uni-easyinput
v-model="formData.phone"
placeholder="请输入联系方式"
type="number" />
</uni-forms-item>
<!-- 车牌号仅车辆来访显示 -->
<uni-forms-item
label="车牌号:"
name="carNumber"
required
v-if="activeTab === 'car'"
>
<view class="plate-input-wrap">
<!-- 省份简称选择框 -->
<view
class="plate-item province-item"
@click="showProvinceClick"
>
<text>{{ plateChars[0] || '选' }}</text>
</view>
<!-- 6个字符输入框省份+6=完整车牌 -->
<input
v-for="(char, index) in plateChars.slice(1)"
:key="index"
ref="el => plateInputRefs[index + 1] = el"
class="plate-item char-item"
v-model="plateChars[index + 1]"
maxlength="1"
type="text"
@input="handlePlateInput(index + 1, $event)"
placeholder=""
@keydown="handlePlateKeydown(index + 1, $event)"/>
</view>
</uni-forms-item>
<!-- 随行人数 -->
<uni-forms-item label="随行人数:" name="escortNum">
<uni-easyinput v-model="formData.escortNum" placeholder="请输入随行人数" type="number"/>
</uni-forms-item>
<!-- 来访时间 -->
<uni-forms-item label="来访时间:" name="visitTime" required>
<uni-datetime-picker type="datetime" v-model="formData.visitTime" @change="handleTimeChange" />
</uni-forms-item>
<!-- 离开时间 -->
<uni-forms-item label="离开时间:" name="leaveTime" required>
<uni-datetime-picker type="datetime" v-model="formData.leaveTime" @change="handleTimeChange" />
</uni-forms-item>
</uni-forms>
</view>
<!-- 提示文字 -->
<view class="tips-text">
预计来访时间和预计离开时间间隔不能超过24小时
<view>车辆停留请勿超出有效期需及时驶出小区</view>
</view>
</scroll-view>
<!-- 底部按钮 -->
<view class="btn-footer">
<button
class="main-btn"
@click="activeTab === 'car' ? submitInvite() : goToQrcode()"
>
{{ activeTab === 'car' ? '确认邀请' : '生成通行二维码' }}
</button>
</view>
<!-- 省份选择弹窗 -->
<uni-popup v-model="showProvinceSelect" type="center" ref="popup">
<view class="popup-select-wrap">
<text class="popup-title">选择省份简称</text>
<uni-data-select
v-model="plateChars[0]"
:localdata="provinceList"
@confirm="onProvinceConfirm"
@cancel="popup.close()"
placeholder="请选择省份简称"
></uni-data-select>
</view>
</uni-popup>
</view>
</template>
<script setup>
import { ref, reactive, watch, nextTick } from 'vue';
// 激活的Tabcar-车辆来访person-人员来访
const activeTab = ref('car');
const popup = ref(null)
// 表单数据
const formData = reactive({
address: '', // 来访地址
name: '', // 访客姓名
gender: '', // 访客性别
phone: '', // 联系方式
carNumber: '', // 车牌号(同步输入的完整车牌)
escortNum: '', // 随行人数
visitTime: '', // 来访时间
leaveTime: '' // 离开时间
});
// 表单Ref用于调用校验方法
const formRef = ref(null);
// 表单校验规则
const formRules = reactive({
address: {
rules: [{ required: true, errorMessage: '请选择来访地址' }]
},
name: {
rules: [{ required: true, errorMessage: '请输入访客姓名' }]
},
gender: {
rules: [{ required: true, errorMessage: '请选择访客性别' }]
},
phone: {
rules: [
{ required: true, errorMessage: '请输入正确的手机号' },
{ pattern: /^1[3-9]\d{9}$/, errorMessage: '请输入正确的手机号' }
]
},
carNumber: {
rules: [
{
required: true,
errorMessage: "请输入车牌号",
},
{
validateFunction: (rule, value, callback) => {
// 仅车辆来访时生效
if (activeTab.value !== 'car') return callback();
// 1. 校验省份是否选择
if (!plateChars.value[0]) {
return callback('请选择省份简称');
}
// 2. 拼接完整车牌号
const fullPlate = plateChars.value.join('').trim();
// 3. 校验车牌号长度(省份+5/6位字符
if (fullPlate.length < 7) {
return callback('请输入完整车牌号(省份+5位字符');
}
if (fullPlate.length > 8) {
return callback('车牌号长度超出限制最多7位');
}
// 4. 同步到formData.carNumber供接口提交
formData.carNumber = fullPlate;
callback();
}
}
]
},
visitTime: {
rules: [{ required: true, errorMessage: '请选择来访时间' }]
},
leaveTime: {
rules: [
{ required: true, errorMessage: '请选择离开时间' },
{
validateFunction: (rule, value, callback) => {
// 1. 空值校验
if (!value) return callback('请选择离开时间');
if (!formData.visitTime) return callback('请先选择来访时间');
// 2. 校验时间格式有效性
const visitTime = new Date(formData.visitTime);
const leaveTime = new Date(value);
if (isNaN(visitTime.getTime()) || isNaN(leaveTime.getTime())) {
return callback('请选择有效的时间');
}
// 3. 校验离开时间不能早于来访时间
if (leaveTime <= visitTime) {
return callback('离开时间不能早于来访时间');
}
// 4. 校验时间间隔≤24小时
const diffHours = (leaveTime.getTime() - visitTime.getTime()) / (1000 * 60 * 60);
if (diffHours > 24) {
return callback('来访和离开时间间隔不能超过24小时');
}
callback();
}
}
]
}
});
// 地址列表
const addressList = ref([
{ text: '桂花园别墅1号楼1层', value: '桂花园别墅1号楼1层' },
{ text: '桂花园别墅2号楼2层', value: '桂花园别墅2号楼2层' }
]);
// 性别列表
const sexList = ref([
{ text: '男', value: '男' },
{ text: '女', value: '女' }
]);
// 省份简称列表(国标规范)
const provinceList = ref([
{ text: '京', value: '京' },
{ text: '沪', value: '沪' },
{ text: '粤', value: '粤' },
{ text: '苏', value: '苏' },
{ text: '浙', value: '浙' },
{ text: '鲁', value: '鲁' },
{ text: '川', value: '川' },
{ text: '渝', value: '渝' },
{ text: '湘', value: '湘' },
{ text: '鄂', value: '鄂' }
]);
// 车牌号逐位存储索引0省份1-6字符 → 共7位兼容普通/新能源车牌)
const plateChars = ref(['', '', '', '', '', '', '','']);
const showProvinceSelect = ref(false); // 省份选择弹窗
const plateInputRefs = ref([]); // 输入框refs用于自动聚焦
// 打开省份弹框
const showProvinceClick = () => {
popup.value.open()
};
// 选择省份后赋值
const onProvinceConfirm = () => {
showProvinceSelect.value = false;
popup.value.close();
// 省份选择后自动聚焦第一个字符输入框
nextTick(() => {
plateInputRefs.value[1]?.focus();
});
};
// 车牌号字符输入处理(优化过滤和自动聚焦)
const handlePlateInput = (index, e) => {
// 过滤非字母数字,转大写(车牌仅支持字母/数字)
let val = e.detail.value.replace(/[^A-Za-z0-9]/g, '').toUpperCase();
// 仅保留1个字符
val = val.slice(-1);
plateChars.value[index] = val;
// 自动聚焦下一个输入框(如果当前有值且不是最后一位)
if (val && index < 7) { // 最后一位是索引6无需再聚焦
nextTick(() => {
plateInputRefs.value[index + 1]?.focus();
});
}
// 输入完成后触发表单校验
if (activeTab.value === 'car') {
formRef.value.validateField('carNumber');
}
};
// 车牌号删除键处理(优化回退逻辑)
const handlePlateKeydown = (index, e) => {
// 监听回退键keyCode 8
if (e.keyCode === 8) {
// 当前输入框为空,聚焦上一个
if (!plateChars.value[index] && index > 1) {
nextTick(() => {
plateInputRefs.value[index - 1]?.focus();
});
} else {
// 清空当前输入框
plateChars.value[index] = '';
}
}
};
// 时间变更时重新校验离开时间
const handleTimeChange = () => {
if (formData.leaveTime) {
formRef.value.validateField('leaveTime');
}
};
// 切换Tab
const switchTab = (tab) => {
activeTab.value = tab;
// 切换Tab后重置表单
if (tab === 'person') {
resetForm();
}
// resetForm();
nextTick(() =>{
formRef.value.clearValidate() // 清空所有校验提示
// formRef.value.validate();
})
};
// 空方法占位
const changeAddress = () => {};
const changeSex = (e) => {};
// 提交前表单校验
const submitForm = async () => {
try {
// 调用uni-forms内置校验
await formRef.value.validate();
return true;
} catch (err) {
return false;
}
};
// 车辆来访:提交确认邀请
const submitInvite = async () => {
const isValid = await submitForm();
if (!isValid) return;
// 模拟接口提交
uni.showToast({
title: '邀请成功',
icon: 'success',
duration: 2000
});
setTimeout(() => {
resetForm();
}, 2000);
};
// 跳转到二维码页面(人员来访)
const goToQrcode = async () => {
const isValid = await submitForm();
if (!isValid) return;
const formStr = encodeURIComponent(JSON.stringify(formData));
uni.navigateTo({
url: `/pages/visitor/qrcode-page?form=${formStr}`
});
};
// 重置表单
const resetForm = () => {
formData.address = '';
formData.name = '';
formData.gender = '';
formData.phone = '';
formData.carNumber = '';
formData.escortNum = '';
formData.visitTime = '';
formData.leaveTime = '';
plateChars.value = ['', '', '', '', '', '', '']; // 重置车牌号输入
};
</script>
<style scoped>
.visitor-add-page {
background-color: #f8f8f8;
min-height: 100vh;
}
/* Tab 切换样式 */
.tab-bar {
display: flex;
background: #fff;
}
.tab-item {
flex: 1;
text-align: center;
padding: 20rpx 0;
font-size: 32rpx;
color: #666;
border-bottom: 4rpx solid transparent;
}
.tab-item.active {
color: #007aff;
border-bottom-color: #007aff;
}
/* 表单样式 */
.form-scroll {
height: calc(100vh - 220rpx);
}
.form-section {
background: #fff;
margin-top: 20rpx;
padding: 20rpx;
}
/* 车牌号逐位输入样式 */
.plate-input-wrap {
flex: 1;
display: flex;
align-items: center;
gap: 4rpx;
}
.plate-item {
width: 52rpx;
height: 60rpx;
line-height: 60rpx;
text-align: center;
border: 1rpx solid #ddd;
border-radius: 4rpx;
font-size: 28rpx;
color: #333;
}
.province-item {
background-color: #f8f8f8;
}
.char-item {
border: 1px solid #666;
outline: none;
padding: 0;
margin: 0 3rpx;
}
.char-item:focus {
border-color: #007aff;
box-shadow: 0 0 4rpx rgba(0, 122, 255, 0.5);
}
/* 省份选择弹窗样式 */
.popup-select-wrap {
background: #fff;
border-radius: 12rpx;
padding: 30rpx;
width: 600rpx;
margin: 0 auto;
}
.popup-title {
font-size: 32rpx;
font-weight: 600;
color: #333;
margin-bottom: 20rpx;
display: block;
text-align: center;
}
/* 提示文字 */
.tips-text {
margin: 20rpx;
font-size: 24rpx;
color: #999;
line-height: 1.6;
}
/* 底部按钮 */
.btn-footer {
padding: 20rpx;
background: #fff;
}
.main-btn {
background-color: #007aff;
color: #fff;
border-radius: 8rpx;
height: 88rpx;
font-size: 32rpx;
}
</style>

View File

@@ -0,0 +1,172 @@
<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

@@ -0,0 +1,273 @@
<template>
<view class="visitor-page">
<!-- 标签切换栏 -->
<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 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-color: #f5f5f5;
min-height: 100vh;
}
/* 标签切换栏 */
.tab-bar {
display: flex;
background-color: #fff;
border-bottom: 2px solid #f5f5f5;
}
.tab-item {
flex: 1;
text-align: center;
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: 50%;
transform: translateX(-50%);
width: 40px;
height: 2px;
background-color: #007aff;
}
/* 列表容器 */
.list-container {
height: calc(100vh - 150px); /* 预留导航、标签、按钮高度 */
}
/* 访客列表项 */
.visitor-item {
background-color: #fff;
margin: 10px 15px;
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>

Binary file not shown.

After

Width:  |  Height:  |  Size: 413 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 267 KiB

Submodule property-uniapp-project/static/js/chinaRegions added at 456201d979

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 350 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Some files were not shown because too many files have changed in this diff Show More