rentease-app/pages/room-add/room-add.vue

415 lines
12 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view class="room-add-page">
<view class="custom-nav safe-area-top">
<view class="nav-content">
<view class="nav-btn" @click="goBack">
<uni-icons type="left" size="22" color="#1E293B"></uni-icons>
</view>
<text class="nav-title">{{isEdit ? '编辑房间' : '添加房间'}}</text>
<view class="nav-btn" @click="saveRoom">
<text class="save-text">保存</text>
</view>
</view>
</view>
<scroll-view scroll-y class="form-content">
<view class="form-card">
<view class="form-item">
<text class="form-label">所属公寓 <text class="required">*</text></text>
<view class="form-control">
<picker mode="selector" :range="apartmentOptions" :value="selectedApartmentIndex" @change="onApartmentChange">
<view class="picker-value" :class="{ placeholder: !form.apartmentId }">
<text>{{selectedApartmentLabel}}</text>
<uni-icons type="arrowright" size="16" color="#94A3B8"></uni-icons>
</view>
</picker>
</view>
</view>
<view class="form-item">
<text class="form-label">房间号 <text class="required">*</text></text>
<view class="form-control">
<input
type="text"
v-model="form.roomNumber"
placeholder="请输入房间号101"
class="form-input"
maxlength="10"
/>
</view>
</view>
<view class="form-item">
<text class="form-label">楼层</text>
<view class="form-control">
<input
type="number"
v-model="form.floor"
placeholder="请输入楼层"
class="form-input"
/>
</view>
</view>
<view class="form-item">
<text class="form-label">户型</text>
<view class="form-control">
<input
type="text"
v-model="form.roomType"
placeholder="请输入户型,如一室一厅"
class="form-input"
/>
</view>
</view>
<view class="form-item">
<text class="form-label">面积 (m²)</text>
<view class="form-control">
<input
type="digit"
v-model="form.area"
placeholder="请输入面积"
class="form-input"
/>
</view>
</view>
<view class="form-item">
<text class="form-label">月租金 (元)</text>
<view class="form-control">
<input
type="digit"
v-model="form.monthlyPrice"
placeholder="请输入月租金"
class="form-input"
/>
</view>
</view>
<view class="form-item">
<text class="form-label">年租金 (元)</text>
<view class="form-control">
<input
type="digit"
v-model="form.yearlyPrice"
placeholder="请输入年租金"
class="form-input"
/>
</view>
</view>
<view class="form-item">
<text class="form-label">押金 (元)</text>
<view class="form-control">
<input
type="digit"
v-model="form.deposit"
placeholder="请输入押金"
class="form-input"
/>
</view>
</view>
<view class="form-item">
<text class="form-label">排序</text>
<view class="form-control">
<input
type="number"
v-model="form.sortOrder"
placeholder="数字越小越靠前"
class="form-input"
/>
</view>
</view>
<view class="form-item">
<text class="form-label">主状态</text>
<view class="form-control">
<picker mode="selector" :range="statusOptions" :value="statusIndex" @change="onStatusChange">
<view class="picker-value" :class="{ placeholder: !form.status }">
<text>{{getStatusLabel(form.status)}}</text>
<uni-icons type="arrowright" size="16" color="#94A3B8"></uni-icons>
</view>
</picker>
</view>
</view>
<view class="form-item" v-if="form.status === 'rented'">
<text class="form-label">租房标识</text>
<view class="form-control">
<picker mode="selector" :range="rentalStatusOptions" :value="rentalStatusIndex" @change="onRentalStatusChange">
<view class="picker-value" :class="{ placeholder: !form.rentalStatus }">
<text>{{getRentalStatusLabel(form.rentalStatus)}}</text>
<uni-icons type="arrowright" size="16" color="#94A3B8"></uni-icons>
</view>
</picker>
</view>
</view>
</view>
<button class="btn-submit" :loading="submitLoading" @click="saveRoom">{{isEdit ? '保存修改' : '确认添加'}}</button>
<view class="safe-area-bottom" style="height: 40rpx;"></view>
</scroll-view>
</view>
</template>
<script>
import roomApi from '@/api/room.js'
import apartmentApi from '@/api/apartment.js'
export default {
data() {
return {
isEdit: false,
roomId: null,
submitLoading: false,
apartments: [],
statusOptions: ['空房', '预定', '在租'],
statusValues: ['empty', 'reserved', 'rented'],
rentalStatusOptions: ['正常', '即将到期', '已到期'],
rentalStatusValues: ['normal', 'soon_expire', 'expired'],
form: {
apartmentId: '',
roomNumber: '',
floor: '',
roomType: '',
area: '',
monthlyPrice: '',
yearlyPrice: '',
deposit: '',
sortOrder: 0,
status: 'empty',
rentalStatus: 'normal'
}
}
},
computed: {
apartmentOptions() {
return this.apartments.map(a => a.name)
},
selectedApartmentIndex() {
const index = this.apartments.findIndex(a => a.id === this.form.apartmentId)
return index >= 0 ? index : -1
},
selectedApartmentLabel() {
const apartment = this.apartments.find(a => a.id === this.form.apartmentId)
return apartment ? apartment.name : '请选择公寓'
},
statusIndex() {
return this.statusValues.indexOf(this.form.status)
},
rentalStatusIndex() {
return this.rentalStatusValues.indexOf(this.form.rentalStatus)
}
},
onLoad(options) {
this.loadApartments()
if (options.id) {
this.isEdit = true
this.roomId = options.id
this.loadRoomDetail(options.id)
} else if (options.apartmentId) {
this.form.apartmentId = parseInt(options.apartmentId)
}
},
methods: {
async loadApartments() {
try {
const res = await apartmentApi.list()
if (res.data) {
this.apartments = res.data
}
} catch (error) {
console.error('加载公寓列表失败:', error)
}
},
async loadRoomDetail(id) {
try {
const res = await roomApi.getDetail(id)
if (res.data) {
const data = res.data
this.form = {
apartmentId: data.apartmentId,
roomNumber: data.roomNumber,
floor: data.floor ? String(data.floor) : '',
roomType: data.roomType || '',
area: data.area ? String(data.area) : '',
monthlyPrice: data.monthlyPrice ? String(data.monthlyPrice) : '',
yearlyPrice: data.yearlyPrice ? String(data.yearlyPrice) : '',
deposit: data.deposit ? String(data.deposit) : '',
sortOrder: data.sortOrder !== undefined ? data.sortOrder : 0,
status: data.status || 'empty',
rentalStatus: data.rentalStatus || 'normal'
}
}
} catch (error) {
console.error('加载房间详情失败:', error)
uni.showToast({ title: '加载失败', icon: 'none' })
}
},
onApartmentChange(e) {
this.form.apartmentId = this.apartments[e.detail.value].id
},
onStatusChange(e) {
this.form.status = this.statusValues[e.detail.value]
},
onRentalStatusChange(e) {
this.form.rentalStatus = this.rentalStatusValues[e.detail.value]
},
getStatusLabel(value) {
const index = this.statusValues.indexOf(value)
return index >= 0 ? this.statusOptions[index] : '请选择状态'
},
getRentalStatusLabel(value) {
const index = this.rentalStatusValues.indexOf(value)
return index >= 0 ? this.rentalStatusOptions[index] : '请选择租房标识'
},
async saveRoom() {
if (!this.form.apartmentId) {
uni.showToast({ title: '请选择所属公寓', icon: 'none' })
return
}
if (!this.form.roomNumber.trim()) {
uni.showToast({ title: '请输入房间号', icon: 'none' })
return
}
this.submitLoading = true
try {
const data = {
apartmentId: this.form.apartmentId,
roomNumber: this.form.roomNumber.trim(),
floor: this.form.floor ? parseInt(this.form.floor) : null,
roomType: this.form.roomType.trim() || null,
area: this.form.area ? parseFloat(this.form.area) : null,
monthlyPrice: this.form.monthlyPrice ? parseFloat(this.form.monthlyPrice) : null,
yearlyPrice: this.form.yearlyPrice ? parseFloat(this.form.yearlyPrice) : null,
deposit: this.form.deposit ? parseFloat(this.form.deposit) : null,
sortOrder: this.form.sortOrder !== '' ? parseInt(this.form.sortOrder) : 0,
status: this.form.status,
rentalStatus: this.form.rentalStatus
}
if (this.isEdit) {
await roomApi.update(this.roomId, data)
uni.showToast({ title: '修改成功', icon: 'success' })
} else {
await roomApi.create(data)
uni.showToast({ title: '添加成功', icon: 'success' })
}
setTimeout(() => uni.navigateBack(), 1500)
} catch (error) {
console.error('保存失败:', error)
uni.showToast({ title: this.isEdit ? '修改失败' : '添加失败', icon: 'none' })
} finally {
this.submitLoading = false
}
},
goBack() {
uni.navigateBack()
}
}
}
</script>
<style scoped>
.room-add-page {
min-height: 100vh;
background: #F8FAFC;
display: flex;
flex-direction: column;
}
.custom-nav {
background: #FFFFFF;
border-bottom: 2rpx solid #F1F5F9;
}
.nav-content {
display: flex;
align-items: center;
justify-content: space-between;
padding: 20rpx 32rpx;
}
.nav-btn {
width: 72rpx;
height: 72rpx;
display: flex;
align-items: center;
justify-content: center;
border-radius: 50%;
}
.nav-title {
font-size: 34rpx;
font-weight: 600;
color: #1E293B;
}
.save-text {
font-size: 28rpx;
color: #2563EB;
font-weight: 600;
}
.form-content {
flex: 1;
padding: 24rpx 32rpx;
}
.form-card {
background: #FFFFFF;
border-radius: 20rpx;
padding: 0 24rpx;
margin-bottom: 40rpx;
}
.form-item {
display: flex;
align-items: center;
padding: 24rpx 0;
border-bottom: 2rpx solid #F1F5F9;
}
.form-item:last-child {
border-bottom: none;
}
.form-label {
width: 180rpx;
font-size: 28rpx;
color: #64748B;
flex-shrink: 0;
}
.form-control {
flex: 1;
}
.required {
color: #EF4444;
}
.picker-value {
display: flex;
align-items: center;
justify-content: space-between;
font-size: 28rpx;
color: #1E293B;
}
.picker-value.placeholder {
color: #94A3B8;
}
.form-input {
width: 100%;
height: 48rpx;
font-size: 28rpx;
color: #1E293B;
text-align: right;
}
.form-input::placeholder {
color: #94A3B8;
}
.btn-submit {
background: linear-gradient(135deg, #2563EB 0%, #1D4ED8 100%);
color: #FFFFFF;
font-size: 30rpx;
font-weight: 600;
height: 88rpx;
line-height: 88rpx;
border-radius: 16rpx;
border: none;
width: 100%;
}
</style>