1
0

3 Коммитууд 3155e18ebe ... 6cdc4781a3

Эзэн SHA1 Мессеж Огноо
  刘昊林 6cdc4781a3 Merge branch 'master' of http://192.168.110.30:3000/nahida/info-assets-manage 1 долоо хоног өмнө
  刘昊林 11adbca48a 更新最近的提交 1 долоо хоног өмнө
  丁烨烨 e27b9509c0 跟新部分首页模块中的公租房,标准化厂房,创新创业基地增加一个状态筛选,租房信息-租房合同管理中的表格展示内容修改到期时间为合同到期时间,新增一个租金到期时间列,租房信息-租房收据管理中的表格展示内容中新增一个电费列展示 4 сар өмнө

+ 1 - 0
.env.production

@@ -1,4 +1,5 @@
 # 变量必须以 VITE_ 为前缀才能暴露给外部读取
 NODE_ENV = 'production'
 VITE_APP_BASE_API = 'http://172.16.102.52:8801'
+
 VITE_MINIO_BASE_URL = 'http://172.16.102.52:9000/zksy-file'

+ 1 - 1
src/router/index.ts

@@ -68,7 +68,7 @@ dynamicRoutesList.forEach((item) => {
 
 // 处理初始加载后添加的路由
 router.isReady().then(() => {
-  console.log('路由已完全初始化')
+  // console.log('路由已完全初始化')
 })
 
 export default router

+ 12 - 0
src/router/route.ts

@@ -127,6 +127,18 @@ export const routeList:RouterType[] = [
     name: '退据统计',
     icon: 'Location',
     addr: 'tj/tjtj',
+  },
+  {
+    path: 'tj/sjbb',
+    name: '收据报表',
+    icon: 'Location',
+    addr: 'tj/sjbb',
+  },
+  {
+    path: 'tj/tjbb',
+    name: '退据报表',
+    icon: 'Location',
+    addr: 'tj/tjbb',
   }
   // {
   //   path: 'gzfxx',

+ 5 - 2
src/stores/permissionStore.ts

@@ -16,8 +16,9 @@ interface PermissionReponse extends BaseResponse {
 }
 
 export const usePermissionStore = defineStore('permission', () => {
-  const search = window.location.search // 获取"?appToken=xxx&appId=xxx"
+  const search = window.location.search // 获取"?=xxx&appId=xxx"
   const params = new URLSearchParams(search)
+
   const getUserPermission = async () => {
 
     if (!params.get('appId') || !params.get('appToken')) {
@@ -30,11 +31,13 @@ export const usePermissionStore = defineStore('permission', () => {
         params: {
           appId: params.get('appId'),
           appToken: params.get('appToken'),
+          // appId: '0bc14fd11d27ac047f1a15114314367d',
+          // appToken: 'b87ed71f0594243c44bb6b85a190eebd4c32e06a6c540b06ac5974babef32a0e51a4dedeba15a92ab9b1bf97adeda823',
         },
       },
     )
 
-    console.log(11111,res)
+    // console.log(11111,res)
     if (res.code !== 200) {
       ElMessage.error(res.msg)
       return

+ 817 - 0
src/views/tj/sjbb.vue

@@ -0,0 +1,817 @@
+<template>
+  <div class="dashboard-container">
+    <!-- 筛选区域(新增类型选择器) -->
+    <div class="filter-panel">
+      <div class="filter-item">
+        <label class="filter-label">选择年份:</label>
+        <el-date-picker size="default"
+                        v-model="searchForm.selectedYear"
+                        type="year"
+                        placeholder="选择年份"
+                        @change="handleYearChange"
+                        format="YYYY"
+                        value-format="YYYY"
+        />
+      </div>
+      <div class="filter-item">
+        <label class="filter-label">选择月份:</label>
+        <el-select
+          v-model="searchForm.selectedMonth"
+          placeholder="请先选择年份"
+          size="default"
+          class="w-48!"
+          :disabled="!searchForm.selectedYear"
+          :disabled-text="!searchForm.selectedYear ? '请先选择年份' : ''"
+        >
+          <el-option
+            v-for="month in availableMonths"
+            :key="month"
+            :label="`${month}月`"
+            :value="month"
+          />
+        </el-select>
+      </div>
+      <!-- 新增:类型选择器 -->
+      <div class="filter-item">
+        <label class="filter-label">选择类型:</label>
+        <el-select
+          v-model="searchForm.selectedType"
+          placeholder="请选择数据类型"
+          size="default"
+          class="w-48!"
+          :disabled="!searchForm.selectedYear"
+        >
+          <el-option
+            v-for="type in availableTypes"
+            :key="type.value"
+            :label="type.label"
+            :value="type.value"
+          />
+        </el-select>
+      </div>
+
+      <div>
+        <el-button type="primary" @click="getSelection()" :icon="Search" size="default">
+          搜索
+        </el-button>
+      </div>
+      <div>
+        <el-button
+          color="#626aef"
+          @click="getMonthDetail()"
+          :icon="Tickets"
+          size="default"
+          :disabled="!isMonthDetailEnabled"
+          :title="!isMonthDetailEnabled ? '请先选择年份和月份' : ''"
+        >
+          月详情
+        </el-button>
+      </div>
+      <!-- 新增打印按钮 -->
+      <div>
+        <el-button type="success" @click="printTable" size="default">
+          打印表格
+        </el-button>
+      </div>
+    </div>
+
+    <!-- 数据表格 -->
+    <div class="table-container" id="printTarget">
+      <el-table
+        :data="filteredTableData"
+        border
+        stripe
+        style="width: 100%"
+        :loading="tableLoading"
+        empty-text="暂无相关收据数据"
+        :summary-method="getSummaries"
+        show-summary
+        :span-method="handleSpanMethod"
+      >
+        <!-- 1. 序号列(固定显示) -->
+        <el-table-column label="序号" align="center" width="50">
+          <template #default="scope">
+            {{ scope.$index + 1 }}
+          </template>
+        </el-table-column>
+
+        <!-- 2. 收据号(仅详情显示) -->
+        <el-table-column
+          v-if="showMonthDetailColumns"
+          prop="receiptCode"
+          label="收据号"
+          align="center"
+          width="150"
+        />
+
+        <!-- 3. 房间信息(仅详情显示) -->
+        <el-table-column
+          v-if="showMonthDetailColumns"
+          prop="payer"
+          label="房间信息"
+          align="center"
+          width="120"
+        />
+
+        <!-- 4. 付款时间(始终显示) -->
+        <el-table-column prop="receiptTime" label="付款时间" align="center"/>
+
+        <!-- 5. 租金(始终显示) -->
+        <el-table-column prop="rent" label="租金(元)" align="center">
+          <template #default="scope">
+            {{ scope.row.rent?.toLocaleString('zh-CN', {minimumFractionDigits: 2}) || '0.00' }}
+          </template>
+        </el-table-column>
+
+        <!-- 6. 押金(始终显示) -->
+        <el-table-column prop="deposit" label="押金(元)" align="center">
+          <template #default="scope">
+            {{ scope.row.deposit?.toLocaleString('zh-CN', {minimumFractionDigits: 2}) || '0.00' }}
+          </template>
+        </el-table-column>
+
+        <!-- 7. 水费(始终显示) -->
+        <el-table-column prop="waterFee" label="水费(元)" align="center">
+          <template #default="scope">
+            {{ scope.row.waterFee?.toLocaleString('zh-CN', {minimumFractionDigits: 2}) || '0.00' }}
+          </template>
+        </el-table-column>
+
+        <!-- 8. 电费(始终显示) -->
+        <el-table-column prop="electricityBill" label="电费(元)" align="center">
+          <template #default="scope">
+            {{
+              scope.row.electricityBill?.toLocaleString('zh-CN', {minimumFractionDigits: 2}) || '0.00'
+            }}
+          </template>
+        </el-table-column>
+
+        <!-- 9. 物业费(始终显示) -->
+        <el-table-column prop="propertyFee" label="物业费(元)" align="center">
+          <template #default="scope">
+            {{
+              scope.row.propertyFee?.toLocaleString('zh-CN', {minimumFractionDigits: 2}) || '0.00'
+            }}
+          </template>
+        </el-table-column>
+
+        <!-- 10. 收取时段(仅详情显示) -->
+        <el-table-column
+          v-if="showMonthDetailColumns"
+          prop="rentPeriod"
+          label="收取时段"
+          align="center"
+          width="200"
+        />
+        <!-- 11. 经手人(仅详情显示) -->
+        <el-table-column
+          v-if="showMonthDetailColumns"
+          prop="operator"
+          label="经手人"
+          align="center"
+          width="60"
+        />
+        <!-- 12. 付款方式(仅详情显示) -->
+        <el-table-column
+          v-if="showMonthDetailColumns"
+          prop="payType"
+          label="付款方式"
+          align="center"
+          width="70"
+        />
+      </el-table>
+    </div>
+  </div>
+</template>
+
+<script setup lang="ts">
+import {ref, computed, onMounted, reactive} from 'vue'
+import {clientGet} from "@/utils/request.js";
+import {ElMessage} from "element-plus";
+import {Search, Tickets, Printer} from "lucide-vue-next";
+
+// 定义基础类型接口
+interface BaseResponse {
+  code: number
+  msg: string
+}
+
+// 扩展收据数据类型接口(匹配页面显示的月详情字段)
+interface ReceiptData {
+  deposit: number         // 押金
+  electricityBill: number // 电费
+  propertyFee: number     // 物业费
+  receiptTime: string     // 付款时间(原收款时间)
+  rent: number            // 租金
+  waterFee: number        // 水费
+  // 月详情专属字段(和页面表格列一一对应)
+  receiptCode?: string    // 收据号
+  operator?: string       // 经手人
+  payType?: string        // 付款方式
+  payer?: string          // 房间信息
+  rentPeriod?: string     // 收取时段
+}
+
+// 新增:类型选择器选项类型
+interface TypeOption {
+  label: string // 显示文本
+  value: string | number // 传递值
+}
+
+// 接口响应类型
+interface ReceiptDataResponse extends BaseResponse {
+  data: ReceiptData[]
+}
+
+const availableMonths = ref<number[]>([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12])
+
+// 新增:类型选择器可选选项(可根据业务需求修改)
+const availableTypes = ref<TypeOption[]>([
+  { label: '公租房', value: '公租房' },
+  { label: '厂房', value: '厂房' },
+  { label: '创新创业基地', value: '创新创业基地' }
+])
+
+// 加载状态
+const tableLoading = ref(false)
+
+// 搜索表单(新增selectedType)
+const searchForm = reactive({
+  selectedYear: '', // 选中的年份 YYYY格式
+  selectedMonth: '',
+  selectedType: '公租房' // 新增:默认选中"全部收据"
+})
+
+// 核心优化:控制月详情列是否显示
+const showMonthDetailColumns = ref(false)
+
+// 核心优化:计算属性 - 判断月详情按钮是否启用(年份和月份都必须选中)
+const isMonthDetailEnabled = computed(() => {
+  return !!searchForm.selectedYear && !!searchForm.selectedMonth
+})
+
+// 原始收据数据
+const rawTableData = ref<ReceiptData[]>([])
+
+// 筛选后的表格数据(仅保留年份筛选)
+const filteredTableData = computed(() => {
+  let data = [...rawTableData.value]
+  return data
+})
+
+// 合并后的通用数据获取方法(新增:传递类型参数到接口)
+const fetchReceiptData = async (apiPath: string, isDetail = false) => {
+  tableLoading.value = true
+  try {
+    // 构建请求参数(新增:selectedType)
+    const params = {
+      year: searchForm.selectedYear,
+      month: searchForm.selectedMonth,
+      assetType: searchForm.selectedType // 新增:类型参数,传递到后端接口
+    }
+
+    const res = await clientGet<any, ReceiptDataResponse>(
+      apiPath,
+      {params},
+    )
+
+    console.log('收据接口返回数据:', res)
+
+    // 接口返回校验
+    if (!res) {
+      ElMessage.error('接口返回数据为空')
+      rawTableData.value = []
+      if (isDetail) showMonthDetailColumns.value = false
+      return
+    }
+
+    if (res.code !== 200) {
+      ElMessage.error(res.msg || '数据获取失败')
+      rawTableData.value = []
+      if (isDetail) showMonthDetailColumns.value = false
+      return
+    }
+
+    // 赋值接口返回数据
+    rawTableData.value = res.data || []
+    if (rawTableData.value.length === 0) {
+      ElMessage.warning('暂无收据数据')
+      if (isDetail) showMonthDetailColumns.value = false
+    } else {
+      if (isDetail) showMonthDetailColumns.value = true
+    }
+
+  } catch (error) {
+    ElMessage.error('网络异常,数据获取失败')
+    console.error('获取收据数据失败:', error)
+    rawTableData.value = []
+    if (isDetail) showMonthDetailColumns.value = false
+  } finally {
+    tableLoading.value = false
+  }
+}
+
+// 年份变更处理(优化逻辑:清空月份选择,重置类型为全部)
+const handleYearChange = (val: string) => {
+  if (!val) {
+    searchForm.selectedMonth = '';
+    searchForm.selectedType = '公租房'; // 新增:重置类型选择
+    rawTableData.value = [];
+    ElMessage.warning('请选择年份进行筛选')
+    showMonthDetailColumns.value = false
+    return
+  }
+  // 核心优化:切换年份时清空月份选择,避免数据不一致
+  searchForm.selectedMonth = '';
+  searchForm.selectedType = '公租房'; // 新增:重置类型选择
+  // 关闭详情列
+  showMonthDetailColumns.value = false
+  // 调用通用方法获取年度数据
+  fetchReceiptData('/AReceiptInfo/receipt/stat/year')
+}
+
+const getSelection = () => {
+  // 新增:校验年份选择
+  if (!searchForm.selectedYear) {
+    ElMessage.warning('请先选择年份')
+    return
+  }
+
+  // 搜索时关闭详情列(恢复基础表头)
+  showMonthDetailColumns.value = false
+
+  if (!searchForm.selectedMonth) {
+    console.log('获取当前年份数据')
+    // 调用通用方法获取年度数据
+    fetchReceiptData('/AReceiptInfo/receipt/stat/year')
+    return
+  }
+  console.log('获取指定月份数据')
+  // 调用通用方法获取月度数据
+  fetchReceiptData('/AReceiptInfo/receipt/stat/month')
+}
+
+const getMonthDetail = () => {
+  // 双重保险:即使按钮禁用,仍保留逻辑校验(防止非常规操作)
+  if (!isMonthDetailEnabled.value) {
+    ElMessage.warning('请先选择年份和月份')
+    return
+  }
+
+  console.log('获取指定年月的日维度数据')
+  // 传入isDetail=true标识这是详情请求
+  fetchReceiptData('/AReceiptInfo/receipt/stat/day', true)
+}
+
+// 核心优化:动态合计行计算方法 - 根据展示模式切换合计列索引
+const getSummaries = (param: { columns: any[], data: ReceiptData[] }) => {
+  const { columns, data } = param;
+  const sums = [];
+
+  columns.forEach(() => sums.push(''));
+  sums[0] = '合计'; // 序号列显示"合计"
+
+  // 定义不同模式下的合计配置
+  let summaryIndexs: number[] = [];
+  const summaryFields = ['rent', 'deposit', 'waterFee', 'electricityBill', 'propertyFee'];
+
+  // 判断当前展示模式:
+  // showMonthDetailColumns为true → 月详情模式(索引4,5,6,7,8)
+  // showMonthDetailColumns为false → 年份/月份汇总模式(索引2,3,4,5,6)
+  if (showMonthDetailColumns.value) {
+    // 月详情模式:4=租金、5=押金、6=水费、7=电费、8=物业费
+    summaryIndexs = [4, 5, 6, 7, 8];
+  } else {
+    // 年份/月份汇总模式:2=租金、3=押金、4=水费、5=电费、6=物业费
+    summaryIndexs = [2, 3, 4, 5, 6];
+  }
+
+  // 执行合计计算
+  summaryIndexs.forEach((index, fieldIndex) => {
+    // 防止索引越界
+    if (index >= columns.length) return;
+
+    const field = summaryFields[fieldIndex];
+    const sum = data.reduce((total, item) => {
+      return total + (Number(item[field]) || 0);
+    }, 0);
+    sums[index] = sum.toLocaleString('zh-CN', { minimumFractionDigits: 2 });
+  });
+
+  return sums;
+};
+
+// 修复:合并单元格方法(核心修改 - 适配动态合计列)
+const handleSpanMethod = ({ row, column, rowIndex, columnIndex }: {
+  row: any,
+  column: any,
+  rowIndex: number,
+  columnIndex: number
+}) => {
+  // 1. 识别合计行(Element Plus的footer行有type标识)
+  if (row && row.type === 'footer') {
+    // 定义需要显示的数值列
+    const keepProps = ['rent', 'deposit', 'waterFee', 'electricityBill', 'propertyFee'];
+    const prop = column.prop as string;
+
+    // 2. 保留序号列(index=0)和数值列,其他列隐藏
+    if (columnIndex === 0 || (prop && keepProps.includes(prop))) {
+      return { rowspan: 1, colspan: 1 }; // 显示该列
+    } else {
+      return { rowspan: 0, colspan: 0 }; // 隐藏该列
+    }
+  }
+
+  // 非合计行不处理
+  return { rowspan: 1, colspan: 1 };
+};
+
+// 核心优化:打印表格功能 - 适配动态合计列(新增:显示选中的类型)
+const printTable = () => {
+  // 检查是否有数据可打印
+  if (filteredTableData.value.length === 0) {
+    ElMessage.warning('暂无收据数据,无法打印')
+    return
+  }
+
+  try {
+    // 创建打印窗口
+    const printWindow = window.open('', '_blank')
+    if (!printWindow) {
+      ElMessage.error('浏览器弹窗被拦截,请允许弹窗后重试')
+      return
+    }
+
+    // 计算合计数据
+    const calculatePrintSummary = () => {
+      const summary = {
+        rent: 0,
+        deposit: 0,
+        waterFee: 0,
+        electricityBill: 0,
+        propertyFee: 0
+      };
+
+      filteredTableData.value.forEach(item => {
+        summary.rent += Number(item.rent) || 0;
+        summary.deposit += Number(item.deposit) || 0;
+        summary.waterFee += Number(item.waterFee) || 0;
+        summary.electricityBill += Number(item.electricityBill) || 0;
+        summary.propertyFee += Number(item.propertyFee) || 0;
+      });
+
+      return summary;
+    };
+
+    const printSummary = calculatePrintSummary();
+
+    // 新增:获取当前选中类型的显示文本
+    const getSelectedTypeLabel = () => {
+      const currentType = availableTypes.value.find(item => item.value === searchForm.selectedType)
+      return currentType?.label || '全部收据'
+    }
+
+    // 构建标准化的表格HTML(按新列顺序)
+    const buildTableHtml = () => {
+      // 基础表头配置(按新顺序:序号、付款时间、租金、押金、水费、电费、物业费)
+      let headers = [
+        { label: '序号', align: 'center', width: '50px' },
+        { label: '付款时间', align: 'center', width: '120px' },
+        { label: '租金(元)', align: 'center', width: '120px' },
+        { label: '押金(元)', align: 'center', width: '120px' },
+        { label: '水费(元)', align: 'center', width: '120px' },
+        { label: '电费(元)', align: 'center', width: '120px' },
+        { label: '物业费(元)', align: 'center', width: '120px' },
+      ];
+
+      // 基础数据单元格模板(基础列)
+      let baseCellsTemplate = (item: ReceiptData, index: number) => `
+        <td style="text-align: center; width: 50px; vertical-align: top;">${index + 1}</td>
+        <td style="text-align: center; width: 120px; vertical-align: top;">${item.receiptTime || ''}</td>
+        <td style="text-align: center; width: 120px; vertical-align: top;">${item.rent?.toLocaleString('zh-CN', {minimumFractionDigits: 2}) || '0.00'}</td>
+        <td style="text-align: center; width: 120px; vertical-align: top;">${item.deposit?.toLocaleString('zh-CN', {minimumFractionDigits: 2}) || '0.00'}</td>
+        <td style="text-align: center; width: 120px; vertical-align: top;">${item.waterFee?.toLocaleString('zh-CN', {minimumFractionDigits: 2}) || '0.00'}</td>
+        <td style="text-align: center; width: 120px; vertical-align: top;">${item.electricityBill?.toLocaleString('zh-CN', {minimumFractionDigits: 2}) || '0.00'}</td>
+        <td style="text-align: center; width: 120px; vertical-align: top;">${item.propertyFee?.toLocaleString('zh-CN', {minimumFractionDigits: 2}) || '0.00'}</td>
+      `;
+
+      // 如果显示详情列,插入完整列顺序
+      if (showMonthDetailColumns.value) {
+        headers = [
+          { label: '序号', align: 'center', width: '50px' },
+          { label: '收据号', align: 'center', width: '150px' },
+          { label: '房间信息', align: 'center', width: '120px' },
+          { label: '付款时间', align: 'center', width: '120px' },
+          { label: '租金(元)', align: 'center', width: '120px' },
+          { label: '押金(元)', align: 'center', width: '120px' },
+          { label: '水费(元)', align: 'center', width: '120px' },
+          { label: '电费(元)', align: 'center', width: '120px' },
+          { label: '物业费(元)', align: 'center', width: '120px' },
+          { label: '收取时段', align: 'center', width: '200px' },
+          { label: '经手人', align: 'center', width: '60px' },
+          { label: '付款方式', align: 'center', width: '70px' },
+        ];
+
+        // 扩展单元格模板(完整列顺序)
+        baseCellsTemplate = (item: ReceiptData, index: number) => `
+          <td style="text-align: center; width: 50px; vertical-align: top;">${index + 1}</td>
+          <td style="text-align: center; width: 150px; vertical-align: top;">${item.receiptCode || '-'}</td>
+          <td style="text-align: center; width: 120px; vertical-align: top;">${item.payer || '-'}</td>
+          <td style="text-align: center; width: 120px; vertical-align: top;">${item.receiptTime || ''}</td>
+          <td style="text-align: center; width: 120px; vertical-align: top;">${item.rent?.toLocaleString('zh-CN', {minimumFractionDigits: 2}) || '0.00'}</td>
+          <td style="text-align: center; width: 120px; vertical-align: top;">${item.deposit?.toLocaleString('zh-CN', {minimumFractionDigits: 2}) || '0.00'}</td>
+          <td style="text-align: center; width: 120px; vertical-align: top;">${item.waterFee?.toLocaleString('zh-CN', {minimumFractionDigits: 2}) || '0.00'}</td>
+          <td style="text-align: center; width: 120px; vertical-align: top;">${item.electricityBill?.toLocaleString('zh-CN', {minimumFractionDigits: 2}) || '0.00'}</td>
+          <td style="text-align: center; width: 120px; vertical-align: top;">${item.propertyFee?.toLocaleString('zh-CN', {minimumFractionDigits: 2}) || '0.00'}</td>
+          <td style="text-align: center; width: 200px; vertical-align: top; word-break: break-all;">${item.rentPeriod || '-'}</td>
+          <td style="text-align: center; width: 60px; vertical-align: top;">${item.operator || '-'}</td>
+          <td style="text-align: center; width: 70px; vertical-align: top;">${item.payType || '-'}</td>
+        `;
+      }
+
+      // 构建表头行
+      const headerRow = `
+        <tr>
+          ${headers.map(header => `
+            <th style="text-align: ${header.align}; ${header.width ? `width: ${header.width};` : ''}; vertical-align: middle;">${header.label}</th>
+          `).join('')}
+        </tr>
+      `;
+
+      // 构建数据行
+      const dataRows = filteredTableData.value.map((item, index) => `
+        <tr>${baseCellsTemplate(item, index)}</tr>
+      `).join('');
+
+      // 生成合计行(适配动态列顺序)
+      let summaryRow = '';
+      if (printSummary) {
+        let summaryCells = [];
+        if (showMonthDetailColumns.value) {
+          // 月详情模式下的合计行
+          summaryCells = [
+            '<td style="text-align: center; width: 50px; font-weight: bold; vertical-align: middle;">合计</td>',
+            '<td style="text-align: center; width: 150px;"></td>',
+            '<td style="text-align: center; width: 120px;"></td>',
+            '<td style="text-align: center; width: 120px;"></td>',
+            `<td style="text-align: center; width: 120px; font-weight: bold; vertical-align: middle;">${printSummary.rent.toLocaleString('zh-CN', {minimumFractionDigits: 2})}</td>`,
+            `<td style="text-align: center; width: 120px; font-weight: bold; vertical-align: middle;">${printSummary.deposit.toLocaleString('zh-CN', {minimumFractionDigits: 2})}</td>`,
+            `<td style="text-align: center; width: 120px; font-weight: bold; vertical-align: middle;">${printSummary.waterFee.toLocaleString('zh-CN', {minimumFractionDigits: 2})}</td>`,
+            `<td style="text-align: center; width: 120px; font-weight: bold; vertical-align: middle;">${printSummary.electricityBill.toLocaleString('zh-CN', {minimumFractionDigits: 2})}</td>`,
+            `<td style="text-align: center; width: 120px; font-weight: bold; vertical-align: middle;">${printSummary.propertyFee.toLocaleString('zh-CN', {minimumFractionDigits: 2})}</td>`,
+            '<td style="text-align: center; width: 200px;"></td>',
+            '<td style="text-align: center; width: 60px;"></td>',
+            '<td style="text-align: center; width: 70px;"></td>'
+          ];
+        } else {
+          // 年份/月份汇总模式下的合计行
+          summaryCells = [
+            '<td style="text-align: center; width: 50px; font-weight: bold; vertical-align: middle;">合计</td>',
+            '<td style="text-align: center; width: 120px;"></td>',
+            `<td style="text-align: center; width: 120px; font-weight: bold; vertical-align: middle;">${printSummary.rent.toLocaleString('zh-CN', {minimumFractionDigits: 2})}</td>`,
+            `<td style="text-align: center; width: 120px; font-weight: bold; vertical-align: middle;">${printSummary.deposit.toLocaleString('zh-CN', {minimumFractionDigits: 2})}</td>`,
+            `<td style="text-align: center; width: 120px; font-weight: bold; vertical-align: middle;">${printSummary.waterFee.toLocaleString('zh-CN', {minimumFractionDigits: 2})}</td>`,
+            `<td style="text-align: center; width: 120px; font-weight: bold; vertical-align: middle;">${printSummary.electricityBill.toLocaleString('zh-CN', {minimumFractionDigits: 2})}</td>`,
+            `<td style="text-align: center; width: 120px; font-weight: bold; vertical-align: middle;">${printSummary.propertyFee.toLocaleString('zh-CN', {minimumFractionDigits: 2})}</td>`,
+          ];
+        }
+
+        summaryRow = `
+          <tr style="background-color: #f0f0f0; font-weight: bold;">
+            ${summaryCells.join('')}
+          </tr>
+        `;
+      }
+
+      // 完整表格HTML
+      return `
+        <table class="print-table">
+          <thead>${headerRow}</thead>
+          <tbody>
+            ${dataRows}
+            ${summaryRow}
+          </tbody>
+        </table>
+      `;
+    };
+
+    // 构建打印内容(新增:显示选中的类型)
+    printWindow.document.write(`
+      <!DOCTYPE html>
+      <html>
+      <head>
+        <meta charset="UTF-8">
+        <title>${showMonthDetailColumns.value ? '收据详情' : '收据汇总'}-${searchForm.selectedYear}年${searchForm.selectedMonth ? searchForm.selectedMonth + '月' : ''}-${getSelectedTypeLabel()}</title>
+        <style>
+          /* 基础样式重置 */
+          * {
+            margin: 0 !important;
+            padding: 0 !important;
+            box-sizing: border-box !important;
+            font-family: 'Microsoft YaHei', sans-serif !important;
+            line-height: 1.4 !important;
+          }
+
+          body {
+            padding: 10px !important;
+            width: 100% !important;
+            height: 100% !important;
+            font-size: 12px !important;
+          }
+
+          /* 标题样式 */
+          .print-title {
+            text-align: center !important;
+            font-size: 18px !important;
+            font-weight: bold !important;
+            margin-bottom: 15px !important;
+            width: 100% !important;
+            page-break-after: avoid !important;
+          }
+
+          /* 核心:标准化打印表格 */
+          .print-table {
+            width: 100% !important;
+            border-collapse: collapse !important;
+            border-spacing: 0 !important;
+            table-layout: fixed !important;
+            page-break-inside: auto !important;
+          }
+
+          .print-table th {
+            border: 1px solid #000 !important;
+            padding: 8px !important;
+            vertical-align: middle !important;
+            font-size: 12px !important;
+            font-weight: bold !important;
+            background-color: #f0f0f0 !important;
+            white-space: nowrap !important;
+            text-align: center !important;
+          }
+
+          .print-table td {
+            border: 1px solid #000 !important;
+            padding: 8px !important;
+            vertical-align: top !important;
+            font-size: 12px !important;
+            white-space: normal !important;
+            word-wrap: break-word !important;
+            word-break: break-all !important;
+            text-align: center !important;
+            min-height: 30px !important;
+          }
+
+          /* 合计行样式 */
+          .print-table tr:last-child td {
+            vertical-align: middle !important;
+            background-color: #f0f0f0 !important;
+          }
+
+          /* 打印核心样式 */
+          @media print {
+            @page {
+              margin: 5mm !important;
+              size: auto !important;
+            }
+
+            body {
+              width: 100% !important;
+            }
+
+            .print-table tr {
+              page-break-inside: avoid !important;
+            }
+
+            .print-table td {
+              white-space: normal !important;
+              word-wrap: break-word !important;
+              word-break: break-all !important;
+            }
+          }
+        </style>
+      </head>
+      <body>
+        <div class="print-container">
+          <div class="print-title">
+            ${showMonthDetailColumns.value ? '收据详情数据' : '收据汇总数据'} - ${searchForm.selectedYear}年${searchForm.selectedMonth ? searchForm.selectedMonth + '月' : ''} - ${getSelectedTypeLabel()}
+          </div>
+          ${buildTableHtml()}
+        </div>
+      </body>
+      </html>
+    `);
+
+    // 完成文档写入
+    printWindow.document.close()
+
+    // 优化打印执行逻辑
+    const executePrint = () => {
+      try {
+        printWindow.focus()
+        setTimeout(() => {
+          printWindow.print()
+          setTimeout(() => {
+            printWindow.close()
+          }, 500)
+        }, 200)
+      } catch (e) {
+        ElMessage.error('打印执行失败:' + (e as Error).message)
+      }
+    }
+
+    // 兼容不同浏览器的加载状态
+    if (printWindow.document.readyState === 'complete') {
+      executePrint()
+    } else {
+      printWindow.addEventListener('load', executePrint)
+    }
+  } catch (error) {
+    ElMessage.error('打印失败:' + (error as Error).message)
+    console.error('打印错误:', error)
+  }
+}
+
+// 初始化
+onMounted(() => {
+  // 默认选中当前年
+  const currentYear = new Date().getFullYear().toString()
+  searchForm.selectedYear = currentYear
+  // 调用通用方法获取年度数据
+  fetchReceiptData('/AReceiptInfo/receipt/stat/year')
+})
+</script>
+
+<style scoped>
+.dashboard-container {
+  margin: 20px auto;
+  padding: 0 20px;
+  font-family: 'Microsoft YaHei', sans-serif;
+}
+
+/* 筛选区域样式 */
+.filter-panel {
+  display: flex;
+  align-items: center;
+  gap: 20px;
+  padding: 16px 20px;
+  background: #f8f9fa;
+  border-radius: 8px;
+  margin-bottom: 20px;
+  flex-wrap: wrap;
+}
+
+.filter-item {
+  display: flex;
+  align-items: center;
+  gap: 8px;
+}
+
+.filter-label {
+  font-size: 14px;
+  color: #333;
+  font-weight: 500;
+  white-space: nowrap;
+}
+
+/* 表格区域样式 */
+.table-container {
+  background: #fff;
+  padding: 20px;
+  border-radius: 8px;
+  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
+}
+
+/* 响应式适配 */
+@media (max-width: 768px) {
+  .filter-panel {
+    flex-direction: column;
+    align-items: flex-start;
+  }
+}
+
+/* 表格单元格样式优化 */
+:deep(.el-table__cell) {
+  padding: 12px 0;
+}
+
+/* 禁用状态样式优化 */
+:deep(.el-select.is-disabled) {
+  opacity: 0.7;
+}
+:deep(.el-button.is-disabled) {
+  cursor: not-allowed !important;
+}
+
+/* 合计行样式优化 - 增强显示效果 */
+:deep(.el-table__footer-wrapper) {
+  font-weight: bold !important;
+}
+:deep(.el-table__footer-wrapper .el-table__cell) {
+  background-color: #f0f0f0 !important;
+  color: #333 !important;
+  font-size: 14px !important;
+}
+:deep(.el-table__footer) {
+  border-top: 2px solid #e6e6e6 !important;
+}
+</style>

+ 0 - 7
src/views/tj/sjtj.vue

@@ -381,13 +381,6 @@ const formatAmount = (amount: number) => {
 <template>
   <div class="min-h-screen bg-gradient-to-br from-indigo-50 via-white to-purple-50 p-6">
     <div class="max-full mx-auto">
-      <div class="mb-8">
-        <h1 class="text-4xl font-bold text-gray-900 mb-2 flex items-center gap-3">
-          <BarChart3 class="w-10 h-10 text-indigo-600" />
-        </h1>
-        <h1 class="text-gray-600">收据统计</h1>
-      </div>
-
       <div class="mb-6 flex items-center gap-4">
         <Calendar class="w-5 h-5 text-indigo-600" />
         <span class="text-gray-700 font-medium">选择年份:</span>

+ 603 - 0
src/views/tj/tjbb.vue

@@ -0,0 +1,603 @@
+<template>
+  <div class="dashboard-container">
+    <!-- 筛选区域(新增类型选择器) -->
+    <div class="filter-panel">
+      <div class="filter-item">
+        <label class="filter-label">选择年份:</label>
+        <el-date-picker
+          size="default"
+          v-model="searchForm.selectedYear"
+          type="year"
+          placeholder="选择年份"
+          @change="handleYearChange"
+          format="YYYY"
+          value-format="YYYY"
+        />
+      </div>
+      <div class="filter-item">
+        <label class="filter-label">选择月份:</label>
+        <el-select
+          v-model="searchForm.selectedMonth"
+          placeholder="请先选择年份"
+          size="default"
+          class="w-48!"
+          :disabled="!searchForm.selectedYear"
+        >
+          <el-option
+            v-for="month in availableMonths"
+            :key="month"
+            :label="`${month}月`"
+            :value="month"
+          />
+        </el-select>
+      </div>
+      <!-- 新增:类型选择器(和收据报表保持一致) -->
+      <div class="filter-item">
+        <label class="filter-label">选择类型:</label>
+        <el-select
+          v-model="searchForm.selectedType"
+          placeholder="请选择数据类型"
+          size="default"
+          class="w-48!"
+          :disabled="!searchForm.selectedYear"
+        >
+          <el-option
+            v-for="type in availableTypes"
+            :key="type.value"
+            :label="type.label"
+            :value="type.value"
+          />
+        </el-select>
+      </div>
+
+      <div>
+        <el-button type="primary" @click="getSelection()" :icon="Search" size="default">
+          搜索
+        </el-button>
+      </div>
+      <div>
+        <el-button
+          color="#626aef"
+          @click="getMonthDetail()"
+          :icon="Tickets"
+          size="default"
+          :disabled="!isMonthDetailEnabled"
+          :title="!isMonthDetailEnabled ? '请先选择年份和月份' : ''"
+        >
+          月详情
+        </el-button>
+      </div>
+      <div>
+        <el-button type="success" @click="printTable" size="default">
+          打印表格
+        </el-button>
+      </div>
+    </div>
+
+    <!-- 数据表格 -->
+    <div class="table-container" id="printTarget">
+      <el-table
+        :data="filteredTableData"
+        border
+        stripe
+        style="width: 100%"
+        :loading="tableLoading"
+        empty-text="暂无相关收据数据"
+        :summary-method="getSummaries"
+        show-summary
+        :span-method="handleSpanMethod"
+      >
+        <!-- 1. 序号列 -->
+        <el-table-column label="序号" align="center" width="50">
+          <template #default="scope">
+            {{ scope.$index + 1 }}
+          </template>
+        </el-table-column>
+
+        <!-- 2. 房间信息(仅详情显示) -->
+        <el-table-column
+          v-if="showMonthDetailColumns"
+          prop="tenant"
+          label="房间信息"
+          align="center"
+        />
+
+        <!-- 3. 退款时间 -->
+        <el-table-column
+          prop="refundTime"
+          label="退款时间"
+          align="center"
+        />
+
+        <!-- 4. 租金 -->
+        <el-table-column
+          prop="rent"
+          label="租金(元)"
+          align="center"
+        >
+          <template #default="scope">
+            {{ scope.row.rent?.toLocaleString('zh-CN', {minimumFractionDigits: 2}) || '0.00' }}
+          </template>
+        </el-table-column>
+
+        <!-- 5. 押金 -->
+        <el-table-column
+          prop="deposit"
+          label="押金(元)"
+          align="center"
+        >
+          <template #default="scope">
+            {{ scope.row.deposit?.toLocaleString('zh-CN', {minimumFractionDigits: 2}) || '0.00' }}
+          </template>
+        </el-table-column>
+
+        <!-- 6. 物业费 -->
+        <el-table-column
+          prop="propertyFee"
+          label="物业费(元)"
+          align="center"
+        >
+          <template #default="scope">
+            {{ scope.row.propertyFee?.toLocaleString('zh-CN', {minimumFractionDigits: 2}) || '0.00' }}
+          </template>
+        </el-table-column>
+
+        <!-- 7. 经手人(仅详情显示) -->
+        <el-table-column
+          v-if="showMonthDetailColumns"
+          prop="operator"
+          label="经手人"
+          align="center"
+        />
+      </el-table>
+    </div>
+  </div>
+</template>
+
+<script setup lang="ts">
+import { ref, computed, onMounted, reactive } from 'vue'
+import { clientGet } from "@/utils/request.js";
+import { ElMessage } from "element-plus";
+import { Search, Tickets, Printer } from "lucide-vue-next";
+
+// 类型定义优化
+interface RefundData {
+  deposit: number         // 押金
+  propertyFee: number     // 物业费
+  refundTime: string      // 退款时间
+  rent: number            // 租金
+  tenant?: string         // 房间信息
+  operator?: string       // 经手人
+  // 其他备用字段
+  waterFee?: number       // 水费
+  electricityBill?: number// 电费
+  refundCode?: string     // 收据号
+  payType?: string        // 付款方式
+  rentPeriod?: string     // 收取时段
+}
+
+// 新增:类型选择器选项类型(和收据报表一致)
+interface TypeOption {
+  label: string // 显示文本
+  value: string | number // 传递值
+}
+
+interface ApiResponse<T> {
+  code: number
+  msg: string
+  data: T[]
+}
+
+// 常量定义
+const availableMonths = ref<number[]>([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12])
+// 新增:类型选择器可选选项(和收据报表完全一致)
+const availableTypes = ref<TypeOption[]>([
+  { label: '公租房', value: '公租房' },
+  { label: '厂房', value: '厂房' },
+  { label: '创新创业基地', value: '创新创业基地' }
+])
+const API_BASE_URL = '/arefund/refund/stat'
+
+// 响应式数据
+const tableLoading = ref(false)
+const showMonthDetailColumns = ref(false)
+const rawTableData = ref<RefundData[]>([])
+
+// 搜索表单(新增selectedType,默认选中公租房)
+const searchForm = reactive({
+  selectedYear: '', // YYYY格式
+  selectedMonth: '',
+  selectedType: '公租房' // 新增:类型筛选值,和收据报表默认值一致
+})
+
+// 计算属性
+const isMonthDetailEnabled = computed(() => {
+  return !!searchForm.selectedYear && !!searchForm.selectedMonth
+})
+
+const filteredTableData = computed(() => {
+  return [...rawTableData.value]
+})
+
+// 接口请求方法(修改:传递类型参数assetType到后端)
+const fetchRefundData = async (apiPath: string, isDetail = false) => {
+  tableLoading.value = true
+  try {
+    const params = {
+      year: searchForm.selectedYear,
+      month: searchForm.selectedMonth,
+      assetType: searchForm.selectedType // 新增:传递类型参数,和收据报表字段名一致
+    }
+
+    const res = await clientGet<ApiResponse<RefundData>>(apiPath, { params })
+
+    if (!res) {
+      ElMessage.error('接口返回数据为空')
+      rawTableData.value = []
+      showMonthDetailColumns.value = false
+      return
+    }
+
+    if (res.code !== 200) {
+      ElMessage.error(res.msg || '数据获取失败')
+      rawTableData.value = []
+      showMonthDetailColumns.value = false
+      return
+    }
+
+    rawTableData.value = res.data || []
+    if (rawTableData.value.length === 0) {
+      ElMessage.warning('暂无收据数据')
+      showMonthDetailColumns.value = false
+    } else {
+      showMonthDetailColumns.value = isDetail
+    }
+
+  } catch (error) {
+    ElMessage.error('网络异常,数据获取失败')
+    console.error('获取退款数据失败:', error)
+    rawTableData.value = []
+    showMonthDetailColumns.value = false
+  } finally {
+    tableLoading.value = false
+  }
+}
+
+// 事件处理方法(修改:切换年份时重置类型为默认公租房)
+const handleYearChange = (val: string) => {
+  if (!val) {
+    searchForm.selectedMonth = ''
+    searchForm.selectedType = '公租房' // 新增:重置类型选择
+    rawTableData.value = []
+    ElMessage.warning('请选择年份进行筛选')
+    showMonthDetailColumns.value = false
+    return
+  }
+  searchForm.selectedMonth = ''
+  searchForm.selectedType = '公租房' // 新增:重置类型选择
+  showMonthDetailColumns.value = false
+  fetchRefundData(`${API_BASE_URL}/year`)
+}
+
+const getSelection = () => {
+  if (!searchForm.selectedYear) {
+    ElMessage.warning('请先选择年份')
+    return
+  }
+
+  showMonthDetailColumns.value = false
+  const apiPath = !searchForm.selectedMonth
+    ? `${API_BASE_URL}/year`
+    : `${API_BASE_URL}/month`
+
+  fetchRefundData(apiPath)
+}
+
+const getMonthDetail = () => {
+  if (!isMonthDetailEnabled.value) {
+    ElMessage.warning('请先选择年份和月份')
+    return
+  }
+  fetchRefundData(`${API_BASE_URL}/day`, true)
+}
+
+// 合计行计算(适配当前列结构)
+const getSummaries = (param: { columns: any[], data: RefundData[] }) => {
+  const { columns, data } = param
+  const sums = Array(columns.length).fill('')
+  sums[0] = '合计' // 序号列显示合计
+
+  // 定义合计字段和对应索引
+  const summaryConfig = showMonthDetailColumns.value
+    ? {
+      // 详情模式:3=租金、4=押金、5=物业费
+      indexes: [3, 4, 5],
+      fields: ['rent', 'deposit', 'propertyFee']
+    }
+    : {
+      // 汇总模式:2=租金、3=押金、4=物业费
+      indexes: [2, 3, 4],
+      fields: ['rent', 'deposit', 'propertyFee']
+    }
+
+  // 计算合计值
+  summaryConfig.indexes.forEach((index, fieldIndex) => {
+    if (index >= columns.length) return
+
+    const field = summaryConfig.fields[fieldIndex]
+    const sum = data.reduce((total, item) => {
+      return total + (Number(item[field]) || 0)
+    }, 0)
+
+    sums[index] = sum.toLocaleString('zh-CN', { minimumFractionDigits: 2 })
+  })
+
+  return sums
+}
+
+// 合并单元格方法(优化合计行显示)
+const handleSpanMethod = ({ row, column, columnIndex }: {
+  row: any,
+  column: any,
+  columnIndex: number
+}) => {
+  // 仅处理合计行
+  if (row && row.type === 'footer') {
+    // 需要显示的列:序号列 + 数值列(租金、押金、物业费)
+    const valueColumns = ['rent', 'deposit', 'propertyFee']
+    const isValueColumn = valueColumns.includes(column.prop as string)
+
+    if (columnIndex === 0 || isValueColumn) {
+      return { rowspan: 1, colspan: 1 }
+    } else {
+      return { rowspan: 0, colspan: 0 } // 隐藏非数值列
+    }
+  }
+
+  return { rowspan: 1, colspan: 1 }
+}
+
+// 打印功能(修改:新增显示选中的类型,和收据报表保持一致)
+const printTable = () => {
+  if (filteredTableData.value.length === 0) {
+    ElMessage.warning('暂无收据数据,无法打印')
+    return
+  }
+
+  try {
+    const printWindow = window.open('', '_blank')
+    if (!printWindow) {
+      ElMessage.error('浏览器弹窗被拦截,请允许弹窗后重试')
+      return
+    }
+
+    // 新增:获取当前选中类型的显示文本
+    const getSelectedTypeLabel = () => {
+      const currentType = availableTypes.value.find(item => item.value === searchForm.selectedType)
+      return currentType?.label || '公租房'
+    }
+
+    // 计算打印用的合计数据
+    const calculatePrintSummary = () => {
+      return filteredTableData.value.reduce((summary, item) => {
+        return {
+          rent: summary.rent + (Number(item.rent) || 0),
+          deposit: summary.deposit + (Number(item.deposit) || 0),
+          propertyFee: summary.propertyFee + (Number(item.propertyFee) || 0)
+        }
+      }, { rent: 0, deposit: 0, propertyFee: 0 })
+    }
+
+    const printSummary = calculatePrintSummary()
+
+    // 构建打印表格HTML
+    const buildPrintTable = () => {
+      // 定义表头和列宽
+      let headers = showMonthDetailColumns.value
+        ? [
+          { label: '序号', width: '50px' },
+          { label: '房间信息', width: '120px' },
+          { label: '退款时间', width: '150px' },
+          { label: '租金(元)', width: '120px' },
+          { label: '押金(元)', width: '120px' },
+          { label: '物业费(元)', width: '120px' },
+          { label: '经手人', width: '60px' }
+        ]
+        : [
+          { label: '序号', width: '50px' },
+          { label: '退款时间', width: '150px' },
+          { label: '租金(元)', width: '120px' },
+          { label: '押金(元)', width: '120px' },
+          { label: '物业费(元)', width: '120px' }
+        ]
+
+      // 构建表头行
+      const headerHtml = headers.map(h =>
+        `<th style="width: ${h.width}; text-align: center; padding: 8px; border: 1px solid #000; background: #f0f0f0;">${h.label}</th>`
+      ).join('')
+
+      // 构建数据行
+      const rowsHtml = filteredTableData.value.map((item, index) => {
+        const cells = showMonthDetailColumns.value
+          ? [
+            `<td style="width: 50px; text-align: center; padding: 8px; border: 1px solid #000;">${index + 1}</td>`,
+            `<td style="width: 120px; text-align: center; padding: 8px; border: 1px solid #000;">${item.tenant || '-'}</td>`,
+            `<td style="width: 150px; text-align: center; padding: 8px; border: 1px solid #000;">${item.refundTime || '-'}</td>`,
+            `<td style="width: 120px; text-align: center; padding: 8px; border: 1px solid #000;">${(item.rent || 0).toLocaleString('zh-CN', { minimumFractionDigits: 2 })}</td>`,
+            `<td style="width: 120px; text-align: center; padding: 8px; border: 1px solid #000;">${(item.deposit || 0).toLocaleString('zh-CN', { minimumFractionDigits: 2 })}</td>`,
+            `<td style="width: 120px; text-align: center; padding: 8px; border: 1px solid #000;">${(item.propertyFee || 0).toLocaleString('zh-CN', { minimumFractionDigits: 2 })}</td>`,
+            `<td style="width: 60px; text-align: center; padding: 8px; border: 1px solid #000;">${item.operator || '-'}</td>`
+          ]
+          : [
+            `<td style="width: 50px; text-align: center; padding: 8px; border: 1px solid #000;">${index + 1}</td>`,
+            `<td style="width: 150px; text-align: center; padding: 8px; border: 1px solid #000;">${item.refundTime || '-'}</td>`,
+            `<td style="width: 120px; text-align: center; padding: 8px; border: 1px solid #000;">${(item.rent || 0).toLocaleString('zh-CN', { minimumFractionDigits: 2 })}</td>`,
+            `<td style="width: 120px; text-align: center; padding: 8px; border: 1px solid #000;">${(item.deposit || 0).toLocaleString('zh-CN', { minimumFractionDigits: 2 })}</td>`,
+            `<td style="width: 120px; text-align: center; padding: 8px; border: 1px solid #000;">${(item.propertyFee || 0).toLocaleString('zh-CN', { minimumFractionDigits: 2 })}</td>`
+          ]
+        return `<tr>${cells.join('')}</tr>`
+      }).join('')
+
+      // 构建合计行
+      const summaryCells = showMonthDetailColumns.value
+        ? [
+          `<td style="width: 50px; text-align: center; padding: 8px; border: 1px solid #000; font-weight: bold; background: #f0f0f0;">合计</td>`,
+          `<td style="width: 120px; text-align: center; padding: 8px; border: 1px solid #000; background: #f0f0f0;"></td>`,
+          `<td style="width: 150px; text-align: center; padding: 8px; border: 1px solid #000; background: #f0f0f0;"></td>`,
+          `<td style="width: 120px; text-align: center; padding: 8px; border: 1px solid #000; font-weight: bold; background: #f0f0f0;">${printSummary.rent.toLocaleString('zh-CN', { minimumFractionDigits: 2 })}</td>`,
+          `<td style="width: 120px; text-align: center; padding: 8px; border: 1px solid #000; font-weight: bold; background: #f0f0f0;">${printSummary.deposit.toLocaleString('zh-CN', { minimumFractionDigits: 2 })}</td>`,
+          `<td style="width: 120px; text-align: center; padding: 8px; border: 1px solid #000; font-weight: bold; background: #f0f0f0;">${printSummary.propertyFee.toLocaleString('zh-CN', { minimumFractionDigits: 2 })}</td>`,
+          `<td style="width: 60px; text-align: center; padding: 8px; border: 1px solid #000; background: #f0f0f0;"></td>`
+        ]
+        : [
+          `<td style="width: 50px; text-align: center; padding: 8px; border: 1px solid #000; font-weight: bold; background: #f0f0f0;">合计</td>`,
+          `<td style="width: 150px; text-align: center; padding: 8px; border: 1px solid #000; background: #f0f0f0;"></td>`,
+          `<td style="width: 120px; text-align: center; padding: 8px; border: 1px solid #000; font-weight: bold; background: #f0f0f0;">${printSummary.rent.toLocaleString('zh-CN', { minimumFractionDigits: 2 })}</td>`,
+          `<td style="width: 120px; text-align: center; padding: 8px; border: 1px solid #000; font-weight: bold; background: #f0f0f0;">${printSummary.deposit.toLocaleString('zh-CN', { minimumFractionDigits: 2 })}</td>`,
+          `<td style="width: 120px; text-align: center; padding: 8px; border: 1px solid #000; font-weight: bold; background: #f0f0f0;">${printSummary.propertyFee.toLocaleString('zh-CN', { minimumFractionDigits: 2 })}</td>`
+        ]
+
+      const summaryHtml = `<tr>${summaryCells.join('')}</tr>`
+
+      // 完整表格
+      return `
+        <table style="width: 100%; border-collapse: collapse; font-family: 'Microsoft YaHei', sans-serif;">
+          <thead><tr>${headerHtml}</tr></thead>
+          <tbody>
+            ${rowsHtml}
+            ${summaryHtml}
+          </tbody>
+        </table>
+      `
+    }
+
+    // 构建打印页面内容(修改:标题中加入选中的类型)
+    const printContent = `
+      <!DOCTYPE html>
+      <html>
+      <head>
+        <meta charset="UTF-8">
+        <title>${showMonthDetailColumns.value ? '退款详情' : '退款汇总'}-${searchForm.selectedYear}年${searchForm.selectedMonth ? searchForm.selectedMonth + '月' : ''}-${getSelectedTypeLabel()}</title>
+        <style>
+          body {
+            padding: 20px;
+            font-family: 'Microsoft YaHei', sans-serif;
+            font-size: 12px;
+          }
+          h1 {
+            text-align: center;
+            font-size: 16px;
+            margin-bottom: 20px;
+          }
+          @media print {
+            @page {
+              margin: 15mm;
+            }
+          }
+        </style>
+      </head>
+      <body>
+        <h1>${showMonthDetailColumns.value ? '退款详情数据' : '退款汇总数据'} - ${searchForm.selectedYear}年${searchForm.selectedMonth ? searchForm.selectedMonth + '月' : ''} - ${getSelectedTypeLabel()}</h1>
+        ${buildPrintTable()}
+      </body>
+      </html>
+    `
+
+    printWindow.document.write(printContent)
+    printWindow.document.close()
+
+    // 执行打印
+    setTimeout(() => {
+      printWindow.focus()
+      printWindow.print()
+      setTimeout(() => printWindow.close(), 500)
+    }, 200)
+
+  } catch (error) {
+    ElMessage.error('打印失败:' + (error as Error).message)
+    console.error('打印错误:', error)
+  }
+}
+
+// 初始化
+onMounted(() => {
+  // 默认选中当前年份
+  searchForm.selectedYear = new Date().getFullYear().toString()
+  fetchRefundData(`${API_BASE_URL}/year`)
+})
+</script>
+
+<style scoped>
+.dashboard-container {
+  margin: 20px auto;
+  padding: 0 20px;
+  font-family: 'Microsoft YaHei', sans-serif;
+}
+
+.filter-panel {
+  display: flex;
+  align-items: center;
+  gap: 20px;
+  padding: 16px 20px;
+  background: #f8f9fa;
+  border-radius: 8px;
+  margin-bottom: 20px;
+  flex-wrap: wrap;
+}
+
+.filter-item {
+  display: flex;
+  align-items: center;
+  gap: 8px;
+}
+
+.filter-label {
+  font-size: 14px;
+  color: #333;
+  font-weight: 500;
+  white-space: nowrap;
+}
+
+.table-container {
+  background: #fff;
+  padding: 20px;
+  border-radius: 8px;
+  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
+}
+
+/* 响应式适配 */
+@media (max-width: 768px) {
+  .filter-panel {
+    flex-direction: column;
+    align-items: flex-start;
+  }
+}
+
+/* 表格样式优化 */
+:deep(.el-table__cell) {
+  padding: 12px 0;
+}
+
+:deep(.el-select.is-disabled) {
+  opacity: 0.7;
+}
+
+:deep(.el-button.is-disabled) {
+  cursor: not-allowed !important;
+}
+
+/* 合计行样式增强 */
+:deep(.el-table__footer-wrapper) {
+  font-weight: bold !important;
+}
+
+:deep(.el-table__footer-wrapper .el-table__cell) {
+  background-color: #f0f0f0 !important;
+  color: #333 !important;
+  font-size: 14px !important;
+}
+
+:deep(.el-table__footer) {
+  border-top: 2px solid #e6e6e6 !important;
+}
+</style>

+ 0 - 6
src/views/tj/tjtj.vue

@@ -381,12 +381,6 @@ const formatAmount = (amount: number) => {
 <template>
   <div class="min-h-screen bg-gradient-to-br from-emerald-50 via-white to-cyan-50 p-6">
     <div class="max-full mx-auto">
-      <div class="mb-8">
-        <h1 class="text-4xl font-bold text-gray-900 mb-2 flex items-center gap-3">
-          <BarChart3 class="w-10 h-10 text-emerald-600" />
-        </h1>
-        <h1 class="text-gray-600">退据统计</h1>
-      </div>
 
       <div class="mb-6 flex items-center gap-4">
         <Calendar class="w-5 h-5 text-emerald-600" />

+ 21 - 6
src/views/zf/fwxq.vue

@@ -51,6 +51,8 @@ const currentStep = ref(0)
 const submitting = ref(false)
 const terminating = ref(false)
 const loading = ref(true)
+const panduanareaisTrue = ref(true)
+
 const MINIO_URL = import.meta.env.VITE_MINIO_BASE_URL
 
 const propertyInfo = ref({
@@ -740,6 +742,16 @@ const nextStep = async () => {
   }
 }
 
+const geiAreaValueisTrue = async() => {
+  if(panduanareaisTrue.value){
+    console.log('当前有面积')
+    showRentalDialog.value = true;
+  }else{
+    console.log('当前没有面积')
+    ElMessage.error('此房屋没有填写房屋面积,请在"租房信息-租房信息管理-详情"中填写房屋信息后重试!')
+  }
+}
+
 const handleCloseRentalDialog = () => {
   showRentalDialog.value = false
   currentStep.value = 0
@@ -949,12 +961,14 @@ const getData = async () => {
       ElMessage.error(res.msg)
       return
     }
-    const data = res.data
+    const data = res.data;
+
     // 新增:获取收据信息
     await getReceiptData(data.contractInfo?.contractNumber || '')
-
-    console.log('接口返回数据:', data)
-
+    console.log(1111,data.houseInfo.area)
+    if(data.houseInfo.area === '' || data.houseInfo.area === null){
+      panduanareaisTrue.value = false;
+    }
     propertyInfo.value = {
       id: route.query.houseId as string,
       buildingNumber: data.houseName || '',
@@ -1426,9 +1440,10 @@ onMounted(() => {
               v-if="propertyInfo.status === '空闲'"
               type="primary"
               size="large"
-              @click="showRentalDialog = true"
+              @click="geiAreaValueisTrue"
               class="px-8 py-3"
             >
+
               <el-icon class="mr-2"><Plus /></el-icon>
               出租房屋
             </el-button>
@@ -1475,7 +1490,7 @@ onMounted(() => {
       width="800px"
       :before-close="handleCloseRentalDialog"
     >
-      <el-form :model="rentalForm" :rules="rentalRules" ref="rentalFormRef" label-width="120px">
+      <el-form :model="rentalForm" :rules="rentalRules" ref="rentalFormRef">
         <el-steps :active="currentStep" finish-status="success" class="mb-8">
           <el-step title="租户信息" />
           <el-step title="合同条款" />

+ 138 - 32
src/views/zf/zflb.vue

@@ -6,6 +6,7 @@ import {
   ElCol,
   ElContainer,
   ElIcon,
+  ElInput,
   ElInputNumber,
   ElMain,
   ElMessage,
@@ -47,6 +48,7 @@ export interface ASimplifiedHouseInfoVo {
   updateTime: string // 更新时间
   area: string // 面积
   isArrears: boolean // 是否欠费
+  contractExpirationDate?: string | null // 合同到期日期
 }
 
 export interface BaseResponse {
@@ -67,15 +69,16 @@ export interface ASimplifiedHouseInfoVoListResponse extends BaseResponse {
 
 const router = useRouter()
 const route = useRoute()
+
 // 响应式数据
-const activeTab = ref(route.query.currentActive as string)
+const activeTab = ref(route.query.currentActive as string || 'housing')
 const currentPage = ref(1)
 const pageSize = ref(12)
 const sortBy = ref('default')
 const loading = ref(false)
 const total = ref(0)
 
-// 筛选条件
+// 筛选条件 - 核心修改:默认选中空闲状态(value: '1')
 const filters = ref({
   priceRange: {
     min: undefined as number | undefined,
@@ -88,7 +91,8 @@ const filters = ref({
   district: '',
   building: undefined as number | undefined,
   floor: undefined as number | undefined,
-  houseName: undefined as number | undefined,
+  houseName: undefined as string | undefined,
+  status: '1' as string | undefined // 默认值:空闲(数字字符串'1')
 })
 
 // 房屋列表数据
@@ -106,11 +110,11 @@ const sortMap = {
   'price-desc': '1', // 租金从高到低
   'price-asc': '2', // 租金从低到高
   'area-desc': '3', // 面积从高到低
-  'area-asc': '4', // 面积从低到高
+  'area-asc': '4', // 面积从小到大
   default: '1', // 默认排序
 }
 
-// 解析租金范围 - 简化版本,直接使用对象
+// 解析租金范围
 const parseRentRange = () => {
   return {
     min: filters.value.priceRange.min,
@@ -118,7 +122,7 @@ const parseRentRange = () => {
   }
 }
 
-// 解析面积范围 - 简化版本,直接使用对象
+// 解析面积范围
 const parseAreaRange = () => {
   return {
     min: filters.value.areaRange.min,
@@ -134,6 +138,7 @@ const getList = async () => {
     const rentRange = parseRentRange()
     const areaRange = parseAreaRange()
 
+    // 构建请求参数
     const params = {
       assetType: assetTypeMap[activeTab.value as keyof typeof assetTypeMap],
       pageNum: currentPage.value,
@@ -145,14 +150,14 @@ const getList = async () => {
       ...(areaRange.max && { areaMax: areaRange.max }),
       ...(filters.value.building && { building: filters.value.building?.toString() }),
       ...(filters.value.floor && { floor: filters.value.floor?.toString() }),
-      ...(filters.value.houseName && { houseName: filters.value.houseName?.toString() }),
+      ...(filters.value.houseName && { houseName: filters.value.houseName }),
+      // 始终传递状态参数(默认是'1',即空闲)
+      ...(filters.value.status && { status: filters.value.status })
     }
 
     const response = await clientGet<typeof params, ASimplifiedHouseInfoVoListResponse>(
       '/asimplifiedHouseInfo/getList',
-      {
-        params: params,
-      },
+      { params: params }
     )
 
     if (response.code === 200) {
@@ -175,18 +180,29 @@ const getList = async () => {
 
 // 格式化显示数据
 const formatDisplayData = (item: ASimplifiedHouseInfoVo) => {
+  // 状态反向映射:后端返回的数字值转前端显示文本
+  const statusDisplayMap = {
+    '1': '可租',
+    '2': '已租',
+    '3': '到期',
+    '空闲': '可租', // 兼容原有文本值
+    '已租': '已租',
+    '到期': '到期'
+  }
+
   return {
     id: item.id,
-    buildingNumber: item.building, //楼栋数
-    houseNameNumber: item.houseName,
+    buildingNumber: item.building || '-',
+    houseNameNumber: item.houseName || '-',
     priceRange: item.rentRange ? `${item.rentRange}` : '面议',
-    address: item.address,
+    address: item.address || '未填写',
     area: parseInt(item.area) || 0,
-    status: item.status === '空闲' ? '可租' : '已租',
-    floor: item.floor,
-    facilities: '配套设施', // 接口没有这个字段,可以根据实际情况调整
-    building: item.building,
-    isArrears: item.isArrears, // 是否欠费
+    status: statusDisplayMap[item.status] || item.status,
+    floor: item.floor || '-',
+    facilities: '配套设施',
+    building: item.building || '-',
+    isArrears: item.isArrears,
+    contractExpirationDate: item.contractExpirationDate || null,
   }
 }
 
@@ -195,22 +211,57 @@ const formattedList = computed(() => {
   return houseList.value.map(formatDisplayData)
 })
 
-// 方法
+// 优化状态标签类型判断逻辑
+const getStatusTagType = (contractExpirationDate: string | null, status: string) => {
+  // 处理空值情况
+  if (!contractExpirationDate) contractExpirationDate = null
+
+  // 可租/空闲状态
+  if (status === '可租' || status === '空闲') {
+    return 'success'
+  }
+
+  // 已租且有到期日期(即将到期)
+  if (status === '已租' && contractExpirationDate && contractExpirationDate !== '') {
+    return 'danger'
+  }
+
+  // 已租无到期日期
+  if (status === '已租') {
+    return 'warning'
+  }
+
+  // 到期状态
+  if (status === '到期') {
+    return 'danger'
+  }
+
+  return 'success'
+}
+
+// 获取状态对应的按钮类型
+const getStatusButtonType = (contractExpirationDate: string | null, status: string) => {
+  return getStatusTagType(contractExpirationDate, status)
+}
+
+// 标签页切换处理
 const handleTabClick = (tab: any) => {
   if (tab.paneName === activeTab.value) {
     return
   }
   activeTab.value = tab.paneName
   currentPage.value = 1
-  resetFilters()
+  // 切换标签页时保留默认的空闲状态筛选
   getList()
 }
 
+// 手动应用筛选
 const applyFilters = () => {
   currentPage.value = 1
   getList()
 }
 
+// 重置筛选条件 - 核心修改:重置后仍保留空闲状态
 const resetFilters = () => {
   filters.value = {
     priceRange: {
@@ -225,22 +276,32 @@ const resetFilters = () => {
     building: undefined,
     floor: undefined,
     houseName: undefined,
+    status: '1' // 重置后依然默认选中空闲状态
   }
 }
 
+// 分页变化处理
 const handlePageChange = (page: number) => {
   currentPage.value = page
   getList()
 }
 
+// 排序变化处理
 const handleSortChange = () => {
   currentPage.value = 1
   getList()
 }
 
+// 状态变化自动筛选
+const handleStatusChange = () => {
+  currentPage.value = 1
+  getList()
+}
+
 const goBack = () => {
   router.back()
 }
+
 // 查看详情
 const viewDetails = (item: any) => {
   router.push({
@@ -254,7 +315,14 @@ const viewDetails = (item: any) => {
 // 监听排序变化
 watch(sortBy, handleSortChange)
 
-// 组件挂载时获取数据
+// 监听状态变化 - 选择后自动筛选
+watch(() => filters.value.status, (newVal) => {
+  if (newVal !== undefined) {
+    handleStatusChange()
+  }
+}, { immediate: false })
+
+// 组件挂载时获取数据(默认加载空闲状态数据)
 onMounted(() => {
   getList()
 })
@@ -401,15 +469,31 @@ onMounted(() => {
                     <el-input
                       v-model="filters.houseName"
                       placeholder="房间名称"
-                      :min="1"
-                      :max="9999"
                       class="w-full"
-                      controls-position="right"
                       size="default"
+                      clearable
                     />
                   </div>
                 </el-col>
 
+                <el-col :xs="24" :sm="12" :md="2">
+                  <div class="mb-4">
+                    <label class="block text-sm font-medium text-gray-700 mb-2">房屋状态</label>
+                    <!-- 核心修改:默认选中空闲,且移除清空功能(避免删除默认值) -->
+                    <el-select
+                      v-model="filters.status"
+                      placeholder="请选择状态"
+                      style="width: 100%"
+                      size="default"
+                      @change="handleStatusChange"
+                    >
+                      <el-option label="空闲" value="1"/>
+                      <el-option label="已租" value="2"/>
+                      <el-option label="到期" value="3"/>
+                    </el-select>
+                  </div>
+                </el-col>
+
                 <el-col :xs="24" :sm="12" :md="6" style="display: flex">
                   <div class="mb-4 flex items-end">
                     <el-button
@@ -468,23 +552,32 @@ onMounted(() => {
                 class="h-full shadow-xl hover:shadow-2xl transition-all duration-300 rounded-2xl border-0 overflow-hidden group cursor-pointer"
               >
                 <div>
-                  <!-- 状态标签移至下方 -->
-                  <div class="flex items-center justify-between pt-2 gap-2">
-                    <div class="flex items-center gap-2">
+                  <!-- 状态标签 -->
+                  <div class="flex items-center justify-between pt-2 gap-2 flex-wrap">
+                    <div class="flex items-center gap-2 flex-wrap">
                       <el-tag
-                        :type="item.status === '可租' ? 'success' : 'warning'"
+                        :type="getStatusTagType(item.contractExpirationDate,item.status)"
                         size="large"
                         class="custom-large-tag"
                       >
                         {{ item.status }}
                       </el-tag>
+
                       <el-tag
-                        v-if="item.isArrears"
+                        v-if="item.contractExpirationDate && item.contractExpirationDate !== ''"
                         type="danger"
                         size="large"
                         class="custom-large-tag"
-                        >缴费提醒</el-tag
                       >
+                        合同到期日: {{ item.contractExpirationDate }}
+                      </el-tag>
+
+                      <el-tag
+                        v-if="item.isArrears"
+                        type="danger"
+                        size="large"
+                        class="custom-large-tag"
+                      >缴费提醒</el-tag>
                     </div>
                     <div class="text-right">
                       <div class="text-2xl font-bold text-blue-600">{{ item.priceRange }}</div>
@@ -505,7 +598,7 @@ onMounted(() => {
                       <el-icon class="mr-2 text-green-500"><Maximize /></el-icon>
                       <span class="text-sm">占地面积:{{ item.area }}㎡</span>
                     </div>
-                    <div class="flex items-center text-gray-600" v-if="item.floor">
+                    <div class="flex items-center text-gray-600" v-if="item.floor && item.floor !== '-'">
                       <el-icon class="mr-2 text-purple-500"><Building2 /></el-icon>
                       <span class="text-sm">楼层:{{ item.floor }}</span>
                     </div>
@@ -516,7 +609,7 @@ onMounted(() => {
                   </div>
                   <div class="flex gap-2">
                     <el-button
-                      :type="item.status === '可租' ? 'success' : 'warning'"
+                      :type="getStatusButtonType(item.contractExpirationDate,item.status)"
                       size="default"
                       class="w-full"
                       @click="viewDetails(item)"
@@ -557,6 +650,11 @@ onMounted(() => {
 <style scoped>
 .el-card {
   border: none;
+  transition: all 0.3s ease;
+}
+
+.el-card:hover {
+  transform: translateY(-2px);
 }
 
 .el-tabs__item {
@@ -612,4 +710,12 @@ onMounted(() => {
   font-size: 16px !important;
   padding: 10px 16px !important;
 }
+
+/* 响应式优化 */
+@media (max-width: 768px) {
+  .custom-large-tag {
+    font-size: 14px !important;
+    padding: 8px 12px !important;
+  }
+}
 </style>

+ 52 - 17
src/views/zfgl/zfhtgl.vue

@@ -227,6 +227,7 @@ const returnReceiptLoading = ref(false)
 const returnReceiptGenerating = ref(false)
 const currentReturnReceiptContractId = ref('')
 const currentReturnReceiptContractNumber = ref('')
+const currentReturnReceiptSimplifiedHouseId = ref('')
 
 // 退据表单数据
 const returnReceiptForm = reactive<ReturnReceiptDto>({
@@ -704,9 +705,9 @@ const gainReceipt = async (contractId: string, dto: ReceiptDto) => {
   return res
 }
 
+console.log(2222,permission,userInfo.value?.personName)
 // 打开收据对话框
 const openReceiptDialog = async (contractId: string, contractNumber: string) => {
-  console.log(2222,userInfo.value?.personName)
 
   currentReceiptContractId.value = contractId
   currentReceiptContractNumber.value = contractNumber
@@ -735,7 +736,7 @@ const openReceiptDialog = async (contractId: string, contractNumber: string) =>
     const response = await getCanBeReceiptData(contractId)
     if (response.code === 200 && response.data) {
       // 填充表单数据
-      receiptForm.paymentUnit = response.data.jkdw || ''
+      receiptForm.paymentUnit = response.data.ld + '栋 ' + response.data.lc + '层 ' + response.data.jkdw + '房间' || ''
       receiptForm.yj = response.data.yj || 0
       receiptForm.zj = response.data.zj || 0
       // 计算总金额
@@ -941,9 +942,10 @@ const gainReturnReceipt = async (contractId: string, dto: ReturnReceiptDto) => {
 }
 
 // 打开退据对话框
-const openReturnReceiptDialog = async (contractId: string, contractNumber: string) => {
+const openReturnReceiptDialog = async (contractId: string, contractNumber: string,simplifiedHouseId: string) => {
   currentReturnReceiptContractId.value = contractId
   currentReturnReceiptContractNumber.value = contractNumber
+  currentReturnReceiptSimplifiedHouseId.value = simplifiedHouseId
   returnReceiptVisible.value = true
   returnReceiptLoading.value = true
 
@@ -967,7 +969,7 @@ const openReturnReceiptDialog = async (contractId: string, contractNumber: strin
     const response = await getCanBeReturnReceiptData(contractId)
     if (response.code === 200 && response.data) {
       // 填充表单数据
-      returnReceiptForm.zh = response.data.zh || ''
+      returnReceiptForm.zh = response.data.ld + '栋 ' + response.data.lc + '层 ' + response.data.zh + '房间'
       returnReceiptForm.yj = response.data.yj || 0
       // 计算总金额
       calculateReturnTotalAmount()
@@ -1047,6 +1049,9 @@ const generateReturnReceipt = async () => {
       returnReceiptForm,
     )
 
+
+    console.log(123,currentReturnReceiptSimplifiedHouseId.value)
+
     if (response.code === 999) {
       // 退据已存在,让用户选择是否预览
       ElMessage.success('退据已存在!')
@@ -1070,6 +1075,10 @@ const generateReturnReceipt = async () => {
       }
     } else if (response.code === 200 && response.data) {
       ElMessage.success('退据生成成功!')
+      // 刷新当前合同的退据列表
+      //这里处理退据后退房操作
+      await confirmTerminate(currentReturnReceiptSimplifiedHouseId.value)
+      getList()
       // 自动预览生成的退据
       const returnReceiptUrl = response.data
       await previewDocument(returnReceiptUrl, `${currentReturnReceiptContractNumber.value} - 退据`)
@@ -1088,6 +1097,31 @@ const generateReturnReceipt = async () => {
   }
 }
 
+//退据后退房
+const confirmTerminate = async (hostId: string) => {
+  try {
+    const res = await clientGet<
+      {
+        houseId: string
+      },
+      BaseResponse
+    >('/acontractInfo/returnRent', {
+      params: {
+        houseId: hostId
+      },
+    })
+    if (res.code !== 200) {
+      ElMessage.error(res.msg)
+      return
+    }
+    ElMessage.success(res.msg)
+  } catch (error) {
+    console.error(error)
+    ElMessage.error('退租失败,请重试')
+  } finally {
+  }
+}
+
 // 关闭退据对话框
 const closeReturnReceiptDialog = () => {
   returnReceiptVisible.value = false
@@ -1120,16 +1154,17 @@ onMounted(() => {
 </script>
 
 <template>
-  <div class="p-6 bg-gray-50 min-h-screen font-sans">
+  <div class="font-sans">
     <!-- 页面标题 -->
-    <div class="mb-6">
-      <h1 class="text-2xl font-bold text-gray-800 mb-2">租房合同管理</h1>
-      <p class="text-gray-600">管理和查看所有租房合同信息</p>
-    </div>
+<!--    <div class="mb-6">-->
+<!--      <h1 class="text-2xl font-bold text-gray-800 mb-2">租房合同管理</h1>-->
+<!--      <p class="text-gray-600">管理和查看所有租房合同信息</p>-->
+<!--    </div>-->
 
     <!-- 搜索区域 -->
+    <div class="">
     <ElCard class="mb-6" shadow="never">
-      <ElForm :model="searchForm" inline class="bg-gray-50 p-5 rounded-lg">
+      <ElForm :model="searchForm" inline class="rounded-lg">
         <ElFormItem label="合同编号:" size="default">
           <ElInput
             v-model="searchForm.contractNumber"
@@ -1180,9 +1215,9 @@ onMounted(() => {
         </ElFormItem>
       </ElForm>
     </ElCard>
-
+    </div>
     <!-- 表格区域 -->
-    <ElCard shadow="never" class="rounded-lg overflow-hidden">
+<!--    <ElCard shadow="never" class="rounded-lg overflow-hidden">-->
       <ElTable
         :data="tableData"
         :loading="loading"
@@ -1199,7 +1234,8 @@ onMounted(() => {
         <ElTableColumn prop="assetType" label="房屋类型"/>
         <ElTableColumn prop="contractDate" label="签约日期" width="140" />
         <ElTableColumn prop="contractTime" label="合同期限" width="120" />
-        <ElTableColumn prop="contractExpirationDate" label="到期日期" width="140" />
+        <ElTableColumn prop="contractExpirationDate" label="合同到期时间" width="140" />
+        <ElTableColumn prop="maxReceiptEndDate" label="租金到期时间" width="140" />
         <ElTableColumn prop="contractDeposit" label="押金(元)" width="140">
           <template #default="{ row }">
             <span class="text-orange-600 font-medium">
@@ -1311,7 +1347,7 @@ onMounted(() => {
                   text
                   :icon="RotateCcw"
                   :disabled="!userInfo?.isBusinessAtt"
-                  @click="openReturnReceiptDialog(row.id, row.contractNumber)"
+                  @click="openReturnReceiptDialog(row.id, row.contractNumber,row.simplifiedHouseId)"
                 >
                   获取退据
                 </ElButton>
@@ -1327,7 +1363,7 @@ onMounted(() => {
       </ElTable>
 
       <!-- 分页 -->
-      <div class="flex justify-center mt-6">
+      <div class="flex justify-center mt-0">
         <ElPagination
           v-model:current-page="currentPage"
           v-model:page-size="pageSize"
@@ -1338,7 +1374,7 @@ onMounted(() => {
           @current-change="handleCurrentChange"
         />
       </div>
-    </ElCard>
+<!--    </ElCard>-->
 
     <!-- 文档预览对话框 -->
     <ElDialog
@@ -1512,7 +1548,6 @@ onMounted(() => {
               </div>
             </div>
           </div>
-
           <ElFormItem label="交款单位" prop="paymentUnit">
             <ElInput v-model="receiptForm.paymentUnit" placeholder="请输入交款单位名称" clearable />
           </ElFormItem>

+ 27 - 27
src/views/zfgl/zfsjgl.vue

@@ -1,6 +1,6 @@
 <script setup lang="ts">
 import { onMounted, reactive, ref } from 'vue'
-import { ElMessage, ElMessageBox } from 'element-plus'
+import {ElInput, ElMessage, ElMessageBox} from 'element-plus'
 import { Download, RefreshCw, Search, Trash2 } from 'lucide-vue-next'
 import { clientGet, clientPost } from '@/utils/request.ts'
 
@@ -14,6 +14,7 @@ interface AReceiptInfo {
   totalAmount?: number
   rent?: number
   propertyFee?: number
+  electricityBill?:number
   deposit?: number
   waterFee?: number
   receiptReason?: string
@@ -221,42 +222,42 @@ onMounted(() => {
 </script>
 
 <template>
-  <div class="p-6 bg-gray-50 min-h-screen">
+  <div class="bg-gray-50">
     <div class="bg-white rounded-lg shadow-sm">
-      <!-- 页面标题 -->
-      <div class="px-6 py-4 border-b border-gray-200">
-        <h1 class="text-xl font-semibold text-gray-900">合同收据管理</h1>
-      </div>
-
       <!-- 搜索表单 -->
       <div class="p-6 border-b border-gray-200">
         <el-form :model="searchForm" inline class="search-form">
-          <el-form-item label="收据单号">
+          <el-form-item label="收据单号" size="default">
             <el-input
               v-model="searchForm.receiptNumber"
               placeholder="请输入收据单号"
+              size="default"
               clearable
+              style="width: 180px"
               class="w-48"
               @keyup.enter="handleSearch"
             />
           </el-form-item>
 
-          <el-form-item label="交款单位">
+          <el-form-item label="交款单位" size="default">
             <el-input
               v-model="searchForm.payer"
               placeholder="请输入交款单位"
+              size="default"
               clearable
               class="w-48"
+              style="width: 150px"
               @keyup.enter="handleSearch"
             />
           </el-form-item>
 
-          <el-form-item label="收款方式">
+          <el-form-item label="收款方式" size="default">
             <el-select
               v-model="searchForm.paymentMethod"
               placeholder="请选择收款方式"
+              size="default"
               clearable
-              style="width: 12rem"
+              style="width: 10rem"
             >
               <el-option
                 v-for="item in paymentMethods"
@@ -267,54 +268,48 @@ onMounted(() => {
             </el-select>
           </el-form-item>
 
-          <el-form-item label="合同编号">
+          <el-form-item label="合同编号" size="default">
             <el-input
               v-model="searchForm.contactNumber"
               placeholder="请输入合同编号"
+              size="default"
               clearable
+              style="width: 180px"
               class="w-48"
               @keyup.enter="handleSearch"
             />
           </el-form-item>
 
           <el-form-item>
-            <el-button type="primary" @click="handleSearch" :loading="loading">
+            <el-button type="primary" @click="handleSearch" :loading="loading" size="default">
               <Search class="w-4 h-4 mr-1" v-if="!loading" />
               搜索
             </el-button>
-            <el-button @click="handleReset">
+            <el-button @click="handleReset" size="default">
               <RefreshCw class="w-4 h-4 mr-1" />
               重置
             </el-button>
-          </el-form-item>
-        </el-form>
-      </div>
-
-      <!-- 操作按钮 -->
-      <div class="px-6 py-4 border-b border-gray-200">
-        <div class="flex justify-between items-center">
-          <div class="flex gap-2">
             <el-button
               type="danger"
               :disabled="selectedRows.length === 0"
               @click="handleBatchDelete"
+              size="default"
             >
               <Trash2 class="w-4 h-4 mr-1" />
               批量删除 ({{ selectedRows.length }})
             </el-button>
-          </div>
-
-          <div class="text-sm text-gray-500">共 {{ total }} 条记录</div>
-        </div>
+          </el-form-item>
+        </el-form>
       </div>
 
       <!-- 数据表格 -->
-      <div class="px-6">
+      <div>
         <el-table
           :data="tableData"
           v-loading="loading"
           @selection-change="handleSelectionChange"
           stripe
+          border
           class="w-full"
         >
           <el-table-column type="selection" width="55" />
@@ -346,6 +341,11 @@ onMounted(() => {
               {{ formatAmount(row.propertyFee) }}
             </template>
           </el-table-column>
+          <el-table-column prop="electricityBill" label="电费" width="100" align="right">
+            <template #default="{ row }">
+              {{ formatAmount(row.electricityBill) }}
+            </template>
+          </el-table-column>
 
           <el-table-column prop="deposit" label="押金" width="100" align="right">
             <template #default="{ row }">

+ 26 - 24
src/views/zfgl/zftjgl.vue

@@ -198,40 +198,39 @@ onMounted(() => {
 </script>
 
 <template>
-  <div class="p-6 bg-gray-50 min-h-screen">
+  <div class="bg-gray-50 min-h-screen">
     <div class="bg-white rounded-lg shadow-sm">
-      <!-- 页面标题 -->
-      <div class="px-6 py-4 border-b border-gray-200">
-        <h1 class="text-xl font-semibold text-gray-900">租房退据管理</h1>
-      </div>
 
       <!-- 搜索表单区域 -->
       <div class="p-6 border-b border-gray-200">
         <el-form :model="searchForm" inline class="search-form">
-          <el-form-item label="租户名称">
+          <el-form-item label="租户名称" size="default">
             <el-input
               v-model="searchForm.tenant"
               placeholder="请输入租户名称"
+              size="default"
               clearable
               class="w-48"
               @keyup.enter="handleSearch"
             />
           </el-form-item>
 
-          <el-form-item label="所属单位">
+          <el-form-item label="所属单位" size="default">
             <el-input
               v-model="searchForm.unit"
               placeholder="请输入所属单位"
+              size="default"
               clearable
               class="w-48"
               @keyup.enter="handleSearch"
             />
           </el-form-item>
 
-          <el-form-item label="合同编号">
+          <el-form-item label="合同编号" size="default">
             <el-input
               v-model="searchForm.contractNumber"
-              placeholder="请输入所属单位"
+              placeholder="请输入合同编号"
+              size="default"
               clearable
               class="w-48"
               @keyup.enter="handleSearch"
@@ -239,43 +238,46 @@ onMounted(() => {
           </el-form-item>
 
           <el-form-item>
-            <el-button type="primary" @click="handleSearch" :loading="loading">
+            <el-button type="primary" @click="handleSearch" :loading="loading" size="default">
               <Search class="w-4 h-4 mr-1" v-if="!loading" />
               搜索
             </el-button>
-            <el-button @click="handleReset">
+            <el-button @click="handleReset" size="default">
               <RefreshCw class="w-4 h-4 mr-1" />
               重置
             </el-button>
-          </el-form-item>
-        </el-form>
-      </div>
-
-      <!-- 操作按钮区域 -->
-      <div class="px-6 py-4 border-b border-gray-200">
-        <div class="flex justify-between items-center">
-          <div class="flex gap-2">
             <el-button
               type="danger"
               :disabled="selectedRows.length === 0"
               @click="handleBatchDelete"
+              size="default"
             >
               <Trash2 class="w-4 h-4 mr-1" />
               批量删除 ({{ selectedRows.length }})
             </el-button>
-          </div>
-
-          <div class="text-sm text-gray-500">共 {{ total }} 条记录</div>
-        </div>
+          </el-form-item>
+        </el-form>
       </div>
 
+      <!-- 操作按钮区域 -->
+<!--      <div class="px-6 py-4 border-b border-gray-200">-->
+<!--        <div class="flex justify-between items-center">-->
+<!--          <div class="flex gap-2">-->
+
+<!--          </div>-->
+
+<!--          <div class="text-sm text-gray-500">共 {{ total }} 条记录</div>-->
+<!--        </div>-->
+<!--      </div>-->
+
       <!-- 数据表格区域 -->
-      <div class="px-6">
+      <div class="">
         <el-table
           :data="tableData"
           v-loading="loading"
           @selection-change="handleSelectionChange"
           stripe
+          border
           class="w-full"
         >
           <el-table-column type="selection" width="55" />

+ 162 - 142
src/views/zfgl/zfxxgl.vue

@@ -1,6 +1,25 @@
 <script setup lang="ts">
-import { computed, onMounted, reactive, ref } from 'vue'
-import { ElMessage, ElMessageBox } from 'element-plus'
+import {computed, onMounted, reactive, ref} from 'vue'
+
+import {
+  ElButton,
+  ElCard,
+  ElDialog,
+  ElForm,
+  ElFormItem,
+  ElInput,
+  ElInputNumber,
+  ElMessage,
+  ElMessageBox,
+  ElOption,
+  ElPagination,
+  ElSelect,
+  ElSpace,
+  ElTable,
+  ElTableColumn,
+  ElTag,
+  ElUpload,
+} from 'element-plus'
 import {
   Delete,
   Download,
@@ -16,8 +35,8 @@ import {
   Users,
   Wrench,
 } from 'lucide-vue-next'
-import { clientGet, clientPost } from '@/utils/request.ts'
-import { renderAsync } from 'docx-preview'
+import {clientGet, clientPost} from '@/utils/request.ts'
+import {renderAsync} from 'docx-preview'
 
 interface ASimplifiedHouseInfo {
   id: string
@@ -338,7 +357,7 @@ const getList = async () => {
 
     const res = await clientGet<any, ASimplifiedHouseInfoReponse>(
       '/asimplifiedHouseInfo/findByPage',
-      { params },
+      {params},
     )
     if (res.code !== 200) {
       ElMessage.error(res.msg)
@@ -378,7 +397,7 @@ const getDetail = async (simplifiedHouseId: string) => {
   const res = await clientGet<{ id: string }, AHouseInfoDetailResponse>(
     '/ahouseInfoDetail/getBySimplifiedHouseId',
     {
-      params: { simplifiedHouseId },
+      params: {simplifiedHouseId},
     },
   )
   if (res.code !== 200) {
@@ -412,7 +431,7 @@ const getDeviceList = async (simplifiedHouseId: string) => {
   const res = await clientGet<{ id: string }, ADeviceInfoListResponse>(
     '/adeviceInfo/getBySimplifiedHouseId',
     {
-      params: { simplifiedHouseId },
+      params: {simplifiedHouseId},
     },
   )
   if (res.code !== 200) {
@@ -472,7 +491,7 @@ const getDeviceMaintainRecord = async (deviceId: string) => {
   const res = await clientGet<{ deviceId: string }, AMaintenanceRecordsListReponse>(
     '/amaintenanceRecords/getByDeviceId',
     {
-      params: { deviceId },
+      params: {deviceId},
     },
   )
   if (res.code !== 200) {
@@ -623,7 +642,7 @@ const handleAdd = () => {
 const handleEdit = (row: ASimplifiedHouseInfo) => {
   isEdit.value = true
   dialogTitle.value = '编辑房屋信息'
-  Object.assign(houseForm, { ...row })
+  Object.assign(houseForm, {...row})
   dialogVisible.value = true
 }
 
@@ -673,7 +692,7 @@ const handleViewDetail = async (row: ASimplifiedHouseInfo) => {
   // 获取房屋详细信息
   const detail = await getDetail(row.id)
   if (detail) {
-    Object.assign(detailForm, { ...detail, simplifiedHouseId: row.id })
+    Object.assign(detailForm, {...detail, simplifiedHouseId: row.id})
   } else {
     Object.assign(detailForm, {
       id: '',
@@ -747,7 +766,7 @@ const handleAddDevice = () => {
 
 const handleEditDevice = (device: ADeviceInfo) => {
   isDeviceEdit.value = true
-  Object.assign(deviceForm, { ...device })
+  Object.assign(deviceForm, {...device})
   deviceDialogVisible.value = true
 }
 
@@ -815,7 +834,7 @@ const handleAddMaintenance = () => {
 
 const handleEditMaintenance = (record: AMaintenanceRecords) => {
   isMaintenanceEdit.value = true
-  Object.assign(maintenanceForm, { ...record })
+  Object.assign(maintenanceForm, {...record})
   maintenanceRecordDialogVisible.value = true
 }
 
@@ -907,77 +926,76 @@ onMounted(() => {
 </script>
 
 <template>
-  <div class="p-0 bg-gray-50 min-h-screen">
+  <div class="p-0 bg-gray-50">
     <div class="bg-white rounded-lg shadow-sm">
       <!-- 页面标题 -->
-      <div class="flex items-center gap-3 p-6 border-b border-gray-200">
-        <div class="flex items-center justify-center w-10 h-10 bg-blue-100 rounded-lg">
-          <Home class="w-5 h-5 text-blue-600" />
-        </div>
-        <div>
-          <h1 class="text-xl font-semibold text-gray-900">租房信息管理</h1>
-          <p class="text-sm text-gray-500">管理房屋基本信息、详细信息和设备信息</p>
-        </div>
-      </div>
+      <!--      <div class="flex items-center gap-3 p-6 border-b border-gray-200">-->
+      <!--        <div class="flex items-center justify-center w-10 h-10 bg-blue-100 rounded-lg">-->
+      <!--          <Home class="w-5 h-5 text-blue-600" />-->
+      <!--        </div>-->
+      <!--        <div>-->
+      <!--          <h1 class="text-xl font-semibold text-gray-900">租房信息管理</h1>-->
+      <!--          <p class="text-sm text-gray-500">管理房屋基本信息、详细信息和设备信息</p>-->
+      <!--        </div>-->
+      <!--      </div>-->
 
       <!-- 搜索区域 -->
-      <div class="py-4 px-4 border-b border-gray-200">
-        <el-form :model="searchForm" inline class="flex-grow flex flex-wrap">
-          <el-form-item label="">
-            <span slot="label" class="font-size-4.5">资产类型:</span>
-            <el-select
-              v-model="searchForm.assetType"
-              placeholder="请选择资产类型"
-              clearable
-              style="width: 10rem"
-              size="default"
-            >
-              <el-option label="公租房" value="公租房" />
-              <el-option label="厂房" value="厂房" />
-              <el-option label="创新创业基地" value="创新创业基地" />
-            </el-select>
-          </el-form-item>
-          <el-form-item>
-            <span slot="label" class="font-size-4.5">楼栋:</span>
-            <el-input
-              v-model="searchForm.building"
-              style="width: 240px"
-              size="default"
-              placeholder="请输入楼栋"
-              clearable
-            />
-          </el-form-item>
-
-          <el-form-item>
-            <span slot="label" class="font-size-4.5">房间名称:</span>
-            <el-input
-              v-model="searchForm.houseName"
-              style="width: 240px"
-              size="default"
-              placeholder="请输入房间名称"
-              clearable
-            />
-          </el-form-item>
-          <el-form-item>
-            <span slot="label" class="font-size-4.5">状态:</span>
-            <el-select
-              v-model="searchForm.status"
-              placeholder="请选择状态"
-              clearable
-              style="width: 8rem"
-              size="default"
-            >
-              <el-option label="空闲" value="空闲" />
-              <el-option label="已租" value="已租" />
-            </el-select>
-          </el-form-item>
-          <el-form-item>
+      <div>
+        <ElCard class="mb-6" shadow="never">
+          <ElForm :model="searchForm" inline class="flex-grow flex flex-wrap">
+            <ElFormItem label="资产类型" size="default">
+              <el-select
+                v-model="searchForm.assetType"
+                placeholder="请选择资产类型"
+                clearable
+                style="width: 10rem"
+                size="default"
+              >
+                <el-option label="公租房" value="公租房"/>
+                <el-option label="厂房" value="厂房"/>
+                <el-option label="创新创业基地" value="创新创业基地"/>
+              </el-select>
+            </ElFormItem>
+            <ElFormItem label="楼栋" size="default">
+              <ElInput
+                v-model="searchForm.building"
+                style="width: 100px"
+                size="default"
+                placeholder="请输入楼栋"
+                clearable
+              />
+            </ElFormItem>
+
+            <ElFormItem label="房间名称" size="default">
+              <ElInput
+                v-model="searchForm.houseName"
+                style="width: 130px"
+                size="default"
+                placeholder="请输入房间名称"
+                clearable
+              />
+            </ElFormItem>
+
+            <ElFormItem label="状态" size="default">
+              <el-select
+                v-model="searchForm.status"
+                placeholder="请选择状态"
+                clearable
+                style="width: 8rem"
+                size="default"
+              >
+                <el-option label="空闲" value="空闲"/>
+                <el-option label="已租" value="已租"/>
+              </el-select>
+            </ElFormItem>
             <el-button type="primary" @click="handleSearch" :icon="Search" size="default"
-              >搜索</el-button
+            >搜索
+            </el-button
             >
             <el-button @click="handleReset" size="default">重置</el-button>
             <el-button type="primary" @click="handleAdd" :icon="Plus" size="default"
-              >新增房屋</el-button
+            >新增房屋
+            </el-button
             >
             <el-button
               type="danger"
@@ -988,8 +1006,8 @@ onMounted(() => {
             >
               批量删除
             </el-button>
-          </el-form-item>
-        </el-form>
+          </ElForm>
+        </ElCard>
       </div>
 
       <!-- 操作按钮区域 -->
@@ -1009,15 +1027,17 @@ onMounted(() => {
       <!--      </div>-->
 
       <!-- 数据表格 -->
-      <div class="px-6">
+      <!--      <ElCard shadow="never" class="">-->
+      <div class="">
         <el-table
           :data="tableData"
           v-loading="loading"
           @selection-change="handleSelectionChange"
           class="w-full"
           stripe
+          border
         >
-          <el-table-column type="selection" width="55" />
+          <el-table-column type="selection" width="55"/>
           <el-table-column prop="assetType" label="资产类型" width="100">
             <template #default="{ row }">
               <el-tag :type="getAssetTypeTag(row.assetType)">
@@ -1025,10 +1045,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="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'">
@@ -1036,7 +1056,7 @@ onMounted(() => {
               </el-tag>
             </template>
           </el-table-column>
-          <el-table-column prop="rentRange" label="租金" width="150" />
+          <el-table-column prop="rentRange" label="租金" width="150"/>
           <el-table-column label="操作" width="250" fixed="right">
             <template #default="{ row }">
               <div class="flex gap-2">
@@ -1054,7 +1074,7 @@ onMounted(() => {
           </el-table-column>
         </el-table>
       </div>
-
+      <!--      </ElCard>-->
       <!-- 分页 -->
       <div class="flex justify-center p-6">
         <el-pagination
@@ -1079,19 +1099,19 @@ onMounted(() => {
       <el-form :model="houseForm" label-width="100px" class="grid grid-cols-2 gap-4">
         <el-form-item label="资产类型" required>
           <el-select v-model="houseForm.assetType" 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="楼栋" required>
-          <el-input v-model="houseForm.building" placeholder="请输入楼栋" />
+          <el-input v-model="houseForm.building" placeholder="请输入楼栋"/>
         </el-form-item>
         <el-form-item label="楼层">
-          <el-input v-model="houseForm.floor" placeholder="请输入楼层" />
+          <el-input v-model="houseForm.floor" placeholder="请输入楼层"/>
         </el-form-item>
         <el-form-item label="房间名称" required>
-          <el-input v-model="houseForm.houseName" placeholder="请输入房间名称" />
+          <el-input v-model="houseForm.houseName" placeholder="请输入房间名称"/>
         </el-form-item>
         <el-form-item label="状态">
           <el-select
@@ -1101,8 +1121,8 @@ onMounted(() => {
             default-first-option
             disabled
           >
-            <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="租金">
@@ -1114,7 +1134,7 @@ onMounted(() => {
           />
         </el-form-item>
         <el-form-item label="地址" class="col-span-2">
-          <el-input v-model="houseForm.address" placeholder="请输入详细地址" />
+          <el-input v-model="houseForm.address" placeholder="请输入详细地址"/>
         </el-form-item>
       </el-form>
       <template #footer>
@@ -1138,14 +1158,14 @@ onMounted(() => {
         <el-tab-pane label="房屋详情" name="detail">
           <template #label>
             <div class="flex items-center gap-2">
-              <Info class="w-4 h-4" />
+              <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-input v-model="detailForm.area" placeholder="请输入面积"/>
               </el-form-item>
               <el-form-item label="房屋类型">
                 <el-select
@@ -1153,14 +1173,14 @@ onMounted(() => {
                   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-option label="厂房" value="厂房" />
-                  <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-option label="套间" value="套间"/>
+                  <el-option label="厂房" value="厂房"/>
+                  <el-option label="仓库" value="仓库"/>
                 </el-select>
               </el-form-item>
               <el-form-item label="房屋介绍" class="col-span-2">
@@ -1182,9 +1202,9 @@ onMounted(() => {
         <el-tab-pane label="设备管理" name="device">
           <template #label>
             <div class="flex items-center gap-2">
-              <Settings class="w-4 h-4" />
+              <Settings class="w-4 h-4"/>
               <span>设备管理</span>
-              <el-badge :value="deviceList.length" class="ml-1" />
+              <el-badge :value="deviceList.length" class="ml-1"/>
             </div>
           </template>
           <div class="p-4">
@@ -1199,10 +1219,10 @@ onMounted(() => {
             <!-- 设备列表 -->
             <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="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>
@@ -1242,7 +1262,7 @@ onMounted(() => {
 
               <!-- 空状态 -->
               <div v-if="deviceList.length === 0" class="text-center py-12">
-                <Package class="w-16 h-16 text-gray-400 mx-auto mb-4" />
+                <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>
@@ -1254,7 +1274,7 @@ onMounted(() => {
         <el-tab-pane v-if="showTenantAndContractTabs" label="租户信息" name="tenant">
           <template #label>
             <div class="flex items-center gap-2">
-              <Users class="w-4 h-4" />
+              <Users class="w-4 h-4"/>
               <span>租户信息</span>
             </div>
           </template>
@@ -1292,7 +1312,7 @@ onMounted(() => {
               </div>
             </div>
             <div v-else class="text-center py-12">
-              <Users class="w-16 h-16 text-gray-400 mx-auto mb-4" />
+              <Users class="w-16 h-16 text-gray-400 mx-auto mb-4"/>
               <p class="text-gray-500 text-lg">暂无租户信息</p>
             </div>
           </div>
@@ -1302,7 +1322,7 @@ onMounted(() => {
         <el-tab-pane v-if="showTenantAndContractTabs" label="合同信息" name="contract">
           <template #label>
             <div class="flex items-center gap-2">
-              <FileText class="w-4 h-4" />
+              <FileText class="w-4 h-4"/>
               <span>合同信息</span>
             </div>
           </template>
@@ -1334,7 +1354,7 @@ onMounted(() => {
                     <div class="flex items-center gap-3">
                       <span class="text-gray-600 font-medium w-24">押金:</span>
                       <span class="text-green-600 font-semibold"
-                        >¥{{ contractInfo.contractDeposit }}</span
+                      >¥{{ contractInfo.contractDeposit }}</span
                       >
                     </div>
                     <div class="flex items-center gap-3">
@@ -1354,7 +1374,7 @@ onMounted(() => {
                   <!-- 原合同 -->
                   <div class="flex items-center justify-between p-4 bg-white rounded-lg border">
                     <div class="flex items-center gap-3">
-                      <FileText class="w-6 h-6 text-blue-600" />
+                      <FileText class="w-6 h-6 text-blue-600"/>
                       <div>
                         <p class="font-medium text-gray-900">原合同文档</p>
                         <p class="text-sm text-gray-500">原始合同文件</p>
@@ -1389,7 +1409,7 @@ onMounted(() => {
                     class="flex items-center justify-between p-4 bg-white rounded-lg border"
                   >
                     <div class="flex items-center gap-3">
-                      <FileText class="w-6 h-6 text-green-600" />
+                      <FileText class="w-6 h-6 text-green-600"/>
                       <div>
                         <p class="font-medium text-gray-900">签订后合同</p>
                         <p class="text-sm text-gray-500">已签署的合同文件</p>
@@ -1421,7 +1441,7 @@ onMounted(() => {
                     class="flex items-center justify-center p-8 bg-white rounded-lg border border-dashed border-gray-300"
                   >
                     <div class="text-center">
-                      <FileText class="w-12 h-12 text-gray-400 mx-auto mb-2" />
+                      <FileText class="w-12 h-12 text-gray-400 mx-auto mb-2"/>
                       <p class="text-gray-500">签订后合同暂未上传</p>
                     </div>
                   </div>
@@ -1429,7 +1449,7 @@ onMounted(() => {
               </div>
             </div>
             <div v-else class="text-center py-12">
-              <FileText class="w-16 h-16 text-gray-400 mx-auto mb-4" />
+              <FileText class="w-16 h-16 text-gray-400 mx-auto mb-4"/>
               <p class="text-gray-500 text-lg">暂无合同信息</p>
             </div>
           </div>
@@ -1475,26 +1495,26 @@ onMounted(() => {
     >
       <el-form :model="deviceForm" label-width="100px">
         <el-form-item label="设备名称" required>
-          <el-input v-model="deviceForm.deviceName" placeholder="请输入设备名称" />
+          <el-input v-model="deviceForm.deviceName" placeholder="请输入设备名称"/>
         </el-form-item>
         <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-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="deviceForm.deviceBrand" placeholder="请输入设备品牌" />
+          <el-input v-model="deviceForm.deviceBrand" placeholder="请输入设备品牌"/>
         </el-form-item>
         <el-form-item label="设备型号">
-          <el-input v-model="deviceForm.deviceNumber" placeholder="请输入设备型号" />
+          <el-input v-model="deviceForm.deviceNumber" placeholder="请输入设备型号"/>
         </el-form-item>
         <el-form-item label="设备价格">
-          <el-input v-model="deviceForm.devicePrice" placeholder="请输入设备价格" />
+          <el-input v-model="deviceForm.devicePrice" placeholder="请输入设备价格"/>
         </el-form-item>
       </el-form>
       <template #footer>
@@ -1539,7 +1559,7 @@ onMounted(() => {
             min-width="150"
             show-overflow-tooltip
           />
-          <el-table-column prop="maintenancePerson" label="维护人员" width="100" />
+          <el-table-column prop="maintenancePerson" label="维护人员" width="100"/>
           <el-table-column prop="maintenanceCost" label="维护费用" width="100">
             <template #default="{ row }">
               <span class="text-red-600 font-medium">
@@ -1580,7 +1600,7 @@ onMounted(() => {
 
         <!-- 空状态 -->
         <div v-if="maintenanceList.length === 0" class="text-center py-12">
-          <Wrench class="w-16 h-16 text-gray-400 mx-auto mb-4" />
+          <Wrench 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>
@@ -1618,17 +1638,17 @@ onMounted(() => {
             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-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="maintenanceForm.maintenancePerson" placeholder="请输入维护人员" />
+          <el-input v-model="maintenanceForm.maintenancePerson" placeholder="请输入维护人员"/>
         </el-form-item>
 
         <el-form-item label="维护费用">
@@ -1647,10 +1667,10 @@ onMounted(() => {
             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-option label="已完成" value="已完成"/>
+            <el-option label="已取消" value="已取消"/>
           </el-select>
         </el-form-item>
 

+ 1 - 1
vite.config.ts

@@ -32,8 +32,8 @@ export default defineConfig({
     proxy: {
       '/api': {
         // target: 'http://192.168.110.93:8801',
+        // docker restart tt
         target: 'http://192.168.110.235:8801',
-
         changeOrigin: true,
         rewrite: (path) => path.replace(/^\/api/, ''),
       },