代码提交

This commit is contained in:
zengqiyang 2025-04-28 19:35:40 +08:00
parent ecb085c6b4
commit 4f3585574e
11 changed files with 461 additions and 218 deletions

View File

@ -1,4 +1,4 @@
NODE_ENV = 'development'
# 开发环境API地址
VUE_APP_BASE_API = http://localhost:8082/api
VUE_APP_BASE_API = http://192.168.137.45:8080

View File

@ -69,7 +69,7 @@ export function updateBuilding(data) {
export function deleteBuilding(ids) {
return request({
url: `/room/building/remove/${ids}`,
method: 'post'
method: 'get'
})
}
@ -119,7 +119,7 @@ export function updateFloor(data) {
export function deleteFloor(ids) {
return request({
url: `/room/floor/remove/${ids}`,
method: 'post'
method: 'get'
})
}

View File

@ -12,7 +12,7 @@ Vue.config.productionTip = false
// 添加下载方法
Vue.prototype.download = function(res, fileName) {
// 创建blob链接
const blob = new Blob([res.data])
const blob = new Blob([res])
const link = document.createElement('a')
link.href = URL.createObjectURL(blob)
link.download = fileName

View File

@ -211,11 +211,11 @@ export default {
rules: {
classificationCode: [
{ required: true, message: '请输入分类编码', trigger: 'blur' },
{ min: 2, max: 20, message: '长度在 2 到 20 个字符', trigger: 'blur' }
{ min: 1, max: 20, message: '长度在 1 到 20 个字符', trigger: 'blur' }
],
classificationName: [
{ required: true, message: '请输入分类名称', trigger: 'blur' },
{ min: 2, max: 50, message: '长度在 2 到 50 个字符', trigger: 'blur' }
{ min: 1, max: 50, message: '长度在 1 到 50 个字符', trigger: 'blur' }
],
parentId: [
{ required: false, message: '请选择上级分类', trigger: 'change' }

View File

@ -12,44 +12,15 @@
</div>
<!-- 搜索条件 -->
<el-form :model="queryParams" ref="queryForm" :inline="true" label-width="68px">
<el-form-item>
<el-input v-model="queryParams.keyword" placeholder="输入名称/编码/分类/品牌/位置" style="width: 300px" clearable @keyup.enter.native="handleQuery">
<el-button slot="append" icon="el-icon-search" @click="handleQuery"></el-button>
</el-input>
</el-form-item>
<el-form-item>
<el-button type="text" @click="toggleAdvanced">
{{ advanced ? '收起' : '展开' }}
<i :class="advanced ? 'el-icon-arrow-up' : 'el-icon-arrow-down'"></i>
</el-button>
</el-form-item>
<el-form-item>
<el-popover
placement="bottom"
width="200"
trigger="click"
v-model="columnFilterVisible"
style="margin-left: 10px;">
<el-checkbox-group v-model="selectedColumns">
<el-checkbox label="all" @change="handleCheckAllChange">全部</el-checkbox>
<el-divider></el-divider>
<el-checkbox v-for="(item, index) in columns" :key="index" :label="item.prop" :disabled="item.disabled">{{ item.label }}</el-checkbox>
</el-checkbox-group>
<el-button slot="reference" type="primary" size="mini" icon="el-icon-s-operation">列显示</el-button>
</el-popover>
</el-form-item>
</el-form>
<!-- 高级搜索条件展开时显示 -->
<el-form v-show="advanced" :model="queryParams" ref="advancedQueryForm" :inline="true" label-width="100px">
<el-form :model="queryParams" ref="queryForm" :inline="true" label-width="100px">
<el-form-item label="资产状态" prop="assetStatus">
<el-select v-model="queryParams.assetStatus" placeholder="请选择资产状态" clearable style="width: 180px">
<el-select size="small" v-model="queryParams.assetStatus" placeholder="请选择资产状态" clearable style="width: 180px">
<el-option v-for="dict in statusOptions" :key="dict.value" :label="dict.label" :value="dict.value" />
</el-select>
</el-form-item>
<el-form-item label="资产分类" prop="classificationId">
<el-cascader
size="small"
v-model="queryParams.classificationId"
:options="classificationOptions"
:props="{ checkStrictly: true, value: 'id', label: 'name', emitPath: false }"
@ -58,12 +29,13 @@
/>
</el-form-item>
<el-form-item label="资产名称" prop="assetName">
<el-input v-model="queryParams.assetName" placeholder="请输入资产名称" clearable style="width: 180px" />
<el-input size="small" v-model="queryParams.assetName" placeholder="请输入资产名称" clearable style="width: 180px" />
</el-form-item>
<el-form-item label="所在位置">
<el-cascader
v-model="queryParams.locationId"
:options="locationOptions"
size="small"
:props="{
checkStrictly: true,
emitPath: false,
@ -78,16 +50,17 @@
/>
</el-form-item>
<el-form-item label="资产编码" prop="assetCode">
<el-input v-model="queryParams.assetCode" placeholder="请输入资产编码" clearable style="width: 180px" />
<el-input size="small" v-model="queryParams.assetCode" placeholder="请输入资产编码" clearable style="width: 180px" />
</el-form-item>
<el-form-item label="设备序列号" prop="serialNumber">
<el-input v-model="queryParams.serialNumber" placeholder="请输入设备序列号" clearable style="width: 180px" />
<el-input size="small" v-model="queryParams.serialNumber" placeholder="请输入设备序列号" clearable style="width: 180px" />
</el-form-item>
<el-form-item label="管理员" prop="adminUserId">
<el-select
v-model="queryParams.adminUserId"
placeholder="请选择管理员"
clearable
size="small"
filterable
remote
:remote-method="remoteSearchAdmin"
@ -104,15 +77,27 @@
</el-select>
</el-form-item>
<el-form-item label="保养到期" prop="maintenanceExpired">
<el-select v-model="queryParams.maintenanceExpired" placeholder="请选择" clearable style="width: 180px">
<el-select v-model="queryParams.maintenanceExpired" size="small" placeholder="请选择" clearable style="width: 180px">
<el-option label="全部" value="" />
<el-option label="是" value="1" />
<el-option label="否" value="0" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" style="margin-right: 10px;" size="mini" @click="resetQuery">重置</el-button>
<el-popover
placement="bottom"
width="200"
trigger="click"
v-model="columnFilterVisible">
<el-checkbox-group v-model="selectedColumns">
<el-checkbox label="all" @change="handleCheckAllChange">全部</el-checkbox>
<el-divider></el-divider>
<el-checkbox v-for="(item, index) in columns" :key="index" :label="item.prop" :disabled="item.disabled">{{ item.label }}</el-checkbox>
</el-checkbox-group>
<el-button slot="reference" type="primary" size="mini" icon="el-icon-s-operation">列显示</el-button>
</el-popover>
</el-form-item>
</el-form>
@ -197,7 +182,7 @@
</el-card>
<!-- 导入对话框 -->
<el-dialog :title="upload.title" :visible.sync="upload.open" width="500px" append-to-body>
<el-dialog :title="importTitle" :visible.sync="upload.open" width="400px" append-to-body>
<el-upload
ref="upload"
:limit="1"
@ -205,9 +190,8 @@
:headers="upload.headers"
:action="upload.url"
:disabled="upload.isUploading"
:on-progress="handleFileUpload"
:on-progress="handleFileUploadProgress"
:on-success="handleFileSuccess"
:on-error="handleFileError"
:auto-upload="false"
drag>
<i class="el-icon-upload"></i>
@ -281,16 +265,16 @@ export default {
printOpen: false,
//
loading: false,
//
assetList: [],
//
ids: [],
//
single: true,
//
multiple: true,
//
advanced: false,
//
total: 0,
//
assetList: [],
//
assetsToPrint: [],
//
@ -325,7 +309,6 @@ export default {
queryParams: {
pageNum: 1,
pageSize: 10,
keyword: '',
assetStatus: '',
classificationId: undefined,
assetName: '',
@ -339,8 +322,7 @@ export default {
upload: {
//
open: false,
//
title: '',
//
isUploading: false,
//
headers: {},
@ -578,17 +560,32 @@ export default {
return typeMap[assetStatus] || 'info'
},
/** 切换高级搜索 */
toggleAdvanced() {
this.advanced = !this.advanced
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNum = 1
this.getList()
},
/** 处理列显示设置 */
handleColumnDisplay(index) {
//
if (index !== 0) {
this.columns[index].visible = !this.columns[index].visible
/** 重置按钮操作 */
resetQuery() {
// 使Element UIresetFields
if (this.$refs.queryForm) {
this.$refs.queryForm.resetFields()
}
this.queryParams = {
pageNum: 1,
pageSize: 10,
assetStatus: '',
classificationId: undefined,
assetName: '',
locationId: undefined,
assetCode: '',
serialNumber: '',
adminUserId: undefined,
maintenanceExpired: ''
}
this.handleQuery()
},
/** 全选/取消全选处理 */
@ -607,34 +604,6 @@ export default {
}
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNum = 1
this.getList()
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm('queryForm')
if (this.advanced) {
this.resetForm('advancedQueryForm')
}
this.queryParams = {
pageNum: 1,
pageSize: 10,
keyword: '',
assetStatus: '',
classificationId: undefined,
assetName: '',
locationId: undefined,
assetCode: '',
serialNumber: '',
adminUserId: undefined,
maintenanceExpired: ''
}
this.handleQuery()
},
/** 多选框选中数据 */
handleSelectionChange(selection) {
this.ids = selection.map(item => item.id)
@ -733,11 +702,11 @@ export default {
/** 导入按钮操作 */
handleImport() {
this.upload.open = true
this.upload.title = '导入资产数据'
this.importTitle = '导入资产数据'
},
/** 处理文件上传中 */
handleFileUpload() {
handleFileUploadProgress() {
this.upload.isUploading = true
},
@ -747,17 +716,15 @@ export default {
this.$refs.upload.clearFiles()
this.upload.open = false
if (response.code === API_SUCCESS_CODE) {
this.$message.success('导入成功' + (response.data && response.data.successCount ? response.data.successCount : 0) + '条数据')
this.$alert(`成功导入${response.data && response.data.successCount ? response.data.successCount : 0}条数据`, { type: 'success' })
this.getList()
} else {
//
this.$alert(response.msg || '导入失败,正在下载错误文件', '导入结果', { type: 'error' })
this.download(response, `资产导入错误文件_${new Date().getTime()}.xlsx`)
}
},
/** 文件上传失败处理 */
handleFileError() {
this.upload.isUploading = false
this.$message.error('导入失败')
},
/** 提交上传文件 */
submitFileForm() {
this.$refs.upload.submit()

View File

@ -216,12 +216,12 @@ export default {
rules: {
locationCode: [
{ required: true, message: '请输入位置编码', trigger: 'blur' },
{ min: 2, max: 20, message: '长度在 2 到 20 个字符', trigger: 'blur' },
{ min: 1, max: 20, message: '长度在 1 到 20 个字符', trigger: 'blur' },
{ validator: this.validateLocationCode, trigger: 'blur' }
],
locationName: [
{ required: true, message: '请输入位置名称', trigger: 'blur' },
{ min: 2, max: 50, message: '长度在 2 到 50 个字符', trigger: 'blur' }
{ min: 1, max: 50, message: '长度在 1 到 50 个字符', trigger: 'blur' }
],
parentId: [
{ required: false, message: '请选择上级位置', trigger: 'change' }

View File

@ -11,7 +11,7 @@
<el-col :span="4">
<div class="stat-card">
<div class="stat-label">管理面积</div>
<div class="stat-value">{{ statistics.managementArea }} </div>
<div class="stat-value">{{ statistics.manageArea }} </div>
</div>
</el-col>
<el-col :span="4">
@ -23,13 +23,13 @@
<el-col :span="4">
<div class="stat-card">
<div class="stat-label">在租合同数</div>
<div class="stat-value">{{ statistics.contractCount }} </div>
<div class="stat-value">{{ statistics.rentedContracts }} </div>
</div>
</el-col>
<el-col :span="4">
<div class="stat-card">
<div class="stat-label">待租面积</div>
<div class="stat-value">{{ statistics.availableArea }} </div>
<div class="stat-value">{{ statistics.rentableArea }} </div>
</div>
</el-col>
<el-col :span="4">
@ -98,18 +98,18 @@
<el-checkbox label="standardHeight">标准层高(m)</el-checkbox>
<el-checkbox label="buildingArea">建筑面积()</el-checkbox>
<el-checkbox label="propertyArea">产权面积()</el-checkbox>
<el-checkbox label="availableArea">可租面积()</el-checkbox>
<el-checkbox label="rentableArea">可租面积()</el-checkbox>
<el-checkbox label="selfUseArea">自用面积()</el-checkbox>
<el-checkbox label="supportingArea">配套面积()</el-checkbox>
<el-checkbox label="parkingArea">车位面积()</el-checkbox>
<el-checkbox label="managementArea">管理面积()</el-checkbox>
<el-checkbox label="roomCount">房间总数()</el-checkbox>
<el-checkbox label="occupancyRate">出租率(%)</el-checkbox>
<el-checkbox label="manageArea">管理面积()</el-checkbox>
<el-checkbox label="totalRooms">房间总数()</el-checkbox>
<el-checkbox label="rentalRate">出租率(%)</el-checkbox>
<el-checkbox label="rentedArea">在租面积()</el-checkbox>
<el-checkbox label="contractCount">在租合同数</el-checkbox>
<el-checkbox label="yearTarget">本年营收目标(万元)</el-checkbox>
<el-checkbox label="rentedContracts">在租合同数</el-checkbox>
<!-- <el-checkbox label="yearTarget">本年营收目标(万元)</el-checkbox> -->
<el-checkbox label="availableRentArea">待租面积()</el-checkbox>
<el-checkbox label="availableRoomCount">待租房间数</el-checkbox>
<el-checkbox label="waitingRooms">待租房间数</el-checkbox>
</el-checkbox-group>
<el-button slot="reference" type="primary" size="mini" icon="el-icon-s-operation">列显示</el-button>
</el-popover>
@ -132,8 +132,34 @@
:label="col.label"
:prop="col.prop"
:min-width="col.width"
align="center"
/>
align="center">
<template slot-scope="scope">
<!-- 特殊处理buildingTags列 -->
<template v-if="col.prop === 'buildingTags'">
<span v-if="scope.row.buildingTags">
<el-tag
v-for="tagId in (typeof scope.row.buildingTags === 'string' ?
scope.row.buildingTags.split(',').filter(id => id && id.trim() !== '') :
Array.isArray(scope.row.buildingTags) ?
scope.row.buildingTags.map(id => String(id)) :
[String(scope.row.buildingTags)])"
:key="tagId"
size="mini"
style="margin-right: 5px">
{{ getTagNameById(tagId) }}
</el-tag>
</span>
</template>
<!-- 特殊处理isHot列 -->
<template v-else-if="col.prop === 'isHot'">
<!-- isHot的特殊渲染逻辑 -->
</template>
<!-- 其他列使用默认渲染 -->
<template v-else>
{{ formatColumnValue(scope.row[col.prop], col.prop) }}
</template>
</template>
</el-table-column>
<el-table-column label="操作" width="200" fixed="right">
<template slot-scope="scope">
<!-- <el-button size="mini" type="text" @click="handleDetail(scope.row)">详情</el-button> -->
@ -152,7 +178,8 @@
:page-sizes="[10, 20, 50, 100]"
:page-size="queryParams.pageSize"
layout="total, sizes, prev, pager, next, jumper"
:total="total">
:total="total"
class="pagination">
</el-pagination>
</el-card>
@ -191,7 +218,7 @@
</el-col>
<el-col :span="12">
<el-form-item label="所属地区" prop="region">
<el-input v-model="form.region" placeholder="请输入所属地区" />
<region-selector v-model="form.region" placeholder="请选择所属地区" />
</el-form-item>
</el-col>
</el-row>
@ -339,7 +366,13 @@
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="楼宇标签" prop="buildingTags">
<el-select v-model="form.buildingTags" multiple placeholder="请选择楼宇标签">
<el-select
v-model="form.buildingTags"
multiple
placeholder="请选择楼宇标签"
:popper-append-to-body="false"
@visible-change="onTagSelectorVisibleChange"
>
<el-option
v-for="item in tagOptions"
:key="item.id"
@ -603,7 +636,7 @@
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="待租面积">
<el-input v-model="detail.waitingArea" disabled />
<el-input v-model="detail.rentedContracts" disabled />
</el-form-item>
</el-col>
<el-col :span="12">
@ -666,9 +699,13 @@ import { getFloorList, addFloor, updateFloor, deleteFloor, getFloorListByBuildin
import { getProjectList,getTagByType } from '@/api/project'
import { getAccountList } from '@/api/finance'
import { API_SUCCESS_CODE } from '@/utils/constants'
import RegionSelector from '@/components/RegionSelector'
export default {
name: 'BuildingList',
components: {
RegionSelector
},
data() {
//
const validateBuildingName = (rule, value, callback) => {
@ -719,7 +756,8 @@ export default {
pageSize: 10,
projectId: undefined,
buildingName: undefined,
buildingCode: undefined
buildingCode: undefined,
region: undefined
},
//
form: {},
@ -748,11 +786,11 @@ export default {
],
buildingCode: [
{ required: true, message: '请输入楼宇编号', trigger: 'blur' },
{ min: 2, max: 20, message: '长度在 2 到 20 个字符', trigger: 'blur' }
{ min: 1, max: 20, message: '长度在 1 到 20 个字符', trigger: 'blur' }
],
buildingName: [
{ required: true, message: '请输入楼宇名称', trigger: 'blur' },
{ min: 2, max: 50, message: '长度在 2 到 50 个字符', trigger: 'blur' },
{ min: 1, max: 50, message: '长度在 1 到 50 个字符', trigger: 'blur' },
{ validator: validateBuildingName, trigger: 'blur' }
],
region: [
@ -806,7 +844,7 @@ export default {
],
floorName: [
{ required: true, message: '请输入楼层名称', trigger: 'blur' },
{ min: 2, max: 20, message: '长度在 2 到 20 个字符', trigger: 'blur' }
{ min: 1, max: 20, message: '长度在 1 到 20 个字符', trigger: 'blur' }
],
remark: [
{ max: 200, message: '长度不能超过200个字符', trigger: 'blur' }
@ -815,18 +853,17 @@ export default {
//
statistics: {
buildingCount: 15,
managementArea: '37,385.14',
manageArea: '37,385.14',
rentedArea: '12,487.68',
contractCount: 58,
availableArea: '24,897.46',
rentedContracts: 58,
rentableArea: '24,897.46',
yearTarget: '200'
},
//
columnFilterVisible: false,
selectedColumns: ['buildingName', 'projectName', 'buildingCode', 'buildingArea',
'availableArea', 'managementArea', 'roomCount', 'occupancyRate', 'rentedArea',
'contractCount', 'address', 'propertyNature', 'yearTarget'],
selectedColumns: ['buildingName', 'projectName', 'buildingCode', 'buildingTags',
'region', 'address', 'propertyNature', 'buildingArea', 'rentableArea'],
//
allColumns: [
@ -840,18 +877,18 @@ export default {
{ prop: 'standardHeight', label: '标准层高(m)', width: '120' },
{ prop: 'buildingArea', label: '建筑面积(m²)', width: '120' },
{ prop: 'propertyArea', label: '产权面积(m²)', width: '120' },
{ prop: 'availableArea', label: '可租面积(m²)', width: '120' },
{ prop: 'rentableArea', label: '可租面积(m²)', width: '120' },
{ prop: 'selfUseArea', label: '自用面积(m²)', width: '120' },
{ prop: 'supportingArea', label: '配套面积(m²)', width: '120' },
{ prop: 'parkingArea', label: '车位面积(m²)', width: '120' },
{ prop: 'managementArea', label: '管理面积(m²)', width: '120' },
{ prop: 'roomCount', label: '房间总数(间)', width: '120' },
{ prop: 'occupancyRate', label: '出租率(%)', width: '120' },
{ prop: 'manageArea', label: '管理面积(m²)', width: '120' },
{ prop: 'totalRooms', label: '房间总数(间)', width: '120' },
{ prop: 'rentalRate', label: '出租率(%)', width: '120' },
{ prop: 'rentedArea', label: '在租面积(m²)', width: '120' },
{ prop: 'contractCount', label: '在租合同数', width: '120' },
{ prop: 'yearTarget', label: '本年营收目标(万元)', width: '150' },
{ prop: 'rentedContracts', label: '在租合同数', width: '120' },
// { prop: 'yearTarget', label: '()', width: '150' },
{ prop: 'availableRentArea', label: '待租面积(m²)', width: '120' },
{ prop: 'availableRoomCount', label: '待租房间数', width: '120' }
{ prop: 'waitingRooms', label: '待租房间数', width: '120' }
],
//
originalBuildingName: '',
@ -874,6 +911,14 @@ export default {
if (!this.allColumns.length) return false
//
return this.allColumns.every(col => this.selectedColumns.includes(col.prop))
},
// ID
tagIdToName() {
const map = {}
this.tagOptions.forEach(tag => {
map[tag.id] = tag.tagName
})
return map
}
},
watch: {
@ -933,20 +978,61 @@ export default {
getBuildingList(params).then(res => {
if (res.code === API_SUCCESS_CODE) {
//
console.log('API返回的原始楼宇数据:', JSON.stringify(res.data))
//
const listData = res.data.rows || res.data.list || []
console.log('列表数据项示例:', listData.length > 0 ? listData[0] : '无数据')
this.buildingList = listData.map(item => {
//
let newItem = {...item}
//
if (item.buildingTags !== undefined) {
item.buildingTags = item.buildingTags
if (newItem.buildingTags !== undefined) {
// buildingTags
if (Array.isArray(newItem.buildingTags)) {
newItem.buildingTags = newItem.buildingTags.join(',')
} else if (typeof newItem.buildingTags === 'object' && newItem.buildingTags !== null) {
newItem.buildingTags = Object.values(newItem.buildingTags).join(',')
} else if (newItem.buildingTags === null) {
newItem.buildingTags = ''
}
} else {
newItem.buildingTags = ''
}
if (item.isHot !== undefined) {
item.isHot = item.isHot === '1' || item.isHot === true
//
if (newItem.isHot !== undefined) {
newItem.isHot = newItem.isHot === '1' || newItem.isHot === true
}
if (item.imageUrl !== undefined) {
item.buildingImage = item.imageUrl
// URL
if (newItem.imageUrl !== undefined) {
newItem.buildingImage = newItem.imageUrl
}
return item
//
const numericFields = ['buildingArea', 'propertyArea', 'rentableArea', 'selfUseArea',
'supportingArea', 'parkingArea', 'standardHeight', 'manageArea',
'totalRooms', 'rentalRate', 'rentedArea', 'rentedContracts',
'availableRentArea', 'waitingRooms']
numericFields.forEach(field => {
if (newItem[field] === undefined || newItem[field] === null) {
newItem[field] = 0
}
})
//
const textFields = ['projectName', 'region', 'address', 'propertyNature']
textFields.forEach(field => {
if (newItem[field] === undefined || newItem[field] === null) {
newItem[field] = ''
}
})
return newItem
})
this.total = res.data.total
} else {
@ -978,8 +1064,8 @@ export default {
}
}).catch(() => {
this.accountOptions = [
{ id: 1, accountName: '示例账户1', company: '示例收款公司1', bankName: '中国建设银行', bankAccountNumber: '6214000012345678' },
{ id: 2, accountName: '示例账户2', company: '示例收款公司2', bankName: '中国工商银行', bankAccountNumber: '6217000087654321' }
{ id: '1', accountName: '示例账户1', company: '示例收款公司1', bankName: '中国建设银行', bankAccountNumber: '6214000012345678' },
{ id: '2', accountName: '示例账户2', company: '示例收款公司2', bankName: '中国工商银行', bankAccountNumber: '6217000087654321' }
]
})
},
@ -1015,9 +1101,10 @@ export default {
pageSize: 10,
projectId: undefined,
buildingName: undefined,
buildingCode: undefined
buildingCode: undefined,
region: undefined
}
this.handleQuery()
this.getList()
},
/** 新增按钮操作 */
handleAdd() {
@ -1074,16 +1161,40 @@ export default {
// -
//
if (data.buildingTags) {
data.buildingTags = data.buildingTags
}
if (!data.buildingTags) data.buildingTags = []
else if (typeof data.buildingTags === 'string') {
try {
data.buildingTags = JSON.parse(data.buildingTags)
} catch (e) {
data.buildingTags = data.buildingTags.split(',')
try {
if (data.buildingTags) {
if (typeof data.buildingTags === 'string') {
if (data.buildingTags.trim() === '') {
data.buildingTags = []
} else {
// JSON
try {
let parsed = JSON.parse(data.buildingTags)
data.buildingTags = Array.isArray(parsed) ? parsed.map(id => String(id)) : [String(parsed)]
} catch (e) {
// JSON
data.buildingTags = data.buildingTags.split(',').filter(id => id && id.trim() !== '')
}
}
} else if (Array.isArray(data.buildingTags)) {
//
data.buildingTags = data.buildingTags.map(id => String(id)).filter(id => id)
} else if (data.buildingTags === null || data.buildingTags === undefined) {
data.buildingTags = []
} else if (typeof data.buildingTags === 'object') {
//
data.buildingTags = Object.values(data.buildingTags).map(id => String(id)).filter(id => id)
} else {
//
data.buildingTags = [String(data.buildingTags)]
}
} else {
data.buildingTags = []
}
console.log('处理后的标签数据:', data.buildingTags)
} catch (err) {
console.error('处理楼宇标签出错:', err)
data.buildingTags = []
}
//
@ -1211,9 +1322,14 @@ export default {
},
/** 对话框打开时的处理 */
onDialogOpen() {
//
if (this.tagOptions.length === 0) {
this.getTagOptions()
}
console.log('编辑表单当前标签数据:', this.form.buildingTags)
console.log('可用标签选项:', this.tagOptions)
// DOM
this.$nextTick(() => {
if (this.$refs.form) {
@ -1221,7 +1337,7 @@ export default {
this.$refs.form.clearValidate()
//
const numericFields = ['sortOrder', 'buildingArea', 'propertyArea', 'availableArea',
const numericFields = ['sortOrder', 'buildingArea', 'propertyArea', 'rentableArea',
'selfUseArea', 'supportingArea', 'parkingArea']
numericFields.forEach(field => {
if (this.form[field] !== undefined) {
@ -1241,8 +1357,16 @@ export default {
const formData = JSON.parse(JSON.stringify(this.form))
//
console.log('提交前的标签数据:', formData.buildingTags)
if (Array.isArray(formData.buildingTags)) {
formData.buildingTags = formData.buildingTags.join(',')
// ID
formData.buildingTags = formData.buildingTags.map(id => String(id)).join(',')
} else if (formData.buildingTags === undefined || formData.buildingTags === null) {
formData.buildingTags = ""
} else if (typeof formData.buildingTags === 'number' || typeof formData.buildingTags === 'boolean') {
//
formData.buildingTags = String(formData.buildingTags)
}
if (Array.isArray(formData.facilities)) {
@ -1259,9 +1383,6 @@ export default {
const action = formData.id ? updateBuilding : addBuilding
const successMsg = formData.id ? '修改成功' : '新增成功'
// action
console.log("Action函数:", action.toString())
action(formData).then(res => {
console.log("API响应:", res)
if (res.code === API_SUCCESS_CODE) {
@ -1337,20 +1458,20 @@ export default {
//
const totalStats = {
buildingCount: res.data.total || 0,
managementArea: 0,
manageArea: 0,
rentedArea: 0,
contractCount: 0,
availableArea: 0,
rentedContracts: 0,
rentableArea: 0,
yearTarget: 0
}
//
this.statistics = {
buildingCount: totalStats.buildingCount.toString(),
managementArea: totalStats.managementArea.toFixed(2),
manageArea: totalStats.manageArea.toFixed(2),
rentedArea: totalStats.rentedArea.toFixed(2),
contractCount: totalStats.contractCount.toString(),
availableArea: totalStats.availableArea.toFixed(2),
rentedContracts: totalStats.rentedContracts.toString(),
rentableArea: totalStats.rentableArea.toFixed(2),
yearTarget: totalStats.yearTarget.toString()
}
} else {
@ -1368,10 +1489,10 @@ export default {
// 使
this.statistics = {
buildingCount: '0',
managementArea: '0.00',
manageArea: '0.00',
rentedArea: '0.00',
contractCount: '0',
availableArea: '0.00',
rentedContracts: '0',
rentableArea: '0.00',
yearTarget: '0'
}
},
@ -1607,7 +1728,7 @@ export default {
this.$refs.detailForm.clearValidate()
//
const numericFields = ['rentedArea', 'rentedContracts', 'waitingArea', 'waitingRooms']
const numericFields = ['rentedArea', 'rentedContracts', 'rentedContracts', 'waitingRooms']
numericFields.forEach(field => {
if (this.detail[field] !== undefined) {
this.detail[field] = Number(this.detail[field])
@ -1669,13 +1790,80 @@ export default {
//
getTagOptions() {
getTagByType({ tagType: "楼宇标签", tagName: "楼宇标签" }).then(res => {
this.tagOptions = res.data
}).catch(() => {
if (res.code === API_SUCCESS_CODE && Array.isArray(res.data)) {
this.tagOptions = res.data
console.log('标签选项:', this.tagOptions)
} else {
console.warn('获取标签选项失败或格式错误:', res)
this.tagOptions = [
{ id: '1', tagName: '标签1' },
{ id: '2', tagName: '标签2' }
]
}
}).catch((err) => {
console.error('获取标签选项异常:', err)
this.tagOptions = [
{ id: '1', tagName: '标签1' },
{ id: '2', tagName: '标签2' }
]
})
},
//
onTagSelectorVisibleChange(visible) {
if (visible && (!this.tagOptions || this.tagOptions.length === 0)) {
this.getTagOptions()
}
},
getTagNameById(tagId) {
if (!tagId) return '';
return this.tagIdToName[tagId] || tagId
},
formatColumnValue(value, prop) {
//
const isNumericProp = [
'rentableArea', 'selfUseArea', 'supportingArea', 'parkingArea',
'manageArea', 'totalRooms', 'rentedArea', 'rentedContracts',
'availableRentArea', 'waitingRooms', 'rentalRate', 'standardHeight',
'buildingArea', 'propertyArea'
].includes(prop)
//
if (isNumericProp) {
if (typeof value !== 'number') {
//
const numValue = parseFloat(value)
if (isNaN(numValue)) {
return value || '0'; // 0
}
value = numValue;
}
switch (prop) {
case 'rentableArea':
case 'selfUseArea':
case 'supportingArea':
case 'parkingArea':
case 'manageArea':
case 'buildingArea':
case 'propertyArea':
case 'rentedArea':
case 'availableRentArea':
return value.toFixed(2)
case 'totalRooms':
case 'rentedContracts':
case 'waitingRooms':
return Math.round(value)
case 'rentalRate':
return value.toFixed(2)
case 'standardHeight':
return value.toFixed(2)
default:
return value
}
}
return value
}
}
}
@ -1812,4 +2000,8 @@ export default {
flex-wrap: wrap;
}
}
.pagination {
margin-top: 15px;
text-align: right;
}
</style>

View File

@ -62,9 +62,9 @@
<el-option label="社区养老" value="社区养老" />
</el-select>
</el-form-item>
<el-form-item label="所属地区">
<!-- <el-form-item label="所属地区">
<region-selector v-model="queryParams.region" :value="queryParams.region" placeholder="请选择所属地区" size="small" @change="handleRegionChange" />
</el-form-item>
</el-form-item> -->
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
@ -76,10 +76,10 @@
<el-table-column prop="projectName" label="项目名称" />
<el-table-column prop="projectType" label="项目类型" />
<el-table-column prop="region" label="所属地区" />
<el-table-column prop="totalArea" label="管理面积(㎡)" />
<el-table-column prop="manageArea" label="管理面积(㎡)" />
<el-table-column prop="availableArea" label="可招商面积(㎡)" />
<el-table-column prop="totalBuildings" label="总房源数量" />
<el-table-column prop="availableBuildings" label="可招商房源数量" />
<el-table-column prop="totalRooms" label="总房源数量" />
<el-table-column prop="availableRooms" label="可招商房源数量" />
<el-table-column label="操作" width="200">
<template slot-scope="scope">
<el-button size="mini" type="text" @click="handleDetail(scope.row)">详情</el-button>
@ -97,7 +97,8 @@
:page-sizes="[10, 20, 50, 100]"
:page-size="queryParams.pageSize"
layout="total, sizes, prev, pager, next, jumper"
:total="total">
:total="total"
class="pagination">
</el-pagination>
</el-card>
@ -183,27 +184,27 @@
<el-descriptions-item label="项目类型">{{ detail.projectType }}</el-descriptions-item>
<el-descriptions-item label="所属地区">{{ detail.region }}</el-descriptions-item>
<el-descriptions-item label="详细地址">{{ detail.address }}</el-descriptions-item>
<el-descriptions-item label="管理面积">{{ detail.totalArea }}</el-descriptions-item>
<el-descriptions-item label="管理面积">{{ detail.manageArea }}</el-descriptions-item>
<el-descriptions-item label="可招商面积">{{ detail.availableArea }}</el-descriptions-item>
<el-descriptions-item label="总房源数量">{{ detail.totalBuildings }}</el-descriptions-item>
<el-descriptions-item label="可招商房源数量">{{ detail.availableBuildings }}</el-descriptions-item>
<el-descriptions-item label="排序值">{{ detail.sort }}</el-descriptions-item>
<el-descriptions-item label="竣工时间">{{ detail.completionTime }}</el-descriptions-item>
<el-descriptions-item label="标准层高">{{ detail.standardHeight }}m</el-descriptions-item>
<el-descriptions-item label="物业">{{ detail.property }}</el-descriptions-item>
<el-descriptions-item label="物业费">{{ detail.propertyFee }}/·</el-descriptions-item>
<el-descriptions-item label="车位数量">{{ detail.parkingSpaces }}</el-descriptions-item>
<el-descriptions-item label="车位月租金">{{ detail.parkingFee }}/</el-descriptions-item>
<el-descriptions-item label="空调">{{ detail.airConditioning }}</el-descriptions-item>
<el-descriptions-item label="空调费">{{ detail.airConditioningFee }}/·</el-descriptions-item>
<el-descriptions-item label="空调开放时间">{{ detail.airConditioningTime }}</el-descriptions-item>
<el-descriptions-item label="电梯">{{ detail.elevator }}</el-descriptions-item>
<el-descriptions-item label="网络">{{ detail.network }}</el-descriptions-item>
<el-descriptions-item label="入驻企业">{{ detail.enterprises }}</el-descriptions-item>
<el-descriptions-item label="招商岗位">{{ detail.recruitmentPosition }}</el-descriptions-item>
<el-descriptions-item label="招商部门">{{ detail.recruitmentDepartment }}</el-descriptions-item>
<el-descriptions-item label="VR链接">{{ detail.vrLink }}</el-descriptions-item>
<el-descriptions-item label="招商时间">{{ detail.recruitmentTime }}</el-descriptions-item>
<el-descriptions-item label="总房源数量">{{ detail.totalRooms }}</el-descriptions-item>
<el-descriptions-item label="可招商房源数量">{{ detail.availableRooms }}</el-descriptions-item>
<!-- <el-descriptions-item label="排序值">{{ detail.sort }}</el-descriptions-item> -->
<!-- <el-descriptions-item label="竣工时间">{{ detail.completionTime }}</el-descriptions-item> -->
<!-- <el-descriptions-item label="标准层高">{{ detail.standardHeight }}m</el-descriptions-item> -->
<!-- <el-descriptions-item label="物业">{{ detail.property }}</el-descriptions-item> -->
<!-- <el-descriptions-item label="物业费">{{ detail.propertyFee }}/·</el-descriptions-item> -->
<!-- <el-descriptions-item label="车位数量">{{ detail.parkingSpaces }}</el-descriptions-item> -->
<!-- <el-descriptions-item label="车位月租金">{{ detail.parkingFee }}/</el-descriptions-item> -->
<!-- <el-descriptions-item label="空调">{{ detail.airConditioning }}</el-descriptions-item> -->
<!-- <el-descriptions-item label="空调费">{{ detail.airConditioningFee }}/·</el-descriptions-item> -->
<!-- <el-descriptions-item label="空调开放时间">{{ detail.airConditioningTime }}</el-descriptions-item> -->
<!-- <el-descriptions-item label="电梯">{{ detail.elevator }}</el-descriptions-item> -->
<!-- <el-descriptions-item label="网络">{{ detail.network }}</el-descriptions-item> -->
<!-- <el-descriptions-item label="入驻企业">{{ detail.enterprises }}</el-descriptions-item> -->
<!-- <el-descriptions-item label="招商岗位">{{ detail.recruitmentPosition }}</el-descriptions-item> -->
<!-- <el-descriptions-item label="招商部门">{{ detail.recruitmentDepartment }}</el-descriptions-item> -->
<!-- <el-descriptions-item label="VR链接">{{ detail.vrLink }}</el-descriptions-item> -->
<!-- <el-descriptions-item label="招商时间">{{ detail.recruitmentTime }}</el-descriptions-item> -->
<el-descriptions-item label="项目图片" :span="2">
<el-image
v-if="detail.projectImage"
@ -422,11 +423,11 @@ export default {
},
/** 图片上传前处理 */
beforeImageUpload(file) {
const isJPG = file.type === 'image/jpeg'
const isJPG = file.type === 'image/jpeg' || file.type === 'image/png'
const isLt2M = file.size / 1024 / 1024 < 2
if (!isJPG) {
this.$message.error('上传图片只能是 JPG 格式!')
this.$message.error('上传图片只能是 JPG/PNG 格式!')
}
if (!isLt2M) {
this.$message.error('上传图片大小不能超过 2MB!')
@ -448,13 +449,13 @@ export default {
try {
const res = await getAllProjectStatistics()
this.statistics = {
managementArea: res.data.totalArea || '0',
managementArea: res.data.manageArea || '0',
availableArea: res.data.availableArea || '0',
availableRatio: res.data.availableArea && res.data.totalArea
? ((res.data.availableArea / res.data.totalArea) * 100).toFixed(2)
availableRatio: res.data.availableArea && res.data.manageArea
? ((res.data.availableArea / res.data.manageArea) * 100).toFixed(2)
: '0',
totalRooms: res.data.totalBuildings || '0',
availableRooms: res.data.availableBuildings || '0'
totalRooms: res.data.totalRooms || '0',
availableRooms: res.data.availableRooms || '0'
}
} catch (error) {
console.error('获取统计数据失败:', error)
@ -547,4 +548,8 @@ export default {
height: 178px;
display: block;
}
.pagination {
margin-top: 15px;
text-align: right;
}
</style>

View File

@ -94,6 +94,7 @@
<script>
import { getRoom } from '@/api/room'
import { getTagByType } from '@/api/project'
export default {
name: 'DetailView',
@ -107,7 +108,8 @@ export default {
return {
loading: false,
activeTab: 'basic',
roomDetail: {}
roomDetail: {},
tagOptions: []
}
},
computed: {
@ -129,17 +131,17 @@ export default {
},
leaseStatusText() {
const statusMap = {
'0': '待租',
'1': '已租',
'2': '预订'
'1': '待租',
'2': '已租',
'3': '已锁定'
}
return statusMap[this.roomDetail.rentalStatus] || '-'
},
leaseStatusTagType() {
const statusMap = {
'0': 'success',
'1': 'danger',
'2': 'warning'
'1': 'warning',
'2': 'success',
'3': 'warning'
}
return statusMap[this.roomDetail.rentalStatus] || 'info'
},
@ -208,9 +210,41 @@ export default {
}
},
mounted() {
this.getTagOptions()
this.getRoomDetail()
},
methods: {
/** 获取房源标签选项 */
getTagOptions() {
getTagByType({ tagType: "房源标签", tagName: "房源标签" }).then(res => {
if (res.code === '0000000000000000' && res.data) {
this.tagOptions = res.data
//
if (this.roomDetail.id) {
this.updateRoomTags()
}
}
}).catch(() => {
this.tagOptions = [
{ id: '1', tagName: '标签1' },
{ id: '2', tagName: '标签2' }
]
})
},
/** 更新房源标签显示 */
updateRoomTags() {
if (this.roomDetail.tags) {
const tagIds = this.roomDetail.tags.split(',')
this.roomDetail.tagList = tagIds.map(tagId => {
const tag = this.tagOptions.find(t => t.id === tagId)
return tag || { id: tagId, tagName: `标签${tagId}` }
})
} else {
this.roomDetail.tagList = []
}
},
getRoomDetail() {
if (!this.roomId) return
@ -238,6 +272,9 @@ export default {
if (response.data.floorPlanImages && Array.isArray(response.data.floorPlanImages)) {
this.roomDetail.floorPlanImageUrls = response.data.floorPlanImages.map(item => item.imageUrl)
}
//
this.updateRoomTags()
} else {
this.$message.error(response.msg || '获取房源详情失败')
}

View File

@ -101,7 +101,7 @@
</el-table-column>
<el-table-column label="租赁状态" min-width="100">
<template slot-scope="scope">
<el-tag :type="scope.row.rentalStatus === '1' ? 'success' : scope.row.rentalStatus === '2' ? 'danger' : 'warning'">
<el-tag :type="scope.row.rentalStatus === '1' ? 'warning' : scope.row.rentalStatus === '2' ? 'success' : 'warning'">
{{ scope.row.rentalStatus === '1' ? '待租' : scope.row.rentalStatus === '2' ? '已租' : '暂无' }}
</el-tag>
</template>
@ -191,7 +191,7 @@
</el-dialog>
<!-- 添加/修改房源对话框 -->
<el-dialog :title="title" :visible.sync="open" width="1100px" append-to-body>
<el-dialog :title="title" :visible.sync="open" width="1100px" append-to-body :close-on-click-modal="false">
<room-form
v-if="open"
ref="roomForm"
@ -323,6 +323,10 @@ export default {
listRoom(params).then(response => {
if (response.code === '0000000000000000') {
this.roomList = response.data.list || []
//
this.updateRoomTagsList()
this.total = response.data.total
} else {
this.$message.error(response.msg || '获取房源列表失败')
@ -390,8 +394,21 @@ export default {
/** 获取房源标签选项 */
getTagOptions() {
getTagByType({ tagType: "房源标签", tagName: "房源标签" }).then(res => {
this.tagOptions = res.data
if (res.code === '0000000000000000' && res.data) {
this.tagOptions = res.data
} else {
this.$message.warning('获取房源标签失败,将使用默认标签')
this.tagOptions = [
{ id: '1', tagName: '标签1' },
{ id: '2', tagName: '标签2' }
]
}
//
if (this.roomList.length > 0) {
this.updateRoomTagsList()
}
}).catch(() => {
this.$message.warning('获取房源标签失败,将使用默认标签')
this.tagOptions = [
{ id: '1', tagName: '标签1' },
{ id: '2', tagName: '标签2' }
@ -399,6 +416,21 @@ export default {
})
},
/** 更新房源列表的标签显示 */
updateRoomTagsList() {
this.roomList.forEach(room => {
if (room.tags) {
const tagIds = room.tags.split(',');
room.tagList = tagIds.map(tagId => {
const tag = this.tagOptions.find(t => t.id === tagId);
return tag || { id: tagId, tagName: `标签${tagId}` };
});
} else {
room.tagList = [];
}
});
},
/** 项目选择改变事件 */
handleProjectChange(projectId) {
//
@ -438,8 +470,15 @@ export default {
/** 重置查询条件 */
resetQuery() {
this.dateRange = []
this.resetForm('queryForm')
this.resetForm('advancedQueryForm')
// 使Element UIresetFields
if (this.$refs.queryForm) {
this.$refs.queryForm.resetFields()
}
if (this.$refs.advancedQueryForm) {
this.$refs.advancedQueryForm.resetFields()
}
// Tab
const tabBusinessStatus = this.activeTab === 'commercial' ? '1' : undefined
@ -523,7 +562,10 @@ export default {
this.$alert(`response.data.message `, { type: 'success' })
this.getList()
} else {
//
this.$alert(response.msg || '导入失败,正在下载错误文件', '导入结果', { type: 'error' })
this.download(response, `房源导入错误文件_${new Date().getTime()}.xlsx`)
}
},

View File

@ -12,8 +12,8 @@ module.exports = {
},
proxy: {
'/': {
// target: 'http://192.168.137.3:8080/api/api',
target: 'http://192.168.137.214:8080/api',
// target: 'http://192.168.137.3:8082/api',
target: 'http://192.168.137.45:8080',
changeOrigin: true,
pathRewrite: {
'^/api': '/api'