初始化项目 #1

Open
zengqiyang wants to merge 21 commits from zqy-mst into main
8 changed files with 533 additions and 151 deletions
Showing only changes of commit 516f4cf94d - Show all commits

BIN
pc/public.zip Normal file

Binary file not shown.

54
pc/src/api/resource.js Normal file
View File

@ -0,0 +1,54 @@
import request from '@/utils/request'
/**
* 上传附件
* @param {File} file 文件对象
* @returns {Promise} 返回包含fileId和fileName的Promise
*/
export function uploadAttachment(file) {
const formData = new FormData()
formData.append('file', file)
return request({
url: '/resource/attachment/uploadAttachment',
method: 'post',
data: formData,
headers: {
'Content-Type': 'multipart/form-data'
}
})
}
/**
* 下载附件
* @param {string} fileId 文件ID
* @returns {Promise} 返回文件流的Promise
*/
export function downloadAttachment(fileId) {
return request({
url: `/resource/attachment/downloadAttachment?fileId=${fileId}`,
method: 'get',
responseType: 'blob'
})
}
/**
* 批量删除附件
* @param {Array} fileIds 文件ID数组
* @returns {Promise} 返回操作结果的Promise
*/
export function batchDeleteAttachment(fileIds) {
return request({
url: '/resource/attachment/batchDeleteAttachment',
method: 'post',
data: fileIds
})
}
/**
* 删除单个附件
* @param {string} fileId 文件ID
* @returns {Promise} 返回操作结果的Promise
*/
export function deleteAttachment(fileId) {
return batchDeleteAttachment([fileId])
}

View File

