初始化项目 #1
BIN
pc/public.zip
Normal file
BIN
pc/public.zip
Normal file
Binary file not shown.
54
pc/src/api/resource.js
Normal file
54
pc/src/api/resource.js
Normal 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])
|
||||||
|
}
|
@ -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' ||
|
||||||
|
res.type === 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' ||
|
||||||
|
res.type.includes('excel') ||
|
||||||
|
res.type === 'application/octet-stream')) {
|
||||||
|
// 如果已经是Blob类型且是Excel类型,直接使用
|
||||||
|
const blob = res
|
||||||
const link = document.createElement('a')
|
const link = document.createElement('a')
|
||||||
link.href = URL.createObjectURL(blob)
|
link.href = URL.createObjectURL(blob)
|
||||||
link.download = fileName
|
link.download = fileName
|
||||||
link.click()
|
link.click()
|
||||||
URL.revokeObjectURL(link.href)
|
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({
|
||||||
|
@ -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() {
|
||||||
|
return new Promise((resolve) => {
|
||||||
getTagByType({ tagType: '项目标签',tagName: '项目标签' }).then(res => {
|
getTagByType({ tagType: '项目标签',tagName: '项目标签' }).then(res => {
|
||||||
this.tagOptions = res.data
|
this.tagOptions = res.data || [];
|
||||||
|
resolve(this.tagOptions);
|
||||||
}).catch(() => {
|
}).catch(() => {
|
||||||
this.tagOptions = [
|
this.tagOptions = [
|
||||||
{ id: '1', tagName: '标签1' },
|
{ id: '1', tagName: '标签1' },
|
||||||
{ id: '2', tagName: '标签2' }
|
{ 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 };
|
||||||
|
@ -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 {
|
||||||
|
@ -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,58 +716,166 @@ 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('上传图片只能是图片格式!')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
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 {
|
} else {
|
||||||
return { name: file.name, url: file.url }
|
return item
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
} else {
|
|
||||||
this.$message.error('图片上传失败')
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/** 房源图片删除 */
|
/** 房源图片删除 */
|
||||||
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('上传户型图只能是图片格式!')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
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 {
|
} else {
|
||||||
return { name: file.name, url: file.url }
|
return item
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
} else {
|
|
||||||
this.$message.error('户型图上传失败')
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/** 户型图删除 */
|
/** 户型图删除 */
|
||||||
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 {
|
||||||
|
// 先上传所有图片文件
|
||||||
|
await this.uploadImages()
|
||||||
|
|
||||||
// 准备提交数据
|
// 准备提交数据
|
||||||
const formData = {
|
const formData = {
|
||||||
id: this.form.id,
|
id: this.form.id,
|
||||||
@ -767,27 +927,28 @@ export default {
|
|||||||
tags: this.form.tagIds ? this.form.tagIds.join(',') : ''
|
tags: this.form.tagIds ? this.form.tagIds.join(',') : ''
|
||||||
},
|
},
|
||||||
|
|
||||||
// 图片信息
|
// 图片信息 - 使用上传后获取的fileId
|
||||||
roomImages: this.form.imageUrls ? this.form.imageUrls.map((url, index) => {
|
roomImages: this.imageFileIds.map((fileId, index) => {
|
||||||
return {
|
return {
|
||||||
imageUrl: url,
|
fileId: fileId,
|
||||||
imageType: '房源图片',
|
imageType: '房源图片',
|
||||||
sortOrder: index
|
sortOrder: index
|
||||||
}
|
}
|
||||||
}) : [],
|
}),
|
||||||
|
|
||||||
// 户型图信息
|
// 户型图信息 - 使用上传后获取的fileId
|
||||||
floorPlanImages: this.form.floorPlanImageUrls ? this.form.floorPlanImageUrls.map((url, index) => {
|
floorPlanImages: this.floorPlanFileIds.map((fileId, index) => {
|
||||||
return {
|
return {
|
||||||
imageUrl: url,
|
fileId: fileId,
|
||||||
imageType: '户型图',
|
imageType: '户型图',
|
||||||
sortOrder: index
|
sortOrder: index
|
||||||
}
|
}
|
||||||
}) : []
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const method = this.isEdit ? updateRoom : addRoom
|
const method = this.isEdit ? updateRoom : addRoom
|
||||||
method(formData).then(response => {
|
const response = await method(formData)
|
||||||
|
|
||||||
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>
|
@ -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)
|
||||||
|
|
||||||
|
// 使用axios发送请求,设置responseType为blob
|
||||||
|
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
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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'
|
||||||
|
Loading…
x
Reference in New Issue
Block a user