Files
mp-10572/src/views/house/maimai/components/info-edit.vue
赵忠林 08ef68bfe2 feat(house): 添加房屋信息表单中的单位字段支持
- 在房屋信息模型中添加了租金单位、月租金单位和面积单位字段
- 修改表单界面为面积、单价和总价字段添加单位输入框
- 更新字典选择器的代码配置从 premium 到 premium2
- 设置默认单位值为泰铢和泰铢/菜
- 修复表单提交时使用正确的租金字段映射
2026-01-09 18:44:46 +08:00

793 lines
24 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

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

<!-- User edit弹窗 -->
<template>
<ele-modal
width="75%"
:visible="visible"
:maskClosable="false"
:maxable="maxable"
:title="isUpdate ? '修改房源' : '添加房源'"
:body-style="{ paddingBottom: '28px' }"
@update:visible="updateVisible"
@ok="save"
>
<div style="background-color: #f3f3f3; padding: 8px">
<a-form
ref="formRef"
:model="form"
:rules="rules"
:label-col="{ md: { span: 7 }, sm: { span: 4 }, xs: { span: 24 } }"
:wrapper-col="{ md: { span: 17 }, sm: { span: 20 }, xs: { span: 24 } }"
>
<a-card title="基本信息" :bordered="false">
<template #extra
><a-button type="link" @click="handleEditStatus">{{
editStatus ? '预览' : '编辑'
}}</a-button></template
>
<a-row :gutter="16">
<a-col
v-bind="styleResponsive ? { md: 8, sm: 24, xs: 24 } : { span: 8 }"
>
<a-form-item label="房源ID" name="houseId" v-if="isUpdate" :autoLink="false">
<span>{{ form.houseId }}</span>
</a-form-item>
<a-form-item label="项目名称" name="houseTitle">
<a-input
v-if="editStatus"
allow-clear
placeholder="请输入标题"
v-model:value="form.houseTitle"
/>
<span v-else>{{ form.houseTitle }}</span>
</a-form-item>
<a-form-item label="业主电话" name="phone">
<a-input
v-if="editStatus"
allow-clear
placeholder="请输入业主电话"
v-model:value="form.phone"
/>
<span v-else>{{ form.phone }}</span>
</a-form-item>
<a-form-item label="面积(莱)" name="extent">
<a-space v-if="editStatus">
<a-input
v-if="editStatus"
allow-clear
placeholder="房屋面积"
v-model:value="form.extent"
/>
<a-input
placeholder="单位"
v-model:value="form.extentUnit"
/>
</a-space>
<span v-else>{{ form.extent }}</span>
</a-form-item>
<a-form-item label="单价(莱)" name="rent">
<a-space v-if="editStatus">
<a-input
allow-clear
placeholder="单价"
v-model:value="form.rent"
/>
<a-input
placeholder="单位"
v-model:value="form.rentUnit"
/>
</a-space>
<span v-else>{{ form.rent }}</span>
</a-form-item>
<a-form-item label="总价" name="monthlyRent">
<a-space v-if="editStatus">
<a-input
v-if="editStatus"
allow-clear
placeholder="总价"
v-model:value="form.monthlyRent"
/>
<a-input
placeholder="单位"
v-model:value="form.rentUnit"
/>
</a-space>
<span v-else>{{ form.monthlyRent }}</span>
</a-form-item>
<!-- <a-form-item label="房号" name="roomNumber">-->
<!-- <a-input-->
<!-- v-if="editStatus"-->
<!-- allow-clear-->
<!-- placeholder="请输入房号"-->
<!-- v-model:value="form.roomNumber"-->
<!-- />-->
<!-- <span v-else>{{ form.roomNumber }}</span>-->
<!-- </a-form-item>-->
</a-col>
<a-col
v-bind="styleResponsive ? { md: 8, sm: 24, xs: 24 } : { span: 8 }"
>
<!-- <a-form-item label="楼层" name="floor">-->
<!-- <DictSelect-->
<!-- v-if="editStatus"-->
<!-- dict-code="floor"-->
<!-- v-model:value="form.floor"-->
<!-- placeholder="请选择楼层"-->
<!-- />-->
<!-- <span v-else>{{ form.floor }}</span>-->
<!-- </a-form-item>-->
<!-- <a-form-item label="租赁方式" name="leaseMethod">-->
<!-- <DictSelect-->
<!-- v-if="editStatus"-->
<!-- dict-code="leaseMethod"-->
<!-- v-model:value="form.leaseMethod"-->
<!-- placeholder="请选择租赁方式"-->
<!-- />-->
<!-- <span v-else>{{ form.leaseMethod }}</span>-->
<!-- </a-form-item>-->
<!-- <a-form-item label="房源朝向" name="toward">-->
<!-- <DictSelect-->
<!-- v-if="editStatus"-->
<!-- dict-code="toward"-->
<!-- v-model:value="form.toward"-->
<!-- placeholder="请选择房源朝向"-->
<!-- />-->
<!-- <span v-else>{{ form.toward }}</span>-->
<!-- </a-form-item>-->
<a-form-item label="是否可溢价" name="premium">
<DictSelect
v-if="editStatus"
dict-code="premium2"
v-model:value="form.premium"
placeholder="是否可溢价"
/>
<span v-else>{{ form.premium }}</span>
</a-form-item>
<!-- <a-form-item label="租期" name="tenancy">-->
<!-- <DictSelect-->
<!-- v-if="editStatus"-->
<!-- dict-code="tenancy"-->
<!-- v-model:value="form.tenancy"-->
<!-- placeholder="租期"-->
<!-- />-->
<!-- <span v-else>{{ form.tenancy }}</span>-->
<!-- </a-form-item>-->
<a-form-item label="产权信息" name="property">
<a-input
v-if="editStatus"
allow-clear
placeholder="请输入产权信息"
v-model:value="form.property"
/>
<span v-else>{{ form.property }}</span>
</a-form-item>
<a-form-item label="房产经纪人" name="nickname">
<SelectUser
:placeholder="`请选择发布人`"
v-model:value="form.nickname"
v-if="editStatus"
@done="chooseUserId"
/>
<span v-else>{{ form.nickname }}</span>
</a-form-item>
<a-form-item label="备注">
<a-textarea
v-if="editStatus"
:rows="4"
:maxlength="200"
placeholder="请输入备注"
v-model:value="form.comments"
/>
<div v-else>{{ form.comments }}</div>
</a-form-item>
</a-col>
<a-col
v-bind="styleResponsive ? { md: 8, sm: 24, xs: 24 } : { span: 8 }"
>
<!-- <a-form-item label="所在地区" name="region">-->
<!-- <a-input-group compact v-if="editStatus">-->
<!-- <a-input-->
<!-- disabled-->
<!-- style="width: calc(100% - 32px)"-->
<!-- v-model:value="form.region"-->
<!-- placeholder="所属区域"-->
<!-- />-->
<!-- <a-tooltip title="选择位置">-->
<!-- <a-button @click="openMapPicker">-->
<!-- <template #icon><EnvironmentOutlined /></template>-->
<!-- </a-button>-->
<!-- </a-tooltip>-->
<!-- </a-input-group>-->
<!-- <span v-else>{{ form.region }}</span>-->
<!-- </a-form-item>-->
<a-form-item label="所在省份" name="province">
<a-input
v-if="editStatus"
allow-clear
placeholder="请输入所在省份"
v-model:value="form.province"
/>
<span v-else>{{ form.province }}</span>
</a-form-item>
<a-form-item label="所在城市" name="city">
<a-input
v-if="editStatus"
allow-clear
placeholder="请输入所在城市"
v-model:value="form.city"
/>
<span v-else>{{ form.city }}</span>
</a-form-item>
<a-form-item label="详细地址" name="address">
<a-input
v-if="editStatus"
allow-clear
placeholder="请输入详细地址"
v-model:value="form.address"
/>
<span v-else>{{ form.address }}</span>
</a-form-item>
<!-- <a-form-item label="经度" name="longitude">-->
<!-- <a-input-->
<!-- v-if="editStatus"-->
<!-- allow-clear-->
<!-- placeholder="请输入经度"-->
<!-- v-model:value="form.longitude"-->
<!-- />-->
<!-- <span v-else>{{ form.longitude }}</span>-->
<!-- </a-form-item>-->
<!-- <a-form-item label="纬度" name="latitude">-->
<!-- <a-input-->
<!-- v-if="editStatus"-->
<!-- allow-clear-->
<!-- placeholder="请输入纬度"-->
<!-- v-model:value="form.latitude"-->
<!-- />-->
<!-- <span v-else>{{ form.latitude }}</span>-->
<!-- </a-form-item>-->
</a-col>
</a-row>
</a-card>
<a-divider style="height: 8px" />
<a-card title="房源相册" :bordered="false">
<!-- <template #extra><a-button type="link" @click="handleEditStatus">上传</a-button></template>-->
<div class="content">
<ele-image-upload
v-model:value="files"
:limit="9"
:drag="true"
:item-style="{ width: '150px', height: '113px' }"
:upload-handler="uploadHandlerImages"
@upload="onUploadImages"
/>
<small class="ele-text-placeholder">
请上传应用截图(最多9张)建议宽度800*600像素小于20M/
</small>
</div>
</a-card>
<a-divider style="height: 8px" />
<a-card title="房源视频" :bordered="false">
<!-- <template #extra><a-button type="link" @click="handleEditStatus">上传</a-button></template>-->
<div class="content">
<UploadFile
accept="video/*"
v-model:value="form.videoUrl"
:maxCount="1"
:drag="true"
:item-style="{ width: '150px', height: '113px' }"
:showUploadList="true"
/>
<small class="ele-text-placeholder">
</small>
</div>
</a-card>
<a-divider style="height: 8px" />
<a-card title="房源标签" :bordered="false">
<DictSelectMultiple
v-if="editStatus"
dict-code="houseLabel"
v-model:value="houseLabelData"
placeholder="房源标签"
/>
<span v-else>
<a-space>
<a-tag v-for="tag in houseLabelData">{{ tag }}</a-tag>
</a-space>
</span>
</a-card>
<a-card title="办公室配套" :bordered="false">
<a-textarea
v-if="editStatus"
:rows="4"
:maxlength="200"
placeholder="请输入备注"
v-model:value="form.supporting"
/>
<div v-else>{{ form.supporting }}</div>
</a-card>
<a-divider style="height: 8px" />
<a-card title="详细介绍" :bordered="false">
<template #extra
><a-button type="link" @click="handleEditStatus">{{
editStatus ? '预览' : '编辑'
}}</a-button></template
>
<!-- 编辑器 -->
<tinymce-editor
v-if="editStatus"
v-model:value="content"
:init="config"
placeholder="图片直接粘贴自动上传"
@paste="onPaste"
/>
<!-- 预览 -->
<tinymce-editor
v-else
v-model:value="content"
:init="viewConfig"
/>
</a-card>
</a-form>
<!-- 地图位置选择弹窗 -->
<ele-map-picker
:need-city="true"
:dark-mode="darkMode"
v-model:visible="showMap"
:center="[108.374959, 22.767024]"
:search-type="1"
:zoom="12"
@done="onDone"
/>
</div>
</ele-modal>
</template>
<script lang="ts" setup>
import {ref, reactive, watch, computed} from 'vue';
import { message } from 'ant-design-vue';
import { useThemeStore } from '@/store/modules/theme';
import { storeToRefs } from 'pinia';
import { FormInstance } from 'ant-design-vue/es/form';
import { ItemType } from 'ele-admin-pro/es/ele-image-upload/types';
import { HouseInfo } from '@/api/house/houseInfo/model';
import {uploadFile, uploadOss} from '@/api/system/file';
import { CenterPoint } from 'ele-admin-pro/es/ele-map-picker/types';
import useFormData from '@/utils/use-form-data';
import { addHouseInfo, updateHouseInfo } from '@/api/house/houseInfo';
import { User } from '@/api/system/user/model';
import TinymceEditor from "@/components/TinymceEditor/index.vue";
// 是否是修改
const isUpdate = ref(false);
// 是否开启响应式布局
const themeStore = useThemeStore();
const { styleResponsive } = storeToRefs(themeStore);
const props = defineProps<{
// 弹窗是否打开
visible: boolean;
// 修改回显的数据
data?: HouseInfo | null;
// 类型
type?: number;
}>();
const emit = defineEmits<{
(e: 'done'): void;
(e: 'update:visible', visible: boolean): void;
}>();
// 提交状态
const loading = ref(false);
// 是否显示最大化切换按钮
const maxable = ref(true);
// 用户头像
const avatar = ref<ItemType[]>([]);
// 已上传数据
const files = ref<ItemType[]>([]);
// 已上传数据
const files2 = ref<ItemType[]>([]);
// 是否显示地图选择弹窗
const showMap = ref(false);
// 省市区
const city = ref<string[]>([]);
const { darkMode } = storeToRefs(themeStore);
const editStatus = ref<boolean>(false);
const formRef = ref<FormInstance | null>(null);
const uploadImgContent = ref<string>('');
const content = ref('');
// 表单数据
const { form, resetFields, assignFields } = useFormData<HouseInfo>({
houseId: undefined,
userId: undefined,
houseTitle: undefined,
cityByHouse: undefined,
houseType: undefined,
leaseMethod: undefined,
rent: undefined,
rentUnit: '泰铢',
monthlyRent: undefined,
monthlyRentUnit: undefined,
extent: undefined,
extentUnit: '泰铢/菜',
floor: undefined,
roomNumber: undefined,
realName: undefined,
nickname: undefined,
houseLabel: undefined,
address: undefined,
longitude: undefined,
latitude: undefined,
phone: undefined,
password: undefined,
toward: undefined,
files: undefined,
videoUrl: undefined,
content: undefined,
expirationTime: undefined,
province: undefined,
city: undefined,
region: undefined,
area: undefined,
status: undefined,
comments: '',
sortNumber: undefined,
deleted: undefined,
tenantId: undefined,
createTime: undefined,
updateTime: undefined,
isEdit: undefined,
commission: undefined,
premium: undefined,
propertyFees: undefined,
tenancy: undefined,
supporting: undefined,
property: undefined
});
const monthlyRent = computed<number>(() => {
const {extent, rent} = form
if(extent && rent) {
return extent * rent
}else {
return 0
}
})
/* 更新visible */
const updateVisible = (value: boolean) => {
emit('update:visible', value);
};
// 表单验证规则
const rules = reactive({
houseTitle: [
{
required: true,
type: 'string',
message: '请输入房源标题',
trigger: 'blur'
}
],
// phone: [
// {
// required: true,
// type: 'string',
// message: '请输入合法手机号码',
// trigger: 'blur'
// }
// ],
nickname: [
{
required: true,
type: 'string',
message: '请输入昵称',
trigger: 'blur'
}
],
roles: [
{
required: true,
message: '请选择角色',
type: 'array',
trigger: 'blur'
}
]
// securityStatus: [
// {
// required: true,
// type: 'string',
// message: '请选择安全状态',
// trigger: 'blur'
// }
// ],
// companyName: [
// {
// required: true,
// type: 'string',
// message: '请选择租赁单位',
// trigger: 'blur'
// }
// ],
// customerName: [
// {
// required: true,
// type: 'string',
// message: '请选择承租单位',
// trigger: 'blur'
// }
// ],
// projectRegion: [
// {
// required: true,
// type: 'string',
// message: '请输入房源地址',
// trigger: 'blur'
// }
// ]
});
let houseLabelData = ref<any[]>([])
const initHouseLabel = ()=> {
if(form.houseLabel) {
houseLabelData.value = JSON.parse(form.houseLabel)
}
}
const handleEditStatus = () => {
editStatus.value = !editStatus.value;
};
const chooseUserId = (data: User) => {
console.log(data.nickname,'data......nickname')
form.nickname = data.nickname;
form.userId = data.userId;
};
/* 地图选择后回调 */
const onDone = (location: CenterPoint) => {
console.log(location);
city.value = [
`${location.city?.province}`,
`${location.city?.city}`,
`${location.city?.district}`
];
form.province = `${location.city?.province}`;
form.city = `${location.city?.city}`;
form.region = `${location.city?.district}`;
form.address = `${location.address}`;
form.latitude = `${location.lat}`;
form.longitude = `${location.lng}`;
showMap.value = false;
};
/* 打开位置选择 */
const openMapPicker = () => {
showMap.value = true;
};
/* 上传事件 */
const uploadHandlerImages = (file: File) => {
const item: ItemType = {
file,
uid: (file as any).uid,
name: file.name
};
if (!file.type.startsWith('image')) {
message.error('只能选择图片');
return;
}
if (file.size / 1024 / 1024 > 20) {
message.error('大小不能超过 2MB');
return;
}
onUploadImages(item);
};
// 上传文件
const onUploadImages = (item) => {
const { file } = item;
uploadOss(file)
.then((data) => {
files.value.push({
uid: data.id,
url: data.url,
status: 'done'
});
})
.catch((e) => {
message.error(e.message);
});
};
const onChange = (e) => {
uploadImgContent.value = e.originalEvent.value.content;
}
const editorRef = ref<InstanceType<typeof TinymceEditor> | null>(null);
const config = ref({
height: 500,
images_upload_handler: (blobInfo, success, error) => {
const file = blobInfo.blob();
// 使用 axios 上传,实际开发这段建议写在 api 中再调用 api
const formData = new FormData();
formData.append('file', file, file.name);
uploadFile(<File>file)
.then((result) => {
if (result.length) {
console.log(file.size / 1024);
if (file.size / 1024 / 1024 > 2) {
error('图片大小不能超过 2MB');
}
success(result.url);
} else {
error('上传失败');
}
})
.catch((e) => {
message.error(e.message);
});
},
});
const viewConfig = ref({
toolbar: false,
menubar: false,
height: 620,
darkTheme: true,
inline: true
// quickbars_insert_toolbar: false
});
/* 粘贴图片上传服务器并插入编辑器 */
const onPaste = (e) => {
const items = (e.clipboardData || e.originalEvent.clipboardData).items;
let hasFile = false;
for (let i = 0; i < items.length; i++) {
if (items[i].type.indexOf('image') !== -1) {
let file = items[i].getAsFile();
const item: ItemType = {
file,
uid: (file as any).lastModified,
name: file.name
};
uploadFile(<File>item.file)
.then((result) => {
const addPath = `<img class="content-img" src="${result.url}">\n\r`;
content.value = content.value.replace(uploadImgContent.value,addPath)
})
.catch((e) => {
message.error(e.message);
});
hasFile = true;
}
}
if (hasFile) {
e.preventDefault();
}
}
/* 保存编辑 */
const save = () => {
if (!formRef.value) {
return;
}
formRef.value
.validate()
.then(() => {
loading.value = true;
const formData = {
...form,
content: content.value,
files: JSON.stringify(files.value),
houseLabel: JSON.stringify(houseLabelData.value),
monthlyRent: form.rent,
type: props.type
};
const saveOrUpdate = isUpdate.value ? updateHouseInfo : addHouseInfo;
saveOrUpdate(formData)
.then((msg) => {
loading.value = false;
message.success(msg);
updateVisible(false);
emit('done');
})
.catch((e) => {
loading.value = false;
message.error(e.message);
});
})
.catch(() => {});
};
const reload = () => {
loading.value = true;
};
reload();
watch(
() => props.visible,
(visible) => {
if (visible) {
content.value = '';
if (props.data) {
assignFields({
...props.data
});
files.value = [];
avatar.value = [];
city.value = [
`${props.data.province}`,
`${props.data.city}`,
`${props.data.region}`
];
if (props.data.content) {
content.value = props.data.content;
}
if (props.data.files) {
const arr = JSON.parse(props.data.files);
// 检查 arr 是否为数组
if (Array.isArray(arr)) {
arr.forEach((d, i) => {
files.value.push({
uid: d.uid,
url: d.url,
status: 'done'
});
});
} else {
console.error('props.data.files 解析后不是一个数组:', arr);
}
}
reload();
isUpdate.value = true;
} else {
avatar.value = [];
files.value = []
files2.value = []
houseLabelData.value = []
editStatus.value = true;
isUpdate.value = false;
}
initHouseLabel()
} else {
files.value = []
files2.value = []
resetFields();
}
}
);
</script>
<style lang="less">
.tab-pane {
min-height: 300px;
}
.ml-10 {
margin-left: 5px;
}
.upload-text {
margin-right: 70px;
}
.upload-image {
margin-bottom: 30px;
display: flex;
justify-content: center;
text-align: center;
}
</style>