Commit 88f005b57b8d4411eb1c45fbe5b60192264bd187
1 parent
15981f1e
业主页面开发完成
Showing
13 changed files
with
1784 additions
and
5 deletions
src/api/owner/ownerApi.js
0 → 100644
| 1 | +import request from '@/utils/request' | |
| 2 | +import { getCommunityId } from '@/api/community/communityApi' | |
| 3 | + | |
| 4 | +// 查询业主列表 | |
| 5 | +export function queryOwners(params) { | |
| 6 | + return new Promise((resolve, reject) => { | |
| 7 | + params.communityId = getCommunityId() | |
| 8 | + request({ | |
| 9 | + url: '/owner.queryOwners', | |
| 10 | + method: 'get', | |
| 11 | + params | |
| 12 | + }).then(response => { | |
| 13 | + const res = response.data | |
| 14 | + if (res.code == 0) { | |
| 15 | + resolve(res) | |
| 16 | + } else { | |
| 17 | + reject(new Error(res.msg || '查询业主列表失败')) | |
| 18 | + } | |
| 19 | + }).catch(error => { | |
| 20 | + reject(error) | |
| 21 | + }) | |
| 22 | + }) | |
| 23 | +} | |
| 24 | + | |
| 25 | +// 保存业主 | |
| 26 | +export function saveOwner(data) { | |
| 27 | + return new Promise((resolve, reject) => { | |
| 28 | + data.communityId = getCommunityId() | |
| 29 | + request({ | |
| 30 | + url: '/owner.saveOwner', | |
| 31 | + method: 'post', | |
| 32 | + data | |
| 33 | + }).then(response => { | |
| 34 | + const res = response.data | |
| 35 | + if (res.code == 0) { | |
| 36 | + resolve(res) | |
| 37 | + } else { | |
| 38 | + reject(new Error(res.msg || '保存业主失败')) | |
| 39 | + } | |
| 40 | + }).catch(error => { | |
| 41 | + reject(error) | |
| 42 | + }) | |
| 43 | + }) | |
| 44 | +} | |
| 45 | + | |
| 46 | +// 编辑业主 | |
| 47 | +export function editOwner(data) { | |
| 48 | + return new Promise((resolve, reject) => { | |
| 49 | + data.communityId = getCommunityId() | |
| 50 | + request({ | |
| 51 | + url: '/owner.editOwner', | |
| 52 | + method: 'post', | |
| 53 | + data | |
| 54 | + }).then(response => { | |
| 55 | + const res = response.data | |
| 56 | + if (res.code == 0) { | |
| 57 | + resolve(res) | |
| 58 | + } else { | |
| 59 | + reject(new Error(res.msg || '编辑业主失败')) | |
| 60 | + } | |
| 61 | + }).catch(error => { | |
| 62 | + reject(error) | |
| 63 | + }) | |
| 64 | + }) | |
| 65 | +} | |
| 66 | + | |
| 67 | +// 删除业主 | |
| 68 | +export function deleteOwner(data) { | |
| 69 | + return new Promise((resolve, reject) => { | |
| 70 | + data.communityId = getCommunityId() | |
| 71 | + request({ | |
| 72 | + url: '/owner.deleteOwner', | |
| 73 | + method: 'post', | |
| 74 | + data | |
| 75 | + }).then(response => { | |
| 76 | + const res = response.data | |
| 77 | + if (res.code == 0) { | |
| 78 | + resolve(res) | |
| 79 | + } else { | |
| 80 | + reject(new Error(res.msg || '删除业主失败')) | |
| 81 | + } | |
| 82 | + }).catch(error => { | |
| 83 | + reject(error) | |
| 84 | + }) | |
| 85 | + }) | |
| 86 | +} | |
| 87 | + | |
| 88 | +// 上传图片 | |
| 89 | +export function uploadImage(data) { | |
| 90 | + return new Promise((resolve, reject) => { | |
| 91 | + const formData = new FormData() | |
| 92 | + formData.append('uploadFile', data.file) | |
| 93 | + formData.append('communityId', getCommunityId()) | |
| 94 | + | |
| 95 | + request({ | |
| 96 | + url: 'uploadImage', | |
| 97 | + method: 'post', | |
| 98 | + data: formData, | |
| 99 | + headers: { | |
| 100 | + 'Content-Type': 'multipart/form-data' | |
| 101 | + } | |
| 102 | + }).then(response => { | |
| 103 | + const res = response.data | |
| 104 | + if (res.code == 0) { | |
| 105 | + resolve(res) | |
| 106 | + } else { | |
| 107 | + reject(new Error(res.msg || '上传图片失败')) | |
| 108 | + } | |
| 109 | + }).catch(error => { | |
| 110 | + reject(error) | |
| 111 | + }) | |
| 112 | + }) | |
| 113 | +} | |
| 114 | + | |
| 115 | +// 获取属性规格 | |
| 116 | +export function getAttrSpec(specCd) { | |
| 117 | + return new Promise((resolve, reject) => { | |
| 118 | + request({ | |
| 119 | + url: '/attrSpec.listAttrSpecs', | |
| 120 | + method: 'get', | |
| 121 | + params: { specCd, communityId: getCommunityId() } | |
| 122 | + }).then(response => { | |
| 123 | + const res = response.data | |
| 124 | + if (res.code == 0) { | |
| 125 | + resolve(res.data) | |
| 126 | + } else { | |
| 127 | + reject(new Error(res.msg || '获取属性规格失败')) | |
| 128 | + } | |
| 129 | + }).catch(error => { | |
| 130 | + reject(error) | |
| 131 | + }) | |
| 132 | + }) | |
| 133 | +} | |
| 134 | + | |
| 135 | +// 获取属性值 | |
| 136 | +export function getAttrValue(specCd) { | |
| 137 | + return new Promise((resolve, reject) => { | |
| 138 | + request({ | |
| 139 | + url: '/attrValue.listAttrValues', | |
| 140 | + method: 'get', | |
| 141 | + params: { specCd, communityId: getCommunityId() } | |
| 142 | + }).then(response => { | |
| 143 | + const res = response.data | |
| 144 | + if (res.code == 0) { | |
| 145 | + resolve(res.data) | |
| 146 | + } else { | |
| 147 | + reject(new Error(res.msg || '获取属性值失败')) | |
| 148 | + } | |
| 149 | + }).catch(error => { | |
| 150 | + reject(error) | |
| 151 | + }) | |
| 152 | + }) | |
| 153 | +} | |
| 0 | 154 | \ No newline at end of file | ... | ... |
src/components/owner/addOwner.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <el-dialog :title="$t('listOwner.buttons.add')" :visible.sync="visible" width="800px" @close="resetForm"> | |
| 3 | + <el-form ref="form" :model="form" :rules="rules" label-width="120px"> | |
| 4 | + <el-row :gutter="20"> | |
| 5 | + <el-col :span="12"> | |
| 6 | + <el-form-item :label="$t('listOwner.columns.type')" prop="personType"> | |
| 7 | + <el-select v-model="form.personType" style="width:100%"> | |
| 8 | + <el-option v-for="(type, index) in personTypes" :key="index" :label="type.label" :value="type.value" /> | |
| 9 | + </el-select> | |
| 10 | + </el-form-item> | |
| 11 | + | |
| 12 | + <el-form-item :label="$t('listOwner.columns.role')" prop="personRole"> | |
| 13 | + <el-select v-model="form.personRole" style="width:100%"> | |
| 14 | + <el-option v-for="role in personRoles" :key="role.value" :label="role.label" :value="role.value" /> | |
| 15 | + </el-select> | |
| 16 | + </el-form-item> | |
| 17 | + | |
| 18 | + <el-form-item :label="$t('listOwner.columns.name')" prop="name"> | |
| 19 | + <el-input v-model="form.name" :placeholder="$t('listOwner.placeholders.name')" /> | |
| 20 | + </el-form-item> | |
| 21 | + | |
| 22 | + <el-form-item v-if="form.personType === 'C'" :label="$t('listOwner.buttons.member')"> | |
| 23 | + <el-input v-model="form.concactPerson" /> | |
| 24 | + </el-form-item> | |
| 25 | + | |
| 26 | + <el-form-item :label="$t('listOwner.columns.phone')" prop="link"> | |
| 27 | + <el-input v-model="form.link" :placeholder="$t('listOwner.placeholders.phone')" /> | |
| 28 | + </el-form-item> | |
| 29 | + | |
| 30 | + <el-form-item v-if="form.personType === 'P'" :label="$t('listOwner.columns.gender')" prop="sex"> | |
| 31 | + <el-select v-model="form.sex" style="width:100%"> | |
| 32 | + <el-option v-for="gender in genders" :key="gender.value" :label="gender.label" :value="gender.value" /> | |
| 33 | + </el-select> | |
| 34 | + </el-form-item> | |
| 35 | + </el-col> | |
| 36 | + | |
| 37 | + <el-col :span="12" class="text-center"> | |
| 38 | + <el-image style="width: 200px; height: 150px; border: 1px dashed #ccc;" | |
| 39 | + :src="form.ownerPhotoUrl || '/img/noPhoto.jpg'" fit="cover" /> | |
| 40 | + <el-upload class="mt-10" :show-file-list="false" :before-upload="beforeUpload" :http-request="uploadImage"> | |
| 41 | + <el-button size="small" type="primary"> | |
| 42 | + {{ $t('common.upload') }} | |
| 43 | + </el-button> | |
| 44 | + </el-upload> | |
| 45 | + </el-col> | |
| 46 | + </el-row> | |
| 47 | + | |
| 48 | + <el-row :gutter="20"> | |
| 49 | + <el-col :span="12"> | |
| 50 | + <el-form-item :label="$t('listOwner.columns.backupPhone')"> | |
| 51 | + <el-input v-model="form.concactLink" /> | |
| 52 | + </el-form-item> | |
| 53 | + </el-col> | |
| 54 | + | |
| 55 | + <el-col :span="12"> | |
| 56 | + <el-form-item :label="$t('listOwner.columns.address')"> | |
| 57 | + <el-input v-model="form.address" /> | |
| 58 | + </el-form-item> | |
| 59 | + </el-col> | |
| 60 | + </el-row> | |
| 61 | + | |
| 62 | + <el-row :gutter="20"> | |
| 63 | + <el-col :span="12"> | |
| 64 | + <el-form-item v-if="form.personType === 'C'" :label="$t('listOwner.columns.idCard')"> | |
| 65 | + <el-input v-model="form.idCard" /> | |
| 66 | + </el-form-item> | |
| 67 | + | |
| 68 | + <el-form-item v-else :label="$t('listOwner.columns.idCard')"> | |
| 69 | + <el-input v-model="form.idCard" /> | |
| 70 | + </el-form-item> | |
| 71 | + </el-col> | |
| 72 | + | |
| 73 | + <el-col :span="12"> | |
| 74 | + <el-form-item :label="$t('common.remark')"> | |
| 75 | + <el-input v-model="form.remark" /> | |
| 76 | + </el-form-item> | |
| 77 | + </el-col> | |
| 78 | + </el-row> | |
| 79 | + | |
| 80 | + <div v-for="(item, index) in attrs" :key="index"> | |
| 81 | + <el-row :gutter="20" v-if="index % 2 === 0"> | |
| 82 | + <el-col :span="12"> | |
| 83 | + <el-form-item :label="item.specName"> | |
| 84 | + <el-input v-if="item.specType === '2233'" v-model="item.value" :placeholder="item.specHoldplace" /> | |
| 85 | + <el-select v-else-if="item.specType === '3344'" v-model="item.value" style="width:100%"> | |
| 86 | + <el-option v-for="val in item.values" :key="val.value" :label="val.valueName" :value="val.value" /> | |
| 87 | + </el-select> | |
| 88 | + </el-form-item> | |
| 89 | + </el-col> | |
| 90 | + | |
| 91 | + <el-col :span="12" v-if="index < attrs.length - 1"> | |
| 92 | + <el-form-item :label="attrs[index + 1].specName"> | |
| 93 | + <el-input v-if="attrs[index + 1].specType === '2233'" v-model="attrs[index + 1].value" | |
| 94 | + :placeholder="attrs[index + 1].specHoldplace" /> | |
| 95 | + <el-select v-else-if="attrs[index + 1].specType === '3344'" v-model="attrs[index + 1].value" | |
| 96 | + style="width:100%"> | |
| 97 | + <el-option v-for="val in attrs[index + 1].values" :key="val.value" :label="val.valueName" | |
| 98 | + :value="val.value" /> | |
| 99 | + </el-select> | |
| 100 | + </el-form-item> | |
| 101 | + </el-col> | |
| 102 | + </el-row> | |
| 103 | + </div> | |
| 104 | + </el-form> | |
| 105 | + | |
| 106 | + <div slot="footer" class="dialog-footer"> | |
| 107 | + <el-button @click="visible = false"> | |
| 108 | + {{ $t('listOwner.buttons.cancel') }} | |
| 109 | + </el-button> | |
| 110 | + <el-button type="primary" @click="saveOwner"> | |
| 111 | + {{ $t('listOwner.buttons.save') }} | |
| 112 | + </el-button> | |
| 113 | + </div> | |
| 114 | + </el-dialog> | |
| 115 | +</template> | |
| 116 | + | |
| 117 | +<script> | |
| 118 | +import { saveOwner, getAttrValue, uploadImage } from '@/api/owner/ownerApi' | |
| 119 | +import { getAttrSpecList } from '@/api/dev/attrSpecApi' | |
| 120 | + | |
| 121 | +export default { | |
| 122 | + name: 'AddOwner', | |
| 123 | + data() { | |
| 124 | + return { | |
| 125 | + visible: false, | |
| 126 | + form: { | |
| 127 | + name: '', | |
| 128 | + link: '', | |
| 129 | + address: '', | |
| 130 | + sex: '0', | |
| 131 | + remark: '', | |
| 132 | + ownerPhoto: '', | |
| 133 | + ownerPhotoUrl: '', | |
| 134 | + idCard: '', | |
| 135 | + personType: 'P', | |
| 136 | + personRole: '1', | |
| 137 | + concactPerson: '', | |
| 138 | + concactLink: '' | |
| 139 | + }, | |
| 140 | + attrs: [], | |
| 141 | + personTypes: [ | |
| 142 | + { value: 'P', label: this.$t('listOwner.personType.personal') }, | |
| 143 | + { value: 'C', label: this.$t('listOwner.personType.company') } | |
| 144 | + ], | |
| 145 | + personRoles: [ | |
| 146 | + { value: '1', label: this.$t('listOwner.role.owner') }, | |
| 147 | + { value: '2', label: this.$t('listOwner.role.tenant') } | |
| 148 | + ], | |
| 149 | + genders: [ | |
| 150 | + { value: '0', label: this.$t('listOwner.gender.male') }, | |
| 151 | + { value: '1', label: this.$t('listOwner.gender.female') } | |
| 152 | + ], | |
| 153 | + rules: { | |
| 154 | + name: [ | |
| 155 | + { required: true, message: this.$t('listOwner.rules.nameRequired'), trigger: 'blur' }, | |
| 156 | + { min: 2, max: 64, message: this.$t('listOwner.rules.nameLength'), trigger: 'blur' } | |
| 157 | + ], | |
| 158 | + personType: [ | |
| 159 | + { required: true, message: this.$t('listOwner.rules.typeRequired'), trigger: 'change' } | |
| 160 | + ], | |
| 161 | + personRole: [ | |
| 162 | + { required: true, message: this.$t('listOwner.rules.roleRequired'), trigger: 'change' } | |
| 163 | + ], | |
| 164 | + link: [ | |
| 165 | + { required: true, message: this.$t('listOwner.rules.phoneRequired'), trigger: 'blur' }, | |
| 166 | + { pattern: /^1[3-9]\d{9}$/, message: this.$t('listOwner.rules.phoneFormat'), trigger: 'blur' } | |
| 167 | + ], | |
| 168 | + sex: [ | |
| 169 | + { required: true, message: this.$t('listOwner.rules.genderRequired'), trigger: 'change' } | |
| 170 | + ] | |
| 171 | + } | |
| 172 | + } | |
| 173 | + }, | |
| 174 | + methods: { | |
| 175 | + open() { | |
| 176 | + this.visible = true | |
| 177 | + this.loadAttributes() | |
| 178 | + }, | |
| 179 | + | |
| 180 | + async loadAttributes() { | |
| 181 | + try { | |
| 182 | + const data = await getAttrSpecList({ page: 1, row: 100, tableName: 'building_owner_attr' }) | |
| 183 | + this.attrs = data.filter(item => item.specShow === 'Y') | |
| 184 | + | |
| 185 | + for (const attr of this.attrs) { | |
| 186 | + if (attr.specType === '3344') { | |
| 187 | + attr.values = await getAttrValue(attr.specCd) | |
| 188 | + attr.value = '' | |
| 189 | + } | |
| 190 | + } | |
| 191 | + } catch (error) { | |
| 192 | + console.error('Failed to load attributes:', error) | |
| 193 | + } | |
| 194 | + }, | |
| 195 | + | |
| 196 | + beforeUpload(file) { | |
| 197 | + const isJPG = file.type === 'image/jpeg' | |
| 198 | + const isLt2M = file.size / 1024 / 1024 < 2 | |
| 199 | + | |
| 200 | + if (!isJPG) { | |
| 201 | + this.$message.error(this.$t('listOwner.upload.jpgOnly')) | |
| 202 | + } | |
| 203 | + if (!isLt2M) { | |
| 204 | + this.$message.error(this.$t('listOwner.upload.sizeLimit')) | |
| 205 | + } | |
| 206 | + return isJPG && isLt2M | |
| 207 | + }, | |
| 208 | + | |
| 209 | + async uploadImage({ file }) { | |
| 210 | + try { | |
| 211 | + const res = await uploadImage({ file }) | |
| 212 | + this.form.ownerPhoto = res.data.fileId | |
| 213 | + this.form.ownerPhotoUrl = res.data.url | |
| 214 | + } catch (error) { | |
| 215 | + this.$message.error(this.$t('listOwner.upload.failed')) | |
| 216 | + } | |
| 217 | + }, | |
| 218 | + | |
| 219 | + async saveOwner() { | |
| 220 | + this.$refs.form.validate(async valid => { | |
| 221 | + if (!valid) return | |
| 222 | + | |
| 223 | + try { | |
| 224 | + const params = { | |
| 225 | + ...this.form, | |
| 226 | + attrs: this.attrs.map(attr => ({ | |
| 227 | + specCd: attr.specCd, | |
| 228 | + value: attr.value | |
| 229 | + })) | |
| 230 | + } | |
| 231 | + | |
| 232 | + await saveOwner(params) | |
| 233 | + this.$message.success(this.$t('listOwner.saveSuccess')) | |
| 234 | + this.visible = false | |
| 235 | + this.$emit('success') | |
| 236 | + } catch (error) { | |
| 237 | + this.$message.error(this.$t('listOwner.saveFailed')) | |
| 238 | + } | |
| 239 | + }) | |
| 240 | + }, | |
| 241 | + | |
| 242 | + resetForm() { | |
| 243 | + this.$refs.form.resetFields() | |
| 244 | + this.form = { | |
| 245 | + name: '', | |
| 246 | + link: '', | |
| 247 | + address: '', | |
| 248 | + sex: '0', | |
| 249 | + remark: '', | |
| 250 | + ownerPhoto: '', | |
| 251 | + ownerPhotoUrl: '', | |
| 252 | + idCard: '', | |
| 253 | + personType: 'P', | |
| 254 | + personRole: '1', | |
| 255 | + concactPerson: '', | |
| 256 | + concactLink: '' | |
| 257 | + } | |
| 258 | + this.attrs.forEach(attr => { | |
| 259 | + attr.value = '' | |
| 260 | + }) | |
| 261 | + } | |
| 262 | + } | |
| 263 | +} | |
| 264 | +</script> | |
| 265 | + | |
| 266 | +<style scoped> | |
| 267 | +.text-center { | |
| 268 | + text-align: center; | |
| 269 | +} | |
| 270 | + | |
| 271 | +.mt-10 { | |
| 272 | + margin-top: 10px; | |
| 273 | +} | |
| 274 | +</style> | |
| 0 | 275 | \ No newline at end of file | ... | ... |
src/components/owner/deleteOwner.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <el-dialog :title="$t('listOwner.buttons.delete')" :visible.sync="visible" width="500px"> | |
| 3 | + <p>{{ $t('listOwner.confirmDelete') }}</p> | |
| 4 | + | |
| 5 | + <div slot="footer" class="dialog-footer"> | |
| 6 | + <el-button @click="visible = false"> | |
| 7 | + {{ $t('listOwner.buttons.cancel') }} | |
| 8 | + </el-button> | |
| 9 | + <el-button type="primary" @click="confirmDelete"> | |
| 10 | + {{ $t('common.confirm') }} | |
| 11 | + </el-button> | |
| 12 | + </div> | |
| 13 | + </el-dialog> | |
| 14 | +</template> | |
| 15 | + | |
| 16 | +<script> | |
| 17 | +import { deleteOwner } from '@/api/owner/ownerApi' | |
| 18 | + | |
| 19 | +export default { | |
| 20 | + name: 'DeleteOwner', | |
| 21 | + data() { | |
| 22 | + return { | |
| 23 | + visible: false, | |
| 24 | + owner: {} | |
| 25 | + } | |
| 26 | + }, | |
| 27 | + methods: { | |
| 28 | + open(owner) { | |
| 29 | + this.owner = { ...owner } | |
| 30 | + this.visible = true | |
| 31 | + }, | |
| 32 | + | |
| 33 | + async confirmDelete() { | |
| 34 | + try { | |
| 35 | + await deleteOwner({ | |
| 36 | + ownerId: this.owner.ownerId | |
| 37 | + }) | |
| 38 | + this.$message.success(this.$t('listOwner.deleteSuccess')) | |
| 39 | + this.visible = false | |
| 40 | + this.$emit('success') | |
| 41 | + } catch (error) { | |
| 42 | + this.$message.error(this.$t('listOwner.deleteFailed')) | |
| 43 | + } | |
| 44 | + } | |
| 45 | + } | |
| 46 | +} | |
| 47 | +</script> | |
| 0 | 48 | \ No newline at end of file | ... | ... |
src/components/owner/editOwner.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <el-dialog :title="$t('listOwner.buttons.edit')" :visible.sync="visible" width="800px" @close="resetForm"> | |
| 3 | + <el-form ref="form" :model="form" :rules="rules" label-width="120px"> | |
| 4 | + <el-row :gutter="20"> | |
| 5 | + <el-col :span="12"> | |
| 6 | + <el-form-item :label="$t('listOwner.columns.type')" prop="personType"> | |
| 7 | + <el-select v-model="form.personType" style="width:100%"> | |
| 8 | + <el-option v-for="(type,index) in personTypes" :key="index" :label="type.label" :value="type.value" /> | |
| 9 | + </el-select> | |
| 10 | + </el-form-item> | |
| 11 | + | |
| 12 | + <el-form-item :label="$t('listOwner.columns.role')" prop="personRole"> | |
| 13 | + <el-select v-model="form.personRole" style="width:100%"> | |
| 14 | + <el-option v-for="role in personRoles" :key="role.value" :label="role.label" :value="role.value" /> | |
| 15 | + </el-select> | |
| 16 | + </el-form-item> | |
| 17 | + | |
| 18 | + <el-form-item :label="$t('listOwner.columns.name')" prop="name"> | |
| 19 | + <el-input v-model="form.name" :placeholder="$t('listOwner.placeholders.name')" /> | |
| 20 | + </el-form-item> | |
| 21 | + | |
| 22 | + <el-form-item v-if="form.personType === 'C'" :label="$t('listOwner.buttons.member')"> | |
| 23 | + <el-input v-model="form.concactPerson" /> | |
| 24 | + </el-form-item> | |
| 25 | + | |
| 26 | + <el-form-item :label="$t('listOwner.columns.phone')" prop="link"> | |
| 27 | + <el-input v-model="form.link" :placeholder="$t('listOwner.placeholders.phone')" /> | |
| 28 | + </el-form-item> | |
| 29 | + | |
| 30 | + <el-form-item v-if="form.personType === 'P'" :label="$t('listOwner.columns.gender')" prop="sex"> | |
| 31 | + <el-select v-model="form.sex" style="width:100%"> | |
| 32 | + <el-option v-for="gender in genders" :key="gender.value" :label="gender.label" :value="gender.value" /> | |
| 33 | + </el-select> | |
| 34 | + </el-form-item> | |
| 35 | + </el-col> | |
| 36 | + | |
| 37 | + <el-col :span="12" class="text-center"> | |
| 38 | + <el-image style="width: 200px; height: 150px; border: 1px dashed #ccc;" | |
| 39 | + :src="form.ownerPhotoUrl || '/img/noPhoto.jpg'" fit="cover" /> | |
| 40 | + <el-upload class="mt-10" :show-file-list="false" :before-upload="beforeUpload" :http-request="uploadImage"> | |
| 41 | + <el-button size="small" type="primary"> | |
| 42 | + {{ $t('common.upload') }} | |
| 43 | + </el-button> | |
| 44 | + </el-upload> | |
| 45 | + </el-col> | |
| 46 | + </el-row> | |
| 47 | + | |
| 48 | + <el-row :gutter="20"> | |
| 49 | + <el-col :span="12"> | |
| 50 | + <el-form-item :label="$t('listOwner.columns.backupPhone')"> | |
| 51 | + <el-input v-model="form.concactLink" /> | |
| 52 | + </el-form-item> | |
| 53 | + </el-col> | |
| 54 | + | |
| 55 | + <el-col :span="12"> | |
| 56 | + <el-form-item :label="$t('listOwner.columns.address')"> | |
| 57 | + <el-input v-model="form.address" /> | |
| 58 | + </el-form-item> | |
| 59 | + </el-col> | |
| 60 | + </el-row> | |
| 61 | + | |
| 62 | + <el-row :gutter="20"> | |
| 63 | + <el-col :span="12"> | |
| 64 | + <el-form-item v-if="form.personType === 'C'" :label="$t('listOwner.columns.idCard')"> | |
| 65 | + <el-input v-model="form.idCard" /> | |
| 66 | + </el-form-item> | |
| 67 | + | |
| 68 | + <el-form-item v-else :label="$t('listOwner.columns.idCard')"> | |
| 69 | + <el-input v-model="form.idCard" /> | |
| 70 | + </el-form-item> | |
| 71 | + </el-col> | |
| 72 | + | |
| 73 | + <el-col :span="12"> | |
| 74 | + <el-form-item :label="$t('common.remark')"> | |
| 75 | + <el-input v-model="form.remark" /> | |
| 76 | + </el-form-item> | |
| 77 | + </el-col> | |
| 78 | + </el-row> | |
| 79 | + | |
| 80 | + <div v-for="(item, index) in attrs" :key="index"> | |
| 81 | + <el-row :gutter="20" v-if="index % 2 === 0"> | |
| 82 | + <el-col :span="12"> | |
| 83 | + <el-form-item :label="item.specName"> | |
| 84 | + <el-input v-if="item.specType === '2233'" v-model="item.value" :placeholder="item.specHoldplace" /> | |
| 85 | + <el-select v-else-if="item.specType === '3344'" v-model="item.value" style="width:100%"> | |
| 86 | + <el-option v-for="val in item.values" :key="val.value" :label="val.valueName" :value="val.value" /> | |
| 87 | + </el-select> | |
| 88 | + </el-form-item> | |
| 89 | + </el-col> | |
| 90 | + | |
| 91 | + <el-col :span="12" v-if="index < attrs.length - 1"> | |
| 92 | + <el-form-item :label="attrs[index + 1].specName"> | |
| 93 | + <el-input v-if="attrs[index + 1].specType === '2233'" v-model="attrs[index + 1].value" | |
| 94 | + :placeholder="attrs[index + 1].specHoldplace" /> | |
| 95 | + <el-select v-else-if="attrs[index + 1].specType === '3344'" v-model="attrs[index + 1].value" | |
| 96 | + style="width:100%"> | |
| 97 | + <el-option v-for="val in attrs[index + 1].values" :key="val.value" :label="val.valueName" | |
| 98 | + :value="val.value" /> | |
| 99 | + </el-select> | |
| 100 | + </el-form-item> | |
| 101 | + </el-col> | |
| 102 | + </el-row> | |
| 103 | + </div> | |
| 104 | + </el-form> | |
| 105 | + | |
| 106 | + <div slot="footer" class="dialog-footer"> | |
| 107 | + <el-button @click="visible = false"> | |
| 108 | + {{ $t('listOwner.buttons.cancel') }} | |
| 109 | + </el-button> | |
| 110 | + <el-button type="primary" @click="updateOwner"> | |
| 111 | + {{ $t('listOwner.buttons.save') }} | |
| 112 | + </el-button> | |
| 113 | + </div> | |
| 114 | + </el-dialog> | |
| 115 | +</template> | |
| 116 | + | |
| 117 | +<script> | |
| 118 | +import { editOwner, getAttrValue, uploadImage } from '@/api/owner/ownerApi' | |
| 119 | +import { getAttrSpecList } from '@/api/dev/attrSpecApi' | |
| 120 | + | |
| 121 | +export default { | |
| 122 | + name: 'EditOwner', | |
| 123 | + data() { | |
| 124 | + return { | |
| 125 | + visible: false, | |
| 126 | + form: { | |
| 127 | + ownerId: '', | |
| 128 | + name: '', | |
| 129 | + link: '', | |
| 130 | + address: '', | |
| 131 | + sex: '', | |
| 132 | + remark: '', | |
| 133 | + ownerPhoto: '', | |
| 134 | + ownerPhotoUrl: '', | |
| 135 | + idCard: '', | |
| 136 | + personType: 'P', | |
| 137 | + personRole: '1', | |
| 138 | + concactPerson: '', | |
| 139 | + concactLink: '' | |
| 140 | + }, | |
| 141 | + attrs: [], | |
| 142 | + personTypes: [ | |
| 143 | + { value: 'P', label: this.$t('listOwner.personType.personal') }, | |
| 144 | + { value: 'C', label: this.$t('listOwner.personType.company') } | |
| 145 | + ], | |
| 146 | + personRoles: [ | |
| 147 | + { value: '1', label: this.$t('listOwner.role.owner') }, | |
| 148 | + { value: '2', label: this.$t('listOwner.role.tenant') } | |
| 149 | + ], | |
| 150 | + genders: [ | |
| 151 | + { value: '0', label: this.$t('listOwner.gender.male') }, | |
| 152 | + { value: '1', label: this.$t('listOwner.gender.female') } | |
| 153 | + ], | |
| 154 | + rules: { | |
| 155 | + name: [ | |
| 156 | + { required: true, message: this.$t('listOwner.rules.nameRequired'), trigger: 'blur' }, | |
| 157 | + { min: 2, max: 64, message: this.$t('listOwner.rules.nameLength'), trigger: 'blur' } | |
| 158 | + ], | |
| 159 | + personType: [ | |
| 160 | + { required: true, message: this.$t('listOwner.rules.typeRequired'), trigger: 'change' } | |
| 161 | + ], | |
| 162 | + personRole: [ | |
| 163 | + { required: true, message: this.$t('listOwner.rules.roleRequired'), trigger: 'change' } | |
| 164 | + ], | |
| 165 | + link: [ | |
| 166 | + { required: true, message: this.$t('listOwner.rules.phoneRequired'), trigger: 'blur' }, | |
| 167 | + { pattern: /^1[3-9]\d{9}$/, message: this.$t('listOwner.rules.phoneFormat'), trigger: 'blur' } | |
| 168 | + ], | |
| 169 | + sex: [ | |
| 170 | + { required: true, message: this.$t('listOwner.rules.genderRequired'), trigger: 'change' } | |
| 171 | + ] | |
| 172 | + } | |
| 173 | + } | |
| 174 | + }, | |
| 175 | + methods: { | |
| 176 | + open(owner) { | |
| 177 | + this.form = { ...owner } | |
| 178 | + this.visible = true | |
| 179 | + this.loadAttributes() | |
| 180 | + }, | |
| 181 | + | |
| 182 | + async loadAttributes() { | |
| 183 | + try { | |
| 184 | + const data = await getAttrSpecList({ page: 1, row: 100, tableName: 'building_owner_attr'}) | |
| 185 | + this.attrs = data.filter(item => item.specShow === 'Y') | |
| 186 | + | |
| 187 | + for (const attr of this.attrs) { | |
| 188 | + if (attr.specType === '3344') { | |
| 189 | + attr.values = await getAttrValue(attr.specCd) | |
| 190 | + } | |
| 191 | + | |
| 192 | + // 设置已有属性值 | |
| 193 | + if (this.form.ownerAttrDtos) { | |
| 194 | + const attrDto = this.form.ownerAttrDtos.find(dto => dto.specCd === attr.specCd) | |
| 195 | + attr.value = attrDto ? attrDto.value : '' | |
| 196 | + } | |
| 197 | + } | |
| 198 | + } catch (error) { | |
| 199 | + console.error('Failed to load attributes:', error) | |
| 200 | + } | |
| 201 | + }, | |
| 202 | + | |
| 203 | + beforeUpload(file) { | |
| 204 | + const isJPG = file.type === 'image/jpeg' | |
| 205 | + const isLt2M = file.size / 1024 / 1024 < 2 | |
| 206 | + | |
| 207 | + if (!isJPG) { | |
| 208 | + this.$message.error(this.$t('listOwner.upload.jpgOnly')) | |
| 209 | + } | |
| 210 | + if (!isLt2M) { | |
| 211 | + this.$message.error(this.$t('listOwner.upload.sizeLimit')) | |
| 212 | + } | |
| 213 | + return isJPG && isLt2M | |
| 214 | + }, | |
| 215 | + | |
| 216 | + async uploadImage({ file }) { | |
| 217 | + try { | |
| 218 | + const res = await uploadImage({ file }) | |
| 219 | + this.form.ownerPhoto = res.data.fileId | |
| 220 | + this.form.ownerPhotoUrl = res.data.url | |
| 221 | + } catch (error) { | |
| 222 | + this.$message.error(this.$t('listOwner.upload.failed')) | |
| 223 | + } | |
| 224 | + }, | |
| 225 | + | |
| 226 | + async updateOwner() { | |
| 227 | + this.$refs.form.validate(async valid => { | |
| 228 | + if (!valid) return | |
| 229 | + | |
| 230 | + try { | |
| 231 | + const params = { | |
| 232 | + ...this.form, | |
| 233 | + attrs: this.attrs.map(attr => ({ | |
| 234 | + specCd: attr.specCd, | |
| 235 | + value: attr.value | |
| 236 | + })) | |
| 237 | + } | |
| 238 | + | |
| 239 | + await editOwner(params) | |
| 240 | + this.$message.success(this.$t('listOwner.updateSuccess')) | |
| 241 | + this.visible = false | |
| 242 | + this.$emit('success') | |
| 243 | + } catch (error) { | |
| 244 | + this.$message.error(this.$t('listOwner.updateFailed')) | |
| 245 | + } | |
| 246 | + }) | |
| 247 | + }, | |
| 248 | + | |
| 249 | + resetForm() { | |
| 250 | + this.$refs.form.resetFields() | |
| 251 | + this.form = { | |
| 252 | + ownerId: '', | |
| 253 | + name: '', | |
| 254 | + link: '', | |
| 255 | + address: '', | |
| 256 | + sex: '', | |
| 257 | + remark: '', | |
| 258 | + ownerPhoto: '', | |
| 259 | + ownerPhotoUrl: '', | |
| 260 | + idCard: '', | |
| 261 | + personType: 'P', | |
| 262 | + personRole: '1', | |
| 263 | + concactPerson: '', | |
| 264 | + concactLink: '' | |
| 265 | + } | |
| 266 | + this.attrs = [] | |
| 267 | + } | |
| 268 | + } | |
| 269 | +} | |
| 270 | +</script> | |
| 271 | + | |
| 272 | +<style scoped> | |
| 273 | +.text-center { | |
| 274 | + text-align: center; | |
| 275 | +} | |
| 276 | + | |
| 277 | +.mt-10 { | |
| 278 | + margin-top: 10px; | |
| 279 | +} | |
| 280 | +</style> | |
| 0 | 281 | \ No newline at end of file | ... | ... |
src/components/room/searchRoom.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <el-dialog :title="$t('searchRoom.title')" :visible.sync="dialogVisible" width="70%" @close="handleClose"> | |
| 3 | + <div class="ibox"> | |
| 4 | + <el-row v-if="showSearchCondition"> | |
| 5 | + <el-col :span="8"> | |
| 6 | + <el-input :placeholder="$t('searchRoom.floorNumPlaceholder')" :readonly="floorNumInputReadonly" | |
| 7 | + v-model.trim="currentFloorNum" clearable /> | |
| 8 | + </el-col> | |
| 9 | + <el-col :span="8" :offset="1"> | |
| 10 | + <el-input :placeholder="$t('searchRoom.roomNumPlaceholder')" v-model.trim="currentRoomNum" clearable /> | |
| 11 | + </el-col> | |
| 12 | + <el-col :span="6" :offset="1"> | |
| 13 | + <el-button type="primary" @click="searchRooms"> | |
| 14 | + <i class="el-icon-search"></i> | |
| 15 | + {{ $t('searchRoom.search') }} | |
| 16 | + </el-button> | |
| 17 | + <el-button type="primary" @click="resetRooms"> | |
| 18 | + <i class="el-icon-refresh"></i> | |
| 19 | + {{ $t('searchRoom.reset') }} | |
| 20 | + </el-button> | |
| 21 | + </el-col> | |
| 22 | + </el-row> | |
| 23 | + | |
| 24 | + <el-table :data="rooms" style="width: 100%; margin-top: 15px" border stripe> | |
| 25 | + <el-table-column prop="roomId" :label="$t('searchRoom.roomId')" align="center" /> | |
| 26 | + <el-table-column prop="floorNum" :label="$t('searchRoom.floorNum')" align="center"> | |
| 27 | + <template #default="{ row }"> | |
| 28 | + {{ row.floorNum }}{{ $t('searchRoom.building') }} | |
| 29 | + </template> | |
| 30 | + </el-table-column> | |
| 31 | + <el-table-column prop="unitNum" :label="$t('searchRoom.unitNum')" align="center"> | |
| 32 | + <template #default="{ row }"> | |
| 33 | + {{ row.unitNum }}{{ $t('searchRoom.unit') }} | |
| 34 | + </template> | |
| 35 | + </el-table-column> | |
| 36 | + <el-table-column prop="roomNum" :label="$t('searchRoom.roomNum')" align="center"> | |
| 37 | + <template #default="{ row }"> | |
| 38 | + {{ row.roomNum }}{{ $t('searchRoom.room') }} | |
| 39 | + </template> | |
| 40 | + </el-table-column> | |
| 41 | + <el-table-column prop="layer" :label="$t('searchRoom.layer')" align="center"> | |
| 42 | + <template #default="{ row }"> | |
| 43 | + {{ row.layer }}{{ $t('searchRoom.floor') }} | |
| 44 | + </template> | |
| 45 | + </el-table-column> | |
| 46 | + <el-table-column :label="$t('searchRoom.operation')" align="center" width="120"> | |
| 47 | + <template #default="{ row }"> | |
| 48 | + <el-button type="primary" size="mini" @click="chooseRoom(row)"> | |
| 49 | + {{ $t('searchRoom.choose') }} | |
| 50 | + </el-button> | |
| 51 | + </template> | |
| 52 | + </el-table-column> | |
| 53 | + </el-table> | |
| 54 | + | |
| 55 | + <el-pagination @size-change="handleSizeChange" @current-change="handleCurrentChange" | |
| 56 | + :current-page="pagination.currentPage" :page-sizes="[10, 15, 20, 30]" :page-size="pagination.pageSize" | |
| 57 | + layout="total, sizes, prev, pager, next, jumper" :total="pagination.total" style="margin-top: 15px" /> | |
| 58 | + </div> | |
| 59 | + </el-dialog> | |
| 60 | +</template> | |
| 61 | + | |
| 62 | +<script> | |
| 63 | +import { queryRooms, queryRoomsWithSell, queryRoomsWithOutSell } from '@/api/room/roomApi' | |
| 64 | +import { getCommunityId } from '@/api/community/communityApi' | |
| 65 | + | |
| 66 | +export default { | |
| 67 | + name: 'SearchRoom', | |
| 68 | + props: { | |
| 69 | + roomFlag: { | |
| 70 | + type: String, | |
| 71 | + default: '' | |
| 72 | + }, | |
| 73 | + showSearchCondition: { | |
| 74 | + type: String, | |
| 75 | + default: 'true' | |
| 76 | + } | |
| 77 | + }, | |
| 78 | + data() { | |
| 79 | + return { | |
| 80 | + dialogVisible: false, | |
| 81 | + rooms: [], | |
| 82 | + currentRoomNum: '', | |
| 83 | + currentFloorNum: '', | |
| 84 | + floorNumInputReadonly: false, | |
| 85 | + pagination: { | |
| 86 | + currentPage: 1, | |
| 87 | + pageSize: 10, | |
| 88 | + total: 0 | |
| 89 | + } | |
| 90 | + } | |
| 91 | + }, | |
| 92 | + computed: { | |
| 93 | + isShowSearchCondition() { | |
| 94 | + return this.showSearchCondition === 'true' | |
| 95 | + } | |
| 96 | + }, | |
| 97 | + methods: { | |
| 98 | + open() { | |
| 99 | + this.dialogVisible = true | |
| 100 | + this.refreshSearchRoomData() | |
| 101 | + this.loadAllRoomInfo() | |
| 102 | + }, | |
| 103 | + handleClose() { | |
| 104 | + this.dialogVisible = false | |
| 105 | + }, | |
| 106 | + async loadAllRoomInfo() { | |
| 107 | + try { | |
| 108 | + const params = { | |
| 109 | + page: this.pagination.currentPage, | |
| 110 | + row: this.pagination.pageSize, | |
| 111 | + communityId: getCommunityId(), | |
| 112 | + roomNum: this.currentRoomNum, | |
| 113 | + floorNum: this.currentFloorNum, | |
| 114 | + roomFlag: this.roomFlag | |
| 115 | + } | |
| 116 | + | |
| 117 | + let response | |
| 118 | + if (this.roomFlag === '1') { | |
| 119 | + response = await queryRoomsWithSell(params) | |
| 120 | + } else if (this.roomFlag === '2') { | |
| 121 | + response = await queryRoomsWithOutSell(params) | |
| 122 | + } else { | |
| 123 | + response = await queryRooms(params) | |
| 124 | + } | |
| 125 | + | |
| 126 | + this.rooms = response.rooms || [] | |
| 127 | + this.pagination.total = response.records || 0 | |
| 128 | + } catch (error) { | |
| 129 | + console.error('Failed to load room info:', error) | |
| 130 | + this.$message.error(this.$t('searchRoom.loadFailed')) | |
| 131 | + } | |
| 132 | + }, | |
| 133 | + chooseRoom(room) { | |
| 134 | + this.$emit('chooseRoom', room) | |
| 135 | + this.$emit('load-data', { roomId: room.roomId }) | |
| 136 | + this.dialogVisible = false | |
| 137 | + }, | |
| 138 | + searchRooms() { | |
| 139 | + this.pagination.currentPage = 1 | |
| 140 | + this.loadAllRoomInfo() | |
| 141 | + }, | |
| 142 | + resetRooms() { | |
| 143 | + this.currentFloorNum = '' | |
| 144 | + this.currentRoomNum = '' | |
| 145 | + this.pagination.currentPage = 1 | |
| 146 | + this.loadAllRoomInfo() | |
| 147 | + }, | |
| 148 | + refreshSearchRoomData() { | |
| 149 | + this.currentRoomNum = '' | |
| 150 | + }, | |
| 151 | + handleSizeChange(val) { | |
| 152 | + this.pagination.pageSize = val | |
| 153 | + this.loadAllRoomInfo() | |
| 154 | + }, | |
| 155 | + handleCurrentChange(val) { | |
| 156 | + this.pagination.currentPage = val | |
| 157 | + this.loadAllRoomInfo() | |
| 158 | + }, | |
| 159 | + listenerFloorInfo(floorInfo) { | |
| 160 | + this.currentFloorNum = floorInfo.floorNum | |
| 161 | + this.floorNumInputReadonly = true | |
| 162 | + this.searchRooms() | |
| 163 | + }, | |
| 164 | + showOwnerRooms(rooms) { | |
| 165 | + this.dialogVisible = true | |
| 166 | + this.rooms = rooms | |
| 167 | + } | |
| 168 | + }, | |
| 169 | + mounted() { | |
| 170 | + this.$on('listener-floor-info', this.listenerFloorInfo) | |
| 171 | + this.$on('show-owner-rooms', this.showOwnerRooms) | |
| 172 | + }, | |
| 173 | + beforeDestroy() { | |
| 174 | + this.$off('listener-floor-info', this.listenerFloorInfo) | |
| 175 | + this.$off('show-owner-rooms', this.showOwnerRooms) | |
| 176 | + } | |
| 177 | +} | |
| 178 | +</script> | |
| 179 | + | |
| 180 | +<style scoped> | |
| 181 | +.el-input { | |
| 182 | + width: 100%; | |
| 183 | +} | |
| 184 | +</style> | |
| 0 | 185 | \ No newline at end of file | ... | ... |
src/i18n/commonLang.js
| ... | ... | @@ -40,6 +40,7 @@ export const messages = { |
| 40 | 40 | enabled: 'Enabled', |
| 41 | 41 | disabled: 'Disabled', |
| 42 | 42 | import: 'Import', |
| 43 | + remark: 'Remark', | |
| 43 | 44 | } |
| 44 | 45 | }, |
| 45 | 46 | zh: { |
| ... | ... | @@ -83,6 +84,7 @@ export const messages = { |
| 83 | 84 | enabled: '启用', |
| 84 | 85 | disabled: '禁用', |
| 85 | 86 | import: '导入', |
| 87 | + remark: '备注', | |
| 86 | 88 | } |
| 87 | 89 | } |
| 88 | 90 | } |
| 89 | 91 | \ No newline at end of file | ... | ... |
src/i18n/index.js
| ... | ... | @@ -127,6 +127,7 @@ import { messages as addRoomViewMessages } from '../views/room/addRoomViewLang' |
| 127 | 127 | import { messages as roomBindOwnerMessages } from '../views/owner/roomBindOwnerLang' |
| 128 | 128 | import { messages as deleteOwnerRoomMessages } from '../views/owner/deleteOwnerRoomLang' |
| 129 | 129 | import { messages as shopsMessages } from '../views/room/shopsLang' |
| 130 | +import { messages as listOwnerMessages } from '../views/owner/listOwnerLang' | |
| 130 | 131 | |
| 131 | 132 | Vue.use(VueI18n) |
| 132 | 133 | |
| ... | ... | @@ -258,6 +259,7 @@ const messages = { |
| 258 | 259 | ...roomBindOwnerMessages.en, |
| 259 | 260 | ...deleteOwnerRoomMessages.en, |
| 260 | 261 | ...shopsMessages.en, |
| 262 | + ...listOwnerMessages.en, | |
| 261 | 263 | }, |
| 262 | 264 | zh: { |
| 263 | 265 | ...loginMessages.zh, |
| ... | ... | @@ -385,6 +387,7 @@ const messages = { |
| 385 | 387 | ...roomBindOwnerMessages.zh, |
| 386 | 388 | ...deleteOwnerRoomMessages.zh, |
| 387 | 389 | ...shopsMessages.zh, |
| 390 | + ...listOwnerMessages.zh, | |
| 388 | 391 | } |
| 389 | 392 | } |
| 390 | 393 | ... | ... |
src/router/index.js
| ... | ... | @@ -621,6 +621,17 @@ const routes = [ |
| 621 | 621 | name: '/pages/property/shops', |
| 622 | 622 | component: () => import('@/views/room/shopsList.vue') |
| 623 | 623 | }, |
| 624 | + { | |
| 625 | + path: '/pages/property/listOwner', | |
| 626 | + name: '/pages/property/listOwner', | |
| 627 | + component: () => import('@/views/owner/listOwner.vue') | |
| 628 | + }, | |
| 629 | + { | |
| 630 | + path: '/views/owner/ownerBindRoom', | |
| 631 | + name: '/views/owner/ownerBindRoom', | |
| 632 | + component: () => import('@/views/owner/ownerBindRoom.vue') | |
| 633 | + }, | |
| 634 | + | |
| 624 | 635 | // 其他子路由可以在这里添加 |
| 625 | 636 | ] |
| 626 | 637 | }, | ... | ... |
src/views/owner/listOwner.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <div class="list-owner-container"> | |
| 3 | + <el-row :gutter="20"> | |
| 4 | + <el-col :span="3"> | |
| 5 | + <el-card class="role-card"> | |
| 6 | + <ul class="role-list"> | |
| 7 | + <li v-for="(item, index) in personRoles" :key="index" class="role-item" | |
| 8 | + :class="{ 'role-item-active': conditions.personRole === item.statusCd }" @click="switchPersonRole(item)"> | |
| 9 | + {{ item.name }} | |
| 10 | + </li> | |
| 11 | + </ul> | |
| 12 | + </el-card> | |
| 13 | + </el-col> | |
| 14 | + | |
| 15 | + <el-col :span="21"> | |
| 16 | + <el-card class="search-card"> | |
| 17 | + <div slot="header" class="text-left"> | |
| 18 | + <span>{{ $t('listOwner.searchTitle') }}</span> | |
| 19 | + </div> | |
| 20 | + <el-form :model="conditions" label-width="0"> | |
| 21 | + <el-row :gutter="20"> | |
| 22 | + <el-col :span="4"> | |
| 23 | + <el-form-item> | |
| 24 | + <el-select v-model="conditions.personType" :placeholder="$t('listOwner.personTypeName')" style="width:100%"> | |
| 25 | + <el-option v-for="(type,index) in personTypes" :key="index" :label="type.label" :value="type.value" /> | |
| 26 | + </el-select> | |
| 27 | + </el-form-item> | |
| 28 | + </el-col> | |
| 29 | + | |
| 30 | + <el-col :span="4"> | |
| 31 | + <el-form-item> | |
| 32 | + <el-input v-model="conditions.name" :placeholder="$t('listOwner.placeholders.name')" /> | |
| 33 | + </el-form-item> | |
| 34 | + </el-col> | |
| 35 | + | |
| 36 | + <el-col :span="4"> | |
| 37 | + <el-form-item> | |
| 38 | + <el-input v-model="conditions.roomName" :placeholder="$t('listOwner.placeholders.room')" /> | |
| 39 | + </el-form-item> | |
| 40 | + </el-col> | |
| 41 | + | |
| 42 | + <el-col :span="4"> | |
| 43 | + <el-form-item> | |
| 44 | + <el-input v-model="conditions.link" :placeholder="$t('listOwner.placeholders.phone')" type="number" /> | |
| 45 | + </el-form-item> | |
| 46 | + </el-col> | |
| 47 | + | |
| 48 | + <el-col :span="4"> | |
| 49 | + <el-form-item> | |
| 50 | + <el-input v-model="conditions.idCard" :placeholder="$t('listOwner.placeholders.idCard')" /> | |
| 51 | + </el-form-item> | |
| 52 | + </el-col> | |
| 53 | + | |
| 54 | + <el-col :span="4"> | |
| 55 | + <el-form-item> | |
| 56 | + <el-button type="primary" @click="queryOwnerMethod"> | |
| 57 | + {{ $t('listOwner.buttons.search') }} | |
| 58 | + </el-button> | |
| 59 | + <el-button @click="resetOwnerMethod"> | |
| 60 | + {{ $t('listOwner.buttons.reset') }} | |
| 61 | + </el-button> | |
| 62 | + </el-form-item> | |
| 63 | + </el-col> | |
| 64 | + </el-row> | |
| 65 | + </el-form> | |
| 66 | + </el-card> | |
| 67 | + | |
| 68 | + <el-card class="owner-card"> | |
| 69 | + <div slot="header" class="clearfix flex justify-between"> | |
| 70 | + <span>{{ $t('listOwner.ownerInfo') }}</span> | |
| 71 | + <el-button type="primary" size="small" class="float-right" @click="openAddOwnerModal"> | |
| 72 | + {{ $t('listOwner.buttons.add') }} | |
| 73 | + </el-button> | |
| 74 | + </div> | |
| 75 | + | |
| 76 | + <el-table :data="owners" border v-loading="loading" style="width:100%"> | |
| 77 | + <el-table-column :label="$t('listOwner.columns.face')" width="80" align="center"> | |
| 78 | + <template slot-scope="scope"> | |
| 79 | + <el-image style="width:60px;height:60px;border-radius:5px;cursor:pointer;" | |
| 80 | + :src="scope.row.faceUrl || '/img/noPhoto.jpg'" :preview-src-list="[scope.row.faceUrl]" fit="cover" /> | |
| 81 | + </template> | |
| 82 | + </el-table-column> | |
| 83 | + | |
| 84 | + <el-table-column :label="$t('listOwner.columns.name')" align="center"> | |
| 85 | + <template slot-scope="scope"> | |
| 86 | + <div>{{ scope.row.name }}</div> | |
| 87 | + <div v-if="scope.row.personType === 'C'">{{ scope.row.concactPerson }}</div> | |
| 88 | + </template> | |
| 89 | + </el-table-column> | |
| 90 | + | |
| 91 | + <el-table-column prop="personTypeName" :label="$t('listOwner.columns.type')" align="center" /> | |
| 92 | + | |
| 93 | + <el-table-column prop="personRoleName" :label="$t('listOwner.columns.role')" align="center" /> | |
| 94 | + | |
| 95 | + <el-table-column :label="$t('listOwner.columns.gender')" align="center"> | |
| 96 | + <template slot-scope="scope"> | |
| 97 | + {{ scope.row.sex === 0 ? $t('listOwner.gender.male') : $t('listOwner.gender.female') }} | |
| 98 | + </template> | |
| 99 | + </el-table-column> | |
| 100 | + | |
| 101 | + <el-table-column prop="idCard" :label="$t('listOwner.columns.idCard')" align="center"> | |
| 102 | + <template slot-scope="scope"> | |
| 103 | + {{ scope.row.idCard || '-' }} | |
| 104 | + </template> | |
| 105 | + </el-table-column> | |
| 106 | + | |
| 107 | + <el-table-column prop="link" :label="$t('listOwner.columns.phone')" align="center" /> | |
| 108 | + | |
| 109 | + <el-table-column prop="concactLink" :label="$t('listOwner.columns.backupPhone')" align="center"> | |
| 110 | + <template slot-scope="scope"> | |
| 111 | + {{ scope.row.concactLink || '-' }} | |
| 112 | + </template> | |
| 113 | + </el-table-column> | |
| 114 | + | |
| 115 | + <el-table-column prop="address" :label="$t('listOwner.columns.address')" align="center"> | |
| 116 | + <template slot-scope="scope"> | |
| 117 | + {{ scope.row.address || '-' }} | |
| 118 | + </template> | |
| 119 | + </el-table-column> | |
| 120 | + | |
| 121 | + <el-table-column :label="$t('listOwner.columns.rooms')" align="center"> | |
| 122 | + <template slot-scope="scope"> | |
| 123 | + <el-link type="primary" @click="viewOwnerRooms(scope.row)"> | |
| 124 | + {{ scope.row.roomCount || 0 }} | |
| 125 | + </el-link> | |
| 126 | + </template> | |
| 127 | + </el-table-column> | |
| 128 | + | |
| 129 | + <el-table-column :label="$t('listOwner.columns.members')" align="center"> | |
| 130 | + <template slot-scope="scope"> | |
| 131 | + <el-link v-if="scope.row.ownerTypeCd === '1001'" type="primary" @click="viewOwnerMembers(scope.row)"> | |
| 132 | + {{ scope.row.memberCount || 0 }} | |
| 133 | + </el-link> | |
| 134 | + <span v-else>-</span> | |
| 135 | + </template> | |
| 136 | + </el-table-column> | |
| 137 | + | |
| 138 | + <el-table-column :label="$t('listOwner.columns.cars')" align="center"> | |
| 139 | + <template slot-scope="scope"> | |
| 140 | + <el-link type="primary" @click="viewOwnerCars(scope.row)"> | |
| 141 | + {{ scope.row.carCount || 0 }} | |
| 142 | + </el-link> | |
| 143 | + </template> | |
| 144 | + </el-table-column> | |
| 145 | + | |
| 146 | + <el-table-column :label="$t('listOwner.columns.debts')" align="center"> | |
| 147 | + <template slot-scope="scope"> | |
| 148 | + <el-link type="primary" @click="viewOweFees(scope.row)"> | |
| 149 | + {{ scope.row.oweFee || '0.00' }} | |
| 150 | + </el-link> | |
| 151 | + </template> | |
| 152 | + </el-table-column> | |
| 153 | + | |
| 154 | + <el-table-column v-for="(item, index) in listColumns" :key="index" :label="item" align="center"> | |
| 155 | + <template slot-scope="scope"> | |
| 156 | + {{ scope.row.listValues[index] || '-' }} | |
| 157 | + </template> | |
| 158 | + </el-table-column> | |
| 159 | + | |
| 160 | + <el-table-column :label="$t('listOwner.columns.operation')" width="300" align="center" fixed="right"> | |
| 161 | + <template slot-scope="scope"> | |
| 162 | + <el-dropdown> | |
| 163 | + <el-button type="primary" size="mini"> | |
| 164 | + {{ $t('listOwner.buttons.edit') }} | |
| 165 | + </el-button> | |
| 166 | + <el-dropdown-menu slot="dropdown"> | |
| 167 | + <el-dropdown-item @click.native="openEditOwnerModal(scope.row)"> | |
| 168 | + {{ $t('listOwner.actions.editOwner') }} | |
| 169 | + </el-dropdown-item> | |
| 170 | + <el-dropdown-item @click.native="openDeleteOwnerModal(scope.row)"> | |
| 171 | + {{ $t('listOwner.actions.deleteOwner') }} | |
| 172 | + </el-dropdown-item> | |
| 173 | + <el-dropdown-item @click.native="openAddOwnerRoom(scope.row)"> | |
| 174 | + {{ $t('listOwner.actions.addRoom') }} | |
| 175 | + </el-dropdown-item> | |
| 176 | + <el-dropdown-item @click.native="openDeleteOwnerRoom(scope.row)"> | |
| 177 | + {{ $t('listOwner.buttons.unbindRoom') }} | |
| 178 | + </el-dropdown-item> | |
| 179 | + </el-dropdown-menu> | |
| 180 | + </el-dropdown> | |
| 181 | + | |
| 182 | + <el-button v-if="scope.row.ownerTypeCd === '1001'" type="info" size="mini" | |
| 183 | + @click="openOwnerDetailMember(scope.row)"> | |
| 184 | + {{ $t('listOwner.buttons.member') }} | |
| 185 | + </el-button> | |
| 186 | + | |
| 187 | + <el-button type="info" size="mini" @click="openOwnerDetail(scope.row)"> | |
| 188 | + {{ $t('listOwner.buttons.detail') }} | |
| 189 | + </el-button> | |
| 190 | + </template> | |
| 191 | + </el-table-column> | |
| 192 | + </el-table> | |
| 193 | + | |
| 194 | + <el-pagination :current-page.sync="currentPage" :page-sizes="[10, 20, 30, 50]" :page-size="pageSize" | |
| 195 | + :total="total" layout="total, sizes, prev, pager, next, jumper" @size-change="handleSizeChange" | |
| 196 | + @current-change="handlePageChange" /> | |
| 197 | + </el-card> | |
| 198 | + </el-col> | |
| 199 | + </el-row> | |
| 200 | + | |
| 201 | + <add-owner ref="addOwner" @success="handleOwnerSuccess" /> | |
| 202 | + <edit-owner ref="editOwner" @success="handleOwnerSuccess" /> | |
| 203 | + <delete-owner ref="deleteOwner" @success="handleOwnerSuccess" /> | |
| 204 | + <owner-rooms ref="ownerRooms" /> | |
| 205 | + <owner-members ref="ownerMembers" /> | |
| 206 | + <owner-cars ref="ownerCars" /> | |
| 207 | + <owner-owe-fees ref="ownerOweFees" /> | |
| 208 | + | |
| 209 | + </div> | |
| 210 | +</template> | |
| 211 | + | |
| 212 | +<script> | |
| 213 | +import { queryOwners } from '@/api/owner/ownerApi' | |
| 214 | +import AddOwner from '@/components/owner/addOwner' | |
| 215 | +import EditOwner from '@/components/owner/editOwner' | |
| 216 | +import DeleteOwner from '@/components/owner/deleteOwner' | |
| 217 | +import { getAttrSpecList } from '@/api/dev/attrSpecApi' | |
| 218 | +import OwnerRooms from '@/components/owner/ownerRooms' | |
| 219 | +import OwnerMembers from '@/components/owner/ownerMembers' | |
| 220 | +import OwnerCars from '@/components/owner/ownerCars' | |
| 221 | +import OwnerOweFees from '@/components/owner/ownerOweFees' | |
| 222 | + | |
| 223 | + | |
| 224 | +export default { | |
| 225 | + name: 'ListOwner', | |
| 226 | + components: { | |
| 227 | + AddOwner, | |
| 228 | + EditOwner, | |
| 229 | + DeleteOwner, | |
| 230 | + OwnerRooms, | |
| 231 | + OwnerMembers, | |
| 232 | + OwnerCars, | |
| 233 | + OwnerOweFees | |
| 234 | + }, | |
| 235 | + data() { | |
| 236 | + return { | |
| 237 | + loading: false, | |
| 238 | + owners: [], | |
| 239 | + personRoles: [], | |
| 240 | + personTypes: [ | |
| 241 | + { value: '', label: this.$t('common.select') }, | |
| 242 | + { value: 'P', label: this.$t('listOwner.personType.personal') }, | |
| 243 | + { value: 'C', label: this.$t('listOwner.personType.company') } | |
| 244 | + ], | |
| 245 | + conditions: { | |
| 246 | + personRole: '', | |
| 247 | + name: '', | |
| 248 | + roomName: '', | |
| 249 | + link: '', | |
| 250 | + idCard: '', | |
| 251 | + personType: '' | |
| 252 | + }, | |
| 253 | + listColumns: [], | |
| 254 | + currentPage: 1, | |
| 255 | + pageSize: 10, | |
| 256 | + total: 0 | |
| 257 | + } | |
| 258 | + }, | |
| 259 | + created() { | |
| 260 | + this.getPersonRoles() | |
| 261 | + this.getColumns() | |
| 262 | + this.listOwnerData() | |
| 263 | + }, | |
| 264 | + methods: { | |
| 265 | + async getPersonRoles() { | |
| 266 | + this.personRoles = [ | |
| 267 | + { statusCd: '', name: this.$t('listOwner.role.all') }, | |
| 268 | + { statusCd: '1', name: this.$t('listOwner.role.owner') }, | |
| 269 | + { statusCd: '2', name: this.$t('listOwner.role.tenant') } | |
| 270 | + ] | |
| 271 | + }, | |
| 272 | + | |
| 273 | + async getColumns() { | |
| 274 | + try { | |
| 275 | + const data = await getAttrSpecList({ | |
| 276 | + page: 1, | |
| 277 | + row: 100, | |
| 278 | + tableName:'building_owner_attr'}) | |
| 279 | + this.listColumns = data | |
| 280 | + .filter(item => item.specShow === 'Y') | |
| 281 | + .map(item => item.specName) | |
| 282 | + } catch (error) { | |
| 283 | + console.error('Failed to get columns:', error) | |
| 284 | + } | |
| 285 | + }, | |
| 286 | + | |
| 287 | + async listOwnerData() { | |
| 288 | + this.loading = true | |
| 289 | + try { | |
| 290 | + const params = { | |
| 291 | + ...this.conditions, | |
| 292 | + page: this.currentPage, | |
| 293 | + row: this.pageSize | |
| 294 | + } | |
| 295 | + const { data, records } = await queryOwners(params) | |
| 296 | + this.owners = data | |
| 297 | + this.total = records | |
| 298 | + this.dealOwnerAttr(data) | |
| 299 | + } catch (error) { | |
| 300 | + console.error('Failed to get owner list:', error) | |
| 301 | + this.$message.error(this.$t('listOwner.fetchError')) | |
| 302 | + } finally { | |
| 303 | + this.loading = false | |
| 304 | + } | |
| 305 | + }, | |
| 306 | + | |
| 307 | + dealOwnerAttr(owners) { | |
| 308 | + owners.forEach(owner => { | |
| 309 | + owner.listValues = [] | |
| 310 | + if (!owner.ownerAttrDtos || owner.ownerAttrDtos.length === 0) return | |
| 311 | + | |
| 312 | + this.listColumns.forEach(column => { | |
| 313 | + const attr = owner.ownerAttrDtos.find(dto => dto.specName === column) | |
| 314 | + owner.listValues.push(attr ? attr.valueName : '') | |
| 315 | + }) | |
| 316 | + }) | |
| 317 | + }, | |
| 318 | + | |
| 319 | + switchPersonRole(item) { | |
| 320 | + this.conditions.personRole = item.statusCd | |
| 321 | + this.listOwnerData() | |
| 322 | + }, | |
| 323 | + | |
| 324 | + queryOwnerMethod() { | |
| 325 | + this.currentPage = 1 | |
| 326 | + this.listOwnerData() | |
| 327 | + }, | |
| 328 | + | |
| 329 | + resetOwnerMethod() { | |
| 330 | + this.conditions = { | |
| 331 | + personRole: '', | |
| 332 | + name: '', | |
| 333 | + roomName: '', | |
| 334 | + link: '', | |
| 335 | + idCard: '', | |
| 336 | + personType: '' | |
| 337 | + } | |
| 338 | + this.listOwnerData() | |
| 339 | + }, | |
| 340 | + | |
| 341 | + handleSizeChange(size) { | |
| 342 | + this.pageSize = size | |
| 343 | + this.listOwnerData() | |
| 344 | + }, | |
| 345 | + | |
| 346 | + handlePageChange(page) { | |
| 347 | + this.currentPage = page | |
| 348 | + this.listOwnerData() | |
| 349 | + }, | |
| 350 | + | |
| 351 | + openAddOwnerModal() { | |
| 352 | + this.$refs.addOwner.open() | |
| 353 | + }, | |
| 354 | + | |
| 355 | + openEditOwnerModal(owner) { | |
| 356 | + this.$refs.editOwner.open(owner) | |
| 357 | + }, | |
| 358 | + | |
| 359 | + openDeleteOwnerModal(owner) { | |
| 360 | + this.$refs.deleteOwner.open(owner) | |
| 361 | + }, | |
| 362 | + | |
| 363 | + handleOwnerSuccess() { | |
| 364 | + this.listOwnerData() | |
| 365 | + }, | |
| 366 | + | |
| 367 | + viewOwnerRooms(owner) { | |
| 368 | + console.log('View owner rooms:', owner) | |
| 369 | + // 实际项目中跳转到房屋页面 | |
| 370 | + this.$refs.ownerRooms.open(owner) | |
| 371 | + | |
| 372 | + }, | |
| 373 | + | |
| 374 | + viewOwnerMembers(owner) { | |
| 375 | + console.log('View owner members:', owner) | |
| 376 | + // 实际项目中跳转到成员页面 | |
| 377 | + this.$refs.ownerMembers.open(owner.ownerId) | |
| 378 | + | |
| 379 | + }, | |
| 380 | + | |
| 381 | + viewOwnerCars(owner) { | |
| 382 | + console.log('View owner cars:', owner) | |
| 383 | + // 实际项目中跳转到车辆页面 | |
| 384 | + this.$refs.ownerCars.open(owner.ownerId) | |
| 385 | + | |
| 386 | + }, | |
| 387 | + | |
| 388 | + viewOweFees(owner) { | |
| 389 | + console.log('View owe fees:', owner) | |
| 390 | + // 实际项目中跳转到欠费页面 | |
| 391 | + this.$refs.ownerOweFees.open(owner.ownerId) | |
| 392 | + | |
| 393 | + }, | |
| 394 | + | |
| 395 | + openAddOwnerRoom(owner) { | |
| 396 | + console.log('Add owner room:', owner) | |
| 397 | + // 实际项目中跳转到绑定房屋页面 | |
| 398 | + this.$router.push(`/views/owner/ownerBindRoom?ownerId=${owner.ownerId}`) | |
| 399 | + | |
| 400 | + }, | |
| 401 | + | |
| 402 | + openDeleteOwnerRoom(owner) { | |
| 403 | + console.log('Delete owner room:', owner) | |
| 404 | + // 实际项目中跳转到解绑房屋页面 | |
| 405 | + this.$router.push(`/views/owner/deleteOwnerRoom?ownerId=${owner.ownerId}`) | |
| 406 | + | |
| 407 | + }, | |
| 408 | + | |
| 409 | + openOwnerDetail(owner) { | |
| 410 | + console.log('Owner detail:', owner) | |
| 411 | + // 实际项目中跳转到详情页面 | |
| 412 | + }, | |
| 413 | + | |
| 414 | + openOwnerDetailMember(owner) { | |
| 415 | + console.log('Owner detail member:', owner) | |
| 416 | + // 实际项目中跳转到成员详情页面 | |
| 417 | + | |
| 418 | + } | |
| 419 | + } | |
| 420 | +} | |
| 421 | +</script> | |
| 422 | + | |
| 423 | +<style scoped> | |
| 424 | +.list-owner-container { | |
| 425 | + padding: 20px; | |
| 426 | +} | |
| 427 | + | |
| 428 | +.role-card { | |
| 429 | + height: 100%; | |
| 430 | +} | |
| 431 | + | |
| 432 | +.role-list { | |
| 433 | + list-style: none; | |
| 434 | + padding: 0; | |
| 435 | + margin: 0; | |
| 436 | +} | |
| 437 | + | |
| 438 | +.role-item { | |
| 439 | + padding: 10px; | |
| 440 | + text-align: center; | |
| 441 | + cursor: pointer; | |
| 442 | + border-radius: 4px; | |
| 443 | + margin-bottom: 5px; | |
| 444 | + transition: all 0.3s; | |
| 445 | +} | |
| 446 | + | |
| 447 | +.role-item:hover { | |
| 448 | + background-color: #f5f7fa; | |
| 449 | +} | |
| 450 | + | |
| 451 | +.role-item-active { | |
| 452 | + background-color: #409eff; | |
| 453 | + color: white; | |
| 454 | +} | |
| 455 | + | |
| 456 | +.search-card, | |
| 457 | +.owner-card { | |
| 458 | + margin-bottom: 20px; | |
| 459 | +} | |
| 460 | + | |
| 461 | +.float-right { | |
| 462 | + float: right; | |
| 463 | +} | |
| 464 | + | |
| 465 | +.el-dropdown { | |
| 466 | + margin-right: 10px; | |
| 467 | +} | |
| 468 | +</style> | |
| 0 | 469 | \ No newline at end of file | ... | ... |
src/views/owner/listOwnerLang.js
0 → 100644
| 1 | +export const messages = { | |
| 2 | + en: { | |
| 3 | + listOwner: { | |
| 4 | + title: 'Owner Management', | |
| 5 | + searchTitle: 'Search Conditions', | |
| 6 | + ownerInfo: 'Owner Information', | |
| 7 | + selectRoom: 'Select Room', | |
| 8 | + role: { | |
| 9 | + all: 'All', | |
| 10 | + owner: 'Owner', | |
| 11 | + tenant: 'Tenant' | |
| 12 | + }, | |
| 13 | + personType: { | |
| 14 | + personal: 'Personal', | |
| 15 | + company: 'Company' | |
| 16 | + }, | |
| 17 | + personTypeName:'Person Type', | |
| 18 | + gender: { | |
| 19 | + male: 'Male', | |
| 20 | + female: 'Female' | |
| 21 | + }, | |
| 22 | + columns: { | |
| 23 | + face: 'Face', | |
| 24 | + name: 'Name', | |
| 25 | + type: 'Type', | |
| 26 | + role: 'Role', | |
| 27 | + gender: 'Gender', | |
| 28 | + idCard: 'ID Card', | |
| 29 | + phone: 'Phone', | |
| 30 | + backupPhone: 'Backup Phone', | |
| 31 | + address: 'Address', | |
| 32 | + rooms: 'Rooms', | |
| 33 | + members: 'Members', | |
| 34 | + cars: 'Cars', | |
| 35 | + debts: 'Debts', | |
| 36 | + operation: 'Operation' | |
| 37 | + }, | |
| 38 | + placeholders: { | |
| 39 | + name: 'Please enter name', | |
| 40 | + room: 'Building-Unit-Room', | |
| 41 | + phone: 'Please enter phone', | |
| 42 | + idCard: 'Please enter ID card' | |
| 43 | + }, | |
| 44 | + buttons: { | |
| 45 | + search: 'Search', | |
| 46 | + reset: 'Reset', | |
| 47 | + add: 'Add', | |
| 48 | + save: 'Save', | |
| 49 | + cancel: 'Cancel', | |
| 50 | + edit: 'Edit', | |
| 51 | + delete: 'Delete', | |
| 52 | + bindRoom: 'Bind Room', | |
| 53 | + unbindRoom: 'Unbind Room', | |
| 54 | + member: 'Member', | |
| 55 | + detail: 'Detail' | |
| 56 | + }, | |
| 57 | + actions: { | |
| 58 | + editOwner: 'Edit Owner', | |
| 59 | + deleteOwner: 'Delete Owner', | |
| 60 | + addRoom: 'Add Room' | |
| 61 | + }, | |
| 62 | + confirmDelete: 'Are you sure to delete?' | |
| 63 | + } | |
| 64 | + }, | |
| 65 | + zh: { | |
| 66 | + listOwner: { | |
| 67 | + title: '业主管理', | |
| 68 | + searchTitle: '查询条件', | |
| 69 | + ownerInfo: '业主信息', | |
| 70 | + selectRoom: '选择房屋', | |
| 71 | + role: { | |
| 72 | + all: '全部', | |
| 73 | + owner: '业主', | |
| 74 | + tenant: '租客' | |
| 75 | + }, | |
| 76 | + personType: { | |
| 77 | + personal: '个人', | |
| 78 | + company: '公司' | |
| 79 | + }, | |
| 80 | + personTypeName:'人员类型', | |
| 81 | + gender: { | |
| 82 | + male: '男', | |
| 83 | + female: '女' | |
| 84 | + }, | |
| 85 | + columns: { | |
| 86 | + face: '人脸', | |
| 87 | + name: '客户名称', | |
| 88 | + type: '人员类型', | |
| 89 | + role: '人员角色', | |
| 90 | + gender: '性别', | |
| 91 | + idCard: '证件号', | |
| 92 | + phone: '联系手机', | |
| 93 | + backupPhone: '备用手机', | |
| 94 | + address: '地址', | |
| 95 | + rooms: '房屋数', | |
| 96 | + members: '业主成员', | |
| 97 | + cars: '车辆数', | |
| 98 | + debts: '欠费', | |
| 99 | + operation: '操作' | |
| 100 | + }, | |
| 101 | + placeholders: { | |
| 102 | + name: '请输入客户名称', | |
| 103 | + room: '楼栋-单元-房屋', | |
| 104 | + phone: '请输入联系方式', | |
| 105 | + idCard: '请输入业主身份证号' | |
| 106 | + }, | |
| 107 | + buttons: { | |
| 108 | + search: '查询', | |
| 109 | + reset: '重置', | |
| 110 | + add: '添加', | |
| 111 | + save: '保存', | |
| 112 | + cancel: '取消', | |
| 113 | + edit: '修改', | |
| 114 | + delete: '删除', | |
| 115 | + bindRoom: '入住房屋', | |
| 116 | + unbindRoom: '房屋解绑', | |
| 117 | + member: '成员', | |
| 118 | + detail: '详情' | |
| 119 | + }, | |
| 120 | + actions: { | |
| 121 | + editOwner: '修改业主', | |
| 122 | + deleteOwner: '删除业主', | |
| 123 | + addRoom: '入住房屋' | |
| 124 | + }, | |
| 125 | + confirmDelete: '确认是否删除!' | |
| 126 | + } | |
| 127 | + } | |
| 128 | +} | |
| 0 | 129 | \ No newline at end of file | ... | ... |
src/views/owner/ownerBindRoom.vue
0 → 100644
| 1 | +<template> | |
| 2 | + <div class="room-bind-owner-container"> | |
| 3 | + <el-card class="box-card"> | |
| 4 | + <div slot="header" class="flex justify-between"> | |
| 5 | + <div>{{ $t('roomBindOwner.title') }}</div> | |
| 6 | + <div class="header-tools"> | |
| 7 | + <el-button type="primary" size="small" icon="el-icon-close" @click="_goBack"> | |
| 8 | + {{ $t('common.back') }} | |
| 9 | + </el-button> | |
| 10 | + </div> | |
| 11 | + </div> | |
| 12 | + | |
| 13 | + <el-row> | |
| 14 | + <el-col :span="24"> | |
| 15 | + <el-form label-width="120px"> | |
| 16 | + <el-form-item :label="$t('roomBindOwner.owner')"> | |
| 17 | + <el-input v-model.trim="roomBindOwnerInfo.ownerName" | |
| 18 | + :placeholder="$t('roomBindOwner.ownerPlaceholder')" disabled /> | |
| 19 | + </el-form-item> | |
| 20 | + <el-form-item :label="$t('roomBindOwner.room')"> | |
| 21 | + <el-col :span="18"> | |
| 22 | + <el-input v-model.trim="roomBindOwnerInfo.roomName" | |
| 23 | + :placeholder="$t('roomBindOwner.roomPlaceholder')" disabled /> | |
| 24 | + </el-col> | |
| 25 | + <el-col :span="4" :offset="1"> | |
| 26 | + <el-button type="primary" size="small" icon="el-icon-plus" @click="_openChooseRoom"> | |
| 27 | + {{ $t('listOwner.selectRoom') }} | |
| 28 | + </el-button> | |
| 29 | + </el-col> | |
| 30 | + </el-form-item> | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + <el-form-item :label="$t('roomBindOwner.startTime')"> | |
| 35 | + <el-date-picker v-model="roomBindOwnerInfo.startTime" type="date" | |
| 36 | + :placeholder="$t('roomBindOwner.startTimePlaceholder')" value-format="yyyy-MM-dd" | |
| 37 | + class="addStartTime" /> | |
| 38 | + </el-form-item> | |
| 39 | + | |
| 40 | + <el-form-item :label="$t('roomBindOwner.endTime')"> | |
| 41 | + <el-date-picker v-model="roomBindOwnerInfo.endTime" type="date" | |
| 42 | + :placeholder="$t('roomBindOwner.endTimePlaceholder')" value-format="yyyy-MM-dd" | |
| 43 | + class="addEndTime" /> | |
| 44 | + </el-form-item> | |
| 45 | + </el-form> | |
| 46 | + </el-col> | |
| 47 | + </el-row> | |
| 48 | + </el-card> | |
| 49 | + | |
| 50 | + <el-row> | |
| 51 | + <el-col :span="24" class="text-right" style="margin-top: 20px;"> | |
| 52 | + <el-button type="primary" icon="el-icon-check" @click="saveRoomBindOwnerInfo"> | |
| 53 | + {{ $t('common.submit') }} | |
| 54 | + </el-button> | |
| 55 | + </el-col> | |
| 56 | + </el-row> | |
| 57 | + | |
| 58 | + <search-room ref="searchRoomRef" :roomFlag="2" @chooseRoom="handleChooseRoom" /> | |
| 59 | + </div> | |
| 60 | +</template> | |
| 61 | + | |
| 62 | +<script> | |
| 63 | +import { sellRoom } from '@/api/owner/roomBindOwnerApi' | |
| 64 | +import { getCommunityId } from '@/api/community/communityApi' | |
| 65 | +import searchRoom from '@/components/room/searchRoom' | |
| 66 | +import { getDateYYYYMMDD } from '@/utils/dateUtil' | |
| 67 | +import { queryOwners } from '@/api/owner/ownerApi' | |
| 68 | + | |
| 69 | +export default { | |
| 70 | + name: 'RoomBindOwnerList', | |
| 71 | + components: { | |
| 72 | + searchRoom | |
| 73 | + }, | |
| 74 | + data() { | |
| 75 | + return { | |
| 76 | + roomBindOwnerInfo: { | |
| 77 | + roomId: '', | |
| 78 | + roomName: '', | |
| 79 | + ownerId: '', | |
| 80 | + ownerName: '', | |
| 81 | + state: '2001', | |
| 82 | + startTime: '', | |
| 83 | + endTime: '2099-01-01', | |
| 84 | + }, | |
| 85 | + communityId: '' | |
| 86 | + } | |
| 87 | + }, | |
| 88 | + created() { | |
| 89 | + this.communityId = getCommunityId() | |
| 90 | + this.roomBindOwnerInfo.startTime = getDateYYYYMMDD() | |
| 91 | + this.initData() | |
| 92 | + }, | |
| 93 | + methods: { | |
| 94 | + initData() { | |
| 95 | + const ownerId = this.$route.query.ownerId | |
| 96 | + if (ownerId) { | |
| 97 | + this.roomBindOwnerInfo.ownerId = ownerId | |
| 98 | + this.listOwner(ownerId) | |
| 99 | + } | |
| 100 | + }, | |
| 101 | + async listOwner(ownerId) { | |
| 102 | + try { | |
| 103 | + const params = { | |
| 104 | + page: 1, | |
| 105 | + row: 1, | |
| 106 | + memberId: ownerId, | |
| 107 | + communityId: this.communityId | |
| 108 | + } | |
| 109 | + const { data } = await queryOwners(params) | |
| 110 | + this.roomBindOwnerInfo.ownerName = data[0].name | |
| 111 | + } catch (error) { | |
| 112 | + this.$message.error(this.$t('roomBindOwner.fetchRoomError')) | |
| 113 | + } | |
| 114 | + }, | |
| 115 | + handleChooseRoom(room) { | |
| 116 | + this.roomBindOwnerInfo.roomName = room.roomName | |
| 117 | + this.roomBindOwnerInfo.roomId = room.roomId | |
| 118 | + }, | |
| 119 | + _openChooseRoom() { | |
| 120 | + this.$refs.searchRoomRef.open() | |
| 121 | + }, | |
| 122 | + _goBack() { | |
| 123 | + this.$router.go(-1) | |
| 124 | + }, | |
| 125 | + roomBindOwnerValidate() { | |
| 126 | + const rules = { | |
| 127 | + ownerId: { required: true, message: this.$t('roomBindOwner.ownerRequired') }, | |
| 128 | + roomId: { required: true, message: this.$t('roomBindOwner.roomRequired') }, | |
| 129 | + startTime: { required: true, message: this.$t('roomBindOwner.startTimeRequired') }, | |
| 130 | + endTime: { required: true, message: this.$t('roomBindOwner.endTimeRequired') } | |
| 131 | + } | |
| 132 | + | |
| 133 | + const errors = [] | |
| 134 | + Object.keys(rules).forEach(key => { | |
| 135 | + if (!this.roomBindOwnerInfo[key]) { | |
| 136 | + errors.push(rules[key].message) | |
| 137 | + } | |
| 138 | + }) | |
| 139 | + | |
| 140 | + if (errors.length > 0) { | |
| 141 | + this.$message.error(errors[0]) | |
| 142 | + return false | |
| 143 | + } | |
| 144 | + | |
| 145 | + const start = new Date(this.roomBindOwnerInfo.startTime) | |
| 146 | + const end = new Date(this.roomBindOwnerInfo.endTime) | |
| 147 | + if (start >= end) { | |
| 148 | + this.$message.error(this.$t('roomBindOwner.timeError')) | |
| 149 | + return false | |
| 150 | + } | |
| 151 | + | |
| 152 | + return true | |
| 153 | + }, | |
| 154 | + async saveRoomBindOwnerInfo() { | |
| 155 | + if (!this.roomBindOwnerValidate()) return | |
| 156 | + | |
| 157 | + try { | |
| 158 | + const data = { | |
| 159 | + ...this.roomBindOwnerInfo, | |
| 160 | + communityId: this.communityId | |
| 161 | + } | |
| 162 | + | |
| 163 | + const res = await sellRoom(data) | |
| 164 | + if (res.code === 0) { | |
| 165 | + this.$message.success(this.$t('common.submitSuccess')) | |
| 166 | + this._goBack() | |
| 167 | + } else { | |
| 168 | + this.$message.error(res.msg || this.$t('common.submitFailed')) | |
| 169 | + } | |
| 170 | + } catch (error) { | |
| 171 | + this.$message.error(this.$t('common.requestError')) | |
| 172 | + } | |
| 173 | + } | |
| 174 | + } | |
| 175 | +} | |
| 176 | +</script> | |
| 177 | + | |
| 178 | +<style scoped> | |
| 179 | +.room-bind-owner-container { | |
| 180 | + padding: 20px; | |
| 181 | +} | |
| 182 | + | |
| 183 | +.header-tools {} | |
| 184 | + | |
| 185 | +.text-right { | |
| 186 | + text-align: right; | |
| 187 | +} | |
| 188 | + | |
| 189 | +.addStartTime, | |
| 190 | +.addEndTime { | |
| 191 | + width: 100%; | |
| 192 | +} | |
| 193 | +</style> | |
| 0 | 194 | \ No newline at end of file | ... | ... |
src/views/owner/roomBindOwnerList.vue
| 1 | 1 | <template> |
| 2 | 2 | <div class="room-bind-owner-container"> |
| 3 | 3 | <el-card class="box-card"> |
| 4 | - <div slot="header" class="clearfix"> | |
| 5 | - <h5>{{ $t('roomBindOwner.title') }}</h5> | |
| 4 | + <div slot="header" class="flex justify-between" > | |
| 5 | + <div>{{ $t('roomBindOwner.title') }}</div> | |
| 6 | 6 | <div class="header-tools"> |
| 7 | 7 | <el-button type="primary" size="small" icon="el-icon-close" @click="_goBack"> |
| 8 | 8 | {{ $t('common.back') }} |
| ... | ... | @@ -179,9 +179,7 @@ export default { |
| 179 | 179 | } |
| 180 | 180 | |
| 181 | 181 | .header-tools { |
| 182 | - position: absolute; | |
| 183 | - top: 20px; | |
| 184 | - right: 20px; | |
| 182 | + | |
| 185 | 183 | } |
| 186 | 184 | |
| 187 | 185 | .text-right { | ... | ... |
src/views/room/roomLang.js
| ... | ... | @@ -359,6 +359,25 @@ export const messages = { |
| 359 | 359 | successMessage: 'Unit deleted successfully', |
| 360 | 360 | errorMessage: 'Failed to delete unit' |
| 361 | 361 | }, |
| 362 | + searchRoom: { | |
| 363 | + title: 'Select Room', | |
| 364 | + floorNumPlaceholder: 'Enter building number', | |
| 365 | + roomNumPlaceholder: 'Enter room number', | |
| 366 | + search: 'Search', | |
| 367 | + reset: 'Reset', | |
| 368 | + roomId: 'Room ID', | |
| 369 | + floorNum: 'Building Number', | |
| 370 | + unitNum: 'Unit Number', | |
| 371 | + roomNum: 'Room Number', | |
| 372 | + layer: 'Floor', | |
| 373 | + operation: 'Operation', | |
| 374 | + choose: 'Choose', | |
| 375 | + building: 'Building', | |
| 376 | + unit: 'Unit', | |
| 377 | + room: 'Room', | |
| 378 | + floor: 'Floor', | |
| 379 | + loadFailed: 'Failed to load room information' | |
| 380 | + } | |
| 362 | 381 | }, |
| 363 | 382 | zh: { |
| 364 | 383 | roomList: { |
| ... | ... | @@ -720,5 +739,24 @@ export const messages = { |
| 720 | 739 | successMessage: '单元删除成功', |
| 721 | 740 | errorMessage: '删除单元失败' |
| 722 | 741 | }, |
| 742 | + searchRoom: { | |
| 743 | + title: '选择房屋', | |
| 744 | + floorNumPlaceholder: '输入小区楼编号', | |
| 745 | + roomNumPlaceholder: '输入房屋编号', | |
| 746 | + search: '查询', | |
| 747 | + reset: '重置', | |
| 748 | + roomId: '房屋ID', | |
| 749 | + floorNum: '楼栋编号', | |
| 750 | + unitNum: '单元编号', | |
| 751 | + roomNum: '房屋编号', | |
| 752 | + layer: '楼层', | |
| 753 | + operation: '操作', | |
| 754 | + choose: '选择', | |
| 755 | + building: '号楼', | |
| 756 | + unit: '单元', | |
| 757 | + room: '室', | |
| 758 | + floor: '层', | |
| 759 | + loadFailed: '加载房屋信息失败' | |
| 760 | + } | |
| 723 | 761 | } |
| 724 | 762 | } |
| 725 | 763 | \ No newline at end of file | ... | ... |