会员房屋接口

master
zzy 2026-04-21 23:10:50 +08:00
parent 9d31d448e5
commit 554e2d7ec7
4 changed files with 177 additions and 102 deletions

View File

@ -11,7 +11,7 @@ export interface MemberHouse {
buildingNo: string; // 楼号
unitNo: string; // 单元号
roomNo: string; // 门牌号
isOwner?: number; // 是否产权人0-否1-是)
isOwner?: boolean; // 是否产权人false-否true-是)
relationType: string; // 与产权人关系
name: string; // 姓名
mobile: string; // 手机号
@ -20,8 +20,11 @@ export interface MemberHouse {
sex: number; // 性别0-未知1-男2-女)
birthday: string | Dayjs; // 出生日期
attachmentUrl: string; // 附件URL
facePhotoUrl: string; // 人脸照片URL
status?: number; // 认证状态0-待审核1-已认证2-驳回)
rejectReason: string; // 驳回原因
auditorName: string; // 审核人
auditTime: string | Dayjs; // 审核时间
}
// 业主认证信息 API
@ -59,5 +62,10 @@ export const MemberHouseApi = {
// 导出业主认证信息 Excel
exportMemberHouse: async (params) => {
return await request.download({ url: `/community/member-house/export-excel`, params })
},
// 审核业主认证
auditMemberHouse: async (data: { id: number; status: number; rejectReason?: string }) => {
return await request.put({ url: `/community/member-house/audit`, data })
}
}

View File

@ -11,25 +11,25 @@
<el-input v-model="formData.communityName" disabled />
</el-form-item>
<el-form-item label="关联房屋">
<el-input :value="`${formData.buildingNo}${formData.unitNo}${formData.roomNo}`" disabled />
<el-input :value="`${formData.buildingNo}号楼${formData.unitNo}单元${formData.roomNo}`" disabled />
</el-form-item>
<el-form-item label="人脸照片">
<el-image
v-if="formData.attachmentUrl"
:src="formData.attachmentUrl"
:preview-src-list="[formData.attachmentUrl]"
v-if="formData.facePhotoUrl"
:src="formData.facePhotoUrl"
fit="cover"
style="width: 150px; height: 150px"
style="width: 150px; height: 150px; cursor: pointer"
@click="handlePreviewImage(formData.facePhotoUrl)"
/>
<span v-else></span>
</el-form-item>
<el-form-item label="审核结果" prop="auditStatus" required>
<el-radio-group v-model="formData.auditStatus">
<el-form-item label="审核结果" prop="status" required>
<el-radio-group v-model="formData.status">
<el-radio :label="1">通过</el-radio>
<el-radio :label="2">驳回</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item v-if="formData.auditStatus === 2" label="驳回原因" prop="rejectReason" required>
<el-form-item v-if="formData.status === 2" label="驳回原因" prop="rejectReason" required>
<el-input
v-model="formData.rejectReason"
type="textarea"
@ -47,6 +47,7 @@
<script setup lang="ts">
import { MemberHouseApi, MemberHouse } from '@/api/community/memberhouse'
import { createImageViewer } from '@/components/ImageViewer'
defineOptions({ name: 'AuditDialog' })
@ -66,16 +67,11 @@ const formData = ref({
buildingNo: '',
unitNo: '',
roomNo: '',
attachmentUrl: '',
auditStatus: 1,
facePhotoUrl: '',
status: 1,
rejectReason: ''
})
const formRules = reactive({
auditStatus: [{ required: true, message: '请选择审核结果', trigger: 'change' }],
rejectReason: [{ required: true, message: '请输入驳回原因', trigger: 'blur' }]
})
const open = (row: MemberHouse) => {
dialogVisible.value = true
formData.value = {
@ -86,8 +82,8 @@ const open = (row: MemberHouse) => {
buildingNo: row.buildingNo,
unitNo: row.unitNo,
roomNo: row.roomNo,
attachmentUrl: row.attachmentUrl,
auditStatus: 1,
facePhotoUrl: row.facePhotoUrl,
status: 1,
rejectReason: ''
}
}
@ -95,18 +91,18 @@ const open = (row: MemberHouse) => {
defineExpose({ open })
const submitAudit = async () => {
if (formData.value.auditStatus === 2 && !formData.value.rejectReason) {
if (formData.value.status === 2 && !formData.value.rejectReason) {
message.error('请输入驳回原因')
return
}
formLoading.value = true
try {
await MemberHouseApi.updateMemberHouse({
await MemberHouseApi.auditMemberHouse({
id: formData.value.id!,
status: formData.value.auditStatus,
status: formData.value.status,
rejectReason: formData.value.rejectReason
} as MemberHouse)
})
message.success('审核成功')
dialogVisible.value = false
@ -115,4 +111,14 @@ const submitAudit = async () => {
formLoading.value = false
}
}
/** 预览图片 */
const handlePreviewImage = (url: string) => {
createImageViewer({
urlList: [url],
zIndex: 3000,
hideOnClickModal: true,
teleported: true
})
}
</script>

View File

@ -17,6 +17,7 @@
filterable
style="width: 100%"
@change="handleCommunityChange"
:disabled="readonlyMode"
>
<el-option
v-for="item in communityOptions"
@ -40,12 +41,13 @@
filterable
style="flex: 2"
@change="handleBuildingChange"
:disabled="readonlyMode"
>
<el-option
v-for="item in buildingOptions"
:key="item.value"
:label="item.label"
:value="item.label"
:value="item.value"
/>
</el-select>
<el-select
@ -55,12 +57,13 @@
filterable
style="flex: 2"
@change="handleUnitChange"
:disabled="readonlyMode"
>
<el-option
v-for="item in unitOptions"
:key="item.value"
:label="item.label"
:value="item.label"
:value="item.value"
/>
</el-select>
<el-select
@ -70,12 +73,13 @@
filterable
style="flex: 2"
@change="handleRoomChange"
:disabled="readonlyMode"
>
<el-option
v-for="item in roomOptions"
:key="item.value"
:label="item.label"
:value="item.label"
:value="item.value"
/>
</el-select>
</div>
@ -86,7 +90,7 @@
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="是否产权人" prop="isOwner">
<el-radio-group v-model="formData.isOwner">
<el-radio-group v-model="formData.isOwner" :disabled="readonlyMode">
<el-radio
v-for="dict in getBoolDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING)"
:key="dict.value"
@ -99,7 +103,7 @@
</el-col>
<el-col :span="12">
<el-form-item label="与业主关系" prop="relationType">
<el-select v-model="formData.relationType" placeholder="请选择与业主关系" style="width: 100%">
<el-select v-model="formData.relationType" placeholder="请选择与业主关系" style="width: 100%" :disabled="readonlyMode">
<el-option
v-for="dict in getStrDictOptions(DICT_TYPE.COMM_RELATION_TYPE)"
:key="dict.value"
@ -114,12 +118,12 @@
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="姓名" prop="name">
<el-input v-model="formData.name" placeholder="请输入姓名" />
<el-input v-model="formData.name" placeholder="请输入姓名" :disabled="readonlyMode" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="联系方式" prop="mobile">
<el-input v-model="formData.mobile" placeholder="请输入手机号" />
<el-input v-model="formData.mobile" placeholder="请输入手机号" :disabled="readonlyMode" />
</el-form-item>
</el-col>
</el-row>
@ -127,7 +131,7 @@
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="证件类型" prop="idType">
<el-select v-model="formData.idType" placeholder="请选择证件类型" style="width: 100%">
<el-select v-model="formData.idType" placeholder="请选择证件类型" style="width: 100%" :disabled="readonlyMode">
<el-option
v-for="dict in getStrDictOptions(DICT_TYPE.COMM_ID_TYPE)"
:key="dict.value"
@ -139,7 +143,7 @@
</el-col>
<el-col :span="12">
<el-form-item label="证件号码" prop="idNumber">
<el-input v-model="formData.idNumber" placeholder="请输入证件号" />
<el-input v-model="formData.idNumber" placeholder="请输入证件号" :disabled="readonlyMode" />
</el-form-item>
</el-col>
</el-row>
@ -147,7 +151,7 @@
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="性别" prop="sex">
<el-radio-group v-model="formData.sex">
<el-radio-group v-model="formData.sex" :disabled="readonlyMode">
<el-radio
v-for="dict in getIntDictOptions(DICT_TYPE.SYSTEM_USER_SEX)"
:key="dict.value"
@ -166,6 +170,7 @@
value-format="YYYY-MM-DD"
placeholder="选择出生日期"
style="width: 100%"
:disabled="readonlyMode"
/>
</el-form-item>
</el-col>
@ -173,13 +178,13 @@
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="人脸照片" prop="attachmentUrl">
<UploadImg v-model="formData.attachmentUrl" :limit="1" />
<el-form-item label="人脸照片" prop="facePhotoUrl">
<UploadImg v-model="formData.facePhotoUrl" :limit="1" :disabled="readonlyMode" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="附件" prop="attachmentUrls">
<UploadImgs v-model="formData.attachmentUrls" :limit="3" />
<el-form-item label="附件" prop="attachmentUrl">
<UploadImg v-model="formData.attachmentUrl" :limit="3" :disabled="readonlyMode" />
</el-form-item>
</el-col>
</el-row>
@ -188,19 +193,20 @@
<el-icon><DocumentChecked /></el-icon>
</el-divider>
<el-form-item label="审核意见" prop="auditStatus">
<el-select v-model="formData.auditStatus" placeholder="请选择审核意见" style="width: 100%">
<el-option label="通过" :value="1" />
<el-option label="驳回" :value="2" />
</el-select>
<el-form-item label="审核结果" prop="status">
<el-radio-group v-model="formData.status" :disabled="readonlyMode && formType !== 'audit'">
<el-radio :label="1">通过</el-radio>
<el-radio :label="2">驳回</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="审核描述" prop="rejectReason">
<el-form-item v-if="formData.status === 2" label="驳回原因" prop="rejectReason">
<el-input
v-model="formData.rejectReason"
type="textarea"
:rows="4"
placeholder="请输入审核描述"
placeholder="请输入驳回原因"
:disabled="readonlyMode && formType !== 'audit'"
/>
</el-form-item>
</el-form>
@ -229,7 +235,8 @@ const message = useMessage() // 消息弹窗
const dialogVisible = ref(false) //
const dialogTitle = ref('') //
const formLoading = ref(false) // 12
const formType = ref('') // create - update -
const formType = ref('') // create - update - audit -
const readonlyMode = ref(false) //
const formData = ref({
id: undefined,
memberId: undefined,
@ -239,7 +246,7 @@ const formData = ref({
buildingNo: '',
unitNo: '',
roomNo: '',
isOwner: 1,
isOwner: true,
relationType: '',
name: '',
mobile: '',
@ -248,11 +255,10 @@ const formData = ref({
sex: undefined,
birthday: '',
attachmentUrl: '',
attachmentUrls: [],
facePhotoUrl: '',
status: 0,
rejectReason: '',
auditStatus: undefined,
auditUser: '',
auditorName: '',
auditTime: ''
})
const formRules = reactive({
@ -271,7 +277,7 @@ const formRules = reactive({
idNumber: [{ required: true, message: '证件号码不能为空', trigger: 'blur' }],
sex: [{ required: true, message: '请选择性别', trigger: 'change' }],
birthday: [{ required: true, message: '出生日期不能为空', trigger: 'change' }],
auditStatus: [{ required: true, message: '请选择审核意见', trigger: 'change' }]
status: [{ required: true, message: '请选择审核结果', trigger: 'change' }]
})
const formRef = ref() // Ref
const communityOptions = ref<CommunitySimpleVO[]>([]) //
@ -284,8 +290,8 @@ const roomOptions = ref<HouseTreeNode[]>([]) // 房间选项
const loadCommunityList = async () => {
try {
communityOptions.value = await CommunityApi.getCommunitySimpleList()
//
if (communityOptions.value.length > 0 && !formData.value.communityId) {
//
if (communityOptions.value.length > 0 && !formData.value.communityId && formType.value !== 'audit') {
formData.value.communityId = communityOptions.value[0].id
handleCommunityChange(communityOptions.value[0].id)
}
@ -325,7 +331,7 @@ const handleCommunityChange = async (communityId: number | undefined) => {
}
/** 楼号选择变化 */
const handleBuildingChange = (buildingLabel: string) => {
const handleBuildingChange = (buildingValue: string) => {
//
unitOptions.value = []
roomOptions.value = []
@ -333,8 +339,8 @@ const handleBuildingChange = (buildingLabel: string) => {
formData.value.roomNo = ''
formData.value.houseId = undefined
if (buildingLabel) {
const buildingNode = houseTreeOptions.value.find(n => n.label === buildingLabel)
if (buildingValue) {
const buildingNode = houseTreeOptions.value.find(n => n.value === buildingValue)
if (buildingNode?.children) {
unitOptions.value = buildingNode.children
}
@ -342,14 +348,14 @@ const handleBuildingChange = (buildingLabel: string) => {
}
/** 单元号选择变化 */
const handleUnitChange = (unitLabel: string) => {
const handleUnitChange = (unitValue: string) => {
//
roomOptions.value = []
formData.value.roomNo = ''
formData.value.houseId = undefined
if (unitLabel) {
const unitNode = unitOptions.value.find(n => n.label === unitLabel)
if (unitValue) {
const unitNode = unitOptions.value.find(n => n.value === unitValue)
if (unitNode?.children) {
roomOptions.value = unitNode.children
}
@ -357,9 +363,9 @@ const handleUnitChange = (unitLabel: string) => {
}
/** 门牌号选择变化 */
const handleRoomChange = (roomLabel: string) => {
if (roomLabel) {
const roomNode = roomOptions.value.find(n => n.label === roomLabel)
const handleRoomChange = (roomValue: string) => {
if (roomValue) {
const roomNode = roomOptions.value.find(n => n.value === roomValue)
if (roomNode) {
formData.value.houseId = roomNode.value
}
@ -368,45 +374,77 @@ const handleRoomChange = (roomLabel: string) => {
}
}
/** 根据 value 查找节点并返回显示文本 */
const findNodeLabel = (tree: HouseTreeNode[], value: string): string => {
for (const node of tree) {
if (node.value === value) {
return node.label
}
if (node.children) {
const found = findNodeLabel(node.children, value)
if (found) return found
}
}
return value
}
/** 打开弹窗 */
const open = async (type: string, id?: number) => {
dialogVisible.value = true
dialogTitle.value = t('action.' + type)
formType.value = type
readonlyMode.value = type === 'audit'
if (type === 'create') {
dialogTitle.value = t('action.create')
resetForm()
} else if (type === 'update') {
dialogTitle.value = t('action.update')
resetForm()
} else if (type === 'audit') {
dialogTitle.value = '审核业主认证'
resetForm()
}
//
await loadCommunityList()
//
//
if (id) {
formLoading.value = true
try {
const data = await MemberHouseApi.getMemberHouse(id)
formData.value = {
...formData.value,
...data
}
//
if (data.communityId) {
try {
houseTreeOptions.value = await HouseApi.getHouseTree(data.communityId)
buildingOptions.value = houseTreeOptions.value || []
//
// - value
if (data.buildingNo) {
const buildingNode = houseTreeOptions.value.find(n => n.label === data.buildingNo)
if (buildingNode?.children) {
const buildingLabel = data.buildingNo.replace('号楼', '')
const buildingNode = houseTreeOptions.value.find(n => n.label === buildingLabel)
if (buildingNode) {
formData.value.buildingNo = buildingNode.value
if (buildingNode.children) {
unitOptions.value = buildingNode.children
//
if (data.unitNo) {
const unitNode = buildingNode.children.find(n => n.label === data.unitNo)
if (unitNode?.children) {
const unitLabel = data.unitNo.replace('单元', '')
const unitNode = buildingNode.children.find(n => n.label === unitLabel)
if (unitNode) {
formData.value.unitNo = unitNode.value
if (unitNode.children) {
roomOptions.value = unitNode.children
//
if (data.roomNo) {
const roomNode = unitNode.children.find(n => n.label === data.roomNo)
if (roomNode) {
formData.value.roomNo = roomNode.value
formData.value.houseId = roomNode.value
}
}
@ -414,10 +452,18 @@ const open = async (type: string, id?: number) => {
}
}
}
}
}
} catch (error) {
console.error('加载房屋树失败:', error)
}
}
//
formData.value = {
...formData.value,
...data
}
} finally {
formLoading.value = false
}
@ -432,11 +478,21 @@ const submitForm = async () => {
if (!formRef) return
const valid = await formRef.value.validate()
if (!valid) return
//
formLoading.value = true
try {
const data = formData.value as unknown as MemberHouse
if (formType.value === 'create') {
if (formType.value === 'audit') {
//
await MemberHouseApi.auditMemberHouse({
id: formData.value.id!,
status: formData.value.status,
rejectReason: formData.value.rejectReason
})
message.success('审核成功')
} else if (formType.value === 'create') {
await MemberHouseApi.createMemberHouse(data)
message.success(t('common.createSuccess'))
} else {
@ -462,7 +518,7 @@ const resetForm = () => {
buildingNo: '',
unitNo: '',
roomNo: '',
isOwner: 1,
isOwner: true,
relationType: '',
name: '',
mobile: '',
@ -471,11 +527,10 @@ const resetForm = () => {
sex: undefined,
birthday: '',
attachmentUrl: '',
attachmentUrls: [],
facePhotoUrl: '',
status: 0,
rejectReason: '',
auditStatus: undefined,
auditUser: '',
auditorName: '',
auditTime: ''
}
buildingOptions.value = []

View File

@ -140,14 +140,14 @@
<dict-tag :type="DICT_TYPE.COMM_RELATION_TYPE" :value="scope.row.relationType" />
</template>
</el-table-column>
<el-table-column label="人脸照片" align="center" prop="attachmentUrl" width="80">
<el-table-column label="人脸照片" align="center" prop="facePhotoUrl" width="80">
<template #default="scope">
<el-image
v-if="scope.row.attachmentUrl"
:src="scope.row.attachmentUrl"
:preview-src-list="[scope.row.attachmentUrl]"
v-if="scope.row.facePhotoUrl"
:src="scope.row.facePhotoUrl"
fit="cover"
class="w-40px h-40px cursor-pointer"
@click="handlePreviewImage(scope.row.facePhotoUrl)"
/>
<span v-else>-</span>
</template>
@ -157,13 +157,13 @@
<dict-tag :type="DICT_TYPE.COMM_AUDIT_STATUS" :value="scope.row.status" />
</template>
</el-table-column>
<el-table-column label="审核人" align="center" prop="auditUser" width="100" />
<el-table-column label="审核人" align="center" prop="auditorName" width="100" />
<el-table-column label="审核时间" align="center" prop="auditTime" width="160" :formatter="dateFormatter" />
<el-table-column label="操作" align="center" width="160" fixed="right">
<template #default="scope">
<div class="action-buttons">
<el-button
v-if="scope.row.status === 0"
v-if="scope.row.status === 0 || scope.row.status === 2"
link
type="primary"
@click="handleAudit(scope.row)"
@ -204,11 +204,8 @@
</div>
</ContentWrap>
<!-- 表单弹窗添加/修改 -->
<!-- 表单弹窗添加/修改/审核 -->
<MemberHouseForm ref="formRef" @success="getList" />
<!-- 审核弹窗 -->
<AuditDialog ref="auditDialogRef" @success="getList" />
</template>
<script setup lang="ts">
@ -218,7 +215,7 @@ import download from '@/utils/download'
import {DICT_TYPE, getBoolDictOptions, getIntDictOptions, getStrDictOptions} from '@/utils/dict'
import { MemberHouseApi, MemberHouse } from '@/api/community/memberhouse'
import MemberHouseForm from './MemberHouseForm.vue'
import AuditDialog from './AuditDialog.vue'
import { createImageViewer } from '@/components/ImageViewer'
/** 业主认证信息 列表 */
defineOptions({ name: 'MemberHouse' })
@ -294,17 +291,16 @@ const handleDelete = async (id: number) => {
}
/** 审核操作 */
const auditDialogRef = ref()
const handleAudit = (row: MemberHouse) => {
auditDialogRef.value.open(row)
formRef.value.open('audit', row.id)
}
/** 解除绑定操作 */
const handleUnbind = async (row: MemberHouse) => {
try {
await message.confirm('确认要解除该房屋绑定吗?')
await MemberHouseApi.updateMemberHouse({
...row,
await MemberHouseApi.auditMemberHouse({
id: row.id!,
status: 0
})
message.success('解除绑定成功')
@ -330,6 +326,16 @@ const handleExport = async () => {
}
}
/** 预览图片 */
const handlePreviewImage = (url: string) => {
createImageViewer({
urlList: [url],
zIndex: 3000,
hideOnClickModal: true,
teleported: true
})
}
/** 初始化 **/
onMounted(() => {
getList()