@ -11,13 +11,36 @@ Vue.config.productionTip = false
// 添加下载方法 // 添加下载方法
Vue.prototype.download = function(res, fileName) { Vue.prototype.download = function(res, fileName) {
// 创建blob链接 // 检查响应类型
const blob = new Blob([res]) if (res.type && (res.type === 'application/vnd.ms-excel' ||
const link = document.createElement('a') res.type === 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' ||
link.href = URL.createObjectURL(blob) res.type.includes('excel') ||
link.download = fileName res.type === 'application/octet-stream')) {
link.click() // 如果已经是Blob类型且是Excel类型直接使用
URL.revokeObjectURL(link.href) const blob = res
const link = document.createElement('a')
link.href = URL.createObjectURL(blob)
link.download = fileName
link.click()
URL.revokeObjectURL(link.href)
} else {
// 如果不是Blob类型或无法确定类型根据文件扩展名处理
let blob
if (fileName.endsWith('.xlsx') || fileName.endsWith('.xls')) {
// Excel文件需要使用二进制格式
blob = new Blob([res], {
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
})
} else {
// 其他类型使用默认处理
blob = new Blob([res])
}
const link = document.createElement('a')
link.href = URL.createObjectURL(blob)
link.download = fileName
link.click()
URL.revokeObjectURL(link.href)
}
} }
new Vue({ new Vue({

View File

@ -188,6 +188,17 @@
<el-descriptions-item label="可招商面积">{{ detail.availableArea }}</el-descriptions-item> <el-descriptions-item label="可招商面积">{{ detail.availableArea }}</el-descriptions-item>
<el-descriptions-item label="总房源数量">{{ detail.totalRooms }}</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.availableRooms }}</el-descriptions-item>
<el-descriptions-item label="项目标签" :span="2">
<el-tag
v-for="tag in projectTagList"
:key="tag.id"
type="success"
size="medium"
style="margin-right: 8px; margin-bottom: 5px;">
{{ tag.tagName }}
</el-tag>
<span v-if="!projectTagList || projectTagList.length === 0">暂无标签</span>
</el-descriptions-item>
<!-- <el-descriptions-item label="排序值">{{ detail.sort }}</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.completionTime }}</el-descriptions-item> -->
<!-- <el-descriptions-item label="标准层高">{{ detail.standardHeight }}m</el-descriptions-item> --> <!-- <el-descriptions-item label="标准层高">{{ detail.standardHeight }}m</el-descriptions-item> -->
@ -287,6 +298,25 @@ export default {
} }
} }
}, },
computed: {
//
projectTagList() {
if (!this.detail.projectTags || !this.tagOptions || this.tagOptions.length === 0) {
return [];
}
// ID
const tagIds = typeof this.detail.projectTags === 'string'
? this.detail.projectTags.split(',')
: this.detail.projectTags;
// ID
return tagIds.map(tagId => {
const tag = this.tagOptions.find(t => t.id === tagId);
return tag || { id: tagId, tagName: `标签${tagId}` };
});
}
},
created() { created() {
this.getList() this.getList()
this.getTagOptions() this.getTagOptions()
@ -314,14 +344,18 @@ export default {
}, },
/** 获取标签选项 */ /** 获取标签选项 */
getTagOptions() { getTagOptions() {
getTagByType({ tagType: '项目标签',tagName: '项目标签' }).then(res => { return new Promise((resolve) => {
this.tagOptions = res.data getTagByType({ tagType: '项目标签',tagName: '项目标签' }).then(res => {
}).catch(() => { this.tagOptions = res.data || [];
this.tagOptions = [ resolve(this.tagOptions);
{ id: '1', tagName: '标签1' }, }).catch(() => {
{ id: '2', tagName: '标签2' } this.tagOptions = [
] { id: '1', tagName: '标签1' },
}) { id: '2', tagName: '标签2' }
];
resolve(this.tagOptions);
});
});
}, },
/** 搜索按钮操作 */ /** 搜索按钮操作 */
handleQuery() { handleQuery() {
@ -366,7 +400,12 @@ export default {
}) })
}, },
/** 详情按钮操作 */ /** 详情按钮操作 */
handleDetail(row) { async handleDetail(row) {
//
if (this.tagOptions.length === 0) {
await this.getTagOptions();
}
getProjectDetail(row.id).then(res => { getProjectDetail(row.id).then(res => {
// //
const detailData = { ...res.data }; const detailData = { ...res.data };

View File

@ -55,11 +55,11 @@
</el-descriptions> </el-descriptions>
<div class="image-section" v-if="roomDetail.imageUrls && roomDetail.imageUrls.length > 0"> <div class="image-section" v-if="roomImageUrls && roomImageUrls.length > 0">
<div class="section-title">房源图片</div> <div class="section-title">房源图片</div>
<el-carousel :interval="4000" type="card" height="200px"> <el-carousel :interval="4000" type="card" height="200px">
<el-carousel-item v-for="(item, index) in roomDetail.imageUrls" :key="index"> <el-carousel-item v-for="(item, index) in roomImageUrls" :key="index">
<img :src="item" class="carousel-image" /> <img :src="item" class="carousel-image" @click="previewImage(item)" />
</el-carousel-item> </el-carousel-item>
</el-carousel> </el-carousel>
</div> </div>
@ -75,11 +75,11 @@
<el-descriptions-item label="工位数量">{{ workstationCountRange }}</el-descriptions-item> <el-descriptions-item label="工位数量">{{ workstationCountRange }}</el-descriptions-item>
</el-descriptions> </el-descriptions>
<div class="image-section" v-if="roomDetail.floorPlanImageUrls && roomDetail.floorPlanImageUrls.length > 0"> <div class="image-section" v-if="floorPlanImageUrls && floorPlanImageUrls.length > 0">
<div class="section-title">户型图</div> <div class="section-title">户型图</div>
<el-carousel :interval="4000" type="card" height="200px"> <el-carousel :interval="4000" type="card" height="200px">
<el-carousel-item v-for="(item, index) in roomDetail.floorPlanImageUrls" :key="index"> <el-carousel-item v-for="(item, index) in floorPlanImageUrls" :key="index">
<img :src="item" class="carousel-image" /> <img :src="item" class="carousel-image" @click="previewImage(item)" />
</el-carousel-item> </el-carousel-item>
</el-carousel> </el-carousel>
<div class="floor-plan-description" v-if="roomDetail.floorPlanDesc"> <div class="floor-plan-description" v-if="roomDetail.floorPlanDesc">
@ -89,12 +89,18 @@
</div> </div>
</el-tab-pane> </el-tab-pane>
</el-tabs> </el-tabs>
<!-- 图片预览弹窗 -->
<el-dialog :visible.sync="previewVisible" append-to-body>
<img width="100%" :src="previewUrl" alt="预览图片">
</el-dialog>
</div> </div>
</template> </template>
<script> <script>
import { getRoom } from '@/api/room' import { getRoom } from '@/api/room'
import { getTagByType } from '@/api/project' import { getTagByType } from '@/api/project'
import { downloadAttachment } from '@/api/resource'
export default { export default {
name: 'DetailView', name: 'DetailView',
@ -109,7 +115,11 @@ export default {
loading: false, loading: false,
activeTab: 'basic', activeTab: 'basic',
roomDetail: {}, roomDetail: {},
tagOptions: [] tagOptions: [],
roomImageUrls: [],
floorPlanImageUrls: [],
previewVisible: false,
previewUrl: ''
} }
}, },
computed: { computed: {
@ -245,6 +255,12 @@ export default {
} }
}, },
/** 预览图片 */
previewImage(url) {
this.previewUrl = url
this.previewVisible = true
},
getRoomDetail() { getRoomDetail() {
if (!this.roomId) return if (!this.roomId) return
@ -263,14 +279,24 @@ export default {
Object.assign(this.roomDetail, response.data.extendInfo) Object.assign(this.roomDetail, response.data.extendInfo)
} }
// //
this.roomImageUrls = []
if (response.data.roomImages && Array.isArray(response.data.roomImages)) { if (response.data.roomImages && Array.isArray(response.data.roomImages)) {
this.roomDetail.imageUrls = response.data.roomImages.map(item => item.imageUrl) response.data.roomImages.forEach(item => {
if (item.fileId) {
this.roomImageUrls.push(`/resource/attachment/downloadAttachment?fileId=${item.fileId}`)
}
})
} }
// //
this.floorPlanImageUrls = []
if (response.data.floorPlanImages && Array.isArray(response.data.floorPlanImages)) { if (response.data.floorPlanImages && Array.isArray(response.data.floorPlanImages)) {
this.roomDetail.floorPlanImageUrls = response.data.floorPlanImages.map(item => item.imageUrl) response.data.floorPlanImages.forEach(item => {
if (item.fileId) {
this.floorPlanImageUrls.push(`/resource/attachment/downloadAttachment?fileId=${item.fileId}`)
}
})
} }
// //
@ -311,6 +337,7 @@ export default {
width: 100%; width: 100%;
height: 100%; height: 100%;
object-fit: cover; object-fit: cover;
cursor: pointer;
} }
.floor-plan-description { .floor-plan-description {

View File

@ -258,11 +258,21 @@
</el-row> </el-row>
<el-form-item label="房源图片" prop="imageUrls"> <el-form-item label="房源图片" prop="imageFiles">
<el-upload :action="uploadUrl" list-type="picture-card" :headers="uploadHeaders" :file-list="imageList" <div class="image-upload-tip" v-if="imageList.length >= 6">最多上传6张房源图片</div>
:on-preview="handlePictureCardPreview" :on-success="handleImageSuccess" :on-remove="handleImageRemove" <el-upload
action="#"
list-type="picture-card"
:file-list="imageList"
:on-preview="handlePictureCardPreview"
:on-remove="handleImageRemove"
:before-upload="handleBeforeImageUpload"
:on-change="handleImageChange"
:auto-upload="false"
:limit="6"
ref="imageUpload"
multiple> multiple>
<i class="el-icon-plus"></i> <i v-if="imageList.length < 6" class="el-icon-plus"></i>
</el-upload> </el-upload>
<el-dialog :visible.sync="dialogVisible"> <el-dialog :visible.sync="dialogVisible">
<img width="100%" :src="dialogImageUrl" alt=""> <img width="100%" :src="dialogImageUrl" alt="">
@ -320,11 +330,20 @@
@input="handleInputChange" /> @input="handleInputChange" />
</el-form-item> </el-form-item>
<el-form-item label="户型图" prop="floorPlanImageUrls"> <el-form-item label="户型图" prop="floorPlanImageFiles">
<el-upload :action="uploadUrl" list-type="picture-card" :headers="uploadHeaders" <div class="image-upload-tip" v-if="floorPlanImageList.length >= 1">最多上传1张户型图</div>
:file-list="floorPlanImageList" :on-preview="handlePictureCardPreview" <el-upload
:on-success="handleFloorPlanImageSuccess" :on-remove="handleFloorPlanImageRemove" multiple> action="#"
<i class="el-icon-plus"></i> list-type="picture-card"
:file-list="floorPlanImageList"
:on-preview="handlePictureCardPreview"
:on-remove="handleFloorPlanImageRemove"
:before-upload="handleBeforeFloorPlanUpload"
:on-change="handleFloorPlanChange"
:auto-upload="false"
:limit="1"
ref="floorPlanUpload">
<i v-if="floorPlanImageList.length < 1" class="el-icon-plus"></i>
</el-upload> </el-upload>
<el-dialog :visible.sync="dialogVisible"> <el-dialog :visible.sync="dialogVisible">
<img width="100%" :src="dialogImageUrl" alt=""> <img width="100%" :src="dialogImageUrl" alt="">
@ -345,6 +364,7 @@
import { getRoom, addRoom, updateRoom, listProjects, listBuildings, listFloors, listRoomTags, listOwners } from '@/api/room' import { getRoom, addRoom, updateRoom, listProjects, listBuildings, listFloors, listRoomTags, listOwners } from '@/api/room'
import { getProjectList,getTagByType } from '@/api/project' import { getProjectList,getTagByType } from '@/api/project'
import { getBuildingList, getFloorList,getFloorListByBuilding} from '@/api/building' import { getBuildingList, getFloorList,getFloorListByBuilding} from '@/api/building'
import { uploadAttachment, downloadAttachment, deleteAttachment } from '@/api/resource'
export default { export default {
name: 'RoomForm', name: 'RoomForm',
props: { props: {
@ -381,8 +401,16 @@ export default {
ownerOptions: [], ownerOptions: [],
// //
imageList: [], imageList: [],
//
imageFiles: [],
// IDs
imageFileIds: [],
// //
floorPlanImageList: [], floorPlanImageList: [],
//
floorPlanFiles: [],
// IDs
floorPlanFileIds: [],
// URL // URL
uploadUrl: process.env.VUE_APP_BASE_API + '/room/upload', uploadUrl: process.env.VUE_APP_BASE_API + '/room/upload',
// //
@ -411,6 +439,7 @@ export default {
loadValue: '', loadValue: '',
tagIds: [], tagIds: [],
imageUrls: [], imageUrls: [],
imageFileIds: [],
businessStatus: '1', // businessStatus: '1', //
availableDate: '', availableDate: '',
decorationStatus: '4', // decorationStatus: '4', //
@ -428,7 +457,8 @@ export default {
workstationMin: null, workstationMin: null,
workstationMax: null, workstationMax: null,
floorPlanDesc: '', floorPlanDesc: '',
floorPlanImageUrls: [] floorPlanImageUrls: [],
floorPlanFileIds: []
}, },
// //
rules: { rules: {
@ -497,19 +527,41 @@ export default {
// //
this.form.imageUrls = [] this.form.imageUrls = []
this.form.imageFileIds = []
this.imageList = []
this.imageFiles = []
this.imageFileIds = []
if (response.data.roomImages && Array.isArray(response.data.roomImages) && response.data.roomImages.length > 0) { if (response.data.roomImages && Array.isArray(response.data.roomImages) && response.data.roomImages.length > 0) {
this.form.imageUrls = response.data.roomImages.map(item => item.imageUrl) response.data.roomImages.forEach((item, index) => {
this.imageList = response.data.roomImages.map((item, index) => { if (item.fileId) {
return { name: '图片' + (index + 1), url: item.imageUrl } this.form.imageFileIds.push(item.fileId)
this.imageFileIds.push(item.fileId)
this.imageList.push({
name: '图片' + (index + 1),
url: `/resource/attachment/downloadAttachment?fileId=${item.fileId}`,
fileId: item.fileId
})
}
}) })
} }
// //
this.form.floorPlanImageUrls = [] this.form.floorPlanImageUrls = []
this.form.floorPlanFileIds = []
this.floorPlanImageList = []
this.floorPlanFiles = []
this.floorPlanFileIds = []
if (response.data.floorPlanImages && Array.isArray(response.data.floorPlanImages) && response.data.floorPlanImages.length > 0) { if (response.data.floorPlanImages && Array.isArray(response.data.floorPlanImages) && response.data.floorPlanImages.length > 0) {
this.form.floorPlanImageUrls = response.data.floorPlanImages.map(item => item.imageUrl) response.data.floorPlanImages.forEach((item, index) => {
this.floorPlanImageList = response.data.floorPlanImages.map((item, index) => { if (item.fileId) {
return { name: '户型图' + (index + 1), url: item.imageUrl } this.form.floorPlanFileIds.push(item.fileId)
this.floorPlanFileIds.push(item.fileId)
this.floorPlanImageList.push({
name: '户型图' + (index + 1),
url: `/resource/attachment/downloadAttachment?fileId=${item.fileId}`,
fileId: item.fileId
})
}
}) })
} }
@ -664,130 +716,239 @@ export default {
this.dialogVisible = true this.dialogVisible = true
}, },
/** 房源图片上传成功 */ /** 房源图片上传前检查 */
handleImageSuccess(response, file, fileList) { handleBeforeImageUpload(file) {
if (response.code === '0000000000000000') { const isImage = file.type.indexOf('image/') === 0
this.form.imageUrls.push(response.data.imageUrl) const isLt5M = file.size / 1024 / 1024 < 5
this.imageList = fileList.map(file => {
if (file.response) { if (!isImage) {
return { name: file.name, url: file.response.data.imageUrl } this.$message.error('上传图片只能是图片格式!')
} else { return false
return { name: file.name, url: file.url }
}
})
} else {
this.$message.error('图片上传失败')
} }
if (!isLt5M) {
this.$message.error('上传图片大小不能超过 5MB!')
return false
}
if (this.imageList.length >= 6) {
this.$message.error('最多上传6张房源图片!')
return false
}
return true
},
/** 房源图片变化 */
handleImageChange(file, fileList) {
this.imageFiles = fileList.filter(item => !item.fileId && item.raw)
this.imageList = fileList.map(item => {
if (item.response) {
return {
name: item.name,
url: `/resource/attachment/downloadAttachment?fileId=${item.response.data.fileId}`,
fileId: item.response.data.fileId
}
} else {
return item
}
})
}, },
/** 房源图片删除 */ /** 房源图片删除 */
handleImageRemove(file, fileList) { handleImageRemove(file, fileList) {
const url = file.url || (file.response && file.response.data.url) //
this.form.imageUrls = this.form.imageUrls.filter(item => item !== url) if (file.fileId) {
this.imageFileIds = this.imageFileIds.filter(id => id !== file.fileId)
deleteAttachment(file.fileId).catch(() => {
this.$message.error('删除图片失败')
})
}
//
this.imageFiles = this.imageFiles.filter(item => item.uid !== file.uid)
this.imageList = fileList this.imageList = fileList
}, },
/** 户型图上传成功 */ /** 户型图上传前检查 */
handleFloorPlanImageSuccess(response, file, fileList) { handleBeforeFloorPlanUpload(file) {
if (response.code === '0000000000000000') { const isImage = file.type.indexOf('image/') === 0
this.form.floorPlanImageUrls.push(response.data.imageUrl) const isLt5M = file.size / 1024 / 1024 < 5
this.floorPlanImageList = fileList.map(file => {
if (file.response) { if (!isImage) {
return { name: file.name, url: file.response.data.imageUrl } this.$message.error('上传户型图只能是图片格式!')
} else { return false
return { name: file.name, url: file.url }
}
})
} else {
this.$message.error('户型图上传失败')
} }
if (!isLt5M) {
this.$message.error('上传户型图大小不能超过 5MB!')
return false
}
if (this.floorPlanImageList.length >= 1) {
this.$message.error('最多上传1张户型图!')
return false
}
return true
},
/** 户型图变化 */
handleFloorPlanChange(file, fileList) {
this.floorPlanFiles = fileList.filter(item => !item.fileId && item.raw)
this.floorPlanImageList = fileList.map(item => {
if (item.response) {
return {
name: item.name,
url: `/resource/attachment/downloadAttachment?fileId=${item.response.data.fileId}`,
fileId: item.response.data.fileId
}
} else {
return item
}
})
}, },
/** 户型图删除 */ /** 户型图删除 */
handleFloorPlanImageRemove(file, fileList) { handleFloorPlanImageRemove(file, fileList) {
const url = file.url || (file.response && file.response.data.url) //
this.form.floorPlanImageUrls = this.form.floorPlanImageUrls.filter(item => item !== url) if (file.fileId) {
this.floorPlanFileIds = this.floorPlanFileIds.filter(id => id !== file.fileId)
deleteAttachment(file.fileId).catch(() => {
this.$message.error('删除户型图失败')
})
}
//
this.floorPlanFiles = this.floorPlanFiles.filter(item => item.uid !== file.uid)
this.floorPlanImageList = fileList this.floorPlanImageList = fileList
}, },
/** 上传图片文件 */
async uploadImages() {
try {
//
//
for (const file of this.imageFiles) {
if (file.raw) {
try {
const response = await uploadAttachment(file.raw)
if (response.code === '0000000000000000') {
this.imageFileIds.push(response.data.fileId)
} else {
this.$message.error(`房源图片 ${file.name} 上传失败`)
}
} catch (error) {
console.error('上传房源图片失败:', error)
this.$message.error(`房源图片 ${file.name} 上传失败`)
}
}
}
//
for (const file of this.floorPlanFiles) {
if (file.raw) {
try {
const response = await uploadAttachment(file.raw)
if (response.code === '0000000000000000') {
this.floorPlanFileIds.push(response.data.fileId)
} else {
this.$message.error(`户型图 ${file.name} 上传失败`)
}
} catch (error) {
console.error('上传户型图失败:', error)
this.$message.error(`户型图 ${file.name} 上传失败`)
}
}
}
return true
} catch (error) {
console.error('上传图片过程中出错:', error)
this.$message.error('上传图片过程中出错,请重试')
return false
}
},
/** 提交表单 */ /** 提交表单 */
submitForm() { submitForm() {
this.$refs.form.validate(valid => { this.$refs.form.validate(async valid => {
if (valid) { if (valid) {
this.loading = true this.loading = true
// try {
const formData = { //
id: this.form.id, await this.uploadImages()
projectId: this.form.projectId,
buildingId: this.form.buildingId,
floorId: this.form.floorId,
roomNumber: this.form.roomNumber,
roomDigitalNumber: this.form.roomDigitalNumber,
roomManageNumber: this.form.roomManageNumber,
buildingArea: this.form.buildingArea,
rentalArea: this.form.rentalArea,
billingArea: this.form.billingArea,
innerArea: this.form.innerArea,
isVirtual: this.form.isVirtual,
deliveryTime: this.formatDate(this.form.deliveryTime),
ownerId: this.form.ownerId,
propertyNature: this.form.propertyNature,
height: this.form.height,
loadValue: this.form.loadValue,
roomStatus: this.form.roomStatus,
roomType: this.form.roomType,
remark: this.form.remark,
// //
businessInfo: { const formData = {
rentalStatus: this.form.rentalStatus, id: this.form.id,
businessStatus: this.form.businessStatus, projectId: this.form.projectId,
availableDate: this.formatDate(this.form.availableDate), buildingId: this.form.buildingId,
decorationStatus: this.form.decorationStatus, floorId: this.form.floorId,
price: this.form.price, roomNumber: this.form.roomNumber,
priceUnit: this.form.priceUnit, roomDigitalNumber: this.form.roomDigitalNumber,
floorPrice: this.form.floorPrice, roomManageNumber: this.form.roomManageNumber,
floorPriceUnit: this.form.floorPriceUnit, buildingArea: this.form.buildingArea,
businessNumber: this.form.businessNumber, rentalArea: this.form.rentalArea,
businessCondition: this.form.businessCondition billingArea: this.form.billingArea,
}, innerArea: this.form.innerArea,
isVirtual: this.form.isVirtual,
deliveryTime: this.formatDate(this.form.deliveryTime),
ownerId: this.form.ownerId,
propertyNature: this.form.propertyNature,
height: this.form.height,
loadValue: this.form.loadValue,
roomStatus: this.form.roomStatus,
roomType: this.form.roomType,
remark: this.form.remark,
// //
extendInfo: { businessInfo: {
roomRecordNumber: this.form.roomRecordNumber, rentalStatus: this.form.rentalStatus,
usageRate: this.form.usageRate, businessStatus: this.form.businessStatus,
officeLayout: this.form.officeLayout, availableDate: this.formatDate(this.form.availableDate),
windowOrientation: this.form.windowOrientation, decorationStatus: this.form.decorationStatus,
rentFreePeriod: this.form.rentFreePeriod, price: this.form.price,
minLeaseTerm: this.form.minLeaseTerm, priceUnit: this.form.priceUnit,
workstationMin: this.form.workstationMin, floorPrice: this.form.floorPrice,
workstationMax: this.form.workstationMax, floorPriceUnit: this.form.floorPriceUnit,
floorPlanDesc: this.form.floorPlanDesc, businessNumber: this.form.businessNumber,
tags: this.form.tagIds ? this.form.tagIds.join(',') : '' businessCondition: this.form.businessCondition
}, },
// //
roomImages: this.form.imageUrls ? this.form.imageUrls.map((url, index) => { extendInfo: {
return { roomRecordNumber: this.form.roomRecordNumber,
imageUrl: url, usageRate: this.form.usageRate,
imageType: '房源图片', officeLayout: this.form.officeLayout,
sortOrder: index windowOrientation: this.form.windowOrientation,
} rentFreePeriod: this.form.rentFreePeriod,
}) : [], minLeaseTerm: this.form.minLeaseTerm,
workstationMin: this.form.workstationMin,
workstationMax: this.form.workstationMax,
floorPlanDesc: this.form.floorPlanDesc,
tags: this.form.tagIds ? this.form.tagIds.join(',') : ''
},
// // - 使fileId
floorPlanImages: this.form.floorPlanImageUrls ? this.form.floorPlanImageUrls.map((url, index) => { roomImages: this.imageFileIds.map((fileId, index) => {
return { return {
imageUrl: url, fileId: fileId,
imageType: '户型图', imageType: '房源图片',
sortOrder: index sortOrder: index
} }
}) : [] }),
}
// - 使fileId
floorPlanImages: this.floorPlanFileIds.map((fileId, index) => {
return {
fileId: fileId,
imageType: '户型图',
sortOrder: index
}
})
}
const method = this.isEdit ? updateRoom : addRoom
const response = await method(formData)
const method = this.isEdit ? updateRoom : addRoom
method(formData).then(response => {
if (response.code === '0000000000000000') { if (response.code === '0000000000000000') {
this.$message.success(this.isEdit ? '修改成功' : '新增成功') this.$message.success(this.isEdit ? '修改成功' : '新增成功')
this.$emit('refreshList') this.$emit('refreshList')
@ -795,10 +956,12 @@ export default {
} else { } else {
this.$message.error(response.msg || (this.isEdit ? '修改失败' : '新增失败')) this.$message.error(response.msg || (this.isEdit ? '修改失败' : '新增失败'))
} }
} catch (error) {
console.error('提交表单失败', error)
this.$message.error(this.isEdit ? '修改失败' : '新增失败')
} finally {
this.loading = false this.loading = false
}).catch(() => { }
this.loading = false
})
} }
}) })
}, },
@ -879,5 +1042,11 @@ export default {
::v-deep .el-input-number .el-input__inner { ::v-deep .el-input-number .el-input__inner {
text-align: left; text-align: left;
} }
.image-upload-tip {
font-size: 12px;
color: #909399;
margin-bottom: 5px;
}
} }
</style> </style>

View File

@ -171,6 +171,7 @@
:on-progress="handleFileUploadProgress" :on-progress="handleFileUploadProgress"
:on-success="handleFileSuccess" :on-success="handleFileSuccess"
:auto-upload="false" :auto-upload="false"
:http-request="customUploadRequest"
drag> drag>
<i class="el-icon-upload"></i> <i class="el-icon-upload"></i>
<div class="el-upload__text">将文件拖到此处<em>点击上传</em></div> <div class="el-upload__text">将文件拖到此处<em>点击上传</em></div>
@ -208,6 +209,7 @@
import { listRoom, exportRoom, downloadTemplate, importRoom, listRoomTags, listProjects, listBuildings, listFloors, delRoom } from '@/api/room' import { listRoom, exportRoom, downloadTemplate, importRoom, listRoomTags, listProjects, listBuildings, listFloors, delRoom } from '@/api/room'
import { getProjectList,getTagByType } from '@/api/project' import { getProjectList,getTagByType } from '@/api/project'
import { getBuildingList,getFloorList,getFloorListByBuilding } from '@/api/building' import { getBuildingList,getFloorList,getFloorListByBuilding } from '@/api/building'
import axios from 'axios'
import DetailView from './components/DetailView' import DetailView from './components/DetailView'
import RoomForm from './components/RoomForm' import RoomForm from './components/RoomForm'
@ -558,14 +560,26 @@ export default {
this.upload.isUploading = false this.upload.isUploading = false
this.$refs.upload.clearFiles() this.$refs.upload.clearFiles()
this.importOpen = false this.importOpen = false
// Blob
if (response instanceof Blob) {
//
this.$alert('导入失败,正在下载错误文件', '导入结果', { type: 'error' })
this.download(response, `房源导入错误文件_${new Date().getTime()}.xlsx`)
return
}
//
if (response.code === '0000000000000000') { if (response.code === '0000000000000000') {
this.$alert(`response.data.message `, { type: 'success' }) this.$alert(response.data.message || '导入成功', '导入结果', { type: 'success' })
this.getList() this.getList()
} else { } else {
// //
this.$alert(response.msg || '导入失败', '导入结果', { type: 'error' })
this.$alert(response.msg || '导入失败,正在下载错误文件', '导入结果', { type: 'error' }) //
this.download(response, `房源导入错误文件_${new Date().getTime()}.xlsx`) if (response.data && response.data.errorFile) {
this.download(response.data.errorFile, `房源导入错误文件_${new Date().getTime()}.xlsx`)
}
} }
}, },
@ -639,6 +653,62 @@ export default {
'5': '元/年' '5': '元/年'
}; };
return unitMap[unitType] || ''; return unitMap[unitType] || '';
},
/** 自定义上传请求 */
customUploadRequest(options) {
this.upload.isUploading = true
const { file, onSuccess, onError, onProgress } = options
// FormData
const formData = new FormData()
formData.append('file', file)
// 使axiosresponseTypeblob
axios.post(this.upload.url, formData, {
headers: {
'Content-Type': 'multipart/form-data',
...this.upload.headers
},
responseType: 'blob', // blob
onUploadProgress: (progressEvent) => {
onProgress({
percent: (progressEvent.loaded / progressEvent.total * 100).toFixed(2)
})
}
}).then(response => {
// Excel
const contentType = response.headers['content-type']
const contentDisposition = response.headers['content-disposition']
// Excel
if (contentType && (
contentType.includes('excel') ||
contentType.includes('spreadsheetml') ||
contentType === 'application/octet-stream')) {
// Blob
onSuccess(response.data)
} else {
// JSON
const reader = new FileReader()
reader.onload = () => {
try {
const jsonResponse = JSON.parse(reader.result)
onSuccess(jsonResponse)
} catch (error) {
// JSON
onSuccess(response.data)
}
}
reader.onerror = () => {
onError(new Error('解析响应失败'))
}
reader.readAsText(response.data)
}
}).catch(error => {
onError(error)
this.upload.isUploading = false
})
} }
} }
} }

View File

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