修改社区和楼栋的代码

This commit is contained in:
aaron 2025-03-12 18:25:06 +08:00
parent ffe82a5f84
commit 0885b4deef
2 changed files with 458 additions and 125 deletions

View File

@ -3,7 +3,7 @@
<div class="building-list">
<div class="table-header">
<h1>楼栋列表</h1>
<a-button type="primary" @click="showAddModal">添加楼栋</a-button>
<a-button type="primary" @click="showAddModal">批量添加楼栋</a-button>
</div>
<!-- 添加筛选区域 -->
@ -30,20 +30,44 @@
</a-form>
</div>
<a-table
:columns="columns"
:data-source="tableData"
:pagination="pagination"
:loading="loading"
@change="handleTableChange"
row-key="id"
<!-- 使用分组展示楼栋数据 -->
<div v-if="!loading" class="community-building-list">
<div v-for="community in groupedData" :key="community.community_id" class="community-card">
<div class="community-header">
<h3>{{ community.community_name }}</h3>
<span class="building-count"> {{ community.buildings.length }} </span>
</div>
<div class="building-grid">
<div
v-for="building in community.buildings"
:key="building.building_id"
class="building-item"
@click="showEditModal(building, community)"
>
</a-table>
<div class="building-content">
<div class="building-name">{{ building.building_name }}</div>
<div class="building-id">ID: {{ building.building_id }}</div>
</div>
<div class="building-edit-icon">
<edit-outlined />
</div>
</div>
</div>
</div>
<!-- 添加楼栋模态框 -->
<!-- 无数据时显示 -->
<a-empty v-if="groupedData.length === 0" description="暂无楼栋数据" />
</div>
<!-- 加载中状态 -->
<div v-else class="loading-container">
<a-spin tip="加载中..."></a-spin>
</div>
<!-- 批量添加楼栋模态框 -->
<a-modal
v-model:visible="addModalVisible"
title="添加楼栋"
title="批量添加楼栋"
@cancel="handleCancel"
:confirmLoading="confirmLoading"
width="500px"
@ -75,8 +99,81 @@
/>
</a-form-item>
<a-form-item label="起始楼栋号" name="from_number" required>
<a-input-number
v-model:value="formState.from_number"
:min="1"
:precision="0"
style="width: 100%"
placeholder="请输入起始楼栋号"
/>
</a-form-item>
<a-form-item label="结束楼栋号" name="to_number" required>
<a-input-number
v-model:value="formState.to_number"
:min="1"
:precision="0"
style="width: 100%"
placeholder="请输入结束楼栋号"
/>
<div class="form-item-tip">
将批量创建从起始楼栋号到结束楼栋号的所有楼栋
</div>
</a-form-item>
</a-form>
</a-modal>
<!-- 编辑楼栋模态框 -->
<a-modal
v-model:visible="editModalVisible"
title="编辑楼栋"
@cancel="handleEditCancel"
:confirmLoading="editConfirmLoading"
width="500px"
>
<template #footer>
<div class="modal-footer-with-delete">
<a-popconfirm
title="确定要删除该楼栋吗?"
ok-text="确定"
cancel-text="取消"
@confirm="handleDeleteBuilding"
:disabled="deleteLoading"
>
<a-button danger :loading="deleteLoading">删除楼栋</a-button>
</a-popconfirm>
<a-space>
<a-button @click="handleEditCancel">取消</a-button>
<a-button type="primary" :loading="editConfirmLoading" @click="handleEditSave">
保存
</a-button>
</a-space>
</div>
</template>
<a-form
ref="editFormRef"
:model="editFormState"
:rules="editRules"
:label-col="{ span: 6 }"
:wrapper-col="{ span: 16 }"
class="building-form"
>
<a-form-item label="所属小区" required>
<a-input
v-model:value="currentCommunityName"
disabled
style="width: 100%"
/>
</a-form-item>
<a-form-item label="楼栋名称" name="building_name" required>
<a-input v-model:value="formState.building_name" placeholder="请输入楼栋名称" />
<a-input
v-model:value="editFormState.building_name"
placeholder="请输入楼栋名称"
style="width: 100%"
/>
</a-form-item>
</a-form>
</a-modal>
@ -85,76 +182,45 @@
</template>
<script>
import { defineComponent, ref, onMounted } from 'vue'
import { defineComponent, ref, onMounted, computed } from 'vue'
import { message } from 'ant-design-vue'
import { getBuildingList, getCommunityList, createBuilding } from '@/api/community'
import dayjs from 'dayjs'
import { getCommunityList } from '@/api/community'
import request from '@/utils/request'
import PageContainer from '@/components/PageContainer.vue'
import { EditOutlined } from '@ant-design/icons-vue'
export default defineComponent({
components: {
PageContainer
PageContainer,
EditOutlined
},
setup() {
const loading = ref(false)
const tableData = ref([])
const groupedData = ref([])
const communityOptions = ref([])
const pagination = ref({
current: 1,
pageSize: 10,
total: 0,
showSizeChanger: true,
showTotal: (total) => `${total} 条记录`
})
const columns = [
{
title: 'ID',
dataIndex: 'id',
key: 'id',
width: 80,
align: 'center',
},
{
title: '所属小区',
dataIndex: 'community_name',
key: 'community_name',
width: 150,
},
{
title: '楼栋名称',
dataIndex: 'building_name',
key: 'building_name',
width: 150,
}
]
//
const formatDateTime = (value) => {
if (!value) return ''
return dayjs(value).format('YYYY-MM-DD HH:mm:ss')
}
//
const filterForm = ref({
community_id: undefined
})
//
// 使
const fetchData = async () => {
try {
loading.value = true
const params = {
skip: (pagination.value.current - 1) * pagination.value.pageSize,
limit: pagination.value.pageSize,
community_id: filterForm.value.community_id //
}
const res = await getBuildingList(params)
// 使
const res = await request.get('/api/community/building/group_by_community')
if (res.code === 200) {
tableData.value = res.data.items
pagination.value.total = res.data.total
//
if (filterForm.value.community_id) {
groupedData.value = res.data.filter(
item => item.community_id === filterForm.value.community_id
)
} else {
groupedData.value = res.data
}
} else {
message.error(res.message || '获取数据失败')
}
@ -168,7 +234,6 @@ export default defineComponent({
//
const handleFilter = () => {
pagination.value.current = 1 //
fetchData()
}
@ -188,26 +253,34 @@ export default defineComponent({
}
}
//
const handleTableChange = (pag) => {
pagination.value.current = pag.current
pagination.value.pageSize = pag.pageSize
fetchData()
}
//
//
const addModalVisible = ref(false)
const confirmLoading = ref(false)
const formRef = ref(null)
const formState = ref({
community_id: undefined,
building_name: ''
from_number: 1,
to_number: 1
})
const rules = {
community_id: [{ required: true, message: '请选择所属小区' }],
building_name: [{ required: true, message: '请输入楼栋名称' }]
from_number: [
{ required: true, message: '请输入起始楼栋号' },
{ type: 'number', min: 1, message: '起始楼栋号必须大于等于1' }
],
to_number: [
{ required: true, message: '请输入结束楼栋号' },
{ type: 'number', min: 1, message: '结束楼栋号必须大于等于1' },
{ validator: (rule, value) => {
if (value < formState.value.from_number) {
return Promise.reject('结束楼栋号必须大于等于起始楼栋号');
}
return Promise.resolve();
}
}
]
}
//
@ -216,21 +289,33 @@ export default defineComponent({
fetchCommunityOptions()
}
//
// -
const handleAdd = () => {
formRef.value.validate().then(async () => {
try {
confirmLoading.value = true
const res = await createBuilding(formState.value)
//
const buildingNames = [];
for (let i = formState.value.from_number; i <= formState.value.to_number; i++) {
buildingNames.push(`${i}`);
}
// 使
const res = await request.post('/api/community/building/batch', {
community_id: formState.value.community_id,
building_names: buildingNames
})
if (res.code === 200) {
message.success('添加成功')
message.success('批量添加成功')
addModalVisible.value = false
fetchData() //
} else {
message.error(res.message || '添加失败')
}
} catch (error) {
console.error('添加楼栋失败:', error)
console.error('批量添加楼栋失败:', error)
message.error('添加失败')
} finally {
confirmLoading.value = false
@ -244,6 +329,86 @@ export default defineComponent({
addModalVisible.value = false
}
//
const editModalVisible = ref(false)
const editConfirmLoading = ref(false)
const deleteLoading = ref(false)
const editFormRef = ref(null)
const currentBuildingId = ref(null)
const currentCommunityName = ref('')
const editFormState = ref({
building_name: ''
})
const editRules = {
building_name: [{ required: true, message: '请输入楼栋名称' }]
}
//
const showEditModal = (building, community) => {
currentBuildingId.value = building.building_id
currentCommunityName.value = community.community_name
editFormState.value.building_name = building.building_name
editModalVisible.value = true
}
//
const handleEditSave = () => {
editFormRef.value.validate().then(async () => {
try {
editConfirmLoading.value = true
// 使
const res = await request.put(`/api/community/building/${currentBuildingId.value}`, {
building_name: editFormState.value.building_name
})
if (res.code === 200) {
message.success('修改成功')
editModalVisible.value = false
fetchData() //
} else {
message.error(res.message || '修改失败')
}
} catch (error) {
console.error('修改楼栋失败:', error)
message.error('修改失败')
} finally {
editConfirmLoading.value = false
}
})
}
//
const handleDeleteBuilding = async () => {
try {
deleteLoading.value = true
// 使
const res = await request.delete(`/api/community/building/${currentBuildingId.value}`)
if (res.code === 200) {
message.success('删除成功')
editModalVisible.value = false
fetchData() //
} else {
message.error(res.message || '删除失败')
}
} catch (error) {
console.error('删除楼栋失败:', error)
message.error('删除失败')
} finally {
deleteLoading.value = false
}
}
//
const handleEditCancel = () => {
editFormRef.value?.resetFields()
editModalVisible.value = false
}
onMounted(() => {
fetchData()
fetchCommunityOptions() //
@ -251,11 +416,8 @@ export default defineComponent({
return {
loading,
columns,
tableData,
pagination,
handleTableChange,
formatDateTime,
groupedData,
handleFilter,
addModalVisible,
confirmLoading,
formState,
@ -266,7 +428,18 @@ export default defineComponent({
handleAdd,
handleCancel,
filterForm,
handleFilter
//
editModalVisible,
editConfirmLoading,
deleteLoading,
editFormState,
editFormRef,
editRules,
currentCommunityName,
showEditModal,
handleEditSave,
handleEditCancel,
handleDeleteBuilding
}
}
})
@ -284,24 +457,6 @@ export default defineComponent({
margin: 0;
}
:deep(.ant-table-content) {
overflow-x: auto;
}
:deep(.ant-modal-body) {
padding: 24px;
}
:deep(.ant-form-item) {
margin-bottom: 24px;
}
:deep(.ant-modal-footer) {
text-align: right;
padding: 16px 24px;
border-top: 1px solid #f0f0f0;
}
.table-filter {
margin-bottom: 16px;
padding: 16px 24px;
@ -317,6 +472,121 @@ export default defineComponent({
}
}
/* 社区楼栋列表样式 */
.community-building-list {
display: flex;
flex-direction: column;
gap: 16px;
}
.community-card {
background: #fff;
border-radius: 8px;
padding: 20px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
transition: all 0.3s;
}
.community-card:hover {
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.12);
}
.community-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16px;
padding-bottom: 12px;
border-bottom: 1px solid #f0f0f0;
}
.community-header h3 {
margin: 0;
font-size: 18px;
color: #1890ff;
font-weight: 500;
}
.building-count {
font-size: 14px;
color: rgba(0, 0, 0, 0.45);
background: #f5f5f5;
padding: 4px 10px;
border-radius: 12px;
}
.building-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
gap: 12px;
}
.building-item {
background-color: #f0f7ff;
border-radius: 6px;
transition: all 0.3s;
cursor: pointer;
border: 1px solid #d9e8ff;
position: relative;
overflow: hidden;
}
.building-item:hover {
transform: translateY(-2px);
box-shadow: 0 2px 6px rgba(24, 144, 255, 0.15);
background-color: #e6f7ff;
}
.building-item:hover .building-edit-icon {
opacity: 1;
}
.building-content {
padding: 12px;
text-align: center;
}
.building-name {
font-size: 16px;
font-weight: 500;
color: #1890ff;
margin-bottom: 4px;
}
.building-id {
font-size: 12px;
color: rgba(0, 0, 0, 0.45);
}
.building-edit-icon {
position: absolute;
top: 0;
right: 0;
background-color: rgba(24, 144, 255, 0.1);
color: #1890ff;
padding: 4px;
border-bottom-left-radius: 6px;
opacity: 0;
transition: opacity 0.3s;
}
/* 移除旧的标签样式 */
.building-tags {
display: none;
}
.building-tag {
display: none;
}
/* 加载状态容器 */
.loading-container {
display: flex;
justify-content: center;
align-items: center;
height: 200px;
}
/* 调整表单样式 */
.building-form {
padding: 20px 0;
@ -343,7 +613,8 @@ export default defineComponent({
}
:deep(.ant-input),
:deep(.ant-select) {
:deep(.ant-select),
:deep(.ant-input-number) {
height: 32px !important;
}
@ -366,11 +637,25 @@ export default defineComponent({
}
:deep(.ant-modal-body) {
padding: 0;
padding: 24px;
}
:deep(.ant-modal-footer) {
border-top: 1px solid #f0f0f0;
padding: 16px 24px;
}
.form-item-tip {
font-size: 12px;
color: rgba(0, 0, 0, 0.45);
margin-top: 4px;
}
/* 带删除按钮的模态框底部样式 */
.modal-footer-with-delete {
display: flex;
justify-content: space-between;
align-items: center;
width: 100%;
}
</style>

View File

@ -339,6 +339,19 @@
:confirmLoading="adminSaving"
width="480px"
>
<template #footer>
<a-space>
<a-button @click="handleAdminCancel">取消</a-button>
<a-button
type="primary"
:loading="adminSaving"
@click="handleAdminSave"
>
保存
</a-button>
</a-space>
</template>
<div class="admin-search-form">
<a-form layout="vertical">
<a-form-item label="搜索用户">
@ -351,7 +364,7 @@
</a-form-item>
</a-form>
<div v-if="adminSearchResult && !currentCommunity.admin" class="admin-search-result">
<div v-if="adminSearchResult" class="admin-search-result">
<a-card :class="{ 'admin-card-selected': adminSearchResult.selected }" @click="selectSearchResult(adminSearchResult)">
<div class="admin-info">
<div class="admin-info-item">
@ -370,9 +383,19 @@
</a-card>
</div>
<div v-if="currentCommunity" class="current-admin-info">
<div v-if="currentCommunity && currentCommunity.admin" class="current-admin-info">
<div class="current-admin-title-row">
<div class="current-admin-title">当前服务商信息</div>
<div v-if="currentCommunity.admin" class="admin-info">
<a-button
type="danger"
size="small"
:loading="adminSaving"
@click="handleRemoveAdmin"
>
移除服务商
</a-button>
</div>
<div class="admin-info">
<div class="admin-info-item">
<span class="admin-info-label">用户ID:</span>
<span class="admin-info-value">{{ currentCommunity.admin.userid || currentCommunity.admin_id }}</span>
@ -386,9 +409,6 @@
<span class="admin-info-value">{{ currentCommunity.admin.phone || currentCommunity.admin_phone || '-' }}</span>
</div>
</div>
<div v-else class="no-admin-info">
<a-empty description="暂无服务商" />
</div>
</div>
</div>
</a-modal>
@ -1112,22 +1132,17 @@ export default defineComponent({
//
const handleAdminSave = async () => {
//
if (!adminSearchResult?.value?.selected) {
//
if (currentCommunity.value.admin) {
adminModalVisible.value = false
return
}
if (!adminSearchResult.value) {
message.warning('请先搜索并选择服务商')
return
}
if (!adminSearchResult.value.selected) {
message.warning('请先选择搜索结果中的用户作为服务商')
return
}
try {
adminSaving.value = true
const params = {
@ -1152,6 +1167,32 @@ export default defineComponent({
}
}
//
const handleRemoveAdmin = async () => {
try {
adminSaving.value = true
const params = {
admin_id: null // null
}
const res = await request.put(`/api/community/${currentCommunity.value.id}`, params)
if (res.code === 200) {
message.success('服务商已移除')
adminModalVisible.value = false
//
fetchData()
} else {
message.error(res.message || '移除失败')
}
} catch (error) {
console.error('移除服务商失败:', error)
message.error('移除失败')
} finally {
adminSaving.value = false
}
}
//
const handleAdminCancel = () => {
adminModalVisible.value = false
@ -1222,7 +1263,8 @@ export default defineComponent({
handleSearchAdmin,
handleAdminSave,
handleAdminCancel,
selectSearchResult
selectSearchResult,
handleRemoveAdmin
}
}
})
@ -1715,11 +1757,17 @@ export default defineComponent({
padding-top: 16px;
}
.current-admin-title-row {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12px;
}
.current-admin-title {
font-size: 14px;
font-weight: 500;
color: rgba(0, 0, 0, 0.85);
margin-bottom: 12px;
}
.admin-info {