住户信息结构完成

This commit is contained in:
Zy
2026-04-09 18:41:18 +08:00
parent 267a9a5f29
commit 238aa9a400
10 changed files with 1008 additions and 232 deletions

View File

@@ -95,3 +95,10 @@ export function EditHouseApi(data: addHouseType) {
data data
}); });
} }
/** 根据小区id获取当前小区的树状结构 */
export function getVillageTree(id: number) {
return request({
url: `/house/villageTree/${id}`,
method: 'GET'
});
}

View File

@@ -33,7 +33,7 @@ export const getResident = (id: string | number): AxiosPromise<ResidentVO> => {
*/ */
export const addResident = (data: ResidentForm) => { export const addResident = (data: ResidentForm) => {
return request({ return request({
url: '////resident', url: '/resident',
method: 'post', method: 'post',
data: data data: data
}); });
@@ -61,3 +61,14 @@ export const delResident = (id: string | number | Array<string | number>) => {
method: 'delete' method: 'delete'
}); });
}; };
/**
* 添加住户管理-上传身份证
* @param id
*/
export const UploadidCardApi = () => {
return request({
url: '/resident/uploadImage',
method: 'delete'
});
};

View File

@@ -47,7 +47,7 @@ export interface ResidentVO {
/** /**
* 房屋表id * 房屋表id
*/ */
houseId: string | number; houseId: number;
/** /**
* 0审核中 1 已认证 * 0审核中 1 已认证
@@ -68,6 +68,9 @@ export interface ResidentVO {
* 备注 * 备注
*/ */
remark: string; remark: string;
/** 具体住址 */
houseAddress?: string;
} }
export interface ResidentForm extends BaseEntity { export interface ResidentForm extends BaseEntity {
@@ -119,7 +122,7 @@ export interface ResidentForm extends BaseEntity {
/** /**
* 房屋表id * 房屋表id
*/ */
houseId?: string | number; houseId?: number;
/** /**
* 0审核中 1 已认证 * 0审核中 1 已认证
@@ -143,68 +146,24 @@ export interface ResidentForm extends BaseEntity {
} }
export interface ResidentQuery extends PageQuery { export interface ResidentQuery extends PageQuery {
/**
* 住户编号
*/
code?: number;
/**
* 姓名
*/
name?: string;
/**
* 0业主 1租户
*/
type?: number;
/**
* 0 男性 1女性
*/
gender?: number;
/**
* 身份证号
*/
idCard?: string | number;
/**
* 手机号码
*/
phone?: number;
/**
* 小区id
*/
villageId?: string | number;
/**
* 楼栋id
*/
buildingId?: string | number;
/**
* 房屋表id
*/
houseId?: string | number;
/**
* 0审核中 1 已认证
*/
status?: number;
/**
* 身份证正面照url
*/
cardImageFront?: string;
/**
* 身份证背面照url
*/
cardImageBack?: string;
/** /**
* 日期范围参数 * 日期范围参数
*/ */
params?: any; date?: any;
/**
* 住户类型
* 0业主 1租户
*/
type: 0 | 1;
/**
* 住户状态
* 0审核中 1 已认证
*/
status: 0 | 1;
houseId: number;
villageId: number;
buildingId: number;
unitNo: number;
} }

View File

@@ -106,7 +106,9 @@ li {
.inlineBlock { .inlineBlock {
display: block; display: block;
} }
.flex-column {
flex-direction: column;
}
.clearfix { .clearfix {
&:after { &:after {
visibility: hidden; visibility: hidden;

View File

@@ -80,7 +80,6 @@ declare global {
declare interface PageData<T, D> { declare interface PageData<T, D> {
form: T; form: T;
queryParams: D; queryParams: D;
rules: ElFormRules;
} }
/** /**
* 分页查询参数 * 分页查询参数

View File

@@ -0,0 +1,214 @@
<template>
<div class="ResidentMainBox p-2 w-full h-full">
<PageHeader title="住户详情"></PageHeader>
<div class="ResidentMainBody">
<el-row :span="24" :gutter="20">
<el-col :span="16">
<div class="userInfobox">
<el-card>
<template #header>
<div class="card-header">
<span>基础资料</span>
</div>
</template>
<div class="w-full h-full">
<el-row :gutter="20">
<el-col :span="12">
<div class="userinfo_item">
<div class="title_item">住户姓名</div>
<span> 去丽丽 </span>
</div>
<div class="userinfo_item">
<div class="title_item">手机号码</div>
<span> 去丽丽 </span>
</div>
<div class="userinfo_item">
<div class="title_item">证件类型</div>
<span> 去丽丽 </span>
</div>
<div class="userinfo_item">
<div class="title_item">住户类型</div>
<span> 去丽丽 </span>
</div>
<div class="userinfo_item">
<div class="title_item">住户来源</div>
<span> 去丽丽 </span>
</div>
<div class="userinfo_item">
<div class="title_item">注册时间</div>
<span> 去丽丽 </span>
</div>
<div class="userinfo_item">
<div class="title_item">身份证正面</div>
<span> 去丽丽 </span>
</div>
</el-col>
<el-col :span="12">
<div class="userinfo_item">
<div class="title_item">住户编号</div>
<span> 去丽丽 </span>
</div>
<div class="userinfo_item">
<div class="title_item">住户性别</div>
<span> 去丽丽 </span>
</div>
<div class="userinfo_item">
<div class="title_item">证件号码</div>
<span> 去丽丽 </span>
</div>
<div class="userinfo_item">
<div class="title_item">备注</div>
<span> 去丽丽 </span>
</div>
<div class="userinfo_item">
<div class="title_item">提交审核时间</div>
<span> 去丽丽 </span>
</div>
<div class="userinfo_item">
<div class="title_item">身份证正面</div>
<span> 去丽丽 </span>
</div>
</el-col>
</el-row>
</div>
</el-card>
</div>
<div class="previewbox">
<el-card>
<template #header>
<div class="card-header">
<span>审核</span>
</div>
</template>
<div class="w-full h-full flex mt-2">
<div class="title">审批意见</div>
<div class="preivewBodybox">
<el-input placeholder="请输入" type="textarea" :rows="5"></el-input>
<div class="actions">
<el-button type="primary">审核通过</el-button>
<el-button type="danger">审核不通过</el-button>
<el-button>返回</el-button>
</div>
</div>
</div>
</el-card>
</div>
</el-col>
<el-col :span="8">
<el-card style="position: relative" class="h-full">
<template #header>
<div class="asideCardHeader w-full h-full">
<div class="cardSubtitle">
<div class="line"></div>
<div class="subtitle">审批流程</div>
</div>
</div>
</template>
<div class="w-full h-50%">
<el-steps direction="vertical" :active="0">
<el-step title="提交信息" description="2023/4/23 10:28:43" />
<el-step title="物业审核" description="2023/4/23 11:10:58" />
<el-step title="认证通过" description="2023/4/23 12:35:22" />
</el-steps>
</div>
</el-card>
</el-col>
</el-row>
</div>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import PageHeader from '@/components/Pageheader/index.vue';
</script>
<style scoped lang="scss">
.ResidentMainBox {
display: flex;
flex-direction: column;
.ResidentMainBody {
flex: 1;
margin-top: 20px;
.userInfobox {
.card-header {
font-size: 18px;
height: 60px;
line-height: 60px;
}
.userinfo_item {
margin-bottom: 20px;
height: 60px;
line-height: 60px;
display: flex;
align-items: center;
background: peru;
.title_item {
margin-right: 10px;
width: 100px;
text-align: right;
}
}
}
.previewbox {
margin-top: 20px;
.card-header {
font-size: 18px;
height: 60px;
line-height: 60px;
}
.title {
width: 120px;
padding-right: 20px;
text-align: right;
}
.preivewBodybox {
flex: 1;
height: 200px;
.actions {
margin-top: 20px;
}
}
}
.cardSubtitle {
position: absolute;
left: 0px;
top: 28px;
width: 100px;
background: #fff;
color: #000;
display: flex;
align-items: center;
justify-content: center;
.line {
width: 5px;
height: 19px;
margin-right: 5px;
border-radius: 5px;
background-color: rgba(26, 117, 255, 1);
}
.subtitle {
height: 23px;
line-height: 23px;
color: rgba(51, 51, 51, 1);
font-size: 16px;
}
}
&:deep(div.el-step__head.is-process) {
& > div.el-step__line {
width: 0;
border: 1px dashed gray;
background: transparent;
}
}
&:deep(div.el-step__head.is-finish) {
& > div.el-step__line {
width: 0;
border: 1px solid var(--el-color-primary);
background: transparent;
}
}
}
}
</style>

View File

@@ -0,0 +1,372 @@
<template>
<div class="AddBuildingBox">
<PageHeader :title="userid ? '编辑用户' : '添加用户'"></PageHeader>
<div class="AddBuildingBody">
<div class="title">基本信息</div>
<div class="addBuildingFormBody">
<el-form class="formBody" label-position="left" ref="formRef" :model="form" :rules="rules" label-width="130px">
<el-row>
<el-col :span="11">
<el-form-item label="楼栋房屋" prop="houseId">
<el-cascader v-model="userHousepath" :options="treedata" @change="handleChange" />
</el-form-item>
</el-col>
<el-col :span="2"></el-col>
<el-col :span="11">
<el-form-item label="住户姓名" prop="name">
<el-input v-model="form.name" placeholder="请输入住户姓名" />
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="11">
<el-form-item label="住户性别" prop="gender">
<el-radio-group v-model="form.gender">
<el-radio :value="1"></el-radio>
<el-radio :value="0"></el-radio>
</el-radio-group>
</el-form-item>
</el-col>
<el-col :span="2"></el-col>
<el-col :span="11">
<el-form-item label="身份证号码" prop="idCard">
<el-input v-model="form.idCard" placeholder="请输入证件号码" />
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="11">
<el-form-item label="与业主的关系" prop="type">
<el-select v-model.number="form.type" class="m-2" placeholder="请选择">
<el-option v-for="item in ownerRelationshiplist" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="2"></el-col>
<el-col :span="11">
<el-form-item label="手机号码" prop="phone">
<el-input v-model.number="form.phone" placeholder="请输入手机号码" />
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="11">
<el-form-item label="身份证正面照" prop="cardImageFront">
<el-upload
ref="upload"
class="avatar-uploader"
:action="uploadUrl"
:show-file-list="false"
:on-success="handleAvatarSuccess"
:on-exceed="handleExceed"
:on-error="handleError"
:limit="1"
>
<img v-if="form.cardImageFront" :src="form.cardImageFront" class="avatar" />
<el-icon v-else class="avatar-uploader-icon"><Plus /></el-icon>
</el-upload>
</el-form-item>
</el-col>
<el-col :span="2"></el-col>
<el-col :span="11">
<el-form-item label="身份证背面照" prop="cardImageBack">
<el-upload
ref="upload2"
class="avatar-uploader"
:action="uploadUrl"
:show-file-list="false"
:limit="1"
:on-exceed="handleExceed2"
:on-success="handleAvatar2Success"
:on-error="handleError2"
>
<img v-if="form.cardImageBack" :src="form.cardImageBack" class="avatar" />
<el-icon v-else class="avatar-uploader-icon"><Plus /></el-icon>
</el-upload>
</el-form-item>
</el-col>
</el-row>
<el-form-item label="备注" prop="remark">
<el-input type="textarea" :row="5" v-model.number="form.remark" placeholder="请输入备注" />
</el-form-item>
<el-form-item style="margin-top: 30px">
<el-button type="primary" @click="submitForm">确定</el-button>
<el-button @click="cancelForm">返回</el-button>
</el-form-item>
</el-form>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import PageHeader from '@/components/Pageheader/index.vue';
import { useHouseStore } from '@/store/modules/house';
import { addHouseAPI, EditHouseApi, getHouseApi, getHouselistAPI, getVillageTree } from '@/api/system/house';
import { convertBuildingToTree, getHousePathById, ownerRelationshiplist } from './index';
import { ResidentForm } from '@/api/system/resident/type';
import { CascaderValue, UploadFiles, UploadInstance, UploadProps, UploadRawFile } from 'element-plus';
import { genFileId } from 'element-plus';
import { getCommunitylistAPI } from '@/api/system/community';
import { addResident, getResident, updateResident } from '@/api/system/resident';
const uploadUrl = import.meta.env.VITE_APP_BASE_API + '/resident/uploadImage';
const router = useRouter();
const route = useRoute();
const formRef = ref();
const form = ref<ResidentForm>({
houseId: null,
name: '',
gender: 1,
idCard: '',
type: 0, // 与业主关系 0业主
phone: null, //手机号
cardImageFront: '',
cardImageBack: '',
remark: '' //备注
});
const userHousepath = ref<CascaderValue>([]);
const rules = ref({
houseId: [{ required: true, message: '请选择房屋', trigger: 'blur' }],
name: [{ required: true, message: '请输入名称', trigger: 'blur' }],
gender: [{ required: true, message: '请选择性别', trigger: 'blur' }],
cardImageFront: [{ required: true, message: '请上传身份证照片', trigger: 'blur' }],
cardImageBack: [{ required: true, message: '请上传身份证照片', trigger: 'blur' }],
idCard: [
{ required: true, message: '请输入身份证号', trigger: 'blur' },
{
validator(_, value) {
const reg = /^[1-9]\d{5}(18|19|20)\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])\d{3}[\dXx]$/;
return reg.test(value);
},
message: '请输入正确的18位身份证号码',
trigger: 'blur'
}
],
type: [{ required: true, message: '请选择与业主关系', trigger: 'blur' }],
phone: [
{ required: true, message: '请输入手机号', trigger: 'blur' },
{
validator: (_, value) => {
return /^1[3-9]\d{9}$/.test(value);
},
message: '请输入正确的手机号码',
trigger: 'blur'
}
]
});
// !== 楼栋房屋 =============================================================================
const userid = ref(null);
const treedata = ref([]);
function gettreedata() {
getCommunitylistAPI().then((res) => {
const currentviliage = res.rows.find((item) => item.choiceStatus === 0);
if (currentviliage.villageId) {
getVillageTree(currentviliage.villageId).then((res) => {
treedata.value = convertBuildingToTree(res.data.buildings);
console.log(treedata.value);
// !== 编辑用户 =============================================================================
if (route.query.id) {
userid.value = route.query.id;
getResident(userid.value).then((res) => {
form.value = {
...res.data
};
const arr = getHousePathById(treedata.value, form.value.houseId);
console.log(arr);
userHousepath.value = arr;
});
}
});
}
});
}
gettreedata();
function handleChange(val: CascaderValue) {
userHousepath.value = val;
form.value.villageId = val[0];
form.value.buildingId = val[1];
form.value.houseId = val[2];
}
// !== 上传文件 =============================================================================
// 上传文件成功
const upload = ref<UploadInstance>();
const upload2 = ref<UploadInstance>();
const handleAvatarSuccess: UploadProps['onSuccess'] = (response, uploadFile) => {
if (response.data && response.data.url) {
form.value.cardImageFront = response.data.url;
} else {
ElMessage.error('短时间内不允许重复提交,请稍候再试');
}
};
const handleAvatar2Success: UploadProps['onSuccess'] = (response, uploadFile) => {
if (response.data && response.data.url) {
form.value.cardImageBack = response.data.url;
} else {
ElMessage.error('短时间内不允许重复提交,请稍候再试');
}
};
const handleExceed: UploadProps['onExceed'] = (files) => {
upload.value!.clearFiles();
const file = files[0] as UploadRawFile;
file.uid = genFileId();
upload.value!.handleStart(file);
upload.value?.submit();
};
const handleExceed2: UploadProps['onExceed'] = (files) => {
upload2.value!.clearFiles();
const file = files[0] as UploadRawFile;
file.uid = genFileId();
upload2.value!.handleStart(file);
upload2.value?.submit();
};
const handleError: UploadProps['onError'] = (error: Error, uploadFile: UploadFile, uploadFiles: UploadFiles) => {
console.log(error);
};
const handleError2: UploadProps['onError'] = (error: Error, uploadFile: UploadFile, uploadFiles: UploadFiles) => {
console.log(error);
};
// !== 提交 =============================================================================
// 提交
const submitForm = () => {
formRef.value?.validate(async (valid: boolean) => {
if (valid) {
if (!form.value.cardImageFront.trim()) {
return ElMessage.error('请上传身份证正面照片');
}
if (!form.value.cardImageBack.trim()) {
return ElMessage.error('请上传身份证背面照片');
}
if (userid.value) {
console.log('edit');
updateResident(form.value).then(() => {
ElMessage.success('修改成功');
cancelForm();
});
} else {
console.log('add');
addResident(form.value).then(() => {
ElMessage.success('添加成功');
cancelForm();
});
}
console.log(form.value);
}
});
};
// 推出
const cancelForm = () => {
router.push({
path: '/house/resident'
});
};
</script>
<style scoped lang="scss">
.avatar-uploader {
border: 1px solid #ccc;
}
.avatar-uploader .avatar {
width: 178px;
height: 178px;
display: block;
object-fit: cover;
}
.avatar-uploader .el-upload {
border: 1px dashed var(--el-border-color);
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
transition: var(--el-transition-duration-fast);
}
.avatar-uploader .el-upload:hover {
border-color: var(--el-color-primary);
}
.avatar {
width: 100%;
height: 100%;
object-fit: contain;
}
.el-icon.avatar-uploader-icon {
font-size: 28px;
color: #8c939d;
width: 178px;
height: 178px;
text-align: center;
}
.AddBuildingBox {
padding: 15px;
height: 100%;
width: 100%;
}
.AddBuildingBody {
margin-top: 20px;
display: flex;
flex-direction: column;
justify-content: center;
background-color: white;
padding: 15px;
.title {
height: 40px;
line-height: 40px;
padding-left: 20px;
background-color: rgba(255, 255, 255, 1);
color: rgba(16, 16, 16, 1);
border-bottom: 1px solid #bbbbbb;
margin-bottom: 60px;
}
.formBody {
width: 1050px;
margin: 0 auto;
&:deep(.el-form-item__content, .el-select, .el-input) {
width: 350px !important;
margin-right: 10px;
.el-cascader {
width: 100%;
}
}
.el-form-item {
margin-bottom: 20px;
align-items: center;
}
.unitBox {
.el-input {
width: 440px;
margin-right: 10px;
}
}
.el-button {
width: 80px;
height: 35px;
margin-right: 20px;
}
.houseTypebox {
display: flex;
align-items: center;
.el-input {
width: 100px;
margin-right: 0px;
}
span {
margin: 0 20px;
}
}
}
}
</style>

View File

@@ -0,0 +1,136 @@
interface Tree {
label: string;
value: number;
type: string;
children?: Tree[];
}
/**
* 楼栋/单元/房屋 转 Tree 结构(跳过楼层)
* @param buildings 原始楼栋数组
* @returns Tree[]
*/
export function convertBuildingToTree(buildings: any[]): Tree[] {
if (!buildings || !Array.isArray(buildings)) return [];
return buildings.map((building) => {
// 1. 楼栋
const buildingTree: Tree = {
label: building.buildingName || '未知楼栋',
type: 'buildingId',
value: building.buildingId,
children: []
};
// 2. 单元
const units = building.units || [];
const unitTrees = units.map((unit: any) => {
const unitTree: Tree = {
label: `${unit.unitNo} 单元`,
value: unit.unitNo,
type: 'unitNo',
children: []
};
// ====================== 关键修改 ======================
// 直接从 floors 里把所有 house 全部抽出来,跳过 floor 层级
const allHouses: any[] = [];
const floors = unit.floors || [];
floors.forEach((floor: any) => {
const houses = floor.houses || [];
allHouses.push(...houses); // 把所有房屋合并到一个数组
});
// 房屋直接挂在单元下
unitTree.children = allHouses.map((house: any) => ({
label: house.houseNo || '未知房屋',
value: house.houseId,
type: 'houseId'
}));
// ======================================================
return unitTree;
});
buildingTree.children = unitTrees;
console.log(buildingTree);
return buildingTree;
});
}
// 证件类型
const idCardTypelist = [{}];
// 与业主关系
export const ownerRelationshiplist = [
{
label: '业主',
value: 0
},
{
label: '亲属',
value: 1
},
{
label: '租户',
value: 2
}
];
interface Tree {
label: string;
value: number;
type: string;
children?: Tree[];
}
// 只允许 字符串/数字 类型的 key
type ValidTreeKey = {
[K in keyof Tree]: Tree[K] extends string | number ? K : never;
}[keyof Tree];
/**
* 根据 houseId 查找完整路径(楼栋 > 单元 > 楼层 > 房屋)
* @param treeData 转换后的树形数据
* @param houseId 目标房屋ID
* @returns string[] 路径数组,如 ['1号楼', '1单元', '3层', '301']
*/
export function getHousePathById(treeData: Tree[], houseId: number, response: ValidTreeKey = 'value'): (string | number)[] {
const path: (string | number)[] = [];
// 递归查找
function findNode(nodes: Tree[]): boolean {
for (const node of nodes) {
path.push(node[response]); // 把当前节点加入路径
// 找到目标房屋
if (node.type === 'houseId' && node.value === houseId) {
return true;
}
// 递归子节点
if (node.children && node.children.length > 0) {
if (findNode(node.children)) {
return true;
}
}
path.pop(); // 回溯,移除当前节点
}
return false;
}
findNode(treeData);
return path;
}
/**
* 验证身份证号码18位支持尾号x/X
* @param idCard 身份证号
* @return {boolean} true 符合 false 不符合
*/
export function validateIdCard(idCard: string): boolean {
// 18位身份证正则
const reg = /^[1-9]\d{5}(18|19|20)\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])\d{3}[\dXx]$/;
return reg.test(idCard.trim());
}

View File

@@ -1,32 +1,27 @@
<template> <template>
<div class="p-2"> <div class="p-2 h-full">
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave"> <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div v-show="showSearch" class="mb-[10px]"> <div v-show="showSearch" class="mb-[10px]">
<el-card shadow="hover"> <el-card shadow="hover">
<el-form ref="queryFormRef" :model="queryParams" :inline="true"> <el-form ref="queryFormRef" :model="queryParams" :inline="true">
<el-form-item label="住户编号" prop="code"> <el-form-item label="住户类型" prop="type">
<el-input v-model="queryParams.code" placeholder="请输入住户编号" clearable @keyup.enter="handleQuery" /> <el-select v-model="queryParams.type" placeholder="请输入住户类型" style="width: 240px">
<el-option v-for="item in typelist" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item> </el-form-item>
<el-form-item label="姓名" prop="name"> <el-form-item label="住户状态" prop="status">
<el-input v-model="queryParams.name" placeholder="请输入姓名" clearable @keyup.enter="handleQuery" /> <el-select v-model="queryParams.status" placeholder="请输入住户状态" style="width: 240px">
<el-option v-for="item in statuslist" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item> </el-form-item>
<el-form-item label="0 男性 1女性" prop="gender"> <el-form-item label="提交日期" prop="date">
<el-input v-model="queryParams.gender" placeholder="请输入0 男性 1女性" clearable @keyup.enter="handleQuery" /> <el-date-picker
</el-form-item> v-model="queryParams.date"
<el-form-item label="身份证号" prop="idCard"> type="date"
<el-input v-model="queryParams.idCard" placeholder="请输入身份证号" clearable @keyup.enter="handleQuery" /> placeholder="开始日期~结束日期"
</el-form-item> :disabled-date="disabledDate"
<el-form-item label="手机号码" prop="phone"> :shortcuts="shortcuts"
<el-input v-model="queryParams.phone" placeholder="请输入手机号码" clearable @keyup.enter="handleQuery" /> />
</el-form-item>
<el-form-item label="小区id" prop="villageId">
<el-input v-model="queryParams.villageId" placeholder="请输入小区id" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="楼栋id" prop="buildingId">
<el-input v-model="queryParams.buildingId" placeholder="请输入楼栋id" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="房屋表id" prop="houseId">
<el-input v-model="queryParams.houseId" placeholder="请输入房屋表id" clearable @keyup.enter="handleQuery" />
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button> <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
@@ -37,12 +32,16 @@
</div> </div>
</transition> </transition>
<el-card shadow="never"> <el-card style="height: 100%" shadow="never">
<div class="residentBody"> <div class="residentBody h-full">
<div class="treebox"> <div class="treebox h-full">
<!-- 树状+ --> <div class="treeTitlebox">北境碧桂园小区</div>
<div class="treeboxbody">
<!-- 树状+ -->
<el-tree :highlight-current="true" style="max-width: 600px" :data="treedata" :props="defaultProps" @node-click="handleNodeClick" />
</div>
</div> </div>
<div class="flex-1"> <div class="flex-1 h-full">
<el-row :gutter="10" class="mb8"> <el-row :gutter="10" class="mb8">
<el-col :span="1.5"> <el-col :span="1.5">
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['resident:resident:add']">新增</el-button> <el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['resident:resident:add']">新增</el-button>
@@ -59,29 +58,26 @@
</el-col> </el-col>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar> <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
</el-row> </el-row>
<el-table v-loading="loading" border :data="residentList" @selection-change="handleSelectionChange"> <el-table class="tablebox" v-loading="loading" border :data="residentList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" /> <el-table-column type="selection" width="55" align="center" />
<el-table-column label="姓名" align="center" prop="name" /> <el-table-column label="姓名" min-width="120" align="center" prop="name" />
<el-table-column label="类型" align="center" prop="type"> <el-table-column label="类型" min-width="120" align="center" prop="type">
<template #default="scope"> <template #default="scope">
<el-tag v-if="scope.row.type === 0" size="small">业主</el-tag> <el-tag v-if="scope.row.type === 0">业主</el-tag>
<el-tag v-else size="small">租户</el-tag> <el-tag type="warning" v-else-if="scope.row.type === 1">亲属</el-tag>
<el-tag type="success" v-else-if="scope.row.type === 2">租户</el-tag>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="证件号" align="center" prop="idCard" /> <el-table-column label="证件号" min-width="120" align="center" prop="idCard" />
<el-table-column label="手机号码" align="center" prop="phone" /> <el-table-column label="手机号码" min-width="120" align="center" prop="phone" />
<el-table-column label="房间" align="center" prop="houseId" /> <el-table-column label="房间" min-width="120" align="center" prop="houseAddress" />
<el-table-column label="操作" align="center" fixed="right" class-name="small-padding fixed-width"> <el-table-column label="操作" align="center" fixed="right" class-name="small-padding fixed-width">
<template #default="scope"> <template #default="scope">
<el-tooltip content="审核" placement="top"> <el-button link type="primary" icon="Edit" @click="handleMain(scope.row)" v-hasPermi="['resident:resident:edit']">审核</el-button>
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['resident:resident:edit']"></el-button> <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['resident:resident:edit']">修改</el-button>
</el-tooltip> <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['resident:resident:remove']"
<el-tooltip content="修改" placement="top"> >删除
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['resident:resident:edit']"></el-button> </el-button>
</el-tooltip>
<el-tooltip content="删除" placement="top">
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['resident:resident:remove']"></el-button>
</el-tooltip>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
@@ -95,61 +91,20 @@
</div> </div>
</div> </div>
</el-card> </el-card>
<!-- 添加或修改住户管理对话框 -->
<el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body>
<el-form ref="residentFormRef" :model="form" :rules="rules" label-width="80px">
<el-form-item label="住户编号" prop="code">
<el-input v-model="form.code" placeholder="请输入住户编号" />
</el-form-item>
<el-form-item label="姓名" prop="name">
<el-input v-model="form.name" placeholder="请输入姓名" />
</el-form-item>
<el-form-item label="0 男性 1女性" prop="gender">
<el-input v-model="form.gender" placeholder="请输入0 男性 1女性" />
</el-form-item>
<el-form-item label="身份证号" prop="idCard">
<el-input v-model="form.idCard" placeholder="请输入身份证号" />
</el-form-item>
<el-form-item label="手机号码" prop="phone">
<el-input v-model="form.phone" placeholder="请输入手机号码" />
</el-form-item>
<el-form-item label="小区id" prop="villageId">
<el-input v-model="form.villageId" placeholder="请输入小区id" />
</el-form-item>
<el-form-item label="楼栋id" prop="buildingId">
<el-input v-model="form.buildingId" placeholder="请输入楼栋id" />
</el-form-item>
<el-form-item label="房屋表id" prop="houseId">
<el-input v-model="form.houseId" placeholder="请输入房屋表id" />
</el-form-item>
<el-form-item label="身份证正面照url" prop="cardImageFront">
<el-input v-model="form.cardImageFront" placeholder="请输入身份证正面照url" />
</el-form-item>
<el-form-item label="身份证背面照url" prop="cardImageBack">
<el-input v-model="form.cardImageBack" placeholder="请输入身份证背面照url" />
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input v-model="form.remark" type="textarea" placeholder="请输入内容" />
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button :loading="buttonLoading" type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</template>
</el-dialog>
</div> </div>
</template> </template>
<script setup name="Resident" lang="ts"> <script setup name="Resident" lang="ts">
import { listResident, getResident, delResident, addResident, updateResident } from '@/api/system/resident/index'; import { listResident, getResident, delResident, addResident, updateResident } from '@/api/system/resident/index';
import { ResidentVO, ResidentQuery, ResidentForm } from '@/api/system/resident/type'; import { ResidentVO, ResidentQuery, ResidentForm } from '@/api/system/resident/type';
import { getVillageTree } from '@/api/system/house/index';
import { getCommunitylistAPI } from '@/api/system/community';
import { convertBuildingToTree, getHousePathById } from './index';
import { RootTreeType, TreeNode } from 'element-plus';
const { proxy } = getCurrentInstance() as ComponentInternalInstance; const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const router = useRouter();
const residentList = ref<ResidentVO[]>([]); const residentList = ref<ResidentVO[]>([]);
const buttonLoading = ref(false);
const loading = ref(true); const loading = ref(true);
const showSearch = ref(true); const showSearch = ref(true);
const ids = ref<Array<string | number>>([]); const ids = ref<Array<string | number>>([]);
@@ -158,12 +113,16 @@ const multiple = ref(true);
const total = ref(0); const total = ref(0);
const queryFormRef = ref<ElFormInstance>(); const queryFormRef = ref<ElFormInstance>();
const residentFormRef = ref<ElFormInstance>(); // 住户类型
const typelist = [
const dialog = reactive<DialogOption>({ { label: '业主', value: 0 },
visible: false, { label: '租户', value: 1 }
title: '' ];
}); // 住户状态
const statuslist = [
{ label: '审核中', value: 0 },
{ label: '已认证', value: 1 }
];
const initFormData: ResidentForm = { const initFormData: ResidentForm = {
id: undefined, id: undefined,
@@ -181,62 +140,161 @@ const initFormData: ResidentForm = {
cardImageBack: undefined, cardImageBack: undefined,
remark: undefined remark: undefined
}; };
const data = reactive<PageData<ResidentForm, ResidentQuery>>({ const data = reactive<PageData<ResidentForm, ResidentQuery>>({
form: { ...initFormData }, form: { ...initFormData },
queryParams: { queryParams: {
pageNum: 1, pageNum: 1,
pageSize: 10, pageSize: 10,
code: undefined, date: '',
name: undefined, type: null,
type: undefined, status: null,
gender: undefined,
idCard: undefined, houseId: null,
phone: undefined, villageId: null,
villageId: undefined, buildingId: null,
buildingId: undefined, unitNo: null
houseId: undefined,
status: undefined,
cardImageFront: undefined,
cardImageBack: undefined,
params: {}
},
rules: {
id: [{ required: true, message: '主键 住户管理id 不能为空', trigger: 'blur' }]
} }
}); });
const disabledDate = (time: Date) => {
return time.getTime() > Date.now();
};
const shortcuts = [
{
text: '今天',
value: new Date()
},
{
text: '昨天',
value: () => {
const date = new Date();
date.setDate(date.getDate() - 1);
return date;
}
},
{
text: ' 近3天',
value: () => {
const date = new Date();
date.setDate(date.getDate() - 3);
return date;
}
},
{
text: '最近一周',
value: () => {
const date = new Date();
date.setDate(date.getDate() - 7);
return date;
}
},
{
text: '最近一个月',
value: () => {
const date = new Date();
date.setMonth(date.getMonth() - 1);
return date;
}
},
{
text: '最近三个月',
value: () => {
const date = new Date();
date.setMonth(date.getMonth() - 3); // 真正减3个月不是90天
return date;
}
}
];
const { queryParams, form, rules } = toRefs(data); const { queryParams, form } = toRefs(data);
// ! 树形------------------------------------------------------------------------
interface Tree {
label: string;
value: number;
type: string;
children?: Tree[];
}
// 四个参数对应于节点点击的节点对象TreeNode 的 node 属性, TreeNode和事件对象
const handleNodeClick = (data: Tree, node) => {
const obj2 = {
[data.type]: data.value
};
if (data.type === 'unitNo') {
obj2[node.parent.data.type] = node.parent.data.value;
}
queryParams.value = {
...queryParams.value,
...{
houseId: null,
buildingId: null,
unitNo: null
},
...obj2
};
getList();
};
const treedata = ref<Tree[]>([]);
function gettreedata() {
getCommunitylistAPI().then((res) => {
const currentviliage = res.rows.find((item) => item.choiceStatus === 0);
if (currentviliage.villageId) {
getVillageTree(currentviliage.villageId).then((res) => {
treedata.value = convertBuildingToTree(res.data.buildings);
getList();
});
}
});
}
gettreedata();
const defaultProps = {
children: 'children',
label: 'label'
};
// ! 树形------------------------------------------------------------------------
/** 查询住户管理列表 */ /** 查询住户管理列表 */
const getList = async () => { const getList = async () => {
loading.value = true; loading.value = true;
const res = await listResident(queryParams.value); const res = await listResident(queryParams.value);
residentList.value = res.rows; residentList.value = res.rows;
console.log(residentList.value);
residentList.value.forEach((item) => {
const arr = getHousePathById(treedata.value, item.houseId, 'label');
console.log(arr);
item.houseAddress = arr.join(' ');
});
total.value = res.total; total.value = res.total;
loading.value = false; loading.value = false;
}; };
/** 取消按钮 */
const cancel = () => {
reset();
dialog.visible = false;
};
/** 表单重置 */
const reset = () => {
form.value = { ...initFormData };
residentFormRef.value?.resetFields();
};
/** 搜索按钮操作 */ /** 搜索按钮操作 */
const handleQuery = () => { const handleQuery = () => {
queryParams.value.pageNum = 1; queryParams.value.pageNum = 1;
queryParams.value = {
...queryParams.value,
...{
houseId: null,
buildingId: null,
unitNo: null
}
};
getList(); getList();
}; };
/** 重置按钮操作 */ /** 重置按钮操作 */
const resetQuery = () => { const resetQuery = () => {
queryFormRef.value?.resetFields(); queryFormRef.value?.resetFields();
queryParams.value = {
...queryParams.value,
...{
houseId: null,
buildingId: null,
unitNo: null
}
};
handleQuery(); handleQuery();
}; };
@@ -249,60 +307,40 @@ const handleSelectionChange = (selection: ResidentVO[]) => {
/** 新增按钮操作 */ /** 新增按钮操作 */
const handleAdd = () => { const handleAdd = () => {
reset(); router.push({
dialog.visible = true; path: '/house/addResident'
dialog.title = '添加住户管理'; });
}; };
/** 修改按钮操作 */ /** 修改按钮操作 */
const handleUpdate = async (row?: ResidentVO) => { const handleUpdate = async (row?: ResidentVO) => {
reset(); router.push({
const _id = row?.id || ids.value[0]; path: '/house/addResident',
const res = await getResident(_id); query: {
Object.assign(form.value, res.data); id: row.id
dialog.visible = true; }
dialog.title = '修改住户管理'; });
}; };
/** 审核/ 用户详情 */
/** 提交按钮 */ const handleMain = (row: ResidentVO) => {
const submitForm = () => { router.push({
residentFormRef.value?.validate(async (valid: boolean) => { path: '/house/ResidentMain',
if (valid) { query: {
buttonLoading.value = true; id: row.id
if (form.value.id) {
await updateResident(form.value).finally(() => (buttonLoading.value = false));
} else {
await addResident(form.value).finally(() => (buttonLoading.value = false));
}
proxy?.$modal.msgSuccess('操作成功');
dialog.visible = false;
await getList();
} }
}); });
}; };
/** 删除按钮操作 */ /** 删除按钮操作 */
const handleDelete = async (row?: ResidentVO) => { const handleDelete = async (row?: ResidentVO) => {
const _ids = row?.id || ids.value; const _ids = row?.id || ids.value;
await proxy?.$modal.confirm('是否确认删除住户管理编号为"' + _ids + '"的数据项').finally(() => (loading.value = false)); await proxy?.$modal.confirm('该信息删除后无法恢复,确定要删除吗').finally(() => (loading.value = false));
await delResident(_ids); await delResident(_ids);
proxy?.$modal.msgSuccess('删除成功'); proxy?.$modal.msgSuccess('删除成功');
await getList(); await getList();
}; };
/** 导出按钮操作 */
const handleExport = () => {
proxy?.download(
'resident/resident/export',
{
...queryParams.value
},
`resident_${new Date().getTime()}.xlsx`
);
};
onMounted(() => { onMounted(() => {
getList(); // getList();
}); });
</script> </script>
@@ -311,8 +349,48 @@ onMounted(() => {
display: flex; display: flex;
align-items: center; align-items: center;
.treebox { .treebox {
width: 300px; width: 250px;
margin-right: 10px; margin-right: 10px;
.treeTitlebox {
height: 60px;
line-height: 60px;
padding-left: 20px;
font-size: 18px;
font-weight: 600;
background-color: rgba(233, 242, 255, 0.5);
color: rgba(16, 16, 16, 1);
}
.treeboxbody {
margin-top: 10px;
&:deep(.el-tree-node__content) {
margin-bottom: 5px;
height: 35px;
line-height: 35px;
.el-tree-node__expand-icon {
font-size: 18px;
}
&:hover {
background: #d1e3ff;
.el-text {
color: #227aff;
}
.el-tree-node__expand-icon {
color: #227aff;
}
}
}
}
}
.tablebox {
height: calc(100% - 260px);
max-height: calc(100% - 260px);
/* 空数据也不会无限变宽 */
:deep(.el-table__empty-block) {
width: 100% !important;
max-width: 100% !important;
}
} }
} }
</style> </style>

View File

@@ -63,8 +63,6 @@
<el-table-column label="建筑面积(㎡)" align="center" prop="houseArea" /> <el-table-column label="建筑面积(㎡)" align="center" prop="houseArea" />
<el-table-column label="公摊面积(㎡)" align="center" prop="shareArea" /> <el-table-column label="公摊面积(㎡)" align="center" prop="shareArea" />
<el-table-column label="实用面积(㎡)" align="center" prop="internalArea" /> <el-table-column label="实用面积(㎡)" align="center" prop="internalArea" />
<el-table-column label="关联房屋ID" align="center" prop="houseId" />
<el-table-column label="状态" align="center" prop="status" />
<el-table-column label="备注" align="center" prop="remark" /> <el-table-column label="备注" align="center" prop="remark" />
<el-table-column label="操作" align="center" fixed="right" class-name="small-padding fixed-width"> <el-table-column label="操作" align="center" fixed="right" class-name="small-padding fixed-width">
<template #default="scope"> <template #default="scope">