diff --git a/.env b/.env new file mode 100644 index 0000000..618d8c9 --- /dev/null +++ b/.env @@ -0,0 +1,3 @@ +# 默认环境配置 +NODE_ENV=development +VUE_APP_API_BASE_URL=/api \ No newline at end of file diff --git a/.env.production b/.env.production new file mode 100644 index 0000000..583ddee --- /dev/null +++ b/.env.production @@ -0,0 +1,3 @@ +# 生产环境配置 +NODE_ENV=production +VUE_APP_API_BASE_URL=/api \ No newline at end of file diff --git a/.env.test b/.env.test new file mode 100644 index 0000000..cc587ae --- /dev/null +++ b/.env.test @@ -0,0 +1,3 @@ +# 测试环境配置 +NODE_ENV=test +VUE_APP_API_BASE_URL=/api \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index b316e78..ee5fe32 100644 --- a/package-lock.json +++ b/package-lock.json @@ -780,6 +780,29 @@ "webpack-merge": "^5.7.3", "webpack-virtual-modules": "^0.4.2", "whatwg-fetch": "^3.6.2" + }, + "dependencies": { + "@vue/vue-loader-v15": { + "version": "npm:vue-loader@15.11.1", + "resolved": "https://registry.npmmirror.com/vue-loader/-/vue-loader-15.11.1.tgz", + "integrity": "sha512-0iw4VchYLePqJfJu9s62ACWUXeSqM30SQqlIftbYWM3C+jpPcEHKSPUZBLjSF9au4HTHQ/naF6OGnO3Q/qGR3Q==", + "dev": true, + "requires": { + "@vue/component-compiler-utils": "^3.1.0", + "hash-sum": "^1.0.2", + "loader-utils": "^1.1.0", + "vue-hot-reload-api": "^2.3.0", + "vue-style-loader": "^4.1.0" + }, + "dependencies": { + "hash-sum": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/hash-sum/-/hash-sum-1.0.2.tgz", + "integrity": "sha512-fUs4B4L+mlt8/XAtSOGMUO1TXmAelItBPtJG7CyHJfYTdDjwisntGO2JQz7oUsatOY9o68+57eziUVNw/mRHmA==", + "dev": true + } + } + } } }, "@vue/cli-shared-utils": { @@ -870,27 +893,6 @@ } } }, - "@vue/vue-loader-v15": { - "version": "npm:vue-loader@15.11.1", - "resolved": "https://registry.npmmirror.com/vue-loader/-/vue-loader-15.11.1.tgz", - "integrity": "sha512-0iw4VchYLePqJfJu9s62ACWUXeSqM30SQqlIftbYWM3C+jpPcEHKSPUZBLjSF9au4HTHQ/naF6OGnO3Q/qGR3Q==", - "dev": true, - "requires": { - "@vue/component-compiler-utils": "^3.1.0", - "hash-sum": "^1.0.2", - "loader-utils": "^1.1.0", - "vue-hot-reload-api": "^2.3.0", - "vue-style-loader": "^4.1.0" - }, - "dependencies": { - "hash-sum": { - "version": "1.0.2", - "resolved": "https://registry.npmmirror.com/hash-sum/-/hash-sum-1.0.2.tgz", - "integrity": "sha512-fUs4B4L+mlt8/XAtSOGMUO1TXmAelItBPtJG7CyHJfYTdDjwisntGO2JQz7oUsatOY9o68+57eziUVNw/mRHmA==", - "dev": true - } - } - }, "@vue/web-component-wrapper": { "version": "1.3.0", "resolved": "https://registry.npmmirror.com/@vue/web-component-wrapper/-/web-component-wrapper-1.3.0.tgz", diff --git a/src/api/api.js b/src/api/api.js index af001e7..4aa3f3c 100644 --- a/src/api/api.js +++ b/src/api/api.js @@ -1,180 +1,84 @@ // API服务层,用于与后端进行交互 - -const API_BASE_URL = '/api'; - -// 通用请求函数 -async function request(url, options = {}) { - try { - const response = await fetch(`${API_BASE_URL}${url}`, { - ...options, - headers: { - 'Content-Type': 'application/json', - ...options.headers - } - }); - - if (!response.ok) { - throw new Error(`HTTP error! status: ${response.status}`); - } - - return await response.json(); - } catch (error) { - console.error('API request error:', error); - throw error; - } -} +import { get, post, put, del } from './request'; // 区域管理API export const regionApi = { - getAll: () => request('/regions'), - getById: (id) => request(`/regions/${id}`), - create: (data) => request('/regions', { - method: 'POST', - body: JSON.stringify(data) - }), - update: (id, data) => request(`/regions/${id}`, { - method: 'PUT', - body: JSON.stringify(data) - }), - delete: (id) => request(`/regions/${id}`, { - method: 'DELETE' - }) + getAll: () => get('/regions'), + getById: (id) => get(`/regions/${id}`), + create: (data) => post('/regions', data), + update: (id, data) => put(`/regions/${id}`, data), + delete: (id) => del(`/regions/${id}`) }; -// 构建查询字符串 -function buildQueryString(params) { - const query = Object.entries(params) - .filter(([key, value]) => value !== undefined && value !== null && value !== '') - .map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`) - .join('&'); - return query ? `?${query}` : ''; -} - // 公寓管理API export const apartmentApi = { - getAll: (params = {}) => request(`/apartments${buildQueryString(params)}`), - getById: (id) => request(`/apartments/${id}`), - create: (data) => request('/apartments', { - method: 'POST', - body: JSON.stringify(data) - }), - update: (id, data) => request(`/apartments/${id}`, { - method: 'PUT', - body: JSON.stringify(data) - }), - delete: (id) => request(`/apartments/${id}`, { - method: 'DELETE' - }) + getAll: (params = {}) => get('/apartments', params), + getById: (id) => get(`/apartments/${id}`), + create: (data) => post('/apartments', data), + update: (id, data) => put(`/apartments/${id}`, data), + delete: (id) => del(`/apartments/${id}`) }; // 房间管理API export const roomApi = { - getAll: (params = {}) => request(`/rooms${buildQueryString(params)}`), - getById: (id) => request(`/rooms/${id}`), - create: (data) => request('/rooms', { - method: 'POST', - body: JSON.stringify(data) - }), - update: (id, data) => request(`/rooms/${id}`, { - method: 'PUT', - body: JSON.stringify(data) - }), - delete: (id) => request(`/rooms/${id}`, { - method: 'DELETE' - }) + getAll: (params = {}) => get('/rooms', params), + getById: (id) => get(`/rooms/${id}`), + create: (data) => post('/rooms', data), + update: (id, data) => put(`/rooms/${id}`, data), + delete: (id) => del(`/rooms/${id}`) }; // 租客管理API export const tenantApi = { - getAll: () => request('/tenants'), - getById: (id) => request(`/tenants/${id}`), - create: (data) => request('/tenants', { - method: 'POST', - body: JSON.stringify(data) - }), - update: (id, data) => request(`/tenants/${id}`, { - method: 'PUT', - body: JSON.stringify(data) - }), - delete: (id) => request(`/tenants/${id}`, { - method: 'DELETE' - }) + getAll: () => get('/tenants'), + getById: (id) => get(`/tenants/${id}`), + create: (data) => post('/tenants', data), + update: (id, data) => put(`/tenants/${id}`, data), + delete: (id) => del(`/tenants/${id}`) }; // 合同管理API export const contractApi = { - getAll: () => request('/contracts'), - getById: (id) => request(`/contracts/${id}`), - create: (data) => request('/contracts', { - method: 'POST', - body: JSON.stringify(data) - }), - update: (id, data) => request(`/contracts/${id}`, { - method: 'PUT', - body: JSON.stringify(data) - }), - delete: (id) => request(`/contracts/${id}`, { - method: 'DELETE' - }) + getAll: () => get('/contracts'), + getById: (id) => get(`/contracts/${id}`), + create: (data) => post('/contracts', data), + update: (id, data) => put(`/contracts/${id}`, data), + delete: (id) => del(`/contracts/${id}`) }; // 租房管理API export const rentalApi = { - getAll: (params = {}) => request(`/rentals${buildQueryString(params)}`), - getById: (id) => request(`/rentals/${id}`), - create: (data) => request('/rentals', { - method: 'POST', - body: JSON.stringify(data) - }), - update: (id, data) => request(`/rentals/${id}`, { - method: 'PUT', - body: JSON.stringify(data) - }), - delete: (id) => request(`/rentals/${id}`, { - method: 'DELETE' - }) + getAll: (params = {}) => get('/rentals', params), + getById: (id) => get(`/rentals/${id}`), + create: (data) => post('/rentals', data), + update: (id, data) => put(`/rentals/${id}`, data), + delete: (id) => del(`/rentals/${id}`) }; // 统计分析API export const statisticsApi = { - getRentData: () => request('/statistics/rent'), - getRoomStatus: () => request('/statistics/room-status'), - getRegionHouseStats: () => request('/statistics/region-house'), - getRegionApartmentHouseStats: () => request('/statistics/region-apartment-house') + getRentData: () => get('/statistics/rent'), + getRoomStatus: () => get('/statistics/room-status'), + getRegionHouseStats: () => get('/statistics/region-house'), + getRegionApartmentHouseStats: () => get('/statistics/region-apartment-house') }; // 水费管理API export const waterBillApi = { - getAll: (params = {}) => request(`/water-bills${buildQueryString(params)}`), - getById: (id) => request(`/water-bills/${id}`), - create: (data) => request('/water-bills', { - method: 'POST', - body: JSON.stringify(data) - }), - update: (id, data) => request(`/water-bills/${id}`, { - method: 'PUT', - body: JSON.stringify(data) - }), - delete: (id) => request(`/water-bills/${id}`, { - method: 'DELETE' - }) + getAll: (params = {}) => get('/water-bills', params), + getById: (id) => get(`/water-bills/${id}`), + create: (data) => post('/water-bills', data), + update: (id, data) => put(`/water-bills/${id}`, data), + delete: (id) => del(`/water-bills/${id}`) }; // 电费管理API export const electricityBillApi = { - getAll: (params = {}) => request(`/electricity-bills${buildQueryString(params)}`), - getById: (id) => request(`/electricity-bills/${id}`), - create: (data) => request('/electricity-bills', { - method: 'POST', - body: JSON.stringify(data) - }), - update: (id, data) => request(`/electricity-bills/${id}`, { - method: 'PUT', - body: JSON.stringify(data) - }), - delete: (id) => request(`/electricity-bills/${id}`, { - method: 'DELETE' - }) + getAll: (params = {}) => get('/electricity-bills', params), + getById: (id) => get(`/electricity-bills/${id}`), + create: (data) => post('/electricity-bills', data), + update: (id, data) => put(`/electricity-bills/${id}`, data), + delete: (id) => del(`/electricity-bills/${id}`) }; export default { diff --git a/src/api/request.js b/src/api/request.js new file mode 100644 index 0000000..2f399cb --- /dev/null +++ b/src/api/request.js @@ -0,0 +1,70 @@ +// 统一请求处理 + +const API_BASE_URL = process.env.VUE_APP_API_BASE_URL || '/api'; + +// 通用请求函数 +export async function request(url, options = {}) { + try { + const response = await fetch(`${API_BASE_URL}${url}`, { + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } + }); + + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + + return await response.json(); + } catch (error) { + console.error('API request error:', error); + throw error; + } +} + +// GET 请求 +export function get(url, params = {}) { + const queryString = Object.entries(params) + .filter(([key, value]) => value !== undefined && value !== null && value !== '') + .map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`) + .join('&'); + + const fullUrl = queryString ? `${url}?${queryString}` : url; + + return request(fullUrl, { + method: 'GET' + }); +} + +// POST 请求 +export function post(url, data = {}) { + return request(url, { + method: 'POST', + body: JSON.stringify(data) + }); +} + +// PUT 请求 +export function put(url, data = {}) { + return request(url, { + method: 'PUT', + body: JSON.stringify(data) + }); +} + +// DELETE 请求 +export function del(url) { + return request(url, { + method: 'DELETE' + }); +} + +export default { + get, + post, + put, + delete: del, + request +}; \ No newline at end of file diff --git a/src/views/apartment/List.vue b/src/views/apartment/List.vue index 75c2aef..6eeded2 100644 --- a/src/views/apartment/List.vue +++ b/src/views/apartment/List.vue @@ -37,11 +37,13 @@ @@ -135,7 +137,12 @@ export default { }, handleCurrentChange(val) { this.currentPage = val - this.loadData() + this.loadApartments() + }, + handleSizeChange(val) { + this.pageSize = val + this.currentPage = 1 + this.loadApartments() } } } diff --git a/src/views/contract/List.vue b/src/views/contract/List.vue index 0f6b01f..fb84969 100644 --- a/src/views/contract/List.vue +++ b/src/views/contract/List.vue @@ -19,7 +19,7 @@ 重置 - + @@ -39,11 +39,13 @@ @@ -58,17 +60,13 @@ export default { data() { return { contracts: [], + total: 0, searchForm: { status: '' }, - currentPage: 1 - } - }, - computed: { - filteredContracts() { - return this.contracts.filter(contract => { - return !this.searchForm.status || contract.status == this.searchForm.status - }) + currentPage: 1, + pageSize: 10, + isLoading: false } }, mounted() { @@ -76,12 +74,19 @@ export default { }, methods: { async loadContracts() { + this.isLoading = true try { // 加载合同数据 - const contractsResponse = await contractApi.getAll() + const params = { + status: this.searchForm.status, + page: this.currentPage, + pageSize: this.pageSize + } + const contractsResponse = await contractApi.getAll(params) // 处理合同数据,使用后端返回的关联信息 - this.contracts = contractsResponse.map(contract => { + const contracts = contractsResponse.data || contractsResponse + this.contracts = contracts.map(contract => { return { ...contract, roomNumber: contract.Room ? contract.Room.roomNumber : '', @@ -90,8 +95,11 @@ export default { regionName: contract.Room && contract.Room.Apartment && contract.Room.Apartment.Region ? contract.Room.Apartment.Region.name : '' } }) + this.total = contractsResponse.total || 0 } catch (error) { this.$message.error('加载数据失败') + } finally { + this.isLoading = false } }, handleAdd() { @@ -118,15 +126,24 @@ export default { }) }, handleSearch() { - // 搜索逻辑 + this.currentPage = 1 + this.loadContracts() }, resetSearch() { this.searchForm = { status: '' } + this.currentPage = 1 + this.loadContracts() }, handleCurrentChange(val) { this.currentPage = val + this.loadContracts() + }, + handleSizeChange(val) { + this.pageSize = val + this.currentPage = 1 + this.loadContracts() } } } diff --git a/src/views/house/List.vue b/src/views/house/List.vue index e005804..ac72bd7 100644 --- a/src/views/house/List.vue +++ b/src/views/house/List.vue @@ -27,7 +27,7 @@ 重置 - + @@ -48,11 +48,13 @@ @@ -68,20 +70,14 @@ export default { return { regions: [], houses: [], + total: 0, searchForm: { regionId: '', status: '' }, - currentPage: 1 - } - }, - computed: { - filteredHouses() { - return this.houses.filter(house => { - const regionMatch = !this.searchForm.regionId || house.regionId == this.searchForm.regionId - const statusMatch = !this.searchForm.status || house.status == this.searchForm.status - return regionMatch && statusMatch - }) + currentPage: 1, + pageSize: 10, + isLoading: false } }, mounted() { @@ -89,22 +85,33 @@ export default { }, methods: { async loadData() { + this.isLoading = true try { // 加载区域数据 const regionsResponse = await regionApi.getAll() this.regions = regionsResponse // 加载房源数据 - const housesResponse = await houseApi.getAll() - this.houses = housesResponse.map(house => { + const params = { + regionId: this.searchForm.regionId, + status: this.searchForm.status, + page: this.currentPage, + pageSize: this.pageSize + } + const housesResponse = await houseApi.getAll(params) + const houses = housesResponse.data || housesResponse + this.houses = houses.map(house => { const region = regionsResponse.find(r => r.id == house.regionId) return { ...house, regionName: region ? region.name : '' } }) + this.total = housesResponse.total || 0 } catch (error) { this.$message.error('加载数据失败') + } finally { + this.isLoading = false } }, getStatusType(status) { @@ -147,16 +154,25 @@ export default { }) }, handleSearch() { - // 搜索逻辑 + this.currentPage = 1 + this.loadData() }, resetSearch() { this.searchForm = { regionId: '', status: '' } + this.currentPage = 1 + this.loadData() }, handleCurrentChange(val) { this.currentPage = val + this.loadData() + }, + handleSizeChange(val) { + this.pageSize = val + this.currentPage = 1 + this.loadData() } } } diff --git a/src/views/region/List.vue b/src/views/region/List.vue index 90cb656..54f2c69 100644 --- a/src/views/region/List.vue +++ b/src/views/region/List.vue @@ -7,7 +7,7 @@ 添加区域 - + @@ -21,11 +21,13 @@ @@ -40,7 +42,10 @@ export default { data() { return { regions: [], - currentPage: 1 + total: 0, + currentPage: 1, + pageSize: 10, + isLoading: false } }, mounted() { @@ -48,11 +53,19 @@ export default { }, methods: { async loadRegions() { + this.isLoading = true try { - const response = await regionApi.getAll() - this.regions = response + const params = { + page: this.currentPage, + pageSize: this.pageSize + } + const response = await regionApi.getAll(params) + this.regions = response.data || response + this.total = response.total || 0 } catch (error) { this.$message.error('加载区域数据失败') + } finally { + this.isLoading = false } }, handleAdd() { @@ -62,7 +75,7 @@ export default { this.$router.push(`/region/edit/${id}`) }, async handleDelete(id) { - this.$confirm('确定要删除这个区域吗?', '提示', { + this.$confirm('确定要删除这个区域吗?', '提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' @@ -75,11 +88,17 @@ export default { this.$message.error('删除失败') } }).catch(() => { - this.$message.info('已取消删除') + // 取消删除 }) }, handleCurrentChange(val) { this.currentPage = val + this.loadRegions() + }, + handleSizeChange(val) { + this.pageSize = val + this.currentPage = 1 + this.loadRegions() } } } diff --git a/src/views/rental/Add.vue b/src/views/rental/Add.vue index 754528f..baa0361 100644 --- a/src/views/rental/Add.vue +++ b/src/views/rental/Add.vue @@ -34,8 +34,19 @@ type="date" placeholder="选择开始日期" style="width: 100%" + @change="updateEndDate" > + + + + + - + @@ -88,22 +99,45 @@ export default { status: 'active', remark: '' }, + // 保存返回时的查询参数 + returnQuery: {}, rules: { roomId: [{ required: true, message: '请选择房间', trigger: 'blur' }], tenantName: [{ required: true, message: '请输入租客姓名', trigger: 'blur' }], - tenantPhone: [{ required: true, message: '请输入租客电话', trigger: 'blur' }], - tenantIdCard: [{ required: true, message: '请输入身份证号', trigger: 'blur' }], + tenantPhone: [{ message: '请输入租客电话', trigger: 'blur' }], + tenantIdCard: [{ message: '请输入身份证号', trigger: 'blur' }], startDate: [{ required: true, message: '请选择开始日期', trigger: 'blur' }], endDate: [{ required: true, message: '请选择结束日期', trigger: 'blur' }], rent: [{ required: true, message: '请输入租金', trigger: 'blur' }], - deposit: [{ required: true, message: '请输入押金', trigger: 'blur' }], + deposit: [{ message: '请输入押金', trigger: 'blur' }], status: [{ required: true, message: '请选择状态', trigger: 'blur' }] }, rooms: [], - apartments: [] + apartments: [], + leaseMonths: 1, + monthOptions: [ + { label: '1个月', value: 1 }, + { label: '2个月', value: 2 }, + { label: '3个月', value: 3 }, + { label: '4个月', value: 4 }, + { label: '5个月', value: 5 }, + { label: '6个月', value: 6 }, + { label: '7个月', value: 7 }, + { label: '8个月', value: 8 }, + { label: '9个月', value: 9 }, + { label: '10个月', value: 10 }, + { label: '11个月', value: 11 }, + { label: '12个月', value: 12 } + ] } }, mounted() { + // 获取返回时的查询参数 + this.returnQuery = this.$route.query + // 初始化开始日期为今天 + this.rentalForm.startDate = new Date() + // 初始化结束日期 + this.updateEndDate() this.loadData() }, methods: { @@ -128,7 +162,7 @@ export default { const room = this.rooms.find(r => r.id.toString() == roomId.toString()) if (room) { // 自动填充租金 - this.rentalForm.rent = room.price + this.rentalForm.rent = room.monthlyPrice } } } catch (error) { @@ -148,7 +182,10 @@ export default { try { await rentalApi.create(this.rentalForm) this.$message.success('添加成功') - this.$router.push('/rental/list') + this.$router.push({ + path: '/rental/list', + query: this.returnQuery + }) } catch (error) { this.$message.error('添加失败') } @@ -162,7 +199,17 @@ export default { this.$refs.rentalForm.resetFields() }, goBack() { - this.$router.push('/rental/list') + this.$router.push({ + path: '/rental/list', + query: this.returnQuery + }) + }, + updateEndDate() { + if (this.rentalForm.startDate) { + const endDate = new Date(this.rentalForm.startDate) + endDate.setMonth(endDate.getMonth() + this.leaseMonths) + this.rentalForm.endDate = endDate + } } } } diff --git a/src/views/rental/Detail.vue b/src/views/rental/Detail.vue index 4b08142..98a5dc1 100644 --- a/src/views/rental/Detail.vue +++ b/src/views/rental/Detail.vue @@ -7,9 +7,10 @@
租房 退房 - 打扫 - 维修 - 完成 + 打扫 + 维修 + 打扫完成 + 维修完成 返回
@@ -23,12 +24,17 @@
租金: - ¥{{ room.price }}/月 + + ¥{{ room.monthlyPrice }}/月 + ¥{{ room.yearlyPrice }}/年 +
状态: - + {{ getStatusText(room.status) }} + {{ getSubStatusText(room.subStatus) }} + {{ getOtherStatusText(room.otherStatus) }}
@@ -55,6 +61,12 @@ + + + @@ -174,6 +186,46 @@ 保存 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 取消 + 保存 + +
@@ -193,6 +245,8 @@ export default { electricityBills: [], isLoading: false, activeTab: 'rental', + // 保存返回时的查询参数 + returnQuery: {}, waterBillDialogVisible: false, electricityBillDialogVisible: false, waterBillForm: { @@ -230,10 +284,34 @@ export default { endReading: [{ required: true, message: '请输入结束度数', trigger: 'blur' }], unitPrice: [{ required: true, message: '请输入单价', trigger: 'blur' }], status: [{ required: true, message: '请选择状态', trigger: 'change' }] + }, + rentalDialogVisible: false, + rentalForm: { + id: '', + roomId: '', + tenantName: '', + tenantPhone: '', + tenantIdCard: '', + startDate: '', + endDate: '', + rent: '', + deposit: '', + status: 'active', + remark: '' + }, + rentalRules: { + tenantName: [{ required: true, message: '请输入租客姓名', trigger: 'blur' }], + startDate: [{ required: true, message: '请选择开始日期', trigger: 'blur' }], + endDate: [{ required: true, message: '请选择结束日期', trigger: 'blur' }], + rent: [{ required: true, message: '请输入租金', trigger: 'blur' }], + deposit: [{ message: '请输入押金', trigger: 'blur' }], + status: [{ required: true, message: '请选择状态', trigger: 'blur' }] } } }, mounted() { + // 获取返回时的查询参数 + this.returnQuery = this.$route.query this.loadData() }, methods: { @@ -309,8 +387,44 @@ export default { default: return status } }, + getSubStatusType(status) { + switch (status) { + case 'normal': return 'success' + case 'soon_expire': return 'warning' + case 'expired': return 'danger' + default: return '' + } + }, + getSubStatusText(status) { + switch (status) { + case 'normal': return '正常' + case 'soon_expire': return '即将到期' + case 'expired': return '已到期' + default: return status + } + }, + getOtherStatusType(status) { + switch (status) { + case 'cleaning': return 'info' + case 'maintenance': return 'danger' + default: return '' + } + }, + getOtherStatusText(status) { + switch (status) { + case 'cleaning': return '打扫中' + case 'maintenance': return '维修中' + default: return status + } + }, handleRent() { - this.$router.push(`/rental/add?roomId=${this.room.id}`) + this.$router.push({ + path: `/rental/add`, + query: { + roomId: this.room.id, + ...this.returnQuery + } + }) }, async handleCheckout() { this.$confirm('确定要退房吗?', '提示', { @@ -350,7 +464,7 @@ export default { }).then(async () => { try { const roomId = this.$route.params.id - await roomApi.update(roomId, { status: 'cleaning' }) + await roomApi.update(roomId, { otherStatus: 'cleaning' }) this.$message.success('标记为打扫中成功') this.loadData() } catch (error) { @@ -368,7 +482,7 @@ export default { }).then(async () => { try { const roomId = this.$route.params.id - await roomApi.update(roomId, { status: 'maintenance' }) + await roomApi.update(roomId, { otherStatus: 'maintenance' }) this.$message.success('标记为维修中成功') this.loadData() } catch (error) { @@ -379,15 +493,15 @@ export default { }) }, async handleComplete() { - this.$confirm('确定要完成打扫/维修并将状态改为空房吗?', '提示', { + this.$confirm('确定要完成打扫/维修吗?', '提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'success' }).then(async () => { try { const roomId = this.$route.params.id - await roomApi.update(roomId, { status: 'empty' }) - this.$message.success('操作成功,房间状态已改为空房') + await roomApi.update(roomId, { otherStatus: '' }) + this.$message.success('操作成功,已完成打扫/维修') this.loadData() } catch (error) { this.$message.error('操作失败') @@ -514,8 +628,53 @@ export default { // 取消删除 }) }, + handleEditRental(rental) { + this.rentalForm = { + ...rental, + startDate: rental.startDate ? new Date(rental.startDate) : '', + endDate: rental.endDate ? new Date(rental.endDate) : '' + } + this.rentalDialogVisible = true + }, + async handleSaveRental() { + try { + if (this.rentalForm.id) { + // 更新租赁记录 + await rentalApi.update(this.rentalForm.id, this.rentalForm) + this.$message.success('租赁记录更新成功') + } else { + // 创建租赁记录 + await rentalApi.create(this.rentalForm) + this.$message.success('租赁记录添加成功') + } + this.rentalDialogVisible = false + this.loadData() + } catch (error) { + this.$message.error('操作失败') + } + }, + async handleDeleteRental(id) { + this.$confirm('确定要删除这条租赁记录吗?', '提示', { + confirmButtonText: '确定', + cancelButtonText: '取消', + type: 'danger' + }).then(async () => { + try { + await rentalApi.delete(id) + this.$message.success('租赁记录删除成功') + this.loadData() + } catch (error) { + this.$message.error('删除失败') + } + }).catch(() => { + // 取消删除 + }) + }, goBack() { - this.$router.push('/rental/list') + this.$router.push({ + path: '/rental/list', + query: this.returnQuery + }) } } } diff --git a/src/views/rental/Edit.vue b/src/views/rental/Edit.vue index ed35dc1..8d7a2e3 100644 --- a/src/views/rental/Edit.vue +++ b/src/views/rental/Edit.vue @@ -44,7 +44,7 @@ style="width: 100%" >
- + @@ -85,15 +85,17 @@ export default { deposit: '', status: '' }, + // 保存返回时的查询参数 + returnQuery: {}, rules: { roomId: [{ required: true, message: '请选择房间', trigger: 'blur' }], tenantName: [{ required: true, message: '请输入租客姓名', trigger: 'blur' }], - tenantPhone: [{ required: true, message: '请输入租客电话', trigger: 'blur' }], - tenantIdCard: [{ required: true, message: '请输入身份证号', trigger: 'blur' }], + tenantPhone: [{ message: '请输入租客电话', trigger: 'blur' }], + tenantIdCard: [{ message: '请输入身份证号', trigger: 'blur' }], startDate: [{ required: true, message: '请选择开始日期', trigger: 'blur' }], endDate: [{ required: true, message: '请选择结束日期', trigger: 'blur' }], rent: [{ required: true, message: '请输入租金', trigger: 'blur' }], - deposit: [{ required: true, message: '请输入押金', trigger: 'blur' }], + deposit: [{ message: '请输入押金', trigger: 'blur' }], status: [{ required: true, message: '请选择状态', trigger: 'blur' }] }, rooms: [], @@ -101,6 +103,8 @@ export default { } }, mounted() { + // 获取返回时的查询参数 + this.returnQuery = this.$route.query this.loadData() this.loadRentalData() }, @@ -144,7 +148,10 @@ export default { try { await rentalApi.update(this.rentalForm.id, this.rentalForm) this.$message.success('编辑成功') - this.$router.push('/rental/list') + this.$router.push({ + path: '/rental/list', + query: this.returnQuery + }) } catch (error) { this.$message.error('编辑失败') } @@ -158,7 +165,10 @@ export default { this.loadRentalData() }, goBack() { - this.$router.push('/rental/list') + this.$router.push({ + path: '/rental/list', + query: this.returnQuery + }) } } } diff --git a/src/views/rental/List.vue b/src/views/rental/List.vue index 24f2eb9..a0db197 100644 --- a/src/views/rental/List.vue +++ b/src/views/rental/List.vue @@ -13,17 +13,31 @@ - - + + + + + + + + + + + + + + + + 搜索 重置 @@ -37,17 +51,24 @@ style="cursor: pointer;" >

{{ getApartmentName(room.apartmentId) }}

- {{ getStatusText(room.status) }} +
+ {{ getSubStatusText(room.subStatus) }} + 空房 + {{ getOtherStatusText(room.otherStatus) }} +
{{ room.roomNumber }} {{ room.area }}㎡ - ¥{{ room.price }}/月 +
+ ¥{{ room.monthlyPrice }}/月 + ¥{{ room.yearlyPrice }}/年 +

租客: {{ room.Rentals[0].Tenant.name }}

@@ -87,7 +108,10 @@ export default { total: 0, searchForm: { apartmentId: '', - status: '' + status: '', + subStatus: '', + otherStatus: '', + roomNumber: '' }, currentPage: 1, pageSize: 50, @@ -95,9 +119,26 @@ export default { } }, mounted() { + // 从路由参数中获取查询参数和页码 + this.initFromQuery() this.loadData() }, methods: { + // 从路由参数初始化搜索参数和页码 + initFromQuery() { + const query = this.$route.query + if (query) { + // 初始化搜索参数 + if (query.apartmentId) this.searchForm.apartmentId = parseInt(query.apartmentId) + if (query.status) this.searchForm.status = query.status + if (query.subStatus) this.searchForm.subStatus = query.subStatus + if (query.otherStatus) this.searchForm.otherStatus = query.otherStatus + if (query.roomNumber) this.searchForm.roomNumber = query.roomNumber + // 初始化页码 + if (query.page) this.currentPage = parseInt(query.page) + if (query.pageSize) this.pageSize = parseInt(query.pageSize) + } + }, async loadData() { if (this.isLoading) return @@ -111,6 +152,9 @@ export default { const params = { apartmentId: this.searchForm.apartmentId, status: this.searchForm.status, + subStatus: this.searchForm.subStatus, + otherStatus: this.searchForm.otherStatus, + roomNumber: this.searchForm.roomNumber, page: this.currentPage, pageSize: this.pageSize } @@ -135,23 +179,31 @@ export default { const apartment = this.apartments.find(a => a.id == apartmentId) return apartment ? apartment.name : '' }, - getStatusType(status) { + getSubStatusType(status) { switch (status) { - case 'empty': return '' - case 'rented': return 'success' + case 'normal': return 'success' case 'soon_expire': return 'warning' case 'expired': return 'danger' + default: return '' + } + }, + getSubStatusText(status) { + switch (status) { + case 'normal': return '正常' + case 'soon_expire': return '即将到期' + case 'expired': return '已到期' + default: return status + } + }, + getOtherStatusType(status) { + switch (status) { case 'cleaning': return 'info' case 'maintenance': return 'danger' default: return '' } }, - getStatusText(status) { + getOtherStatusText(status) { switch (status) { - case 'empty': return '空房' - case 'rented': return '在租' - case 'soon_expire': return '即将到期' - case 'expired': return '到期' case 'cleaning': return '打扫中' case 'maintenance': return '维修中' default: return status @@ -163,7 +215,15 @@ export default { }, handleRoomClick(roomId) { try { - this.$router.push(`/rental/detail/${roomId}`) + // 保存当前的查询参数和页码到路由参数 + this.$router.push({ + path: `/rental/detail/${roomId}`, + query: { + ...this.searchForm, + page: this.currentPage, + pageSize: this.pageSize + } + }) } catch (error) { console.error('Navigation error:', error) } @@ -175,18 +235,39 @@ export default { resetSearch() { this.searchForm = { apartmentId: '', - status: '' + status: '', + subStatus: '', + otherStatus: '', + roomNumber: '' } this.currentPage = 1 this.loadData() }, handleCurrentChange(val) { this.currentPage = val + // 更新路由参数,保存查询状态 + this.$router.push({ + path: this.$route.path, + query: { + ...this.searchForm, + page: this.currentPage, + pageSize: this.pageSize + } + }) this.loadData() }, handleSizeChange(val) { this.pageSize = val this.currentPage = 1 + // 更新路由参数,保存查询状态 + this.$router.push({ + path: this.$route.path, + query: { + ...this.searchForm, + page: this.currentPage, + pageSize: this.pageSize + } + }) this.loadData() } } @@ -259,6 +340,7 @@ export default { .room-info { display: flex; justify-content: space-between; + align-items: center; margin-bottom: 15px; padding-bottom: 15px; border-bottom: 1px solid #e4e7ed; @@ -274,11 +356,23 @@ export default { color: #606266; } +.price-info { + display: flex; + flex-direction: column; + align-items: flex-end; +} + .room-price { color: #409EFF; font-weight: 500; } +.room-price.yearly { + font-size: 12px; + color: #909399; + margin-top: 2px; +} + .rental-info { margin-top: 10px; flex: 1; @@ -298,7 +392,7 @@ export default { background-color: #f9f9f9; } -.status-rented { +.status-normal { border-color: #67c23a; background-color: #f0f9eb; } @@ -312,14 +406,4 @@ export default { border-color: #f56c6c; background-color: #fef0f0; } - -.status-cleaning { - border-color: #909399; - background-color: #f4f4f5; -} - -.status-maintenance { - border-color: #303133; - background-color: #f4f4f5; -} \ No newline at end of file diff --git a/src/views/room/Add.vue b/src/views/room/Add.vue index c24ce73..52a0c4a 100644 --- a/src/views/room/Add.vue +++ b/src/views/room/Add.vue @@ -18,15 +18,30 @@ - - + + - - + + + + + + + + + + + + + + + + + @@ -52,8 +67,11 @@ export default { apartmentId: '', roomNumber: '', area: '', - price: '', - status: 'empty' + monthlyPrice: '', + yearlyPrice: '', + status: 'empty', + otherStatus: '', + subStatus: 'normal' }, apartments: [], rules: { @@ -75,7 +93,18 @@ export default { } }, trigger: 'blur' } ], - price: [ + monthlyPrice: [ + { validator: (rule, value, callback) => { + if (value === '' || value === null || value === undefined) { + callback() + } else if (isNaN(Number(value))) { + callback(new Error('请输入数字')) + } else { + callback() + } + }, trigger: 'blur' } + ], + yearlyPrice: [ { validator: (rule, value, callback) => { if (value === '' || value === null || value === undefined) { callback() diff --git a/src/views/room/Edit.vue b/src/views/room/Edit.vue index 20022bc..c854281 100644 --- a/src/views/room/Edit.vue +++ b/src/views/room/Edit.vue @@ -18,15 +18,30 @@ - - + + - - + + + + + + + + + + + + + + + + + @@ -53,9 +68,14 @@ export default { apartmentId: '', roomNumber: '', area: '', - price: '', - status: '' + monthlyPrice: '', + yearlyPrice: '', + status: '', + otherStatus: '', + subStatus: 'normal' }, + // 保存返回时的查询参数 + returnQuery: {}, apartments: [], rules: { apartmentId: [ @@ -76,7 +96,18 @@ export default { } }, trigger: 'blur' } ], - price: [ + monthlyPrice: [ + { validator: (rule, value, callback) => { + if (value === '' || value === null || value === undefined) { + callback() + } else if (isNaN(Number(value))) { + callback(new Error('请输入数字')) + } else { + callback() + } + }, trigger: 'blur' } + ], + yearlyPrice: [ { validator: (rule, value, callback) => { if (value === '' || value === null || value === undefined) { callback() @@ -94,6 +125,8 @@ export default { } }, mounted() { + // 获取返回时的查询参数 + this.returnQuery = this.$route.query this.loadApartments() this.loadRoomData() }, @@ -123,7 +156,10 @@ export default { try { await roomApi.update(this.roomForm.id, this.roomForm) this.$message.success('编辑成功') - this.$router.push('/room/list') + this.$router.push({ + path: '/room/list', + query: this.returnQuery + }) } catch (error) { this.$message.error('编辑失败') } @@ -136,7 +172,10 @@ export default { this.loadRoomData() }, goBack() { - this.$router.push('/room/list') + this.$router.push({ + path: '/room/list', + query: this.returnQuery + }) } } } diff --git a/src/views/room/List.vue b/src/views/room/List.vue index 78a0d12..96de8ff 100644 --- a/src/views/room/List.vue +++ b/src/views/room/List.vue @@ -14,13 +14,24 @@ - - + + + + + + + + + + + + + @@ -40,14 +51,29 @@ - - + + + + + + + - + + + + +