You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
699 lines
18 KiB
699 lines
18 KiB
<template>
|
|
<view class="container">
|
|
<!-- 搜索框 -->
|
|
<view class="search-container">
|
|
<u-search
|
|
v-model="queryParams.telephone"
|
|
placeholder="请输入手机号"
|
|
:show-action="false"
|
|
:clearabled="true"
|
|
bg-color="#fff"
|
|
shape="square"
|
|
@search="handleSearch"
|
|
@clear="handleClear"
|
|
>
|
|
<template v-slot:prefix>
|
|
<u-icon name="search" size="16" color="#999"></u-icon>
|
|
</template>
|
|
</u-search>
|
|
</view>
|
|
|
|
<!-- 预约列表 -->
|
|
<scroll-view
|
|
class="appointment-list"
|
|
scroll-y="true"
|
|
:lower-threshold="150"
|
|
@scrolltolower="handleLoadMore"
|
|
@scroll="handleScroll"
|
|
:refresher-enabled="true"
|
|
:refresher-triggered="refreshing"
|
|
@refresherrefresh="handleRefresh"
|
|
:scroll-top="scrollTop"
|
|
:scroll-with-animation="false"
|
|
:enable-back-to-top="true"
|
|
>
|
|
<view
|
|
class="appointment-card"
|
|
v-for="item in appointmentList"
|
|
:key="item.id"
|
|
>
|
|
<!-- 卡片头部 -->
|
|
<view class="card-header">
|
|
<u-icon name="/static/img/kf.png" size="20" color="#08B3B3"></u-icon>
|
|
<text class="community-info"
|
|
>{{ item.apartmentName }} | {{ item.houseTypeName||'' }}</text
|
|
>
|
|
</view>
|
|
|
|
<!-- 卡片内容 -->
|
|
<view class="card-content">
|
|
<view class="info-row">
|
|
<text class="label">预约人:</text>
|
|
<text class="value">{{ item.graduateName }}</text>
|
|
</view>
|
|
<view class="info-row">
|
|
<text class="label">预约人电话:</text>
|
|
<text class="value">{{ item.telephone }}</text>
|
|
</view>
|
|
<view class="info-row">
|
|
<text class="label">预约时间:</text>
|
|
<text class="value">{{ formatCheckInTime(
|
|
item.reservationTimeFrom,
|
|
item.reservationTimeTo
|
|
) }}</text>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 操作按钮 -->
|
|
<view class="actionButtons" v-if="item.state == 0">
|
|
<button class="btn process" @click="handleViewHouse(item)">
|
|
看房登记
|
|
</button>
|
|
</view>
|
|
</view>
|
|
<!-- 底部加载与无更多提示 -->
|
|
<view class="list-footer" v-if="loadingMore">加载中...</view>
|
|
<view class="list-footer" v-else-if="!hasMore && appointmentList.length > 0">没有更多了</view>
|
|
</scroll-view>
|
|
|
|
<!-- 空状态 -->
|
|
<u-empty
|
|
v-if="appointmentList.length === 0 && !loading"
|
|
text="暂无预约信息"
|
|
mode="list"
|
|
></u-empty>
|
|
|
|
<!-- 加载状态 -->
|
|
<u-loading-page
|
|
v-if="loading"
|
|
:loading="true"
|
|
text="加载中..."
|
|
></u-loading-page>
|
|
|
|
<!-- 看房登记弹框 -->
|
|
<u-popup
|
|
:show="showRegisterPopup"
|
|
mode="center"
|
|
:round="16"
|
|
@close="handleClosePopup"
|
|
bgColor="transparent"
|
|
>
|
|
<view class="register-popup" @click="showStatusDropdown = false">
|
|
<view class="popup-close" @click.stop="handleClosePopup">
|
|
<u-icon
|
|
name="/static/img/close-circle.png"
|
|
size="22"
|
|
color="#666"
|
|
></u-icon>
|
|
</view>
|
|
<view class="popup-title" style="letter-spacing: 1rpx">看房登记</view>
|
|
|
|
<view class="form-container">
|
|
<!-- 看房状态 -->
|
|
<view class="form-item">
|
|
<text class="required">*</text>
|
|
<text class="form-label"> 看房状态 </text>
|
|
<view class="dropdown" @click.stop="toggleStatusDropdown">
|
|
<u-input
|
|
v-model="viewingStatusText"
|
|
placeholder="请选择看房状态"
|
|
:disabled="true"
|
|
:border="true"
|
|
shape="round"
|
|
:custom-style="{ backgroundColor: '#fff' }"
|
|
>
|
|
<template v-slot:suffix>
|
|
<u-icon
|
|
:class="{ 'arrow-open': showStatusDropdown }"
|
|
name="arrow-down"
|
|
size="14"
|
|
color="#999"
|
|
></u-icon>
|
|
</template>
|
|
</u-input>
|
|
<view v-if="showStatusDropdown" class="dropdown-list">
|
|
<view
|
|
v-for="opt in statusColumns"
|
|
:key="opt.value"
|
|
class="dropdown-item"
|
|
@click.stop="selectStatus(opt)"
|
|
>
|
|
{{ opt.text }}
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 看房说明 -->
|
|
<view class="form-item" style="flex-direction: column">
|
|
<text class="form-label">看房说明</text>
|
|
<u-textarea
|
|
v-model="viewingDescription"
|
|
placeholder="请输入看房说明"
|
|
:border="true"
|
|
shape="round"
|
|
:custom-style="{ backgroundColor: '#fff' }"
|
|
:maxlength="200"
|
|
:count="true"
|
|
></u-textarea>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 操作按钮 -->
|
|
<view class="popup-actions">
|
|
<u-button
|
|
type="default"
|
|
plain
|
|
:custom-style="{
|
|
background: '#ffffff',
|
|
color: '#B0B0B0',
|
|
borderColor: '#D7D7D7',
|
|
height: '35px',
|
|
}"
|
|
shape="circle"
|
|
@click="handleCancel"
|
|
>
|
|
取消
|
|
</u-button>
|
|
<u-button
|
|
type="primary"
|
|
:custom-style="{
|
|
background: '#22C1C3',
|
|
color: '#ffffff',
|
|
borderColor: '#22C1C3',
|
|
height: '35px',
|
|
}"
|
|
shape="circle"
|
|
@click="handleConfirm"
|
|
>
|
|
确定
|
|
</u-button>
|
|
</view>
|
|
</view>
|
|
</u-popup>
|
|
</view>
|
|
</template>
|
|
|
|
<script>
|
|
import { listViewing, viewingRegister,viewingDetail } from "@/pages/api";
|
|
export default {
|
|
data() {
|
|
return {
|
|
searchPhone: "", // 搜索手机号
|
|
loading: false, // 加载状态
|
|
appointmentList: [],
|
|
queryParams: {
|
|
pageNum: 1,
|
|
pageSize: 10,
|
|
},
|
|
total: 0,
|
|
hasMore: true,
|
|
refreshing: false,
|
|
loadingMore: false,
|
|
scrollTop: 0,
|
|
isScrolling: false,
|
|
scrollTimer: null,
|
|
showRegisterPopup: false, // 看房登记弹框显示状态
|
|
currentAppointment: {}, // 当前选中的预约信息
|
|
viewingStatus: "1", // 看房状态 (1: 已看房, 0: 未看房,2: 已取消)
|
|
viewingStatusText: "已看房", // 看房状态文本
|
|
viewingDescription: "", // 看房说明
|
|
showStatusDropdown: false, // 看房状态下拉
|
|
statusColumns: [
|
|
{
|
|
text: "已看房",
|
|
value: "1",
|
|
},
|
|
],
|
|
};
|
|
},
|
|
onLoad() {
|
|
this.resetAndLoad();
|
|
},
|
|
// 下拉刷新
|
|
onPullDownRefresh() {
|
|
this.handleRefresh();
|
|
},
|
|
// 上拉加载
|
|
onReachBottom() {
|
|
this.handleLoadMore();
|
|
},
|
|
methods: {
|
|
formatCheckInTime(fromTime, toTime) {
|
|
if (!fromTime || !toTime) {
|
|
return "";
|
|
}
|
|
|
|
try {
|
|
// 解析开始时间
|
|
const fromDate = new Date(fromTime);
|
|
const fromDateStr = fromDate.toISOString().split("T")[0]; // 获取日期部分 YYYY-MM-DD
|
|
const fromTimeStr = fromDate.toTimeString().substring(0, 5); // 获取时间部分 HH:MM
|
|
|
|
// 解析结束时间
|
|
const toDate = new Date(toTime);
|
|
const toTimeStr = toDate.toTimeString().substring(0, 5); // 获取时间部分 HH:MM
|
|
|
|
// 如果开始和结束是同一天,只显示一次日期
|
|
if (fromDateStr === toDate.toISOString().split("T")[0]) {
|
|
return `${fromDateStr} ${fromTimeStr}-${toTimeStr}`;
|
|
} else {
|
|
// 如果不是同一天,显示完整格式
|
|
const toDateStr = toDate.toISOString().split("T")[0];
|
|
return `${fromDateStr} ${fromTimeStr}-${toDateStr} ${toTimeStr}`;
|
|
}
|
|
} catch (error) {
|
|
console.error("日期格式化错误:", error);
|
|
return "";
|
|
}
|
|
},
|
|
// 重置并加载
|
|
resetAndLoad() {
|
|
this.appointmentList = [];
|
|
this.queryParams.pageNum = 1;
|
|
this.hasMore = true;
|
|
this.refreshing = false;
|
|
this.loadingMore = false;
|
|
this.scrollTop = 0; // 重置滚动位置
|
|
this.loadAppointmentList();
|
|
},
|
|
|
|
// 下拉刷新
|
|
handleRefresh() {
|
|
if (this.loading || this.refreshing) return;
|
|
this.refreshing = true;
|
|
this.queryParams.pageNum = 1;
|
|
this.hasMore = true;
|
|
this.scrollTop = 0; // 重置滚动位置
|
|
this.loadAppointmentList();
|
|
},
|
|
|
|
// 滚动事件处理
|
|
handleScroll(e) {
|
|
this.isScrolling = true;
|
|
// 防抖处理
|
|
clearTimeout(this.scrollTimer);
|
|
this.scrollTimer = setTimeout(() => {
|
|
this.isScrolling = false;
|
|
}, 100);
|
|
},
|
|
|
|
// 上拉加载更多
|
|
handleLoadMore() {
|
|
if (this.loading || this.loadingMore || !this.hasMore || this.isScrolling) return;
|
|
this.loadingMore = true;
|
|
this.loadAppointmentList();
|
|
},
|
|
|
|
// 加载预约列表
|
|
loadAppointmentList() {
|
|
if (this.loading || this.refreshing || this.loadingMore) {
|
|
// 防抖,避免重复请求
|
|
}
|
|
// 首屏或非刷新加载时展示整页 loading
|
|
if (this.queryParams.pageNum === 1 && !this.refreshing) {
|
|
this.loading = true;
|
|
}
|
|
listViewing(this.queryParams)
|
|
.then((response) => {
|
|
const rows = Array.isArray(response?.rows) ? response.rows : [];
|
|
if (this.queryParams.pageNum === 1) {
|
|
this.appointmentList = rows;
|
|
} else if (rows.length) {
|
|
this.appointmentList = [...this.appointmentList, ...rows];
|
|
}
|
|
this.total = Number(response?.total || 0);
|
|
// 是否还有更多:优先用返回条数与 pageSize 判断,兼容 total
|
|
const pageHasMore = rows.length >= this.queryParams.pageSize;
|
|
const totalHasMore = this.appointmentList.length < this.total;
|
|
this.hasMore = pageHasMore && (this.total ? totalHasMore : true);
|
|
if (this.hasMore) {
|
|
this.queryParams.pageNum++;
|
|
}
|
|
})
|
|
.catch((err) => {
|
|
// 错误时不改变已有列表,仅结束本次加载状态
|
|
})
|
|
.finally(() => {
|
|
// 统一收敛状态
|
|
if (this.refreshing) {
|
|
uni.stopPullDownRefresh();
|
|
this.refreshing = false;
|
|
}
|
|
this.loading = false;
|
|
this.loadingMore = false;
|
|
});
|
|
},
|
|
|
|
// 搜索预约
|
|
handleSearch(value) {
|
|
this.queryParams.pageNum = 1;
|
|
this.loadAppointmentList();
|
|
},
|
|
|
|
// 清空搜索
|
|
handleClear() {
|
|
this.queryParams = {
|
|
pageNum: 1,
|
|
pageSize: 10,
|
|
telephone: "",
|
|
};
|
|
this.loadAppointmentList();
|
|
},
|
|
|
|
// 看房登记
|
|
handleViewHouse(item) {
|
|
this.currentAppointment = item;
|
|
this.viewingStatus = item.viewingStatus || "1"; // 如果有已有的看房状态,则使用
|
|
this.viewingStatusText =
|
|
this.statusColumns.find((col) => col.value === this.viewingStatus)
|
|
?.text || "已看房";
|
|
this.viewingDescription = item.viewingDescription || "";
|
|
this.showRegisterPopup = true;
|
|
},
|
|
|
|
// 关闭弹框
|
|
handleClosePopup() {
|
|
this.showRegisterPopup = false;
|
|
this.currentAppointment = {};
|
|
this.viewingStatus = "1";
|
|
this.viewingStatusText = "已看房";
|
|
this.viewingDescription = "";
|
|
this.showStatusDropdown = false;
|
|
},
|
|
|
|
// 内联下拉切换与选择
|
|
toggleStatusDropdown() {
|
|
this.showStatusDropdown = !this.showStatusDropdown;
|
|
},
|
|
selectStatus(opt) {
|
|
this.viewingStatus = opt.value;
|
|
this.viewingStatusText = opt.text;
|
|
this.showStatusDropdown = false;
|
|
},
|
|
|
|
// 确定看房登记
|
|
handleConfirm() {
|
|
if (!this.viewingStatus) {
|
|
uni.showToast({
|
|
title: "请选择看房状态",
|
|
icon: "none",
|
|
});
|
|
return;
|
|
}
|
|
const params = {
|
|
id: this.currentAppointment.id,
|
|
state: this.viewingStatus,
|
|
remark: this.viewingDescription,
|
|
};
|
|
viewingRegister(params)
|
|
.then((res) => {
|
|
if (res.code === 200) {
|
|
// 先关闭弹框,避免遮挡 toast
|
|
this.handleClosePopup();
|
|
this.$nextTick(() => {
|
|
uni.showToast({
|
|
title: '提交成功',
|
|
icon: 'none',
|
|
});
|
|
// 成功后重置并刷新列表
|
|
this.resetAndLoad();
|
|
});
|
|
}
|
|
})
|
|
.catch(() => {})
|
|
},
|
|
|
|
// 取消看房登记
|
|
handleCancel() {
|
|
this.handleClosePopup();
|
|
},
|
|
},
|
|
};
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.container {
|
|
min-height: 100vh;
|
|
background-color: #f8f8f8;
|
|
padding: 20rpx;
|
|
}
|
|
|
|
.search-container {
|
|
margin-bottom: 30rpx;
|
|
}
|
|
|
|
.appointment-list {
|
|
height: calc(100vh - 100rpx); /* 减去搜索框和padding的高度,给滚动留更多空间 */
|
|
overflow: hidden;
|
|
.appointment-card {
|
|
background-color: #fff;
|
|
border-radius: 16rpx;
|
|
padding: 30rpx;
|
|
margin-bottom: 20rpx;
|
|
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.05);
|
|
position: relative;
|
|
transition: all 0.3s ease;
|
|
|
|
&:active {
|
|
transform: scale(0.98);
|
|
}
|
|
|
|
.card-header {
|
|
display: flex;
|
|
align-items: center;
|
|
margin-bottom: 24rpx;
|
|
|
|
.community-info {
|
|
margin-left: 16rpx;
|
|
font-size: 32rpx;
|
|
font-weight: 600;
|
|
color: #333;
|
|
}
|
|
}
|
|
|
|
.card-content {
|
|
background: #fbfbfb;
|
|
padding: 20rpx;
|
|
|
|
.info-row {
|
|
display: flex;
|
|
margin-bottom: 16rpx;
|
|
|
|
&:last-child {
|
|
margin-bottom: 0;
|
|
}
|
|
|
|
.label {
|
|
color: #999;
|
|
font-size: 28rpx;
|
|
min-width: 160rpx;
|
|
text-align: right;
|
|
}
|
|
|
|
.value {
|
|
color: #333;
|
|
font-size: 28rpx;
|
|
flex: 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
.actionButtons {
|
|
display: flex;
|
|
justify-content: flex-end;
|
|
align-items: center;
|
|
margin-top: 20rpx;
|
|
margin-left: auto;
|
|
gap: 20rpx;
|
|
width: fit-content;
|
|
}
|
|
|
|
.btn {
|
|
height: 52rpx;
|
|
line-height: 46rpx;
|
|
border-radius: 52rpx;
|
|
background-color: rgba(255, 255, 255, 1);
|
|
color: rgba(8, 179, 179, 1);
|
|
font-size: 28rpx;
|
|
text-align: center;
|
|
font-family: AlibabaPuHui-regular;
|
|
border: 2rpx solid rgba(8, 179, 179, 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* 看房登记弹框样式 */
|
|
.register-popup {
|
|
width: 76%;
|
|
padding: 36rpx 50rpx 32rpx;
|
|
background: radial-gradient(
|
|
110% 140% at 80% 0%,
|
|
rgba(35, 217, 208, 0.35) 0%,
|
|
rgba(35, 217, 208, 0.18) 22%,
|
|
rgba(255, 255, 255, 0) 60%
|
|
),
|
|
radial-gradient(
|
|
120% 140% at 0% 100%,
|
|
rgba(255, 173, 199, 0.28) 0%,
|
|
rgba(255, 173, 199, 0.12) 28%,
|
|
rgba(255, 255, 255, 0) 58%
|
|
),
|
|
#ffffff;
|
|
border-radius: 45rpx;
|
|
box-shadow: 0 12rpx 28rpx rgba(0, 0, 0, 0.18);
|
|
border: 1rpx solid rgba(0, 0, 0, 0.06);
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
position: relative;
|
|
|
|
.popup-close {
|
|
position: absolute;
|
|
top: -35rpx;
|
|
right: -48rpx;
|
|
}
|
|
|
|
.popup-title {
|
|
font-size: 40rpx;
|
|
font-weight: 700;
|
|
color: #111;
|
|
margin-bottom: 36rpx;
|
|
text-align: center;
|
|
}
|
|
|
|
.form-container {
|
|
width: 100%;
|
|
margin-bottom: 32rpx;
|
|
|
|
.appointment-info {
|
|
background-color: #fafafa;
|
|
border-radius: 16rpx;
|
|
padding: 24rpx 30rpx;
|
|
margin-bottom: 28rpx;
|
|
border: 1rpx solid #efefef;
|
|
box-shadow: none;
|
|
|
|
.info-item {
|
|
display: flex;
|
|
margin-bottom: 16rpx;
|
|
align-items: center;
|
|
|
|
&:last-child {
|
|
margin-bottom: 0;
|
|
}
|
|
|
|
.label {
|
|
color: #6c757d;
|
|
font-size: 28rpx;
|
|
min-width: 200rpx;
|
|
font-weight: 500;
|
|
}
|
|
|
|
.value {
|
|
color: #495057;
|
|
font-size: 28rpx;
|
|
flex: 1;
|
|
font-weight: 400;
|
|
}
|
|
}
|
|
}
|
|
|
|
.form-item {
|
|
margin-bottom: 30rpx;
|
|
display: flex;
|
|
|
|
&:last-child {
|
|
margin-bottom: 0;
|
|
}
|
|
.required {
|
|
color: #ff0000;
|
|
font-size: 28rpx;
|
|
margin-top: 16rpx;
|
|
}
|
|
.form-label {
|
|
display: flex;
|
|
align-items: center;
|
|
font-size: 30rpx;
|
|
margin-bottom: 16rpx;
|
|
white-space: nowrap;
|
|
margin-right: 20rpx;
|
|
}
|
|
.u-input,
|
|
.u-textarea {
|
|
background-color: #fff;
|
|
border: 1rpx solid #e6e6e6;
|
|
border-radius: 14rpx;
|
|
padding: 20rpx;
|
|
font-size: 28rpx;
|
|
color: #333;
|
|
min-height: 80rpx;
|
|
transition: all 0.3s ease;
|
|
|
|
&:focus {
|
|
border-color: #08b3b3;
|
|
box-shadow: 0 0 0 2rpx rgba(8, 179, 179, 0.1);
|
|
}
|
|
}
|
|
|
|
.u-textarea {
|
|
padding-top: 16rpx;
|
|
min-height: 120rpx;
|
|
resize: none;
|
|
}
|
|
|
|
/* 下拉样式 */
|
|
.dropdown {
|
|
position: relative;
|
|
}
|
|
.dropdown-list {
|
|
position: absolute;
|
|
top: calc(100% + 8rpx);
|
|
left: 0;
|
|
right: 0;
|
|
background: #ffffff;
|
|
border: 1rpx solid #e6e6e6;
|
|
border-radius: 14rpx;
|
|
box-shadow: 0 8rpx 20rpx rgba(0, 0, 0, 0.08);
|
|
z-index: 9;
|
|
overflow: hidden;
|
|
}
|
|
.dropdown-item {
|
|
padding: 22rpx 24rpx;
|
|
font-size: 28rpx;
|
|
color: #333;
|
|
}
|
|
.dropdown-item:active {
|
|
background: #f6f7fb;
|
|
}
|
|
.arrow-open {
|
|
transform: rotate(180deg);
|
|
transition: transform 0.2s ease;
|
|
}
|
|
}
|
|
}
|
|
|
|
.popup-actions {
|
|
width: 100%;
|
|
display: flex;
|
|
justify-content: space-between;
|
|
margin-top: 36rpx;
|
|
gap: 28rpx;
|
|
|
|
.u-button {
|
|
flex: 1;
|
|
border-radius: 48rpx;
|
|
font-size: 30rpx;
|
|
height: 88rpx;
|
|
line-height: 88rpx;
|
|
}
|
|
}
|
|
}
|
|
|
|
.list-footer {
|
|
text-align: center;
|
|
color: #999;
|
|
font-size: 26rpx;
|
|
padding: 20rpx 0;
|
|
}
|
|
</style>
|
|
|