Selaa lähdekoodia

feat(zfgl): 增加设备管理功能

- 新增设备管理相关的数据结构和接口- 实现设备新增、编辑、删除和列表加载功能
- 在房屋详细信息对话框中增加设备管理标签页
- 优化房屋基本信息和详细信息的展示和编辑
nahida 10 kuukautta sitten
vanhempi
commit
59f10394de
2 muutettua tiedostoa jossa 331 lisäystä ja 57 poistoa
  1. 3 0
      components.d.ts
  2. 328 57
      src/views/zfgl/zfxxgl.vue

+ 3 - 0
components.d.ts

@@ -9,6 +9,7 @@ export {}
 declare module 'vue' {
   export interface GlobalComponents {
     ElAside: typeof import('element-plus/es')['ElAside']
+    ElBadge: typeof import('element-plus/es')['ElBadge']
     ElButton: typeof import('element-plus/es')['ElButton']
     ElCheckbox: typeof import('element-plus/es')['ElCheckbox']
     ElCol: typeof import('element-plus/es')['ElCol']
@@ -36,6 +37,8 @@ declare module 'vue' {
     ElSubMenu: typeof import('element-plus/es')['ElSubMenu']
     ElTable: typeof import('element-plus/es')['ElTable']
     ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
+    ElTabPane: typeof import('element-plus/es')['ElTabPane']
+    ElTabs: typeof import('element-plus/es')['ElTabs']
     ElTag: typeof import('element-plus/es')['ElTag']
     ElUpload: typeof import('element-plus/es')['ElUpload']
     IconCommunity: typeof import('./src/components/icons/IconCommunity.vue')['default']

+ 328 - 57
src/views/zfgl/zfxxgl.vue

@@ -1,8 +1,9 @@
 <script setup lang="ts">
 import { onMounted, reactive, ref } from 'vue'
 import { ElMessage, ElMessageBox } from 'element-plus'
-import { Delete, Edit, Eye, Home, Plus, Search } from 'lucide-vue-next'
+import { Delete, Edit, Eye, Home, Info, Package, Plus, Search, Settings } from 'lucide-vue-next'
 import { clientGet, clientPost } from '@/utils/request.ts'
+import { BaseResponse, PageType } from '@/utils/types.ts' // Import BaseResponse and PageType
 
 interface ASimplifiedHouseInfo {
   id: string
@@ -71,6 +72,41 @@ interface UpdateAHouseInfoDetail {
   houseType?: string | null
 }
 
+interface ADeviceInfo {
+  id: string
+  simplifiedHouseId?: string | null
+  deviceName: string
+  deviceType?: string | null
+  deviceBrand?: string | null
+  deviceNumber?: string | null
+  devicePrice?: string | null
+  createTime?: Date | null
+  updateTime?: Date | null
+}
+
+interface ADeviceInfoListResponse extends BaseResponse {
+  data: ADeviceInfo[]
+}
+
+interface AddADeviceInfo {
+  simplifiedHouseId: string
+  deviceName: string
+  deviceType?: string | null
+  deviceBrand?: string | null
+  deviceNumber?: string | null
+  devicePrice?: string | null
+}
+
+interface UpdateADeviceInfo {
+  id: string
+  simplifiedHouseId: string
+  deviceName: string
+  deviceType?: string | null
+  deviceBrand?: string | null
+  deviceNumber?: string | null
+  devicePrice?: string | null
+}
+
 // 响应式数据
 const loading = ref(false)
 const tableData = ref<ASimplifiedHouseInfo[]>([])
@@ -89,8 +125,11 @@ const searchForm = reactive({
 // 对话框控制
 const dialogVisible = ref(false)
 const detailDialogVisible = ref(false)
+const deviceDialogVisible = ref(false)
 const isEdit = ref(false)
+const isDeviceEdit = ref(false)
 const dialogTitle = ref('新增房屋信息')
+const activeTab = ref('detail')
 
 // 表单数据
 const houseForm = reactive<AddASimplifiedHouseInfo & { id?: string }>({
@@ -110,9 +149,21 @@ const detailForm = reactive<AddAHouseInfoDetail & { id?: string }>({
   houseType: '',
 })
 
-// 选中的行
+const deviceForm = reactive<AddADeviceInfo & { id?: string }>({
+  simplifiedHouseId: '',
+  deviceName: '',
+  deviceType: '',
+  deviceBrand: '',
+  deviceNumber: '',
+  devicePrice: '',
+})
+
+// 选中的行和设备数据
 const selectedRows = ref<ASimplifiedHouseInfo[]>([])
 const currentHouseId = ref('')
+const currentHouseName = ref('')
+const deviceList = ref<ADeviceInfo[]>([])
+const deviceLoading = ref(false)
 
 // API方法
 const addRentingHouse = async (info: AddASimplifiedHouseInfo) => {
@@ -158,7 +209,6 @@ const getList = async () => {
       pageNum: currentPage.value,
       pageSize: pageSize.value,
     }
-
     if (searchForm.status) params.status = searchForm.status
     if (searchForm.houseName) params.houseName = searchForm.houseName
     if (searchForm.building) params.building = searchForm.building
@@ -216,6 +266,50 @@ const getDetail = async (simplifiedHouseId: string) => {
   return res.data
 }
 
+const addDevice = async (info: AddADeviceInfo) => {
+  const res = await clientPost<AddADeviceInfo, BaseResponse>('/adeviceInfo/save', info)
+  if (res.code !== 200) {
+    ElMessage.error(res.msg)
+    return false
+  }
+  ElMessage.success(res.msg)
+  return true
+}
+
+const deleteDevice = async (ids: string[]) => {
+  const res = await clientPost<string[], BaseResponse>('/adeviceInfo/deleteBatch', ids)
+  if (res.code !== 200) {
+    ElMessage.error(res.msg)
+    return false
+  }
+  ElMessage.success(res.msg)
+  return true
+}
+
+const getDeviceList = async (simplifiedHouseId: string) => {
+  const res = await clientGet<{ id: string }, ADeviceInfoListResponse>(
+    '/adeviceInfo/getBySimplifiedHouseId',
+    {
+      params: { simplifiedHouseId },
+    },
+  )
+  if (res.code !== 200) {
+    ElMessage.error(res.msg)
+    return []
+  }
+  return res.data
+}
+
+const updateDevice = async (info: UpdateADeviceInfo) => {
+  const res = await clientPost<UpdateADeviceInfo, BaseResponse>('/adeviceInfo/update', info)
+  if (res.code !== 200) {
+    ElMessage.error(res.msg)
+    return false
+  }
+  ElMessage.success(res.msg)
+  return true
+}
+
 // 页面方法
 const handleSearch = () => {
   currentPage.value = 1
@@ -261,7 +355,6 @@ const handleDelete = async (row: ASimplifiedHouseInfo) => {
       cancelButtonText: '取消',
       type: 'warning',
     })
-
     const success = await deleteBatchRentingHouse([row.id])
     if (success) {
       getList()
@@ -276,14 +369,12 @@ const handleBatchDelete = async () => {
     ElMessage.warning('请选择要删除的记录')
     return
   }
-
   try {
     await ElMessageBox.confirm(`确定要删除选中的 ${selectedRows.value.length} 条记录吗?`, '提示', {
       confirmButtonText: '确定',
       cancelButtonText: '取消',
       type: 'warning',
     })
-
     const ids = selectedRows.value.map((row) => row.id)
     const success = await deleteBatchRentingHouse(ids)
     if (success) {
@@ -296,6 +387,10 @@ const handleBatchDelete = async () => {
 
 const handleViewDetail = async (row: ASimplifiedHouseInfo) => {
   currentHouseId.value = row.id
+  currentHouseName.value = row.houseName || ''
+  activeTab.value = 'detail'
+
+  // 获取房屋详细信息
   const detail = await getDetail(row.id)
   if (detail) {
     Object.assign(detailForm, { ...detail, simplifiedHouseId: row.id })
@@ -308,9 +403,23 @@ const handleViewDetail = async (row: ASimplifiedHouseInfo) => {
       houseType: '',
     })
   }
+
+  // 获取设备列表
+  await loadDeviceList(row.id)
+
   detailDialogVisible.value = true
 }
 
+const loadDeviceList = async (houseId: string) => {
+  deviceLoading.value = true
+  try {
+    const devices = await getDeviceList(houseId)
+    deviceList.value = devices || []
+  } finally {
+    deviceLoading.value = false
+  }
+}
+
 const handleSubmit = async () => {
   let success = false
   if (isEdit.value) {
@@ -318,7 +427,6 @@ const handleSubmit = async () => {
   } else {
     success = await addRentingHouse(houseForm)
   }
-
   if (success) {
     dialogVisible.value = false
     getList()
@@ -332,9 +440,57 @@ const handleDetailSubmit = async () => {
   } else {
     success = await addRentingHouseDetail(detailForm)
   }
+  if (success) {
+    ElMessage.success('房屋详细信息保存成功')
+  }
+}
+
+const handleAddDevice = () => {
+  isDeviceEdit.value = false
+  Object.assign(deviceForm, {
+    id: '',
+    simplifiedHouseId: currentHouseId.value,
+    deviceName: '',
+    deviceType: '',
+    deviceBrand: '',
+    deviceNumber: '',
+    devicePrice: '',
+  })
+  deviceDialogVisible.value = true
+}
 
+const handleEditDevice = (device: ADeviceInfo) => {
+  isDeviceEdit.value = true
+  Object.assign(deviceForm, { ...device })
+  deviceDialogVisible.value = true
+}
+
+const handleDeleteDevice = async (device: ADeviceInfo) => {
+  try {
+    await ElMessageBox.confirm('确定要删除这个设备吗?', '提示', {
+      confirmButtonText: '确定',
+      cancelButtonText: '取消',
+      type: 'warning',
+    })
+    const success = await deleteDevice([device.id])
+    if (success) {
+      await loadDeviceList(currentHouseId.value)
+    }
+  } catch {
+    // 用户取消删除
+  }
+}
+
+const handleDeviceSubmit = async () => {
+  let success = false
+  if (isDeviceEdit.value) {
+    success = await updateDevice(deviceForm as UpdateADeviceInfo)
+  } else {
+    success = await addDevice(deviceForm)
+  }
   if (success) {
-    detailDialogVisible.value = false
+    deviceDialogVisible.value = false
+    await loadDeviceList(currentHouseId.value)
   }
 }
 
@@ -368,7 +524,7 @@ onMounted(() => {
         </div>
         <div>
           <h1 class="text-xl font-semibold text-gray-900">租房信息管理</h1>
-          <p class="text-sm text-gray-500">管理房屋基本信息和详细信息</p>
+          <p class="text-sm text-gray-500">管理房屋基本信息、详细信息和设备信息</p>
         </div>
       </div>
 
@@ -386,7 +542,6 @@ onMounted(() => {
               <el-option label="厂房" value="厂房" />
             </el-select>
           </el-form-item>
-
           <el-form-item label="楼栋">
             <el-input
               v-model="searchForm.building"
@@ -395,7 +550,6 @@ onMounted(() => {
               class="w-40"
             />
           </el-form-item>
-
           <el-form-item label="房间名称">
             <el-input
               v-model="searchForm.houseName"
@@ -404,7 +558,6 @@ onMounted(() => {
               class="w-40"
             />
           </el-form-item>
-
           <el-form-item label="状态">
             <el-select
               v-model="searchForm.status"
@@ -416,7 +569,6 @@ onMounted(() => {
               <el-option label="已租" value="已租" />
             </el-select>
           </el-form-item>
-
           <el-form-item>
             <el-button type="primary" @click="handleSearch" :icon="Search">搜索</el-button>
             <el-button @click="handleReset">重置</el-button>
@@ -450,7 +602,6 @@ onMounted(() => {
           stripe
         >
           <el-table-column type="selection" width="55" />
-
           <el-table-column prop="assetType" label="资产类型" width="100">
             <template #default="{ row }">
               <el-tag :type="row.assetType === '公租房' ? 'primary' : 'success'">
@@ -458,12 +609,10 @@ onMounted(() => {
               </el-tag>
             </template>
           </el-table-column>
-
           <el-table-column prop="houseName" label="房间名称" width="120" />
           <el-table-column prop="building" label="楼栋" width="100" />
           <el-table-column prop="floor" label="楼层" width="100" />
           <el-table-column prop="address" label="地址" min-width="200" show-overflow-tooltip />
-
           <el-table-column prop="status" label="状态" width="100">
             <template #default="{ row }">
               <el-tag :type="row.status === '空闲' ? 'success' : 'warning'">
@@ -471,10 +620,8 @@ onMounted(() => {
               </el-tag>
             </template>
           </el-table-column>
-
           <el-table-column prop="rentRange" label="租金范围" width="150" />
-
-          <el-table-column label="操作" width="200" fixed="right">
+          <el-table-column label="操作" width="250" fixed="right">
             <template #default="{ row }">
               <div class="flex gap-2">
                 <el-button type="primary" size="small" @click="handleViewDetail(row)" :icon="Eye">
@@ -506,7 +653,7 @@ onMounted(() => {
       </div>
     </div>
 
-    <!-- 新增/编辑对话框 -->
+    <!-- 新增/编辑房屋对话框 -->
     <el-dialog
       v-model="dialogVisible"
       :title="dialogTitle"
@@ -520,35 +667,28 @@ onMounted(() => {
             <el-option label="厂房" value="厂房" />
           </el-select>
         </el-form-item>
-
         <el-form-item label="楼栋" required>
           <el-input v-model="houseForm.building" placeholder="请输入楼栋" />
         </el-form-item>
-
         <el-form-item label="楼层">
           <el-input v-model="houseForm.floor" placeholder="请输入楼层" />
         </el-form-item>
-
         <el-form-item label="房间名称" required>
           <el-input v-model="houseForm.houseName" placeholder="请输入房间名称" />
         </el-form-item>
-
         <el-form-item label="状态" required>
           <el-select v-model="houseForm.status" placeholder="请选择状态" class="w-full">
             <el-option label="空闲" value="空闲" />
             <el-option label="已租" value="已租" />
           </el-select>
         </el-form-item>
-
         <el-form-item label="租金范围">
           <el-input v-model="houseForm.rentRange" placeholder="请输入租金范围" />
         </el-form-item>
-
         <el-form-item label="地址" class="col-span-2">
           <el-input v-model="houseForm.address" placeholder="请输入详细地址" />
         </el-form-item>
       </el-form>
-
       <template #footer>
         <div class="flex justify-end gap-3">
           <el-button @click="dialogVisible = false">取消</el-button>
@@ -557,43 +697,164 @@ onMounted(() => {
       </template>
     </el-dialog>
 
-    <!-- 详细信息对话框 -->
+    <!-- 房屋详细信息和设备管理对话框 -->
     <el-dialog
       v-model="detailDialogVisible"
-      title="房屋详细信息"
+      :title="`${currentHouseName} - 详细信息管理`"
+      width="900px"
+      :close-on-click-modal="false"
+      class="detail-dialog"
+    >
+      <el-tabs v-model="activeTab" class="detail-tabs">
+        <!-- 房屋详细信息标签页 -->
+        <el-tab-pane label="房屋详情" name="detail">
+          <template #label>
+            <div class="flex items-center gap-2">
+              <Info class="w-4 h-4" />
+              <span>房屋详情</span>
+            </div>
+          </template>
+          <div class="p-4">
+            <el-form :model="detailForm" label-width="100px" class="grid grid-cols-2 gap-4">
+              <el-form-item label="面积">
+                <el-input v-model="detailForm.area" placeholder="请输入面积" />
+              </el-form-item>
+              <el-form-item label="房屋类型">
+                <el-select
+                  v-model="detailForm.houseType"
+                  placeholder="请选择房屋类型"
+                  class="w-full"
+                >
+                  <el-option label="一室一厅" value="一室一厅" />
+                  <el-option label="两室一厅" value="两室一厅" />
+                  <el-option label="三室一厅" value="三室一厅" />
+                  <el-option label="三室两厅" value="三室两厅" />
+                  <el-option label="厂房" value="厂房" />
+                  <el-option label="仓库" value="仓库" />
+                </el-select>
+              </el-form-item>
+              <el-form-item label="房屋介绍" class="col-span-2">
+                <el-input
+                  v-model="detailForm.introduce"
+                  type="textarea"
+                  :rows="4"
+                  placeholder="请输入房屋介绍"
+                />
+              </el-form-item>
+            </el-form>
+            <div class="flex justify-end mt-4">
+              <el-button type="primary" @click="handleDetailSubmit">保存房屋详情</el-button>
+            </div>
+          </div>
+        </el-tab-pane>
+
+        <!-- 设备管理标签页 -->
+        <el-tab-pane label="设备管理" name="device">
+          <template #label>
+            <div class="flex items-center gap-2">
+              <Settings class="w-4 h-4" />
+              <span>设备管理</span>
+              <el-badge :value="deviceList.length" class="ml-1" />
+            </div>
+          </template>
+          <div class="p-4">
+            <!-- 设备操作按钮 -->
+            <div class="flex justify-between items-center mb-4">
+              <el-button type="primary" @click="handleAddDevice" :icon="Plus" size="small">
+                新增设备
+              </el-button>
+              <div class="text-sm text-gray-500">共 {{ deviceList.length }} 个设备</div>
+            </div>
+
+            <!-- 设备列表 -->
+            <div v-loading="deviceLoading">
+              <el-table :data="deviceList" stripe class="w-full">
+                <el-table-column prop="deviceName" label="设备名称" width="120" />
+                <el-table-column prop="deviceType" label="设备类型" width="100" />
+                <el-table-column prop="deviceBrand" label="品牌" width="100" />
+                <el-table-column prop="deviceNumber" label="型号" width="120" />
+                <el-table-column prop="devicePrice" label="价格" width="100">
+                  <template #default="{ row }">
+                    <span class="text-green-600 font-medium">{{ row.devicePrice }}</span>
+                  </template>
+                </el-table-column>
+                <el-table-column label="操作" width="200" fixed="right">
+                  <template #default="{ row }">
+                    <div class="flex gap-2">
+                      <el-button
+                        type="warning"
+                        size="small"
+                        @click="handleEditDevice(row)"
+                        :icon="Edit"
+                      >
+                        编辑
+                      </el-button>
+                      <el-button
+                        type="danger"
+                        size="small"
+                        @click="handleDeleteDevice(row)"
+                        :icon="Delete"
+                      >
+                        删除
+                      </el-button>
+                    </div>
+                  </template>
+                </el-table-column>
+              </el-table>
+
+              <!-- 空状态 -->
+              <div v-if="deviceList.length === 0" class="text-center py-12">
+                <Package class="w-16 h-16 text-gray-400 mx-auto mb-4" />
+                <p class="text-gray-500 text-lg">暂无设备信息</p>
+                <p class="text-gray-400 text-sm mt-2">点击上方按钮添加设备</p>
+              </div>
+            </div>
+          </div>
+        </el-tab-pane>
+      </el-tabs>
+
+      <template #footer>
+        <div class="flex justify-end">
+          <el-button @click="detailDialogVisible = false">关闭</el-button>
+        </div>
+      </template>
+    </el-dialog>
+
+    <!-- 设备新增/编辑对话框 -->
+    <el-dialog
+      v-model="deviceDialogVisible"
+      :title="isDeviceEdit ? '编辑设备' : '新增设备'"
       width="500px"
       :close-on-click-modal="false"
     >
-      <el-form :model="detailForm" label-width="100px">
-        <el-form-item label="面积">
-          <el-input v-model="detailForm.area" placeholder="请输入面积" />
+      <el-form :model="deviceForm" label-width="100px">
+        <el-form-item label="设备名称" required>
+          <el-input v-model="deviceForm.deviceName" placeholder="请输入设备名称" />
         </el-form-item>
-
-        <el-form-item label="房屋类型">
-          <el-select v-model="detailForm.houseType" placeholder="请选择房屋类型" class="w-full">
-            <el-option label="一室一厅" value="一室一厅" />
-            <el-option label="两室一厅" value="两室一厅" />
-            <el-option label="三室一厅" value="三室一厅" />
-            <el-option label="三室两厅" value="三室两厅" />
-            <el-option label="厂房" value="厂房" />
-            <el-option label="仓库" value="仓库" />
+        <el-form-item label="设备类型">
+          <el-select v-model="deviceForm.deviceType" placeholder="请选择设备类型" class="w-full">
+            <el-option label="家电" value="家电" />
+            <el-option label="家具" value="家具" />
+            <el-option label="厨具" value="厨具" />
+            <el-option label="卫浴" value="卫浴" />
+            <el-option label="办公设备" value="办公设备" />
+            <el-option label="其他" value="其他" />
           </el-select>
         </el-form-item>
-
-        <el-form-item label="房屋介绍">
-          <el-input
-            v-model="detailForm.introduce"
-            type="textarea"
-            :rows="4"
-            placeholder="请输入房屋介绍"
-          />
+        <el-form-item label="设备品牌">
+          <el-input v-model="deviceForm.deviceBrand" placeholder="请输入设备品牌" />
+        </el-form-item>
+        <el-form-item label="设备型号">
+          <el-input v-model="deviceForm.deviceNumber" placeholder="请输入设备型号" />
+        </el-form-item>
+        <el-form-item label="设备价格">
+          <el-input v-model="deviceForm.devicePrice" placeholder="请输入设备价格" />
         </el-form-item>
       </el-form>
-
       <template #footer>
         <div class="flex justify-end gap-3">
-          <el-button @click="detailDialogVisible = false">取消</el-button>
-          <el-button type="primary" @click="handleDetailSubmit">保存</el-button>
+          <el-button @click="deviceDialogVisible = false">取消</el-button>
+          <el-button type="primary" @click="handleDeviceSubmit">确定</el-button>
         </div>
       </template>
     </el-dialog>
@@ -610,12 +871,22 @@ onMounted(() => {
   overflow: hidden;
 }
 
-.el-dialog__header {
-  padding: 20px 20px 10px;
-  border-bottom: 1px solid #f0f0f0;
+.detail-dialog .el-dialog__body {
+  padding: 0;
+}
+
+.detail-tabs .el-tabs__content {
+  padding: 0;
+}
+
+.detail-tabs .el-tab-pane {
+  background: #fafafa;
+  border-radius: 8px;
+  margin: 0 20px 20px;
 }
 
-.el-dialog__body {
-  padding: 20px;
+.el-badge {
+  --el-badge-size: 16px;
+  --el-badge-padding: 4px;
 }
 </style>