feat: 空间管理 -> 房源列表开发

This commit is contained in:
Zzz1zz
2026-01-12 00:19:24 +08:00
parent 39ef0b7785
commit a53cd0e8b8
15 changed files with 1637 additions and 28 deletions
+45 -1
View File
@@ -5,7 +5,11 @@ import {
CreateProjectParam,
ProjectDetailRes,
} from '@/types/business';
import { ProjectPaginationParams, CommonCityRes } from '@/types/comm';
import {
ProjectPaginationParams,
CommonCityRes,
PaginationParams,
} from '@/types/comm';
import { HttpResponse } from './interceptor';
// import type { RouteRecordNormalized } from 'vue-router';
// import { UserState } from '@/store/modules/user/types';
@@ -46,3 +50,43 @@ export function updateProject(
export function getProjectCityList(): Promise<CommonCityRes> {
return axios.get(`project/getCityList`);
}
// ------------- 空间管理 -------------
// 创建房源
export function createRoom(params: any): Promise<HttpResponse> {
return axios.post('rooms/createRoom', params);
}
// 房源列表(分页)
export function getRoomList(params: PaginationParams): Promise<HttpResponse> {
return axios.post('rooms/getRoomList', params);
}
// 更新房源
export function updateRoom(params: any): Promise<HttpResponse> {
return axios.post('rooms/updateRoom', params);
}
// 删除房源
export function delRoom(id: number): Promise<HttpResponse> {
return axios.get(`rooms/delRoom?id=${id}`);
}
// 房源详情
export function getRoom(id: number): Promise<HttpResponse> {
return axios.get(`rooms/getRoom?id=${id}`);
}
// 修改房源状态 status:1招商 2保留 3下架 4在租
export function changeRoomStatus(params: any): Promise<HttpResponse> {
return axios.post('rooms/changeRoomStatus', params);
}
// 房源获取头部楼栋tags
export function getTopBuildingTagsByProjectId(
id: number
): Promise<HttpResponse> {
return axios.get(`roomOver/getTopBuildingTags?project_id=${id}`);
}
// 房源剖视-数据统计
export function getStatisticsData(params: any): Promise<HttpResponse> {
return axios.post('roomOver/getStatisticsData', params);
}
// 房源剖视-房源列表
export function getRoomOverList(params: any): Promise<HttpResponse> {
return axios.post('roomOver/getRoomList', params);
}
+27
View File
@@ -42,3 +42,30 @@ export const VALIDATION_RULES = {
ID_CARD:
/^[1-9]\d{5}(18|19|20)\d{2}((0[1-9])|(1[0-2]))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/,
} as const;
// 房源状态
//状态:1招商 2保留 3下架 4在租
export const HOUSE_STATUS = [
{ id: 1, text: '招商' },
{ id: 2, text: '保留' },
{ id: 3, text: '下架' },
{ id: 4, text: '在租' },
];
// 价格单位选项
export const PRICE_UNITS = [
{ value: 1, text: '元/㎡*月' },
{ value: 2, text: '元/㎡*天' },
{ value: 3, text: '元/天' },
{ value: 4, text: '元/月' },
{ value: 5, text: '元/年' },
] as const;
// 价格单位映射表
export const PRICE_UNIT_MAP = new Map([
[1, '元/㎡*月'],
[2, '元/㎡*天'],
[3, '元/天'],
[4, '元/月'],
[5, '元/年'],
]);
+4
View File
@@ -41,6 +41,7 @@ export default {
'content': '内容',
'table.filter': '筛选',
'table.export': '导出',
'table.import': '导入',
'watch-detail': '查看详情',
'menu.business.project': '项目管理',
'menu.business.space': '空间管理',
@@ -54,6 +55,7 @@ export default {
'menu.business.tenant-list': '租客列表',
'menu.business.tenant.contacts': '租客联系人',
'add-project': '新建项目',
'edit-project': '编辑项目',
'menu.setting.user': '用户设置',
'menu.setting.user-list': '用户列表',
'menu.setting.role': '角色权限',
@@ -123,6 +125,8 @@ export default {
'setting.contract': '合同设置',
'setting.finance': '财务设置',
'setting.data-dictionary': '数据字典',
'add-house': '新建房源',
'edit-house': '编辑房源',
...localeSettings,
...localeMessageBox,
+2 -2
View File
@@ -55,7 +55,7 @@ const BUSINESS: AppRouteRecordRaw = {
component: () =>
import(
/* webpackChunkName: "business-space" */
'@/views/business/space/house-view.vue'
'@/views/business/space/house-view/index.vue'
),
meta: {
locale: 'menu.business.house-view',
@@ -71,7 +71,7 @@ const BUSINESS: AppRouteRecordRaw = {
component: () =>
import(
/* webpackChunkName: "business-space" */
'@/views/business/space/house-list.vue'
'@/views/business/space/house-list/index.vue'
),
meta: {
locale: 'menu.business.house-list',
+17
View File
@@ -114,3 +114,20 @@ export interface ProjectInfo extends BaseEntity {
export type ProjectDetailRes = HttpResponse<{
project: ProjectItem;
}>;
// 创建房源参数
export interface CreateHouseParam {
id?: number; // 编辑时需要
projectId: number; // 项目id
buildingId: number; // 楼栋id
floorId: number; // 楼层id
name: string; // 房源名称
rentArea: number | string; // 计租面积
price: number | string; // 价格
priceType: number; // 价格类型
area: number | string; // 面积
useArea: number | string; // 使用面积
status: number; // 状态
objectKeys: string[]; // 图片数组
tags: number[]; // 标签数组
}
@@ -2,7 +2,7 @@
<a-drawer
:width="670"
:visible="visible"
:title="isEdit ? '编辑条款设置' : '新增条款设置'"
:title="isEdit ? $t('edit-project') : $t('add-project')"
unmount-on-close
@ok="handleOk"
@cancel="handleCancel"
@@ -374,7 +374,7 @@
emit('update:visible', false);
};
const handleDragEnd = () => {
console.log('handleDragEnd');
// 拖拽结束处理
};
onMounted(() => {
getProvinceListData();
+8 -4
View File
@@ -21,7 +21,7 @@
</a-button>
<a-button>
<template #icon>
<icon-download />
<icon-export />
</template>
{{ t('table.export') }}
</a-button>
@@ -184,7 +184,11 @@
!edit
)
return;
edit ? (isLoadingEdit.value = true) : (isLoadingDetail.value = true);
if (edit) {
isLoadingEdit.value = true;
} else {
isLoadingDetail.value = true;
}
const { code, data } = await getProjectDetail(id);
if (code === 200 && data) {
currentProjectInfo.value = data.project;
@@ -194,7 +198,7 @@
};
const handleWatchHouse = async (id: number) => {
try {
console.log(id, 'is id');
// TODO: 实现查看房源功能
} catch (error) {
// 处理错误
}
@@ -207,7 +211,7 @@
getProjectListData();
};
const handleDel = (id: number) => {
console.log(id, 'iiii');
// TODO: 实现删除功能
};
// 分页事件处理
const onPageChange = (page: number) => {
@@ -0,0 +1,409 @@
<template>
<a-drawer
:width="670"
:visible="visible"
:title="isEdit ? $t('edit-house') : $t('add-house')"
unmount-on-close
@ok="handleOk"
@cancel="handleCancel"
>
<a-form
ref="formRef"
:model="form"
:rules="rules"
auto-label-width
layout="vertical"
>
<a-row :gutter="24">
<a-col :span="12">
<!-- 房源名称 -->
<a-form-item label="房源名称" field="name" required>
<a-input
v-model="form.name"
:placeholder="$t('please.enter', { placeholder: '房源名称' })"
/>
</a-form-item>
</a-col>
<a-col :span="12">
<!-- 房源状态 -->
<a-form-item label="房源状态" field="status" required>
<a-select v-model="form.status" placeholder="请选择房源状态">
<a-option
v-for="item in HOUSE_STATUS"
:key="item.id"
:value="item.id"
>
{{ item.text }}
</a-option>
</a-select>
</a-form-item>
</a-col>
<a-col :span="12">
<!-- 所属项目 -->
<a-form-item label="所属项目" field="projectId" required>
<a-select
v-model="form.projectId"
placeholder="请选择所属项目"
@change="handleProjectChange"
>
<a-option
v-for="item in allProjetList"
:key="item.id"
:value="item.id"
>
{{ item.name }}
</a-option>
</a-select>
</a-form-item>
</a-col>
<a-col :span="12">
<!-- 所属楼栋/楼层 -->
<a-form-item label="所属楼栋/楼层" field="buildingId" required>
<a-row :gutter="8">
<a-col :span="12">
<a-select
v-model="form.buildingId"
placeholder="请选择所属楼栋"
:disabled="!form.projectId"
@change="handleBuildingChange"
>
<a-option
v-for="item in allBuildingList"
:key="item.id"
:value="item.id"
>
{{ item.name }}
</a-option>
</a-select>
</a-col>
<a-col :span="12">
<a-select
v-model="form.floorId"
placeholder="请选择所属楼层"
:disabled="!form.buildingId"
>
<a-option
v-for="item in allFloorList"
:key="item.id"
:value="item.id"
>
{{ item.name }}
</a-option>
</a-select>
</a-col>
</a-row>
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="计租面积" field="rentArea" required>
<a-input-number
v-model="form.rentArea"
append="㎡"
:placeholder="$t('please.enter', { placeholder: '房源计租面积' })"
/>
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="招商价格" field="price">
<a-input-number
v-model="form.price"
:placeholder="
$t('please.enter', { placeholder: '房源计租招商价格' })
"
>
<template #append>
<a-select v-model="form.priceType" style="width: 100px">
<a-option
v-for="item in PRICE_UNITS"
:key="item.value"
:value="item.value"
>{{ item.text }}</a-option
>
</a-select>
</template>
</a-input-number>
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="建筑面积(可选)" field="area">
<a-input
v-model="form.area"
append="㎡"
:placeholder="$t('please.enter', { placeholder: '建筑面积' })"
/>
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="使用面积(可选)" field="useArea">
<a-input
v-model="form.useArea"
append="㎡"
:placeholder="$t('please.enter', { placeholder: '使用面积' })"
/>
</a-form-item>
</a-col>
</a-row>
<a-form-item label="标签(可选)" field="tags">
<a-select v-model="form.tags" multiple placeholder="请选择标签">
<a-option
v-for="item in roomTagsList"
:key="item.id"
:value="item.id"
>
{{ item.name }}
</a-option>
</a-select>
</a-form-item>
<a-form-item label="房源图片(可选)" field="objectKeys">
<ImageUpload
v-model:object-keys="form.objectKeys"
v-model:signed-put-u-r-l="form.signedPutURL"
:upload-type="2"
:max-count="5"
list-type="picture-card"
/>
</a-form-item>
</a-form>
</a-drawer>
</template>
<script lang="ts" setup>
import { ref, watch, reactive, onMounted } from 'vue';
import type { FormInstance } from '@arco-design/web-vue';
import { Message } from '@arco-design/web-vue';
import { CreateHouseParam } from '@/types/business';
import ImageUpload from '@/components/common/ImageUpload.vue';
import { HOUSE_STATUS, PRICE_UNITS } from '@/constants';
import {
getAllProjectIdNameList,
getAllBuildingIdNameList,
getAllFloorIdNameList,
} from '@/api/common';
import { getRoomTagList } from '@/api/setting';
import { createRoom, updateRoom } from '@/api/business';
const props = defineProps<{
visible: boolean;
isEdit: boolean;
record?: any | null;
}>();
const emit = defineEmits<{
(e: 'update:visible', val: boolean): void;
(e: 'submitted'): void;
}>();
const isLoading = ref<boolean>(false);
// 表单数据
const formRef = ref<FormInstance>();
const allProjetList = ref<any>([]);
const allBuildingList = ref<any>([]);
const allFloorList = ref<any>([]);
const roomTagsList = ref<any[]>([]);
const defaultForm: CreateHouseParam = {
projectId: null as any,
buildingId: null as any,
floorId: null as any,
name: '', // 房源名称
rentArea: '', // 计租面积
price: '', // 价格
priceType: 1, // 价格类型
area: '', // 面积
useArea: '', // 使用面积
status: 1, // 状态
objectKeys: [], // 图片数组
tags: [], // 标签数组
};
// 扩展表单类型,包含 ImageUpload 需要的 signedPutURL(不提交到后端)
const form = reactive<CreateHouseParam & { signedPutURL: string[] }>({
...defaultForm,
signedPutURL: [],
});
// 校验规则
const rules = reactive({
name: [{ required: true, message: '请输入房源名称' }],
projectId: [{ required: true, message: '请选择项目' }],
buildingId: [{ required: true, message: '请选择楼栋' }],
floorId: [{ required: true, message: '请选择楼层' }],
status: [{ required: true, message: '请选择房源状态' }],
});
watch(
() => props.visible,
async (val) => {
if (val) {
if (props.isEdit && props.record) {
const record = props.record;
// 回显数据 - 使用 Object.assign 批量赋值,处理特殊字段转换
Object.assign(form, {
id: record.id,
projectId: record.projectId,
buildingId: record.buildingId,
floorId: record.floorId,
name: record.name || '',
rentArea: record.rentArea || 0,
price: record.price || 0,
priceType:
PRICE_UNITS.find((i) => i.text === record.priceType)?.value ?? 1,
area: record.area || 0,
useArea: record.useArea || 0,
status: record.status || 1,
objectKeys: record.objectKeys || record.imageUrls || [],
tags: [
...new Set(record?.tags?.map((i) => i.id).filter(Boolean) || []),
],
signedPutURL: record.signedPutURL || [],
});
// 编辑模式下,需要加载对应的楼栋和楼层列表
if (record.projectId) {
await getAllBuildingListData(record.projectId);
if (record.buildingId) {
await getAllFloorListData(record.buildingId);
}
}
} else {
Object.assign(form, { ...defaultForm, signedPutURL: [] });
// 清空楼栋和楼层列表
allBuildingList.value = [];
allFloorList.value = [];
}
}
},
{ immediate: true }
);
const handleOk = async () => {
if (isLoading.value) return;
try {
isLoading.value = true;
const valid = await formRef.value.validate();
if (valid) return;
// 手动验证楼层字段(因为楼栋和楼层在同一个form-item中)
if (!form.floorId) {
formRef.value.setFields({
floorId: { status: 'error', message: '请选择楼层' },
});
return;
}
// 构建提交参数,排除 signedPutURL(仅用于前端显示)
// 将面积、价格相关字段的空值转为0
const params: CreateHouseParam = {
projectId: form.projectId,
buildingId: form.buildingId,
floorId: form.floorId,
name: form.name,
rentArea: form.rentArea || 0,
price: form.price || 0,
priceType: form.priceType,
area: form.area || 0,
useArea: form.useArea || 0,
status: form.status,
objectKeys: form.objectKeys,
tags: form.tags,
};
// 如果是编辑模式,需要包含 id
if (props.isEdit && form.id) {
params.id = form.id;
}
// 调用创建/更新房源的 API
const res = props.isEdit
? await updateRoom(params)
: await createRoom(params);
if (res?.code === 200) {
Message.success(
res?.message || (props.isEdit ? '更新成功' : '创建成功')
);
// 清空表单数据
Object.assign(form, { ...defaultForm, signedPutURL: [] });
allBuildingList.value = [];
allFloorList.value = [];
formRef.value?.clearValidate();
emit('submitted');
emit('update:visible', false);
}
} catch (err: any) {
Message.error(err?.message || (props.isEdit ? '更新失败' : '创建失败'));
} finally {
isLoading.value = false;
}
};
const handleCancel = () => {
emit('update:visible', false);
};
async function getAllProjectListData() {
try {
const { code, data } = await getAllProjectIdNameList();
if (code === 200) {
allProjetList.value = data?.projectsList || [];
}
} catch (error) {
// console.error('获取项目列表失败:', error);
}
}
async function getAllBuildingListData(projectId: number) {
try {
const { code, data } = await getAllBuildingIdNameList(projectId);
if (code === 200) {
allBuildingList.value = data?.buildingList || [];
}
} catch (error) {
// console.error('获取楼栋列表失败:', error);
allBuildingList.value = [];
}
}
async function getAllFloorListData(buildingId: number) {
try {
const { code, data } = await getAllFloorIdNameList(buildingId);
if (code === 200) {
allFloorList.value = data?.floorList || [];
}
} catch (error) {
// console.error('获取楼层列表失败:', error);
allFloorList.value = [];
}
}
// 项目选择变化处理
const handleProjectChange = async (projectId: number) => {
// 清空楼栋和楼层选择
form.buildingId = null as any;
form.floorId = null as any;
allBuildingList.value = [];
allFloorList.value = [];
// 如果选择了项目,加载楼栋列表
if (projectId) {
await getAllBuildingListData(projectId);
}
};
// 楼栋选择变化处理
const handleBuildingChange = async (buildingId: number) => {
// 清空楼层选择
form.floorId = null as any;
allFloorList.value = [];
// 如果选择了楼栋,加载楼层列表
if (buildingId) {
await getAllFloorListData(buildingId);
}
};
const getAllRoomTagListData = async () => {
try {
const { code, data } = await getRoomTagList();
if (code === 200 && data) {
roomTagsList.value = data?.roomTagsList;
}
} catch (error) {
// console.error(error);
}
};
onMounted(() => {
// 初始化数据加载
getAllProjectListData();
getAllRoomTagListData();
});
</script>
<style lang="less" scoped>
:deep(label.arco-form-item-label) {
width: 100% !important;
}
</style>
@@ -0,0 +1,155 @@
<template>
<a-drawer
:width="520"
:visible="visible"
title="房源详情"
:footer="false"
unmount-on-close
@cancel="handleCancel"
>
<div class="flex items-center justify-between">
<div>
<p>
{{
`${detailInfo?.name} -
${detailInfo?.projectName}${detailInfo?.buildingName}${detailInfo?.floorName}`
}}
</p>
<div class="mt-[10px]">
<a-tag :color="getTagsColor(detailInfo?.status)">{{
detailInfo?.statusStr
}}</a-tag>
<a-tag class="mx-[10px]">{{ detailInfo?.rentArea }}㎡</a-tag>
<a-tag>{{ `${detailInfo?.price}${detailInfo?.priceType}` }}</a-tag>
</div>
</div>
<!-- <a-button>
<template #icon>
<icon-edit />
</template>
{{ $t('edit') }}
</a-button> -->
</div>
<div class="mt-[20px]">
<a-tabs v-model:active-key="currentTab" class="">
<a-tab-pane key="1">
<template #title> <icon-calendar /> 基本信息 </template>
<a-alert type="warning">房源空置 xx 天</a-alert>
<a-descriptions bordered class="mt-[10px]" :column="2">
<a-descriptions-item label="房源编码" :span="1">
{{ detailInfo?.code || '-' }}
</a-descriptions-item>
<a-descriptions-item label="房源名称" :span="1">
{{ detailInfo?.name || '-' }}
</a-descriptions-item>
<a-descriptions-item label="所属项目" :span="1">
{{ detailInfo?.projectName || '-' }}
</a-descriptions-item>
<a-descriptions-item label="所属楼栋/楼层" :span="1">
<span>{{ detailInfo?.buildingName || '-' }}</span>
<span>*</span>
<span>{{ detailInfo?.floorName || '-' }}</span>
</a-descriptions-item>
<a-descriptions-item label="计租面积" :span="1">
{{
Number(detailInfo?.rentArea) > 0
? `${detailInfo?.rentArea}㎡`
: '-'
}}
</a-descriptions-item>
<a-descriptions-item label="招商价格" :span="1">
{{
Number(detailInfo?.price) > 0
? `${detailInfo?.rentArea}${detailInfo?.priceType}`
: '-'
}}
</a-descriptions-item>
<a-descriptions-item label="建筑面积" :span="1">
{{ Number(detailInfo?.area) > 0 ? `${detailInfo?.area}㎡` : '-' }}
</a-descriptions-item>
<a-descriptions-item label="使用面积" :span="1">
{{
Number(detailInfo?.useArea) > 0
? `${detailInfo?.useArea}㎡`
: '-'
}}
</a-descriptions-item>
<a-descriptions-item label="标签" :span="2">
<template v-if="detailInfo?.tags?.length">
<a-tag
v-for="item in detailInfo?.tags"
:key="item.id || item.name"
class="mr-[5px] last:mr-0"
>
{{ item.name }}</a-tag
>
</template>
<template v-else> - </template>
</a-descriptions-item>
<a-descriptions-item label="房源图片" :span="2">
<template v-if="detailInfo?.signedPutURL?.length">
<img
v-for="(item, index) in detailInfo?.signedPutURL"
:key="index"
:src="item"
alt=""
class="mt-[5px] last:mr-0 w-[120px] h-[80px] object-contain"
/>
</template>
<template v-else> - </template>
</a-descriptions-item>
</a-descriptions>
</a-tab-pane>
<a-tab-pane key="2">
<template #title> <icon-clock-circle /> 合同信息 </template>
合同信息与租客信息 待开发
</a-tab-pane>
<a-tab-pane key="3">
<template #title> <icon-user /> 租客信息 </template>
合同信息与租客信息 待开发
</a-tab-pane>
</a-tabs>
</div>
</a-drawer>
</template>
<script lang="ts" setup>
import { watch, onMounted, ref } from 'vue';
// import { Message } from '@arco-design/web-vue';
const props = defineProps<{
visible: boolean;
projectId: number | null;
detailInfo: any;
}>();
const emit = defineEmits<{
(e: 'update:visible', val: boolean): void;
}>();
const currentTab = ref<string>('1');
watch(
() => props.visible,
async (val) => {
// 抽屉显示状态变化
},
{ immediate: true }
);
const handleCancel = () => {
emit('update:visible', false);
};
const getTagsColor = (status) => {
const colorMap = {
1: 'orangered',
2: 'orange',
3: 'red',
4: 'green',
};
return colorMap[status];
};
onMounted(() => {});
</script>
<style lang="less" scoped>
:deep(label.arco-form-item-label) {
width: 100% !important;
}
</style>
@@ -0,0 +1,624 @@
<template>
<a-drawer
:width="420"
:visible="visible"
title="筛选"
unmount-on-close
@ok="handleOk"
@cancel="handleCancel"
>
<a-form :model="filterForm" layout="vertical">
<!-- 项目(仅显示) -->
<a-form-item label="项目">
<span class="w-full bg-[#f2f3f5] h-[30px] px-[5px] leading-[30px]">{{
projectName
}}</span>
<!-- <a-input :value="projectName" disabled /> -->
</a-form-item>
<!-- 楼栋(多选) -->
<a-form-item label="楼栋">
<a-select
v-model="filterForm.buildingIds"
multiple
placeholder="请选择楼栋"
:max-tag-count="3"
>
<a-option :value="ALL_BUILDING_ID">所有楼栋</a-option>
<a-option
v-for="item in buildingList"
:key="item.id"
:value="item.id"
>
{{ item.name }}
</a-option>
</a-select>
</a-form-item>
<!-- 楼层(多选) -->
<a-form-item label="楼层">
<a-select
v-model="filterForm.floorIds"
multiple
placeholder="请选择楼层"
:max-tag-count="3"
>
<a-option :value="ALL_FLOOR_ID">所有楼层</a-option>
<a-option v-for="item in floorList" :key="item.id" :value="item.id">
{{ item.name }}
</a-option>
</a-select>
</a-form-item>
<!-- 房源状态(多选) -->
<a-form-item label="房源状态">
<a-select
v-model="filterForm.statusIds"
multiple
placeholder="请选择房源状态"
:max-tag-count="3"
>
<a-option
v-for="item in HOUSE_STATUS"
:key="item.id"
:value="item.id"
>
{{ item.text }}
</a-option>
</a-select>
</a-form-item>
<!-- 计租面积(滑块) -->
<a-form-item label="计租面积(㎡)">
<a-slider
v-model="filterForm.rentArea"
range
:min="rentAreaMin"
:max="rentAreaMax"
:marks="rentAreaMarks"
/>
</a-form-item>
<!-- 标签(多选) -->
<a-form-item label="标签">
<a-select
v-model="filterForm.tagIds"
multiple
placeholder="请选择标签"
:max-tag-count="3"
>
<a-option v-for="item in tagList" :key="item.id" :value="item.id">
{{ item.name }}
</a-option>
</a-select>
</a-form-item>
</a-form>
</a-drawer>
</template>
<script lang="ts" setup>
import { ref, watch, computed, onMounted, nextTick } from 'vue';
import { HOUSE_STATUS } from '@/constants';
import { getRoomTagList } from '@/api/setting';
// 特殊值用于表示"所有"选项(使用负数避免与真实ID冲突)
const ALL_BUILDING_ID = -1;
const ALL_FLOOR_ID = -2;
const props = defineProps<{
visible: boolean;
roomList?: any[]; // 房源列表数据,用于提取筛选选项
projectName?: string; // 项目名称(仅显示)
}>();
const emit = defineEmits<{
(e: 'update:visible', val: boolean): void;
(
e: 'confirm',
payload: {
buildingIds: number[] | null;
floorIds: number[] | null;
statusIds: number[] | null;
rentArea: number[];
tagIds: number[] | null;
filteredList: any[]; // 筛选后的列表数据
}
): void;
}>();
// 保存的筛选状态
const savedFilterState = ref<{
buildingIds: number[];
floorIds: number[];
statusIds: number[];
rentArea: number[];
tagIds: number[];
} | null>(null);
// 筛选表单数据
const filterForm = ref({
buildingIds: [] as number[],
floorIds: [] as number[],
statusIds: [] as number[],
rentArea: [0, 0] as number[],
tagIds: [] as number[],
});
// 楼栋列表(从房源列表中提取唯一值)
const buildingList = computed(() => {
if (!props.roomList || props.roomList.length === 0) return [];
const buildingMap = new Map();
props.roomList.forEach((room) => {
if (room.buildingId && room.buildingName) {
if (!buildingMap.has(room.buildingId)) {
buildingMap.set(room.buildingId, {
id: room.buildingId,
name: room.buildingName,
});
}
}
});
return Array.from(buildingMap.values());
});
// 楼层列表(从房源列表中提取唯一值)
const floorList = computed(() => {
if (!props.roomList || props.roomList.length === 0) return [];
const floorMap = new Map();
props.roomList.forEach((room) => {
if (room.floorId && room.floorName) {
if (!floorMap.has(room.floorId)) {
floorMap.set(room.floorId, {
id: room.floorId,
name: room.floorName,
});
}
}
});
return Array.from(floorMap.values());
});
// 计租面积范围
const rentAreaMin = computed(() => {
if (!props.roomList || props.roomList.length === 0) return 0;
const areas = props.roomList
.map((room) => Number(room.rentArea))
.filter((area) => !isNaN(area) && area > 0);
return areas.length > 0 ? Math.min(...areas) : 0;
});
const rentAreaMax = computed(() => {
if (!props.roomList || props.roomList.length === 0) return 100;
const areas = props.roomList
.map((room) => Number(room.rentArea))
.filter((area) => !isNaN(area) && area > 0);
return areas.length > 0 ? Math.max(...areas) : 100;
});
const rentAreaMarks = computed(() => {
return {
[rentAreaMin.value]: `${rentAreaMin.value}㎡`,
[rentAreaMax.value]: `${rentAreaMax.value}㎡`,
};
});
// 标签列表
const tagList = ref<any[]>([]);
// 获取标签列表
const getTagListData = async () => {
try {
const { code, data } = await getRoomTagList();
if (code === 200 && data) {
tagList.value = data?.roomTagsList || [];
}
} catch (error) {
// console.error(error);
}
};
// 初始化筛选表单
const initFilterForm = () => {
const min = rentAreaMin.value || 0;
const max = rentAreaMax.value || 100;
// 如果有保存的状态,恢复它(但要过滤掉不存在的ID)
if (savedFilterState.value) {
const allBuildingIds = buildingList.value.map((b) => b.id);
const allFloorIds = floorList.value.map((f) => f.id);
// 如果保存的状态中包含"所有"选项,直接使用
let buildingIds = savedFilterState.value.buildingIds;
let floorIds = savedFilterState.value.floorIds;
// 如果保存的状态中不包含"所有"选项,检查是否所有ID都被选中
if (!buildingIds.includes(ALL_BUILDING_ID)) {
const validBuildingIds = buildingIds.filter((id) =>
allBuildingIds.includes(id)
);
// 如果选中的数量等于总数,则使用"所有"选项
if (
validBuildingIds.length === allBuildingIds.length &&
allBuildingIds.length > 0
) {
buildingIds = [ALL_BUILDING_ID];
} else if (validBuildingIds.length > 0) {
buildingIds = validBuildingIds;
} else {
buildingIds = [ALL_BUILDING_ID];
}
}
if (!floorIds.includes(ALL_FLOOR_ID)) {
const validFloorIds = floorIds.filter((id) => allFloorIds.includes(id));
// 如果选中的数量等于总数,则使用"所有"选项
if (
validFloorIds.length === allFloorIds.length &&
allFloorIds.length > 0
) {
floorIds = [ALL_FLOOR_ID];
} else if (validFloorIds.length > 0) {
floorIds = validFloorIds;
} else {
floorIds = [ALL_FLOOR_ID];
}
}
filterForm.value = {
buildingIds,
floorIds,
statusIds: [...savedFilterState.value.statusIds],
rentArea: [...savedFilterState.value.rentArea],
tagIds: [...savedFilterState.value.tagIds],
};
} else {
// 否则初始化,楼栋和楼层默认选中"所有"
filterForm.value = {
buildingIds: [ALL_BUILDING_ID],
floorIds: [ALL_FLOOR_ID],
statusIds: [],
rentArea: [min, max],
tagIds: [],
};
}
};
const handleCancel = () => {
emit('update:visible', false);
};
// 筛选列表数据
const filterRoomList = () => {
if (!props.roomList || props.roomList.length === 0) return [];
let filtered = [...props.roomList];
// 筛选楼栋(如果选择了"所有楼栋",则不过滤)
if (
filterForm.value.buildingIds.length > 0 &&
!filterForm.value.buildingIds.includes(ALL_BUILDING_ID)
) {
filtered = filtered.filter((room) =>
filterForm.value.buildingIds.includes(room.buildingId)
);
}
// 筛选楼层(如果选择了"所有楼层",则不过滤)
if (
filterForm.value.floorIds.length > 0 &&
!filterForm.value.floorIds.includes(ALL_FLOOR_ID)
) {
filtered = filtered.filter((room) =>
filterForm.value.floorIds.includes(room.floorId)
);
}
// 筛选状态
if (filterForm.value.statusIds.length > 0) {
filtered = filtered.filter((room) =>
filterForm.value.statusIds.includes(room.status)
);
}
// 筛选计租面积
const [minArea, maxArea] = filterForm.value.rentArea;
filtered = filtered.filter((room) => {
const area = Number(room.rentArea);
return !isNaN(area) && area >= minArea && area <= maxArea;
});
// 筛选标签
if (filterForm.value.tagIds.length > 0) {
filtered = filtered.filter((room) => {
if (!room.tags || !Array.isArray(room.tags)) return false;
const roomTagIds = room.tags.map((tag: any) => tag.id || tag);
return filterForm.value.tagIds.some((id) => roomTagIds.includes(id));
});
}
return filtered;
};
const handleOk = () => {
// 处理"所有"选项:如果同时选择了"所有"和具体选项,只保留"所有"
let finalBuildingIds = [...filterForm.value.buildingIds];
let finalFloorIds = [...filterForm.value.floorIds];
if (
finalBuildingIds.includes(ALL_BUILDING_ID) &&
finalBuildingIds.length > 1
) {
finalBuildingIds = [ALL_BUILDING_ID];
}
if (finalFloorIds.includes(ALL_FLOOR_ID) && finalFloorIds.length > 1) {
finalFloorIds = [ALL_FLOOR_ID];
}
// 保存当前筛选状态(保存处理后的值)
savedFilterState.value = {
buildingIds: finalBuildingIds,
floorIds: finalFloorIds,
statusIds: [...filterForm.value.statusIds],
rentArea: [...filterForm.value.rentArea],
tagIds: [...filterForm.value.tagIds],
};
// 标准化ID数组:如果选择了"所有"选项,则返回null(表示全部)
const normalizeIds = (ids: number[], allOptionId: number) => {
if (ids.length === 0) return null;
// 如果选择了"所有"选项,则返回null
if (ids.includes(allOptionId)) {
return null;
}
return ids;
};
// 使用处理后的值进行筛选
const originalBuildingIds = filterForm.value.buildingIds;
const originalFloorIds = filterForm.value.floorIds;
filterForm.value.buildingIds = finalBuildingIds;
filterForm.value.floorIds = finalFloorIds;
const filteredList = filterRoomList();
// 恢复原始值(用于显示)
filterForm.value.buildingIds = originalBuildingIds;
filterForm.value.floorIds = originalFloorIds;
emit('confirm', {
buildingIds: normalizeIds(finalBuildingIds, ALL_BUILDING_ID),
floorIds: normalizeIds(finalFloorIds, ALL_FLOOR_ID),
statusIds:
filterForm.value.statusIds.length === 0
? null
: filterForm.value.statusIds,
rentArea: filterForm.value.rentArea,
tagIds:
filterForm.value.tagIds.length === 0 ? null : filterForm.value.tagIds,
filteredList, // 筛选后的列表数据
});
handleCancel();
};
// 用于防止 watch 循环更新的标志
const isUpdatingBuilding = ref(false);
const isUpdatingFloor = ref(false);
// 监听楼栋选择变化
watch(
() => filterForm.value.buildingIds,
(newVal, oldVal) => {
if (isUpdatingBuilding.value) return;
if (!oldVal || oldVal.length === 0) return; // 初始化时跳过
const isAllSelected = newVal.includes(ALL_BUILDING_ID);
const wasAllOnly = oldVal.length === 1 && oldVal[0] === ALL_BUILDING_ID;
const hasSpecificSelected = newVal.some((id) => id !== ALL_BUILDING_ID);
const specificIds = newVal.filter((id) => id !== ALL_BUILDING_ID);
const allBuildingIds = buildingList.value.map((b) => b.id);
// 情况1:用户选择了"所有"选项(从非"所有"状态切换到包含"所有")
// 判断:旧值不包含"所有"或包含多个值,新值包含"所有"且有其他选项
if (isAllSelected && hasSpecificSelected && !wasAllOnly) {
isUpdatingBuilding.value = true;
filterForm.value.buildingIds = [ALL_BUILDING_ID];
nextTick(() => {
isUpdatingBuilding.value = false;
});
return;
}
// 情况2:用户从"所有"切换到选择具体选项(新值不包含"所有",但旧值只有"所有")
if (!isAllSelected && wasAllOnly && hasSpecificSelected) {
// v-model已经处理了,不需要额外操作,允许用户选择
// 检查是否选中的数量等于总数
if (
specificIds.length === allBuildingIds.length &&
allBuildingIds.every((id) => specificIds.includes(id))
) {
// 选中的数量等于总数,自动切换为"所有"
isUpdatingBuilding.value = true;
filterForm.value.buildingIds = [ALL_BUILDING_ID];
nextTick(() => {
isUpdatingBuilding.value = false;
});
}
return;
}
// 情况3:用户选择了具体选项,但当前值还包含"所有"(可能是从"所有"状态添加了具体选项)
if (hasSpecificSelected && isAllSelected && wasAllOnly) {
// 移除"所有"选项,只保留具体选项
isUpdatingBuilding.value = true;
filterForm.value.buildingIds = specificIds;
nextTick(() => {
isUpdatingBuilding.value = false;
// 检查是否选中的数量等于总数
if (
specificIds.length === allBuildingIds.length &&
allBuildingIds.every((id) => specificIds.includes(id))
) {
isUpdatingBuilding.value = true;
filterForm.value.buildingIds = [ALL_BUILDING_ID];
nextTick(() => {
isUpdatingBuilding.value = false;
});
}
});
return;
}
// 情况4:用户一个个勾选具体选项,当选中数量等于总数时,自动切换为"所有"
if (
!isAllSelected &&
hasSpecificSelected &&
specificIds.length === allBuildingIds.length
) {
// 检查是否所有选项都被选中
if (allBuildingIds.every((id) => specificIds.includes(id))) {
isUpdatingBuilding.value = true;
filterForm.value.buildingIds = [ALL_BUILDING_ID];
nextTick(() => {
isUpdatingBuilding.value = false;
});
}
}
}
);
// 监听楼层选择变化
watch(
() => filterForm.value.floorIds,
(newVal, oldVal) => {
if (isUpdatingFloor.value) return;
if (!oldVal || oldVal.length === 0) return; // 初始化时跳过
const isAllSelected = newVal.includes(ALL_FLOOR_ID);
const wasAllOnly = oldVal.length === 1 && oldVal[0] === ALL_FLOOR_ID;
const hasSpecificSelected = newVal.some((id) => id !== ALL_FLOOR_ID);
const specificIds = newVal.filter((id) => id !== ALL_FLOOR_ID);
const allFloorIds = floorList.value.map((f) => f.id);
// 情况1:用户选择了"所有"选项(从非"所有"状态切换到包含"所有")
// 判断:旧值不包含"所有"或包含多个值,新值包含"所有"且有其他选项
if (isAllSelected && hasSpecificSelected && !wasAllOnly) {
isUpdatingFloor.value = true;
filterForm.value.floorIds = [ALL_FLOOR_ID];
nextTick(() => {
isUpdatingFloor.value = false;
});
return;
}
// 情况2:用户从"所有"切换到选择具体选项(新值不包含"所有",但旧值只有"所有")
if (!isAllSelected && wasAllOnly && hasSpecificSelected) {
// v-model已经处理了,不需要额外操作,允许用户选择
// 检查是否选中的数量等于总数
if (
specificIds.length === allFloorIds.length &&
allFloorIds.every((id) => specificIds.includes(id))
) {
// 选中的数量等于总数,自动切换为"所有"
isUpdatingFloor.value = true;
filterForm.value.floorIds = [ALL_FLOOR_ID];
nextTick(() => {
isUpdatingFloor.value = false;
});
}
return;
}
// 情况3:用户选择了具体选项,但当前值还包含"所有"(可能是从"所有"状态添加了具体选项)
if (hasSpecificSelected && isAllSelected && wasAllOnly) {
// 移除"所有"选项,只保留具体选项
isUpdatingFloor.value = true;
filterForm.value.floorIds = specificIds;
nextTick(() => {
isUpdatingFloor.value = false;
// 检查是否选中的数量等于总数
if (
specificIds.length === allFloorIds.length &&
allFloorIds.every((id) => specificIds.includes(id))
) {
isUpdatingFloor.value = true;
filterForm.value.floorIds = [ALL_FLOOR_ID];
nextTick(() => {
isUpdatingFloor.value = false;
});
}
});
return;
}
// 情况4:用户一个个勾选具体选项,当选中数量等于总数时,自动切换为"所有"
if (
!isAllSelected &&
hasSpecificSelected &&
specificIds.length === allFloorIds.length
) {
// 检查是否所有选项都被选中
if (allFloorIds.every((id) => specificIds.includes(id))) {
isUpdatingFloor.value = true;
filterForm.value.floorIds = [ALL_FLOOR_ID];
nextTick(() => {
isUpdatingFloor.value = false;
});
}
}
}
);
// 监听抽屉打开,初始化数据
watch(
() => props.visible,
(val) => {
if (val) {
// 延迟初始化,确保 buildingList 和 floorList 已计算
setTimeout(() => {
initFilterForm();
}, 0);
if (tagList.value.length === 0) {
getTagListData();
}
}
},
{ immediate: true }
);
// 监听楼栋和楼层列表变化,如果当前没有保存状态且列表为空,则默认选中"所有"
watch(
[buildingList, floorList],
() => {
if (props.visible && !savedFilterState.value) {
// 如果楼栋列表为空,默认选中"所有楼栋"
if (filterForm.value.buildingIds.length === 0) {
filterForm.value.buildingIds = [ALL_BUILDING_ID];
}
// 如果楼层列表为空,默认选中"所有楼层"
if (filterForm.value.floorIds.length === 0) {
filterForm.value.floorIds = [ALL_FLOOR_ID];
}
}
},
{ immediate: true }
);
onMounted(() => {
getTagListData();
});
</script>
<style lang="less" scoped>
:deep(label.arco-form-item-label) {
width: 100% !important;
}
.slider-value {
margin-top: 8px;
text-align: center;
color: var(--color-text-2);
font-size: 12px;
}
:deep(.arco-slider-mark) {
text-wrap: nowrap;
}
</style>
-3
View File
@@ -1,3 +0,0 @@
<template>
<div>house-list</div>
</template>
@@ -0,0 +1,338 @@
<template>
<div class="business_project_wrap">
<Breadcrumb :items="['menu.business', 'menu.business.house-list']" />
<a-card class="general-card full_height">
<template #title>
<div class="card-title_between">
<p class="title">{{ t('menu.business.house-list') }}</p>
<div class="flex_center_between">
<a-space>
<a-input-search
v-model="queryParams.keyword"
:style="{ width: '320px' }"
:placeholder="t('please.enter', { placeholder: t('content') })"
@search="handleSearch"
/>
<a-button @click="handleFilter">
<template #icon>
<icon-filter />
</template>
{{ t('table.filter') }}
</a-button>
<a-button>
<template #icon>
<icon-export />
</template>
{{ t('table.export') }}
</a-button>
<a-button>
<template #icon>
<icon-download />
</template>
{{ t('table.import') }}
</a-button>
<a-button type="primary" @click="handleAddHouse">
<template #icon>
<icon-plus />
</template>
{{ t('add-house') }}
</a-button>
</a-space>
</div>
</div>
</template>
<!-- 使用新的 DataTable 组件 -->
<DataTable
:data="renderData"
:columns="projectColumn"
:loading="loading"
:total="total"
:current="current"
:page-size="pageSize"
@page-change="onPageChange"
@page-size-change="onPageSizeChange"
>
<template #tags="{ record }">
<template v-if="record.tags?.length">
<a-tag
v-for="item in record.tags"
:key="item.id"
class="mr-[5px] first:mr-0"
>
{{ item.name }}</a-tag
>
</template>
<span v-else> - </span>
</template>
<template #status="{ record }">
<a-tag :color="getTagsColor(record?.status)">{{
record?.statusStr
}}</a-tag>
</template>
<template #operate="{ record }">
<a-space>
<a-button @click="handleDetail(record.id)">查看</a-button>
<a-popover trigger="click" position="bottom">
<a-button>{{ $t('more') }}</a-button>
<template #content>
<a-space direction="vertical">
<a-button @click="handleEditProject(record.id)">
编辑房源
</a-button>
<a-popconfirm
:ok-text="$t('cancel')"
:cancel-text="$t('delete')"
:ok-button-props="{ type: 'outline' }"
:cancel-button-props="{
type: 'primary',
status: 'danger',
}"
content="确定删除该房源?"
type="error"
@cancel="handleDel(record?.id)"
>
<a-button status="danger">删除项目</a-button>
</a-popconfirm>
</a-space>
</template>
</a-popover>
</a-space>
</template>
</DataTable>
</a-card>
<houseFormDrawer
v-model:visible="showDrawer"
:is-edit="isEdit"
:record="currentRecordInfo"
@submitted="getHouseListData"
/>
<houseInfoDrawer
v-model:visible="showProjectInfoDrawer"
:project-id="currentProjectId"
:detail-info="currentRecordInfo"
/>
<houseListFilterDrawer
v-model:visible="showListFilterDrawer"
:room-list="projectList"
:project-name="currentProjectName"
@confirm="handleFilterConfirm"
/>
</div>
</template>
<script lang="ts" setup>
import { ref, onMounted } from 'vue';
import useLoading from '@/hooks/loading';
import { useI18n } from 'vue-i18n';
import type { TableColumnData } from '@arco-design/web-vue';
import useDataTable from '@/composables/useDataTable';
import { getRoomList, getRoom, delRoom } from '@/api/business';
import { ProjectPaginationParams } from '@/types/comm';
import houseFormDrawer from '../drawer/house-form-drawer.vue';
import houseInfoDrawer from '../drawer/house-info-drawer.vue';
import houseListFilterDrawer from '../drawer/house-list-filter-drawer.vue';
import { Message } from '@arco-design/web-vue';
const { t } = useI18n();
const { loading } = useLoading(false);
const showDrawer = ref<boolean>(false);
const showProjectInfoDrawer = ref<boolean>(false);
const showListFilterDrawer = ref<boolean>(false);
const currentProjectId = ref<number | null>(null);
const isLoadingEdit = ref<boolean>(false);
const isLoadingDetail = ref<boolean>(false);
const isEdit = ref<boolean>(false);
const currentRecordInfo = ref<any | null>(null);
const projectList = ref<any[]>([]);
const currentProjectName = ref<string>(''); // 当前项目名称
const {
data: renderData,
columns: projectColumn,
total,
current,
pageSize,
setData,
setColumns,
setTotal,
setCurrent,
setPageSize,
setLoading,
} = useDataTable({
initialPage: 1,
initialPageSize: 10,
pageSizeOptions: [10, 20, 50, 100],
});
// 通用空值处理函数
const renderEmpty = (value: any) => value ?? '-';
// 列配置
const columns: TableColumnData[] = [
{
title: '房源名称',
dataIndex: 'name',
key: 'name',
render: ({ record }) => renderEmpty(record.name),
},
{
title: '所属项目',
dataIndex: 'projectName',
key: 'projectName',
render: ({ record }) => renderEmpty(record.projectName),
},
{
title: '所属楼栋',
dataIndex: 'buildingName',
key: 'buildingName',
render: ({ record }) => renderEmpty(record.buildingName),
},
{
title: '楼层',
dataIndex: 'floorName',
key: 'floorName',
render: ({ record }) => renderEmpty(record.floorName),
},
{
title: '计租面积(㎡)',
dataIndex: 'rentArea',
key: 'rentArea',
render: ({ record }) => renderEmpty(record.rentArea),
},
{
title: '租金(实时)',
dataIndex: 'price',
key: 'price',
render: ({ record }) => (Number(record.price) <= 0 ? '-' : record?.price),
},
{ title: '标签', dataIndex: 'tags', key: 'tags', slotName: 'tags' },
{
title: '状态',
dataIndex: 'statusStr',
key: 'statusStr',
slotName: 'status',
},
{
title: '合同',
dataIndex: 'contractStatusStr',
key: 'contractStatusStr',
render: ({ record }) => renderEmpty(record.contractStatusStr),
},
{ title: '操作', key: 'operate', slotName: 'operate', width: 200 },
];
const queryParams = ref<ProjectPaginationParams>({
page: current.value,
page_size: pageSize.value,
keyword: '',
});
const getHouseListData = async () => {
setLoading(true);
const { code, data } = await getRoomList(queryParams.value);
if (code === 200 && data) {
const { list, page } = data as {
list?: any[];
page?: { total?: number };
};
projectList.value = Array.isArray(list) ? list : [];
// 提取项目名称(从第一条数据中获取,假设所有房源属于同一项目)
if (projectList.value.length > 0 && projectList.value[0].projectName) {
currentProjectName.value = projectList.value[0].projectName;
}
setData(projectList.value);
setTotal(page?.total ? page.total : 0);
}
setLoading(false);
};
const getRoomData = async (id: number, edit: boolean) => {
if (currentRecordInfo.value && currentRecordInfo.value?.id === id && !edit)
return;
if (edit) {
isLoadingEdit.value = true;
} else {
isLoadingDetail.value = true;
}
const { code, data } = await getRoom(id);
if (code === 200 && data) {
currentRecordInfo.value = (data as { room?: any }).room || null;
}
isLoadingEdit.value = false;
isLoadingDetail.value = false;
};
const handleFilterConfirm = (payload: {
buildingIds: number[] | null;
floorIds: number[] | null;
statusIds: number[] | null;
rentArea: number[];
tagIds: number[] | null;
filteredList: any[]; // 筛选后的列表数据
}) => {
// 更新查询参数(用于后端筛选)
(queryParams.value as any).building_ids = payload.buildingIds;
(queryParams.value as any).floor_ids = payload.floorIds;
(queryParams.value as any).status_ids = payload.statusIds;
(queryParams.value as any).rent_area_min = payload.rentArea?.[0];
(queryParams.value as any).rent_area_max = payload.rentArea?.[1];
(queryParams.value as any).tag_ids = payload.tagIds;
// 使用筛选后的列表数据更新表格
if (payload.filteredList) {
setData(payload.filteredList);
setTotal(payload.filteredList.length);
} else {
// 如果没有筛选数据,重新加载列表数据
getHouseListData();
}
};
const handleDel = async (id: number) => {
const { code, message } = await delRoom(id);
if (code === 200) {
Message.success(message || '删除成功');
// 重新加载列表数据
await getHouseListData();
} else {
Message.error(message || '删除失败');
}
};
// 分页事件处理
const onPageChange = (page: number) => {
setCurrent(page);
current.value = page;
};
const onPageSizeChange = (size: number) => {
setPageSize(size);
pageSize.value = size;
};
const handleAddHouse = () => {
isEdit.value = false;
currentRecordInfo.value = null;
showDrawer.value = true;
};
const handleEditProject = async (id: number) => {
if (isLoadingEdit.value) return;
isEdit.value = true;
await getRoomData(id, true);
showDrawer.value = true;
};
const handleDetail = async (id: number) => {
if (isLoadingDetail.value) return;
await getRoomData(id, false);
showProjectInfoDrawer.value = true;
};
const handleSearch = () => {
getHouseListData();
};
const handleFilter = () => {
showListFilterDrawer.value = true;
};
const getTagsColor = (status) => {
const colorMap = {
1: 'orangered',
2: 'orange',
3: 'red',
4: 'green',
};
return colorMap[status];
};
onMounted(() => {
setColumns(columns);
getHouseListData();
});
</script>
@@ -199,20 +199,10 @@
<script lang="ts" setup>
import { ref, reactive, onMounted, computed, watch } from 'vue';
import type { FormInstance } from '@arco-design/web-vue';
const priceUnits = [
{ value: 1, text: '元/㎡*月' },
{ value: 2, text: '元/㎡*天' },
{ value: 3, text: '元/天' },
{ value: 4, text: '元/月' },
{ value: 5, text: '元/年' },
];
const priceUnitMap = new Map([
[1, '元/㎡*月'],
[2, '元/㎡*天'],
[3, '元/天'],
[4, '元/月'],
[5, '元/年'],
]);
import { PRICE_UNITS, PRICE_UNIT_MAP } from '@/constants';
const priceUnits = PRICE_UNITS;
const priceUnitMap = PRICE_UNIT_MAP;
const props = defineProps<{
visible: boolean;
record?: any | null;
@@ -259,7 +249,7 @@
emit('update:visible', false);
emit('submitted', form);
} catch {
console.warn('❌ 校验未通过');
// 校验未通过
} finally {
isLoading.value = false;
}
@@ -149,7 +149,7 @@
isLoading.value = false;
}
};
// 部门列表
async function getAllProjectListData() {
try {
const { code, data } = await getAllProjectIdNameList();