Quellcode durchsuchen

添加瑞都报表

Zimo vor 2 Tagen
Ursprung
Commit
084610b9de

+ 33 - 0
api/ruiduReport.js

@@ -0,0 +1,33 @@
+import { request } from "@/utils/request";
+
+export function getRuiduReportPage(params) {
+  return request({
+    url: "/pms/iot-rd-daily-report/page",
+    method: "get",
+    params,
+  });
+}
+
+export function getRuiduReportTeamPage(params) {
+  return request({
+    url: "/pms/iot-rd-daily-report/teamReports",
+    method: "get",
+    params,
+  });
+}
+
+export function getRuiduReportWellPage(params) {
+  return request({
+    url: "/pms/iot-rd-daily-report/wellReports",
+    method: "get",
+    params,
+  });
+}
+
+export function getTaskWellNames(params) {
+  return request({
+    url: "/rq/iot-project-task/taskTreeWellNames",
+    method: "get",
+    params,
+  });
+}

+ 25 - 13
locale/en.json

@@ -93,11 +93,13 @@
 	"home.receiveMaintenanceWorkOrderAndSubmit": "Receive maintenance work orders and submit",
 	"home.equipmentMaintenance": "Equipment maintenance",
 	"home.fillMaintenanceWorkOrder": "Fill in maintenance work orders",
-	"home.inspectionWorkOrder": "Inspection work order",
-	"home.receiveInspectionWorkOrderAndSubmit": "Receive inspection work orders and submit",
-	"home.faultReporting": "Fault reporting",
-	"home.fillAndReportFaultWorkOrder": "Fill in and report fault work orders",
-	"home.inventoryQuery": "Inventory query",
+	"home.inspectionWorkOrder": "Inspection work order",
+	"home.receiveInspectionWorkOrderAndSubmit": "Receive inspection work orders and submit",
+	"home.faultReporting": "Fault reporting",
+	"home.fillAndReportFaultWorkOrder": "Fill in and report fault work orders",
+	"home.ruiDuReport": "RuiDu report",
+	"home.ruiDuReportTip": "View daily report data",
+	"home.inventoryQuery": "Inventory query",
 	"home.clickToQueryInventoryData": "Click to query inventory data",
 	"home.equipmentLedger": "Equipment ledger",
 	"home.viewEquipmentLedger": "View equipment ledger",
@@ -129,13 +131,23 @@
 	"user.passwordError1": "New password is the same as old password",
 	"user.passwordError2": "New password does not match confirm password",
 	"operationRecordFilling.responsiblePerson": "Responsible person",
-	"operationRecordFilling.workOrderName": "Work order name",
-	"operationRecordFilling.belongToTeam": "Belonging team",
-	"operationRecordFilling.totalRunningTime": "Total running time",
-	"operationRecordFilling.plcNotice": "The following values are from PLC, please modify if inconsistent",
-	"operationRecordFilling.workOrderDevice": "Work order equipment",
-	"operationRecordFilling.fillContentCannotGreaterThanThreshold": "Filled content cannot be greater than",
-	"workOrder.addDevice": "Add equipment",
+	"operationRecordFilling.workOrderName": "Work order name",
+	"operationRecordFilling.belongToTeam": "Belonging team",
+	"operationRecordFilling.totalRunningTime": "Total running time",
+	"operationRecordFilling.plcNotice": "The following values are from PLC, please modify if inconsistent",
+	"operationRecordFilling.workOrderDevice": "Work order equipment",
+	"operationRecordFilling.fillContentCannotGreaterThanThreshold": "Filled content cannot be greater than",
+	"ruiDuReport.selectTitle": "Select RuiDu report",
+	"ruiDuReport.dailyDetail": "Daily detail",
+	"ruiDuReport.dailyTeamStatistic": "Daily single-team statistics",
+	"ruiDuReport.filterAction": "Filter",
+	"ruiDuReport.filterTitle": "Filter conditions",
+	"ruiDuReport.searchKey": "Search key",
+	"ruiDuReport.searchKeyPlaceholder": "Please enter search key",
+	"ruiDuReport.dept": "Department",
+	"ruiDuReport.constructionBrief": "Brief",
+	"ruiDuReport.createTime": "Create time",
+	"workOrder.addDevice": "Add equipment",
 	"workOrder.addMaterial": "Add material",
 	"workOrder.selectMaterial": "Select material",
 	"workOrder.materialDetails": "Material details",
@@ -482,4 +494,4 @@
 	"overtime.type2": "Maintenance work order",
 	"overtime.type3": "Upkeep work order",
 	"overtime.type4": "Inspection work order"
-}
+}

+ 22 - 10
locale/ja.json

@@ -93,11 +93,13 @@
 	"home.receiveMaintenanceWorkOrderAndSubmit": "保養作業指示書を受け取り、提出",
 	"home.equipmentMaintenance": "機器修理",
 	"home.fillMaintenanceWorkOrder": "修理作業指示書を入力",
-	"home.inspectionWorkOrder": "検査作業指示書",
-	"home.receiveInspectionWorkOrderAndSubmit": "検査作業指示書を受け取り、提出",
-	"home.faultReporting": "故障報告",
-	"home.fillAndReportFaultWorkOrder": "故障作業指示書を入力して報告",
-	"home.inventoryQuery": "在庫照会",
+	"home.inspectionWorkOrder": "検査作業指示書",
+	"home.receiveInspectionWorkOrderAndSubmit": "検査作業指示書を受け取り、提出",
+	"home.faultReporting": "故障報告",
+	"home.fillAndReportFaultWorkOrder": "故障作業指示書を入力して報告",
+	"home.ruiDuReport": "瑞都レポート",
+	"home.ruiDuReportTip": "日報データを表示",
+	"home.inventoryQuery": "在庫照会",
 	"home.clickToQueryInventoryData": "クリックして在庫データを照会",
 	"home.equipmentLedger": "機器台帳",
 	"home.viewEquipmentLedger": "機器台帳を表示",
@@ -132,10 +134,20 @@
 	"operationRecordFilling.workOrderName": "作業指示書名",
 	"operationRecordFilling.belongToTeam": "所属チーム",
 	"operationRecordFilling.totalRunningTime": "累計運行時間",
-	"operationRecordFilling.plcNotice": "以下の数値はPLCから取得したものです。不一致がある場合は修正してください",
-	"operationRecordFilling.workOrderDevice": "作業指示書機器",
-	"operationRecordFilling.fillContentCannotGreaterThanThreshold": "入力内容は次の値を超えることができません",
-	"workOrder.addDevice": "機器を追加",
+	"operationRecordFilling.plcNotice": "以下の数値はPLCから取得したものです。不一致がある場合は修正してください",
+	"operationRecordFilling.workOrderDevice": "作業指示書機器",
+	"operationRecordFilling.fillContentCannotGreaterThanThreshold": "入力内容は次の値を超えることができません",
+	"ruiDuReport.selectTitle": "瑞都レポートを選択",
+	"ruiDuReport.dailyDetail": "日報詳細",
+	"ruiDuReport.dailyTeamStatistic": "日報単一チーム統計",
+	"ruiDuReport.filterAction": "絞り込み",
+	"ruiDuReport.filterTitle": "フィルター条件",
+	"ruiDuReport.searchKey": "検索条件",
+	"ruiDuReport.searchKeyPlaceholder": "検索条件を入力してください",
+	"ruiDuReport.dept": "部門",
+	"ruiDuReport.constructionBrief": "施工概要",
+	"ruiDuReport.createTime": "作成時間",
+	"workOrder.addDevice": "機器を追加",
 	"workOrder.addMaterial": "資材を追加",
 	"workOrder.selectMaterial": "資材を選択",
 	"workOrder.materialDetails": "資材詳細",
@@ -482,4 +494,4 @@
 	"overtime.type2": "修理作業指示書",
 	"overtime.type3": "保養作業指示書",
 	"overtime.type4": "検査作業指示書"
-}
+}

+ 22 - 10
locale/ru.json

@@ -93,11 +93,13 @@
 	"home.receiveMaintenanceWorkOrderAndSubmit": "Получить рабочий заказ на обслуживание и отправить",
 	"home.equipmentMaintenance": "Ремонт оборудования",
 	"home.fillMaintenanceWorkOrder": "Заполнение рабочего заказа на ремонт",
-	"home.inspectionWorkOrder": "Рабочий заказ на инспекцию",
-	"home.receiveInspectionWorkOrderAndSubmit": "Получить рабочий заказ на инспекцию и отправить",
-	"home.faultReporting": "Сообщение о неисправности",
-	"home.fillAndReportFaultWorkOrder": "Заполнение и отправка рабочего заказа на неисправность",
-	"home.inventoryQuery": "Запрос инвентаря",
+	"home.inspectionWorkOrder": "Рабочий заказ на инспекцию",
+	"home.receiveInspectionWorkOrderAndSubmit": "Получить рабочий заказ на инспекцию и отправить",
+	"home.faultReporting": "Сообщение о неисправности",
+	"home.fillAndReportFaultWorkOrder": "Заполнение и отправка рабочего заказа на неисправность",
+	"home.ruiDuReport": "Отчет RuiDu",
+	"home.ruiDuReportTip": "Просмотр данных ежедневного отчета",
+	"home.inventoryQuery": "Запрос инвентаря",
 	"home.clickToQueryInventoryData": "Нажмите, чтобы запросить данные инвентаря",
 	"home.equipmentLedger": "Книга учета оборудования",
 	"home.viewEquipmentLedger": "Просмотр книги учета оборудования",
@@ -132,10 +134,20 @@
 	"operationRecordFilling.workOrderName": "Название рабочего заказа",
 	"operationRecordFilling.belongToTeam": "Принадлежащая команда",
 	"operationRecordFilling.totalRunningTime": "Общее время работы",
-	"operationRecordFilling.plcNotice": "Следующие значения получены из PLC, при несоответствии исправьте",
-	"operationRecordFilling.workOrderDevice": "Оборудование рабочего заказа",
-	"operationRecordFilling.fillContentCannotGreaterThanThreshold": "Заполненное содержимое не может превышать",
-	"workOrder.addDevice": "Добавить оборудование",
+	"operationRecordFilling.plcNotice": "Следующие значения получены из PLC, при несоответствии исправьте",
+	"operationRecordFilling.workOrderDevice": "Оборудование рабочего заказа",
+	"operationRecordFilling.fillContentCannotGreaterThanThreshold": "Заполненное содержимое не может превышать",
+	"ruiDuReport.selectTitle": "Выберите отчет RuiDu",
+	"ruiDuReport.dailyDetail": "Детали ежедневного отчета",
+	"ruiDuReport.dailyTeamStatistic": "Статистика ежедневного отчета по бригаде",
+	"ruiDuReport.filterAction": "Фильтр",
+	"ruiDuReport.filterTitle": "Фильтры",
+	"ruiDuReport.searchKey": "Ключ поиска",
+	"ruiDuReport.searchKeyPlaceholder": "Введите ключ поиска",
+	"ruiDuReport.dept": "Отдел",
+	"ruiDuReport.constructionBrief": "Сводка",
+	"ruiDuReport.createTime": "Время создания",
+	"workOrder.addDevice": "Добавить оборудование",
 	"workOrder.addMaterial": "Добавить материал",
 	"workOrder.selectMaterial": "Выбрать материал",
 	"workOrder.materialDetails": "Детали материала",
@@ -482,4 +494,4 @@
 	"overtime.type2": "Ремонтный рабочий заказ",
 	"overtime.type3": "Рабочий заказ на обслуживание",
 	"overtime.type4": "Рабочий заказ на инспекцию"
-}
+}

+ 18 - 5
locale/zh-Hans.json

@@ -125,9 +125,11 @@
   "home.receiveInspectionWorkOrderAndSubmit": "接收巡检工单并提报",
   "home.faultReporting": "故障上报",
   "home.fillAndReportFaultWorkOrder": "故障工单的填报及上报故障问题",
-  "home.dailyReportRuiDu": "瑞都日报",
-  "home.dailyReportRuiDuTip": "填写日报",
-  "home.dailyReportRuiHen": "瑞恒日报",
+  "home.dailyReportRuiDu": "瑞都日报",
+  "home.dailyReportRuiDuTip": "填写日报",
+  "home.ruiDuReport": "瑞都报表",
+  "home.ruiDuReportTip": "查看日报报表",
+  "home.dailyReportRuiHen": "瑞恒日报",
   "home.dailyReportRuiHenTip": "填写日报",
   "home.dailyReportRuiHenTaskTip": "分配任务",
   "home.dailyReportRuiHenApproval": "审批日报",
@@ -181,8 +183,19 @@
   "operationRecordFilling.totalDeviceCount": "应填设备数",
   "operationRecordFilling.filledDeviceCount": "已填设备数",
   "operationRecordFilling.unfilledDeviceCount": "未填设备数",
-  "operationRecordFilling.PleaseLoadAllItems": "请加载所有填报项后再提交",
-  // --------------------------------------- 状态相关 ----------------------------------------
+  "operationRecordFilling.PleaseLoadAllItems": "请加载所有填报项后再提交",
+  // --------------------------------------- 瑞都报表 ----------------------------------------
+  "ruiDuReport.selectTitle": "选择瑞都报表",
+  "ruiDuReport.dailyDetail": "日报详情",
+  "ruiDuReport.dailyTeamStatistic": "日报单井队统计",
+  "ruiDuReport.filterAction": "筛选",
+  "ruiDuReport.filterTitle": "筛选条件",
+  "ruiDuReport.searchKey": "查询条件",
+  "ruiDuReport.searchKeyPlaceholder": "请输入查询条件",
+  "ruiDuReport.dept": "部门",
+  "ruiDuReport.constructionBrief": "施工简报",
+  "ruiDuReport.createTime": "创建时间",
+  // --------------------------------------- 状态相关 ----------------------------------------
   "status.enable": "启用",
   "status.disable": "停用",
   "status.unaudited": "未审核",

+ 21 - 9
locale/zh-Hant.json

@@ -79,11 +79,13 @@
   "home.receiveMaintenanceWorkOrderAndSubmit": "接收保养工单并提报",
   "home.equipmentMaintenance": "设备维修",
   "home.fillMaintenanceWorkOrder": "填报维修工单",
-  "home.inspectionWorkOrder": "巡检工单",
-  "home.receiveInspectionWorkOrderAndSubmit": "接收巡检工单并提报",
-  "home.faultReporting": "故障上报",
-  "home.fillAndReportFaultWorkOrder": "故障工单的填报及上报故障问题",
-  "home.inventoryQuery": "库存查询",
+  "home.inspectionWorkOrder": "巡检工单",
+  "home.receiveInspectionWorkOrderAndSubmit": "接收巡检工单并提报",
+  "home.faultReporting": "故障上报",
+  "home.fillAndReportFaultWorkOrder": "故障工单的填报及上报故障问题",
+  "home.ruiDuReport": "瑞都报表",
+  "home.ruiDuReportTip": "查看日报报表",
+  "home.inventoryQuery": "库存查询",
   "home.clickToQueryInventoryData": "点击查询库存数据",
   "home.equipmentLedger": "设备台账",
   "home.viewEquipmentLedger": "查看设备台账",
@@ -116,10 +118,20 @@
 
   // ---------------------运行记录--------------------------
   "operationRecordFilling.responsiblePerson": "负责人",
-  "operationRecordFilling.workOrderName": "工单名称",
-  "operationRecordFilling.belongToTeam": "所属队伍",
-  "operationRecordFilling.totalRunningTime": "累计运行时间",
-  // ----------------------------------------------------
+  "operationRecordFilling.workOrderName": "工单名称",
+  "operationRecordFilling.belongToTeam": "所属队伍",
+  "operationRecordFilling.totalRunningTime": "累计运行时间",
+  "ruiDuReport.selectTitle": "选择瑞都报表",
+  "ruiDuReport.dailyDetail": "日报详情",
+  "ruiDuReport.dailyTeamStatistic": "日报单井队统计",
+  "ruiDuReport.filterAction": "筛选",
+  "ruiDuReport.filterTitle": "筛选条件",
+  "ruiDuReport.searchKey": "查询条件",
+  "ruiDuReport.searchKeyPlaceholder": "请输入查询条件",
+  "ruiDuReport.dept": "部门",
+  "ruiDuReport.constructionBrief": "施工简报",
+  "ruiDuReport.createTime": "创建时间",
+  // ----------------------------------------------------
   "workOrder.addDevice": "新增设备",
   "workOrder.addMaterial": "新增物料",
   "workOrder.selectMaterial": "选择物料",

+ 25 - 11
pages.json

@@ -294,17 +294,31 @@
         "navigationBarTitleText": "%ruiDu.detailTitle%"
       }
     },
-    {
-      // 瑞都日报-编辑
-      "path": "pages/ruiDu/edit",
-      "style": {
-        "navigationBarTitleText": "%ruiDu.editTitle%"
-      }
-    },
-    {
-      // 库存查询
-      "path": "pages/inventory/index",
-      "style": {
+    {
+      // 瑞都日报-编辑
+      "path": "pages/ruiDu/edit",
+      "style": {
+        "navigationBarTitleText": "%ruiDu.editTitle%"
+      }
+    },
+    {
+      // 瑞都报表-日报详情
+      "path": "pages/ruiDuReport/daily-detail",
+      "style": {
+        "navigationBarTitleText": "%ruiDuReport.dailyDetail%"
+      }
+    },
+    {
+      // 瑞都报表-日报单井队统计
+      "path": "pages/ruiDuReport/daily-team-statistic",
+      "style": {
+        "navigationBarTitleText": "%ruiDuReport.dailyTeamStatistic%"
+      }
+    },
+    {
+      // 库存查询
+      "path": "pages/inventory/index",
+      "style": {
         "navigationBarTitleText": "%inventory.title%"
       }
     },

+ 100 - 23
pages/home/index.vue

@@ -149,10 +149,10 @@
       </uni-row>
       <view class="card">
         <!-- 瑞都日报 -->
-        <view
-          class="card-cell flex-row align-center justify-between"
-          @click="navigatorTo('/pages/ruiDu/index')"
-          v-if="isShowRuiduDaily">
+        <view
+          class="card-cell flex-row align-center justify-between"
+          @click="navigatorTo('/pages/ruiDu/index')"
+          v-if="isShowRuiduDaily">
           <image src="/static/home/ribao.svg" mode="aspectFill"></image>
           <view class="cell-con flex-row align-center justify-between">
             <view class="cell-text flex-row align-center justify-start">
@@ -163,13 +163,30 @@
                 {{ $t("home.dailyReportRuiDuTip") }}
               </view>
             </view>
-            <uni-icons type="right" :color="'#CACCCF'" size="15" />
-          </view>
-        </view>
-        <view
-          class="card-cell flex-row align-center justify-between"
-          @click="navigatorTo('/pages/ruihen-task/index')"
-          v-if="rhTaskFlag">
+            <uni-icons type="right" :color="'#CACCCF'" size="15" />
+          </view>
+        </view>
+        <!-- 瑞都报表 -->
+        <view
+          class="card-cell flex-row align-center justify-between"
+          @click="openRuiDuReportPopup">
+          <image src="/static/home/ribao.svg" mode="aspectFill"></image>
+          <view class="cell-con flex-row align-center justify-between">
+            <view class="cell-text flex-row align-center justify-start">
+              <view class="title">
+                {{ $t("home.ruiDuReport") }}
+              </view>
+              <view class="subtitle">
+                {{ $t("home.ruiDuReportTip") }}
+              </view>
+            </view>
+            <uni-icons type="right" :color="'#CACCCF'" size="15" />
+          </view>
+        </view>
+        <view
+          class="card-cell flex-row align-center justify-between"
+          @click="navigatorTo('/pages/ruihen-task/index')"
+          v-if="rhTaskFlag">
           <image src="/static/home/ribao.svg" mode="aspectFill"></image>
           <view class="cell-con flex-row align-center justify-between">
             <view class="cell-text flex-row align-center justify-start">
@@ -390,11 +407,27 @@
           </view>
         </view>
       </view>
-    </view>
-    <!-- 升级提示 -->
-    <upgrade />
-  </view>
-</template>
+    </view>
+    <!-- 升级提示 -->
+    <upgrade />
+    <uni-popup ref="ruiDuReportPopup" type="bottom" background-color="#fff" border-radius="10px 10px 0 0">
+      <view class="report-popup">
+        <view class="report-popup-header">
+          {{ $t("ruiDuReport.selectTitle") }}
+        </view>
+        <view class="report-popup-item" @click="navigateToRuiDuReport('/pages/ruiDuReport/daily-detail')">
+          {{ $t("ruiDuReport.dailyDetail") }}
+        </view>
+        <view class="report-popup-item" @click="navigateToRuiDuReport('/pages/ruiDuReport/daily-team-statistic')">
+          {{ $t("ruiDuReport.dailyTeamStatistic") }}
+        </view>
+        <view class="report-popup-cancel" @click="closeRuiDuReportPopup">
+          {{ $t("operation.cancel") }}
+        </view>
+      </view>
+    </uni-popup>
+  </view>
+</template>
 
 <script setup>
 import { onShow } from "@dcloudio/uni-app";
@@ -408,11 +441,23 @@ import { useDeviceStore } from "@/store/modules/device";
 import { messageNavigate } from "@/utils/navigate";
 import Upgrade from "@/components/upgrade.vue";
 
-const navigatorTo = (url) => {
-  uni.navigateTo({
-    url: url,
-  });
-};
+const navigatorTo = (url) => {
+  uni.navigateTo({
+    url: url,
+  });
+};
+
+const ruiDuReportPopup = ref(null);
+const openRuiDuReportPopup = () => {
+  ruiDuReportPopup.value?.open();
+};
+const closeRuiDuReportPopup = () => {
+  ruiDuReportPopup.value?.close();
+};
+const navigateToRuiDuReport = (url) => {
+  closeRuiDuReportPopup();
+  uni.navigateTo({ url });
+};
 
 const messageCount = ref(0);
 onMounted(async () => {
@@ -514,13 +559,45 @@ onShow(async () => {
 </script>
 
 <style lang="scss" scoped>
-.home {
+.home {
   width: 100%;
   height: 100%;
   position: relative;
   box-sizing: border-box;
   overflow: hidden;
-}
+}
+
+.report-popup {
+  padding: 8px 16px calc(61px + env(safe-area-inset-bottom));
+  background: #fff;
+  box-sizing: border-box;
+}
+
+.report-popup-header {
+  height: 44px;
+  line-height: 44px;
+  text-align: center;
+  font-weight: 600;
+  font-size: 16px;
+  color: #333;
+  border-bottom: 1px solid #f0f0f0;
+}
+
+.report-popup-item,
+.report-popup-cancel {
+  height: 48px;
+  line-height: 48px;
+  text-align: center;
+  font-size: 15px;
+  color: #333;
+  border-bottom: 1px solid #f5f5f5;
+}
+
+.report-popup-cancel {
+  margin-top: 8px;
+  color: #666;
+  border-bottom: none;
+}
 
 .row-full {
   width: 100%;

+ 525 - 0
pages/ruiDuReport/daily-detail.vue

@@ -0,0 +1,525 @@
+<template>
+  <view class="page report-page">
+    <z-paging
+      ref="paging"
+      v-model="dataList"
+      class="report-paging"
+      :default-page-size="10"
+      @query="queryList">
+      <view class="report-list">
+        <view class="report-card" v-for="item in dataList" :key="item.id">
+          <view class="card-header">
+            <view class="card-date">{{ formatDate(item.createTime) }}</view>
+            <view class="status-tag">{{
+              rdStatusDict[item.rdStatus] || item.rdStatus || "--"
+            }}</view>
+          </view>
+          <view class="card-body">
+            <view class="field-row">
+              <text class="field-label">{{
+                $t("ruiDu.constructionTeam")
+              }}</text>
+              <text class="field-value">{{ item.deptName || "--" }}</text>
+            </view>
+            <view class="field-row">
+              <text class="field-label">{{ $t("ruiDu.task") }}</text>
+              <text class="field-value">{{ item.taskName || "--" }}</text>
+            </view>
+            <view class="field-row brief-row">
+              <text class="field-label">{{
+                $t("ruiDuReport.constructionBrief")
+              }}</text>
+              <UniTooltip
+                :content="item.constructionBrief || '--'"
+                placement="top">
+                <text class="field-value brief-text">{{
+                  item.constructionBrief || "--"
+                }}</text>
+              </UniTooltip>
+            </view>
+          </view>
+          <view class="card-footer">
+            <button
+              class="detail-btn"
+              type="primary"
+              plain
+              @click="viewDetail(item)">
+              {{ $t("operation.view") }}
+            </button>
+          </view>
+        </view>
+      </view>
+    </z-paging>
+
+    <UniFab
+      :pattern="fabPattern"
+      horizontal="right"
+      vertical="bottom"
+      direction="horizontal"
+      :popMenu="false"
+      @fabClick="openFilterPopup" />
+
+    <uni-popup
+      ref="filterPopup"
+      type="bottom"
+      background-color="#fff"
+      border-radius="10px 10px 0 0">
+      <view class="filter-popup">
+        <view class="filter-header">
+          <text class="filter-action" @click="closeFilterPopup">
+            {{ $t("operation.cancel") }}
+          </text>
+          <text class="filter-title">{{ $t("ruiDuReport.filterTitle") }}</text>
+          <text class="filter-action primary" @click="applyFilter">
+            {{ $t("operation.confirm") }}
+          </text>
+        </view>
+
+        <view class="filter-body">
+          <view class="filter-item">
+            <view class="filter-label">{{ $t("ruiDuReport.searchKey") }}</view>
+            <uni-easyinput
+              v-model="filterForm.searchKey"
+              :inputBorder="false"
+              :styles="inputStyles"
+              :placeholder="$t('ruiDuReport.searchKeyPlaceholder')" />
+          </view>
+
+          <view class="filter-item">
+            <view class="filter-label">{{ $t("ruiDuReport.createTime") }}</view>
+            <uni-datetime-picker
+              v-model="filterForm.createTime"
+              type="datetimerange"
+              return-type="string"
+              :border="false"
+              :placeholder="$t('operation.PleaseSelect')" />
+          </view>
+
+          <view class="filter-item dept-item">
+            <view class="filter-label">{{ $t("ruiDuReport.dept") }}</view>
+            <view class="dept-selected">
+              {{ selectedDeptName || $t("operation.PleaseSelect") }}
+            </view>
+            <view class="tree">
+              <DaTree
+                :data="treeData"
+                labelField="name"
+                valueField="id"
+                disabledField="disabled"
+                defaultExpandAll
+                checkedDisabled
+                :defaultCheckedKeys="filterForm.deptId"
+                @change="handleTreeChange" />
+            </view>
+          </view>
+        </view>
+
+        <view class="filter-footer">
+          <button class="filter-button reset" @click="resetFilter">
+            {{ $t("inventory.search.reset") }}
+          </button>
+          <button class="filter-button" type="primary" @click="applyFilter">
+            {{ $t("operation.search") }}
+          </button>
+        </view>
+      </view>
+    </uni-popup>
+  </view>
+</template>
+
+<script setup>
+import { computed, onMounted, reactive, ref } from "vue";
+import dayjs from "dayjs";
+import DaTree from "@/components/da-tree/index.vue";
+import UniFab from "@/uni_modules/uni-fab/components/uni-fab/uni-fab.vue";
+import UniTooltip from "@/uni_modules/uni-tooltip/components/uni-tooltip/uni-tooltip.vue";
+import { getRuiduReportPage } from "@/api/ruiduReport";
+import { specifiedSimpleDepts } from "@/api";
+import { getDeptId } from "@/utils/auth";
+import { useDataDictStore } from "@/store/modules/dataDict";
+
+const paging = ref(null);
+const filterPopup = ref(null);
+const dataList = ref([]);
+const deptOptions = ref([]);
+const treeData = ref([]);
+const dictStore = useDataDictStore();
+const rdStatusDict = reactive({});
+
+const inputStyles = reactive({
+  backgroundColor: "#f7f8fa",
+  color: "#333",
+});
+
+const fabPattern = reactive({
+  color: "#fff",
+  backgroundColor: "#fff",
+  selectedColor: "#fff",
+  buttonColor: "#004098",
+  iconColor: "#fff",
+  icon: "search",
+});
+
+const filterForm = reactive({
+  searchKey: "",
+  deptId: "",
+  createTime: [],
+});
+
+const selectedDeptName = computed(() => {
+  const current = deptOptions.value.find(
+    (item) => String(item.value) === String(filterForm.deptId)
+  );
+  return current?.text || "";
+});
+
+const handleTree = (
+  data,
+  id = "id",
+  parentId = "parentId",
+  children = "children"
+) => {
+  if (!Array.isArray(data)) return [];
+
+  const childrenListMap = {};
+  const nodeIds = {};
+  const tree = [];
+
+  for (const item of data) {
+    const itemParentId = item[parentId];
+    if (childrenListMap[itemParentId] == null) {
+      childrenListMap[itemParentId] = [];
+    }
+    nodeIds[item[id]] = item;
+    childrenListMap[itemParentId].push(item);
+  }
+
+  for (const item of data) {
+    if (nodeIds[item[parentId]] == null) {
+      tree.push(item);
+    }
+  }
+
+  const adaptToChildrenList = (node) => {
+    if (childrenListMap[node[id]] != null) {
+      node[children] = childrenListMap[node[id]];
+    }
+    if (node[children]) {
+      node[children].forEach(adaptToChildrenList);
+    }
+  };
+
+  tree.forEach(adaptToChildrenList);
+  return tree;
+};
+
+const sortDeptTree = (nodes) => {
+  if (!Array.isArray(nodes)) return [];
+
+  return [...nodes]
+    .sort((a, b) => (a.sort ?? 999999) - (b.sort ?? 999999))
+    .map((node) => ({
+      ...node,
+      children: sortDeptTree(node.children),
+    }));
+};
+
+const loadDeptOptions = async () => {
+  try {
+    const response = await specifiedSimpleDepts(getDeptId());
+    const list = response?.data || [];
+    deptOptions.value = list.map((item) => ({
+      text: item.name,
+      value: item.id,
+    }));
+    treeData.value = sortDeptTree(handleTree(list));
+  } catch (error) {
+    treeData.value = [];
+    deptOptions.value = [];
+  }
+};
+
+const queryList = (pageNo, pageSize) => {
+  getRuiduReportPage({
+    pageNo,
+    pageSize: 10,
+    searchKey: filterForm.searchKey,
+    deptId: filterForm.deptId,
+    createTime: filterForm.createTime,
+  })
+    .then((res) => {
+      paging.value?.complete(res.data?.list || []);
+    })
+    .catch(() => {
+      paging.value?.complete(false);
+    });
+};
+
+const formatDate = (time) => {
+  return time ? dayjs(time).format("YYYY-MM-DD") : "--";
+};
+
+const loadRdStatusDict = async () => {
+  if (dictStore.dataDict.length <= 0) {
+    await dictStore.loadDataDictList();
+  }
+  dictStore.getStrDictOptions("rdStatus").forEach((item) => {
+    rdStatusDict[item.value] = item.label;
+  });
+};
+
+const openFilterPopup = () => {
+  filterPopup.value?.open();
+};
+
+const closeFilterPopup = () => {
+  filterPopup.value?.close();
+};
+
+const handleTreeChange = (value) => {
+  filterForm.deptId = value;
+};
+
+const applyFilter = () => {
+  closeFilterPopup();
+  paging.value?.reload();
+};
+
+const viewDetail = (item) => {
+  if (item.projectId === null && item.taskId === null) {
+    uni.navigateTo({
+      url: "/pages/ruiDu/create?id=" + item.id + "&isview=detail",
+    });
+    return;
+  }
+  uni.navigateTo({
+    url: "/pages/ruiDu/detail?id=" + item.id,
+  });
+};
+
+const resetFilter = () => {
+  filterForm.searchKey = "";
+  filterForm.deptId = "";
+  filterForm.createTime = [];
+};
+
+onMounted(() => {
+  loadDeptOptions();
+  loadRdStatusDict();
+});
+</script>
+
+<style lang="scss" scoped>
+.report-page {
+  padding: 10px !important;
+}
+
+.report-paging {
+  height: 100%;
+}
+
+.report-list {
+  padding: 8px 6px;
+  box-sizing: border-box;
+}
+
+.report-card {
+  position: relative;
+  margin-bottom: 14px;
+  overflow: hidden;
+  background: #ffffff;
+  border: 1px solid rgba(0, 64, 152, 0.08);
+  border-radius: 8px;
+  box-shadow: 0 6px 18px rgba(35, 54, 79, 0.08);
+}
+
+.card-header {
+  min-height: 52px;
+  padding: 14px 16px 10px;
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  box-sizing: border-box;
+  border-bottom: 1px solid #edf1f7;
+}
+
+.card-date {
+  color: #000000;
+  font-weight: 700;
+  font-size: 18px;
+  line-height: 24px;
+}
+
+.status-tag {
+  max-width: 118px;
+  height: 24px;
+  line-height: 24px;
+  padding: 0 9px;
+  border-radius: 12px;
+  background: #004098;
+  color: #ffffff;
+  font-size: 12px;
+  font-weight: 600;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+  box-sizing: border-box;
+}
+
+.card-body {
+  padding: 12px 16px 4px;
+  box-sizing: border-box;
+}
+
+.field-row {
+  display: flex;
+  align-items: flex-start;
+  min-height: 34px;
+  font-size: 14px;
+}
+
+.field-label {
+  width: 82px;
+  flex-shrink: 0;
+  color: #000000;
+  font-weight: 600;
+  font-size: 13px;
+  line-height: 22px;
+}
+
+.field-value {
+  min-width: 0;
+  flex: 1;
+  color: #233044;
+  font-weight: 500;
+  font-size: 14px;
+  line-height: 22px;
+}
+
+.brief-row {
+  padding-top: 4px;
+}
+
+.brief-row :deep(.uni-tooltip) {
+  min-width: 0;
+  flex: 1;
+  display: block;
+}
+
+.brief-text {
+  display: block;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+}
+
+.card-footer {
+  padding: 2px 16px 14px;
+  display: flex;
+  justify-content: flex-end;
+}
+
+.detail-btn {
+  min-width: 72px;
+  height: 30px;
+  line-height: 28px;
+  margin: 0;
+  padding: 0 14px;
+  border-radius: 15px;
+  font-size: 13px;
+}
+
+.filter-popup {
+  height: 86vh;
+  max-height: 86vh;
+  display: flex;
+  flex-direction: column;
+  background: #fff;
+  padding-bottom: env(safe-area-inset-bottom);
+}
+
+.filter-header {
+  height: 48px;
+  padding: 0 16px;
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  border-bottom: 1px solid #f0f0f0;
+  box-sizing: border-box;
+}
+
+.filter-title {
+  font-weight: 600;
+  color: #333;
+  font-size: 16px;
+}
+
+.filter-action {
+  min-width: 48px;
+  color: #666;
+  font-size: 14px;
+
+  &.primary {
+    color: #004098;
+    text-align: right;
+  }
+}
+
+.filter-body {
+  flex: 1;
+  overflow-y: auto;
+  padding: 12px 16px;
+  box-sizing: border-box;
+}
+
+.filter-item {
+  margin-bottom: 14px;
+}
+
+.filter-label {
+  margin-bottom: 8px;
+  color: #333;
+  font-weight: 500;
+  font-size: 14px;
+}
+
+.dept-selected {
+  min-height: 36px;
+  line-height: 36px;
+  padding: 0 10px;
+  margin-bottom: 8px;
+  background: #f7f8fa;
+  color: #666;
+  border-radius: 4px;
+  box-sizing: border-box;
+}
+
+.tree {
+  height: 420px;
+  overflow: hidden;
+  border: 1px solid #f0f0f0;
+  border-radius: 4px;
+}
+
+.filter-footer {
+  display: flex;
+  gap: 10px;
+  padding: 10px 16px 14px;
+  border-top: 1px solid #f0f0f0;
+  box-sizing: border-box;
+}
+
+.filter-button {
+  flex: 1;
+  height: 38px;
+  line-height: 38px;
+  font-size: 14px;
+  margin: 0;
+
+  &.reset {
+    color: #004098;
+    background: #fff;
+    border: 1px solid #004098;
+  }
+}
+</style>

+ 794 - 0
pages/ruiDuReport/daily-team-statistic.vue

@@ -0,0 +1,794 @@
+<template>
+  <view class="page report-page">
+    <view class="mode-tabs">
+      <view
+        v-for="item in modeOptions"
+        :key="item.value"
+        class="mode-tab"
+        :class="{ active: currentMode === item.value }"
+        @click="switchMode(item.value)">
+        {{ item.label }}
+      </view>
+    </view>
+
+    <z-paging
+      ref="paging"
+      v-model="dataList"
+      class="report-paging"
+      :fixed="false"
+      :default-page-size="10"
+      @query="queryList">
+      <view class="report-list">
+        <view class="report-card" v-for="item in dataList" :key="item.id">
+          <view class="card-header">
+            <view>
+              <view class="card-date">{{ formatDate(item.createTime) }}</view>
+            </view>
+            <view class="status-tag">
+              {{ rdStatusDict[item.rdStatus] || item.rdStatus || "--" }}
+            </view>
+          </view>
+
+          <view class="card-body">
+            <view class="field-row">
+              <text class="field-label">施工队伍</text>
+              <text class="field-value">{{ item.deptName || "--" }}</text>
+            </view>
+            <view class="field-row">
+              <text class="field-label">任务</text>
+              <text class="field-value">{{ item.taskName || "--" }}</text>
+            </view>
+            <view class="field-row brief-row">
+              <text class="field-label">施工简报</text>
+              <UniTooltip
+                :content="item.constructionBrief || '--'"
+                placement="top">
+                <text class="field-value brief-text">{{
+                  item.constructionBrief || "--"
+                }}</text>
+              </UniTooltip>
+            </view>
+          </view>
+
+          <view v-if="item.lastGroupIdFlag" class="summary-panel">
+            <view class="summary-header" @click="toggleSummary(item.id)">
+              <text>组汇总</text>
+              <uni-icons
+                :type="isSummaryExpanded(item.id) ? 'up' : 'down'"
+                color="#004098"
+                size="16" />
+            </view>
+            <view v-if="isSummaryExpanded(item.id)" class="summary-grid">
+              <view
+                class="summary-item"
+                v-for="field in summaryFields"
+                :key="field.key">
+                <text class="summary-label">{{ field.label }}</text>
+                <text class="summary-value">{{
+                  formatValue(item[field.key])
+                }}</text>
+              </view>
+            </view>
+          </view>
+        </view>
+      </view>
+    </z-paging>
+
+    <UniFab
+      :pattern="fabPattern"
+      horizontal="right"
+      vertical="bottom"
+      direction="horizontal"
+      :popMenu="false"
+      @fabClick="openFilterPopup" />
+
+    <uni-popup
+      ref="filterPopup"
+      type="bottom"
+      background-color="#fff"
+      border-radius="10px 10px 0 0">
+      <view class="filter-popup">
+        <view class="filter-header">
+          <text class="filter-action" @click="closeFilterPopup">取消</text>
+          <text class="filter-title">筛选条件</text>
+          <text class="filter-action primary" @click="applyFilter">确定</text>
+        </view>
+
+        <view class="filter-body">
+          <view class="filter-item">
+            <view class="filter-label">项目</view>
+            <uni-easyinput
+              v-model="filterForm.contractName"
+              :inputBorder="false"
+              :styles="inputStyles"
+              placeholder="请输入项目" />
+          </view>
+
+          <view v-if="currentMode === 'team'" class="filter-item">
+            <view class="filter-label">任务</view>
+            <uni-easyinput
+              v-model="filterForm.taskName"
+              :inputBorder="false"
+              :styles="inputStyles"
+              placeholder="请输入任务" />
+          </view>
+
+          <view class="filter-item">
+            <view class="filter-label">创建时间</view>
+            <uni-datetime-picker
+              v-model="filterForm.createTime"
+              type="datetimerange"
+              return-type="string"
+              :border="false"
+              placeholder="请选择" />
+          </view>
+
+          <view v-if="currentMode === 'well'" class="filter-item well-tree-item">
+            <view class="filter-label">井</view>
+            <uni-easyinput
+              v-model="wellSearchKey"
+              :inputBorder="false"
+              :styles="inputStyles"
+              placeholder="请输入井名" />
+            <view class="dept-selected">
+              {{ selectedWellText || "请选择" }}
+            </view>
+            <view class="tree well-tree">
+              <DaTree
+                :key="wellTreeRenderKey"
+                :data="wellTreeData"
+                labelField="label"
+                valueField="value"
+                childrenField="children"
+                defaultExpandAll
+                checkedDisabled
+                :filterValue="wellSearchKey"
+                :defaultCheckedKeys="selectedWellKey"
+                @change="handleWellTreeChange" />
+            </view>
+          </view>
+
+          <view v-if="currentMode === 'team'" class="filter-item dept-item">
+            <view class="filter-label">队伍</view>
+            <view class="dept-selected">
+              {{ selectedDeptName || "请选择" }}
+            </view>
+            <view class="tree">
+              <DaTree
+                :data="treeData"
+                labelField="name"
+                valueField="id"
+                disabledField="disabled"
+                defaultExpandAll
+                checkedDisabled
+                :defaultCheckedKeys="filterForm.deptId"
+                @change="handleTreeChange" />
+            </view>
+          </view>
+        </view>
+
+        <view class="filter-footer">
+          <button class="filter-button reset" @click="resetFilter">重置</button>
+          <button class="filter-button" type="primary" @click="applyFilter"
+            >搜索</button
+          >
+        </view>
+      </view>
+    </uni-popup>
+  </view>
+</template>
+
+<script setup>
+import { computed, onMounted, reactive, ref } from "vue";
+import dayjs from "dayjs";
+import DaTree from "@/components/da-tree/index.vue";
+import UniFab from "@/uni_modules/uni-fab/components/uni-fab/uni-fab.vue";
+import UniTooltip from "@/uni_modules/uni-tooltip/components/uni-tooltip/uni-tooltip.vue";
+import {
+  getTaskWellNames,
+  getRuiduReportTeamPage,
+  getRuiduReportWellPage,
+} from "@/api/ruiduReport";
+import { specifiedSimpleDepts } from "@/api";
+import { getDeptId } from "@/utils/auth";
+import { useDataDictStore } from "@/store/modules/dataDict";
+
+const paging = ref(null);
+const filterPopup = ref(null);
+const dataList = ref([]);
+const deptOptions = ref([]);
+const treeData = ref([]);
+const wellTreeData = ref([]);
+const wellTreeRenderKey = ref(0);
+const selectedWellKey = ref("");
+const wellSearchKey = ref("");
+const expandedSummaryIds = ref([]);
+const currentMode = ref("well");
+const dictStore = useDataDictStore();
+const rdStatusDict = reactive({});
+
+const modeOptions = [
+  { label: "井", value: "well" },
+  { label: "队伍", value: "team" },
+];
+
+const summaryFields = [
+  { label: "桥塞", key: "groupIdBridgePlug" },
+  { label: "趟数", key: "groupIdRunCount" },
+  { label: "井数", key: "groupIdCumulativeWorkingWell" },
+  { label: "小时H", key: "groupIdHourCount" },
+  { label: "油耗L", key: "groupIdFuel" },
+  { label: "水方量", key: "groupIdWaterVolume" },
+  { label: "泵车台次", key: "groupIdPumpTrips" },
+  { label: "段数", key: "groupIdCumulativeWorkingLayers" },
+  { label: "仪表/混砂", key: "groupIdMixSand" },
+];
+
+const inputStyles = reactive({
+  backgroundColor: "#f7f8fa",
+  color: "#333",
+});
+
+const fabPattern = reactive({
+  color: "#fff",
+  backgroundColor: "#fff",
+  selectedColor: "#fff",
+  buttonColor: "#004098",
+  iconColor: "#fff",
+  icon: "search",
+});
+
+const getDefaultCreateTime = () => {
+  const end = dayjs().endOf("day").format("YYYY-MM-DD HH:mm:ss");
+  const start = dayjs().subtract(6, "day").startOf("day").format("YYYY-MM-DD HH:mm:ss");
+  return [start, end];
+};
+
+const filterForm = reactive({
+  contractName: "",
+  taskName: "",
+  wellName: "",
+  deptId: getDeptId(),
+  createTime: getDefaultCreateTime(),
+});
+
+const selectedDeptName = computed(() => {
+  const current = deptOptions.value.find(
+    (item) => String(item.value) === String(filterForm.deptId)
+  );
+  return current?.text || "";
+});
+
+const selectedWellText = computed(() => {
+  if (filterForm.wellName) return `井号:${filterForm.wellName}`;
+  if (filterForm.contractName) return `项目:${filterForm.contractName}`;
+  return "";
+});
+
+const handleTree = (
+  data,
+  id = "id",
+  parentId = "parentId",
+  children = "children"
+) => {
+  if (!Array.isArray(data)) return [];
+
+  const childrenListMap = {};
+  const nodeIds = {};
+  const tree = [];
+
+  for (const item of data) {
+    const itemParentId = item[parentId];
+    if (childrenListMap[itemParentId] == null) {
+      childrenListMap[itemParentId] = [];
+    }
+    nodeIds[item[id]] = item;
+    childrenListMap[itemParentId].push(item);
+  }
+
+  for (const item of data) {
+    if (nodeIds[item[parentId]] == null) {
+      tree.push(item);
+    }
+  }
+
+  const adaptToChildrenList = (node) => {
+    if (childrenListMap[node[id]] != null) {
+      node[children] = childrenListMap[node[id]];
+    }
+    if (node[children]) {
+      node[children].forEach(adaptToChildrenList);
+    }
+  };
+
+  tree.forEach(adaptToChildrenList);
+  return tree;
+};
+
+const sortDeptTree = (nodes) => {
+  if (!Array.isArray(nodes)) return [];
+
+  return [...nodes]
+    .sort((a, b) => (a.sort ?? 999999) - (b.sort ?? 999999))
+    .map((node) => ({
+      ...node,
+      children: sortDeptTree(node.children),
+    }));
+};
+
+const loadDeptOptions = async () => {
+  try {
+    const response = await specifiedSimpleDepts(getDeptId());
+    const list = response?.data || [];
+    deptOptions.value = list.map((item) => ({
+      text: item.name,
+      value: item.id,
+    }));
+    treeData.value = sortDeptTree(handleTree(list));
+  } catch (error) {
+    treeData.value = [];
+    deptOptions.value = [];
+  }
+};
+
+const loadWellOptions = async () => {
+  try {
+    const response = await getTaskWellNames({
+      companyId: 163,
+      wellName: "",
+    });
+    const list = response?.data || [];
+    const parentMap = new Map();
+    const tree = [];
+
+    list.forEach((item) => {
+      if (item.type === "1") {
+        const node = {
+          label: item.projectName,
+          value: `project-${item.projectId}`,
+          type: "1",
+          rawData: item,
+          children: [],
+        };
+        parentMap.set(item.projectId, node);
+        tree.push(node);
+      }
+    });
+
+    list.forEach((item) => {
+      if (item.type === "2") {
+        const parent = parentMap.get(item.projectId);
+        const node = {
+          label: item.wellName,
+          value: `well-${item.projectId}-${item.wellName}`,
+          type: "2",
+          rawData: item,
+        };
+        if (parent) {
+          parent.children.push(node);
+        }
+      }
+    });
+
+    wellTreeData.value = tree;
+  } catch (error) {
+    wellTreeData.value = [];
+  }
+};
+
+const queryList = (pageNo, pageSize) => {
+  const request =
+    currentMode.value === "well"
+      ? getRuiduReportTeamPage({
+          pageNo,
+          pageSize,
+          contractName: filterForm.contractName,
+          taskName: filterForm.wellName,
+          createTime: filterForm.createTime,
+        })
+      : getRuiduReportWellPage({
+          pageNo,
+          pageSize,
+          deptId: filterForm.deptId,
+          contractName: filterForm.contractName,
+          taskName: filterForm.taskName,
+          createTime: filterForm.createTime,
+        });
+
+  request
+    .then((res) => {
+      const list = res.data?.list || [];
+      const summaryIds = list
+        .filter((item) => item.lastGroupIdFlag)
+        .map((item) => String(item.id));
+      expandedSummaryIds.value =
+        pageNo === 1
+          ? summaryIds
+          : Array.from(new Set([...expandedSummaryIds.value, ...summaryIds]));
+      paging.value?.complete(list);
+    })
+    .catch(() => {
+      paging.value?.complete(false);
+    });
+};
+
+const switchMode = (mode, reload = true) => {
+  if (currentMode.value === mode) return;
+  currentMode.value = mode;
+  expandedSummaryIds.value = [];
+  if (reload) {
+    paging.value?.reload();
+  }
+};
+
+const openFilterPopup = () => {
+  filterPopup.value?.open();
+};
+
+const closeFilterPopup = () => {
+  filterPopup.value?.close();
+};
+
+const applyFilter = () => {
+  closeFilterPopup();
+  paging.value?.reload();
+};
+
+const resetFilter = () => {
+  filterForm.contractName = "";
+  filterForm.taskName = "";
+  filterForm.wellName = "";
+  filterForm.deptId = getDeptId();
+  filterForm.createTime = getDefaultCreateTime();
+  selectedWellKey.value = "";
+  wellSearchKey.value = "";
+  wellTreeRenderKey.value += 1;
+};
+
+const handleTreeChange = (value) => {
+  filterForm.deptId = value;
+};
+
+const handleWellTreeChange = (value, item) => {
+  const node = item?.originItem;
+  selectedWellKey.value = value;
+  if (node?.type === "1") {
+    filterForm.contractName = node.rawData?.projectName || node.label || "";
+    filterForm.wellName = "";
+    return;
+  }
+  if (node?.type === "2") {
+    filterForm.wellName = node.rawData?.wellName || node.label || "";
+    filterForm.contractName = "";
+  }
+};
+
+const toggleSummary = (id) => {
+  const key = String(id);
+  if (expandedSummaryIds.value.includes(key)) {
+    expandedSummaryIds.value = expandedSummaryIds.value.filter(
+      (item) => item !== key
+    );
+    return;
+  }
+  expandedSummaryIds.value = [...expandedSummaryIds.value, key];
+};
+
+const isSummaryExpanded = (id) => {
+  return expandedSummaryIds.value.includes(String(id));
+};
+
+const formatDate = (time) => {
+  return time ? dayjs(time).format("YYYY-MM-DD") : "--";
+};
+
+const formatValue = (value) => {
+  if (value === 0) return "0";
+  return value ?? "--";
+};
+
+const loadRdStatusDict = async () => {
+  if (dictStore.dataDict.length <= 0) {
+    await dictStore.loadDataDictList();
+  }
+  dictStore.getStrDictOptions("rdStatus").forEach((item) => {
+    rdStatusDict[item.value] = item.label;
+  });
+};
+
+onMounted(() => {
+  loadDeptOptions();
+  loadWellOptions();
+  loadRdStatusDict();
+});
+</script>
+
+<style lang="scss" scoped>
+.report-page {
+  display: flex;
+  flex-direction: column;
+  min-height: 0;
+  padding: 10px !important;
+  overflow: hidden;
+}
+
+.mode-tabs {
+  flex-shrink: 0;
+  height: 42px;
+  padding: 4px;
+  margin-bottom: 8px;
+  display: flex;
+  gap: 4px;
+  background: #ffffff;
+  border-radius: 8px;
+  box-sizing: border-box;
+}
+
+.mode-tab {
+  flex: 1;
+  height: 34px;
+  line-height: 34px;
+  text-align: center;
+  color: #5c6675;
+  font-size: 14px;
+  border-radius: 6px;
+
+  &.active {
+    color: #ffffff;
+    background: #004098;
+    font-weight: 600;
+  }
+}
+
+.report-paging {
+  flex: 1;
+  min-height: 0;
+  height: auto;
+}
+
+.report-list {
+  padding: 8px 6px;
+  box-sizing: border-box;
+}
+
+.report-card {
+  margin-bottom: 14px;
+  overflow: hidden;
+  background: #ffffff;
+  border: 1px solid rgba(0, 64, 152, 0.08);
+  border-radius: 8px;
+  box-shadow: 0 6px 18px rgba(35, 54, 79, 0.08);
+}
+
+.card-header {
+  min-height: 52px;
+  padding: 14px 16px 10px;
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  box-sizing: border-box;
+  border-bottom: 1px solid #edf1f7;
+}
+
+.card-date {
+  color: #000000;
+  font-weight: 700;
+  font-size: 18px;
+  line-height: 24px;
+}
+
+.status-tag {
+  max-width: 112px;
+  height: 24px;
+  line-height: 24px;
+  padding: 0 9px;
+  border-radius: 12px;
+  background: #004098;
+  color: #ffffff;
+  font-size: 12px;
+  font-weight: 600;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+  box-sizing: border-box;
+}
+
+.card-body {
+  padding: 12px 16px 6px;
+  box-sizing: border-box;
+}
+
+.field-row {
+  display: flex;
+  align-items: flex-start;
+  min-height: 30px;
+  font-size: 14px;
+}
+
+.field-label {
+  width: 70px;
+  flex-shrink: 0;
+  color: #000000;
+  font-weight: 600;
+  font-size: 13px;
+  line-height: 22px;
+}
+
+.field-value {
+  min-width: 0;
+  flex: 1;
+  color: #233044;
+  font-weight: 500;
+  font-size: 14px;
+  line-height: 22px;
+}
+
+.summary-grid {
+  display: grid;
+  grid-template-columns: repeat(2, minmax(0, 1fr));
+  gap: 8px;
+}
+
+.summary-item {
+  min-width: 0;
+  padding: 9px 10px;
+  border-radius: 6px;
+  background: #f7f8fa;
+  box-sizing: border-box;
+}
+
+.summary-label {
+  display: block;
+  color: #7a8494;
+  font-size: 12px;
+  line-height: 17px;
+}
+
+.summary-value {
+  display: block;
+  margin-top: 4px;
+  color: #233044;
+  font-weight: 700;
+  font-size: 15px;
+  line-height: 20px;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+}
+
+.brief-row {
+  padding-top: 4px;
+}
+
+.brief-row :deep(.uni-tooltip) {
+  min-width: 0;
+  flex: 1;
+  display: block;
+}
+
+.brief-text {
+  display: block;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+}
+
+.summary-panel {
+  margin: 0 16px 14px;
+  border: 1px solid rgba(0, 64, 152, 0.12);
+  border-radius: 8px;
+  overflow: hidden;
+}
+
+.summary-header {
+  height: 38px;
+  padding: 0 12px;
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  color: #004098;
+  font-weight: 600;
+  font-size: 14px;
+  background: #eef5ff;
+  box-sizing: border-box;
+}
+
+.summary-grid {
+  padding: 10px;
+  background: #ffffff;
+}
+
+.filter-popup {
+  height: 86vh;
+  max-height: 86vh;
+  display: flex;
+  flex-direction: column;
+  background: #fff;
+  padding-bottom: env(safe-area-inset-bottom);
+}
+
+.filter-header {
+  height: 48px;
+  padding: 0 16px;
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  border-bottom: 1px solid #f0f0f0;
+  box-sizing: border-box;
+}
+
+.filter-title {
+  font-weight: 600;
+  color: #333;
+  font-size: 16px;
+}
+
+.filter-action {
+  min-width: 48px;
+  color: #666;
+  font-size: 14px;
+
+  &.primary {
+    color: #004098;
+    text-align: right;
+  }
+}
+
+.filter-body {
+  flex: 1;
+  overflow-y: auto;
+  padding: 12px 16px;
+  box-sizing: border-box;
+}
+
+.filter-item {
+  margin-bottom: 14px;
+}
+
+.filter-label {
+  margin-bottom: 8px;
+  color: #333;
+  font-weight: 500;
+  font-size: 14px;
+}
+
+.dept-selected {
+  min-height: 36px;
+  line-height: 36px;
+  padding: 0 10px;
+  margin-bottom: 8px;
+  background: #f7f8fa;
+  color: #666;
+  border-radius: 4px;
+  box-sizing: border-box;
+}
+
+.tree {
+  height: 360px;
+  overflow: hidden;
+  border: 1px solid #f0f0f0;
+  border-radius: 4px;
+}
+
+.filter-footer {
+  display: flex;
+  gap: 10px;
+  padding: 10px 16px 14px;
+  border-top: 1px solid #f0f0f0;
+  box-sizing: border-box;
+}
+
+.filter-button {
+  flex: 1;
+  height: 38px;
+  line-height: 38px;
+  font-size: 14px;
+  margin: 0;
+
+  &.reset {
+    color: #004098;
+    background: #fff;
+    border: 1px solid #004098;
+  }
+}
+</style>

+ 25 - 0
uni_modules/uni-fab/changelog.md

@@ -0,0 +1,25 @@
+## 1.2.6(2024-10-12)
+- 修复 微信小程序中的getSystemInfo警告
+## 1.2.5(2023-03-29)
+- 新增 pattern.icon 属性,可自定义图标
+## 1.2.4(2022-09-07)
+小程序端由于 style 使用了对象导致报错,[详情](https://ask.dcloud.net.cn/question/152790?item_id=211778&rf=false)
+## 1.2.3(2022-09-05)
+- 修复 nvue 环境下,具有 tabBar 时,fab 组件下部位置无法正常获取 --window-bottom 的bug,详见:[https://ask.dcloud.net.cn/question/110638?notification_id=826310](https://ask.dcloud.net.cn/question/110638?notification_id=826310)
+## 1.2.2(2021-12-29)
+- 更新 组件依赖
+## 1.2.1(2021-11-19)
+- 修复 阴影颜色不正确的bug
+## 1.2.0(2021-11-19)
+- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource)
+- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-fab](https://uniapp.dcloud.io/component/uniui/uni-fab)
+## 1.1.1(2021-11-09) 
+- 新增 提供组件设计资源,组件样式调整
+## 1.1.0(2021-07-30)
+- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
+## 1.0.7(2021-05-12)
+- 新增 组件示例地址
+## 1.0.6(2021-02-05)
+- 调整为uni_modules目录规范
+- 优化 按钮背景色调整
+- 优化 兼容pc端

+ 491 - 0
uni_modules/uni-fab/components/uni-fab/uni-fab.vue

@@ -0,0 +1,491 @@
+<template>
+	<view class="uni-cursor-point">
+		<view v-if="popMenu && (leftBottom||rightBottom||leftTop||rightTop) && content.length > 0" :class="{
+        'uni-fab--leftBottom': leftBottom,
+        'uni-fab--rightBottom': rightBottom,
+        'uni-fab--leftTop': leftTop,
+        'uni-fab--rightTop': rightTop
+      }" class="uni-fab"
+				:style="nvueBottom"
+			>
+			<view :class="{
+          'uni-fab__content--left': horizontal === 'left',
+          'uni-fab__content--right': horizontal === 'right',
+          'uni-fab__content--flexDirection': direction === 'vertical',
+          'uni-fab__content--flexDirectionStart': flexDirectionStart,
+          'uni-fab__content--flexDirectionEnd': flexDirectionEnd,
+		  'uni-fab__content--other-platform': !isAndroidNvue
+        }" :style="{ width: boxWidth, height: boxHeight, backgroundColor: styles.backgroundColor }"
+				class="uni-fab__content" elevation="5">
+				<view v-if="flexDirectionStart || horizontalLeft" class="uni-fab__item uni-fab__item--first" />
+				<view v-for="(item, index) in content" :key="index" :class="{ 'uni-fab__item--active': isShow }"
+					class="uni-fab__item" @click="_onItemClick(index, item)">
+					<image :src="item.active ? item.selectedIconPath : item.iconPath" class="uni-fab__item-image"
+						mode="aspectFit" />
+					<text class="uni-fab__item-text"
+						:style="{ color: item.active ? styles.selectedColor : styles.color }">{{ item.text }}</text>
+				</view>
+				<view v-if="flexDirectionEnd || horizontalRight" class="uni-fab__item uni-fab__item--first" />
+			</view>
+		</view>
+		<view :class="{
+		  'uni-fab__circle--leftBottom': leftBottom,
+		  'uni-fab__circle--rightBottom': rightBottom,
+		  'uni-fab__circle--leftTop': leftTop,
+		  'uni-fab__circle--rightTop': rightTop,
+		  'uni-fab__content--other-platform': !isAndroidNvue
+		}" class="uni-fab__circle uni-fab__plus" :style="{ 'background-color': styles.buttonColor, 'bottom': nvueBottom }" @click="_onClick">
+			<uni-icons class="fab-circle-icon" :type="styles.icon" :color="styles.iconColor" size="32"
+				:class="{'uni-fab__plus--active': isShow && content.length > 0}"></uni-icons>
+			<!-- <view class="fab-circle-v"  :class="{'uni-fab__plus--active': isShow && content.length > 0}"></view>
+			<view class="fab-circle-h" :class="{'uni-fab__plus--active': isShow  && content.length > 0}"></view> -->
+		</view>
+	</view>
+</template>
+
+<script>
+	let platform = 'other'
+	// #ifdef APP-NVUE
+	platform = uni.getSystemInfoSync().platform
+	// #endif
+
+	/**
+	 * Fab 悬浮按钮
+	 * @description 点击可展开一个图形按钮菜单
+	 * @tutorial https://ext.dcloud.net.cn/plugin?id=144
+	 * @property {Object} pattern 可选样式配置项
+	 * @property {Object} horizontal = [left | right] 水平对齐方式
+	 * 	@value left 左对齐
+	 * 	@value right 右对齐
+	 * @property {Object} vertical = [bottom | top] 垂直对齐方式
+	 * 	@value bottom 下对齐
+	 * 	@value top 上对齐
+	 * @property {Object} direction = [horizontal | vertical] 展开菜单显示方式
+	 * 	@value horizontal 水平显示
+	 * 	@value vertical 垂直显示
+	 * @property {Array} content 展开菜单内容配置项
+	 * @property {Boolean} popMenu 是否使用弹出菜单
+	 * @event {Function} trigger 展开菜单点击事件,返回点击信息
+	 * @event {Function} fabClick 悬浮按钮点击事件
+	 */
+	export default {
+		name: 'UniFab',
+		emits: ['fabClick', 'trigger'],
+		props: {
+			pattern: {
+				type: Object,
+				default () {
+					return {}
+				}
+			},
+			horizontal: {
+				type: String,
+				default: 'left'
+			},
+			vertical: {
+				type: String,
+				default: 'bottom'
+			},
+			direction: {
+				type: String,
+				default: 'horizontal'
+			},
+			content: {
+				type: Array,
+				default () {
+					return []
+				}
+			},
+			show: {
+				type: Boolean,
+				default: false
+			},
+			popMenu: {
+				type: Boolean,
+				default: true
+			}
+		},
+		data() {
+			return {
+				fabShow: false,
+				isShow: false,
+				isAndroidNvue: platform === 'android',
+				styles: {
+					color: '#3c3e49',
+					selectedColor: '#007AFF',
+					backgroundColor: '#fff',
+					buttonColor: '#007AFF',
+					iconColor: '#fff',
+					icon: 'plusempty'
+				}
+			}
+		},
+		computed: {
+			contentWidth(e) {
+				return (this.content.length + 1) * 55 + 15 + 'px'
+			},
+			contentWidthMin() {
+				return '55px'
+			},
+			// 动态计算宽度
+			boxWidth() {
+				return this.getPosition(3, 'horizontal')
+			},
+			// 动态计算高度
+			boxHeight() {
+				return this.getPosition(3, 'vertical')
+			},
+			// 计算左下位置
+			leftBottom() {
+				return this.getPosition(0, 'left', 'bottom')
+			},
+			// 计算右下位置
+			rightBottom() {
+				return this.getPosition(0, 'right', 'bottom')
+			},
+			// 计算左上位置
+			leftTop() {
+				return this.getPosition(0, 'left', 'top')
+			},
+			rightTop() {
+				return this.getPosition(0, 'right', 'top')
+			},
+			flexDirectionStart() {
+				return this.getPosition(1, 'vertical', 'top')
+			},
+			flexDirectionEnd() {
+				return this.getPosition(1, 'vertical', 'bottom')
+			},
+			horizontalLeft() {
+				return this.getPosition(2, 'horizontal', 'left')
+			},
+			horizontalRight() {
+				return this.getPosition(2, 'horizontal', 'right')
+			},
+			// 计算 nvue bottom
+			nvueBottom() {
+				// #ifdef APP-NVUE
+				const safeBottom = uni.getSystemInfoSync().windowBottom;
+				return 30 + safeBottom
+				// #endif
+				// #ifndef APP-NVUE
+				return 30
+				// #endif
+			}
+		},
+		watch: {
+			pattern: {
+				handler(val, oldVal) {
+					this.styles = Object.assign({}, this.styles, val)
+				},
+				deep: true
+			}
+		},
+		created() {
+			this.isShow = this.show
+			if (this.top === 0) {
+				this.fabShow = true
+			}
+			// 初始化样式
+			this.styles = Object.assign({}, this.styles, this.pattern)
+		},
+		methods: {
+			_onClick() {
+				this.$emit('fabClick')
+				if (!this.popMenu) {
+					return
+				}
+				this.isShow = !this.isShow
+			},
+			open() {
+				this.isShow = true
+			},
+			close() {
+				this.isShow = false
+			},
+			/**
+			 * 按钮点击事件
+			 */
+			_onItemClick(index, item) {
+				if (!this.isShow) {
+					return
+				}
+				this.$emit('trigger', {
+					index,
+					item
+				})
+			},
+			/**
+			 * 获取 位置信息
+			 */
+			getPosition(types, paramA, paramB) {
+				if (types === 0) {
+					return this.horizontal === paramA && this.vertical === paramB
+				} else if (types === 1) {
+					return this.direction === paramA && this.vertical === paramB
+				} else if (types === 2) {
+					return this.direction === paramA && this.horizontal === paramB
+				} else {
+					return this.isShow && this.direction === paramA ? this.contentWidth : this.contentWidthMin
+				}
+			}
+		}
+	}
+</script>
+
+<style lang="scss" >
+	$uni-shadow-base:0 1px 5px 2px rgba($color: #000000, $alpha: 0.3) !default;
+
+	.uni-fab {
+		position: fixed;
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		justify-content: center;
+		align-items: center;
+		z-index: 10;
+		border-radius: 45px;
+		box-shadow: $uni-shadow-base;
+	}
+
+	.uni-cursor-point {
+		/* #ifdef H5 */
+		cursor: pointer;
+		/* #endif */
+	}
+
+	.uni-fab--active {
+		opacity: 1;
+	}
+
+	.uni-fab--leftBottom {
+		left: 15px;
+		bottom: 30px;
+		/* #ifdef H5 */
+		left: calc(15px + var(--window-left));
+		bottom: calc(30px + var(--window-bottom));
+		/* #endif */
+		// padding: 10px;
+	}
+
+	.uni-fab--leftTop {
+		left: 15px;
+		top: 30px;
+		/* #ifdef H5 */
+		left: calc(15px + var(--window-left));
+		top: calc(30px + var(--window-top));
+		/* #endif */
+		// padding: 10px;
+	}
+
+	.uni-fab--rightBottom {
+		right: 15px;
+		bottom: 30px;
+		/* #ifdef H5 */
+		right: calc(15px + var(--window-right));
+		bottom: calc(30px + var(--window-bottom));
+		/* #endif */
+		// padding: 10px;
+	}
+
+	.uni-fab--rightTop {
+		right: 15px;
+		top: 30px;
+		/* #ifdef H5 */
+		right: calc(15px + var(--window-right));
+		top: calc(30px + var(--window-top));
+		/* #endif */
+		// padding: 10px;
+	}
+
+	.uni-fab__circle {
+		position: fixed;
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		justify-content: center;
+		align-items: center;
+		width: 55px;
+		height: 55px;
+		background-color: #3c3e49;
+		border-radius: 45px;
+		z-index: 11;
+		// box-shadow: $uni-shadow-base;
+	}
+
+	.uni-fab__circle--leftBottom {
+		left: 15px;
+		bottom: 30px;
+		/* #ifdef H5 */
+		left: calc(15px + var(--window-left));
+		bottom: calc(30px + var(--window-bottom));
+		/* #endif */
+	}
+
+	.uni-fab__circle--leftTop {
+		left: 15px;
+		top: 30px;
+		/* #ifdef H5 */
+		left: calc(15px + var(--window-left));
+		top: calc(30px + var(--window-top));
+		/* #endif */
+	}
+
+	.uni-fab__circle--rightBottom {
+		right: 15px;
+		bottom: 30px;
+		/* #ifdef H5 */
+		right: calc(15px + var(--window-right));
+		bottom: calc(30px + var(--window-bottom));
+		/* #endif */
+	}
+
+	.uni-fab__circle--rightTop {
+		right: 15px;
+		top: 30px;
+		/* #ifdef H5 */
+		right: calc(15px + var(--window-right));
+		top: calc(30px + var(--window-top));
+		/* #endif */
+	}
+
+	.uni-fab__circle--left {
+		left: 0;
+	}
+
+	.uni-fab__circle--right {
+		right: 0;
+	}
+
+	.uni-fab__circle--top {
+		top: 0;
+	}
+
+	.uni-fab__circle--bottom {
+		bottom: 0;
+	}
+
+	.uni-fab__plus {
+		font-weight: bold;
+	}
+
+	// .fab-circle-v {
+	// 	position: absolute;
+	// 	width: 2px;
+	// 	height: 24px;
+	// 	left: 0;
+	// 	top: 0;
+	// 	right: 0;
+	// 	bottom: 0;
+	// 	/* #ifndef APP-NVUE */
+	// 	margin: auto;
+	// 	/* #endif */
+	// 	background-color: white;
+	// 	transform: rotate(0deg);
+	// 	transition: transform 0.3s;
+	// }
+
+	// .fab-circle-h {
+	// 	position: absolute;
+	// 	width: 24px;
+	// 	height: 2px;
+	// 	left: 0;
+	// 	top: 0;
+	// 	right: 0;
+	// 	bottom: 0;
+	// 	/* #ifndef APP-NVUE */
+	// 	margin: auto;
+	// 	/* #endif */
+	// 	background-color: white;
+	// 	transform: rotate(0deg);
+	// 	transition: transform 0.3s;
+	// }
+
+	.fab-circle-icon {
+		transform: rotate(0deg);
+		transition: transform 0.3s;
+		font-weight: 200;
+	}
+
+	.uni-fab__plus--active {
+		transform: rotate(135deg);
+	}
+
+	.uni-fab__content {
+		/* #ifndef APP-NVUE */
+		box-sizing: border-box;
+		display: flex;
+		/* #endif */
+		flex-direction: row;
+		border-radius: 55px;
+		overflow: hidden;
+		transition-property: width, height;
+		transition-duration: 0.2s;
+		width: 55px;
+		border-color: #DDDDDD;
+		border-width: 1rpx;
+		border-style: solid;
+	}
+
+	.uni-fab__content--other-platform {
+		border-width: 0px;
+		box-shadow: $uni-shadow-base;
+	}
+
+	.uni-fab__content--left {
+		justify-content: flex-start;
+	}
+
+	.uni-fab__content--right {
+		justify-content: flex-end;
+	}
+
+	.uni-fab__content--flexDirection {
+		flex-direction: column;
+		justify-content: flex-end;
+	}
+
+	.uni-fab__content--flexDirectionStart {
+		flex-direction: column;
+		justify-content: flex-start;
+	}
+
+	.uni-fab__content--flexDirectionEnd {
+		flex-direction: column;
+		justify-content: flex-end;
+	}
+
+	.uni-fab__item {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: column;
+		justify-content: center;
+		align-items: center;
+		width: 55px;
+		height: 55px;
+		opacity: 0;
+		transition: opacity 0.2s;
+	}
+
+	.uni-fab__item--active {
+		opacity: 1;
+	}
+
+	.uni-fab__item-image {
+		width: 20px;
+		height: 20px;
+		margin-bottom: 4px;
+	}
+
+	.uni-fab__item-text {
+		color: #FFFFFF;
+		font-size: 12px;
+		line-height: 12px;
+		margin-top: 2px;
+	}
+
+	.uni-fab__item--first {
+		width: 55px;
+	}
+</style>

+ 85 - 0
uni_modules/uni-fab/package.json

@@ -0,0 +1,85 @@
+{
+  "id": "uni-fab",
+  "displayName": "uni-fab 悬浮按钮",
+  "version": "1.2.6",
+  "description": "悬浮按钮 fab button ,点击可展开一个图标按钮菜单。",
+  "keywords": [
+    "uni-ui",
+    "uniui",
+    "按钮",
+    "悬浮按钮",
+    "fab"
+],
+  "repository": "https://github.com/dcloudio/uni-ui",
+  "engines": {
+    "HBuilderX": ""
+  },
+  "directories": {
+    "example": "../../temps/example_temps"
+  },
+"dcloudext": {
+    "sale": {
+      "regular": {
+        "price": "0.00"
+      },
+      "sourcecode": {
+        "price": "0.00"
+      }
+    },
+    "contact": {
+      "qq": ""
+    },
+    "declaration": {
+      "ads": "无",
+      "data": "无",
+      "permissions": "无"
+    },
+    "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui",
+    "type": "component-vue"
+  },
+  "uni_modules": {
+    "dependencies": ["uni-scss","uni-icons"],
+    "encrypt": [],
+    "platforms": {
+      "cloud": {
+        "tcb": "y",
+        "aliyun": "y",
+        "alipay": "n"
+      },
+      "client": {
+        "App": {
+          "app-vue": "y",
+          "app-nvue": "y"
+        },
+        "H5-mobile": {
+          "Safari": "y",
+          "Android Browser": "y",
+          "微信浏览器(Android)": "y",
+          "QQ浏览器(Android)": "y"
+        },
+        "H5-pc": {
+          "Chrome": "y",
+          "IE": "y",
+          "Edge": "y",
+          "Firefox": "y",
+          "Safari": "y"
+        },
+        "小程序": {
+          "微信": "y",
+          "阿里": "y",
+          "百度": "y",
+          "字节跳动": "y",
+          "QQ": "y"
+        },
+        "快应用": {
+          "华为": "u",
+          "联盟": "u"
+        },
+        "Vue": {
+            "vue2": "y",
+            "vue3": "y"
+        }
+      }
+    }
+  }
+}

+ 9 - 0
uni_modules/uni-fab/readme.md

@@ -0,0 +1,9 @@
+## Fab 悬浮按钮
+> **组件名:uni-fab**
+> 代码块: `uFab`
+
+
+点击可展开一个图形按钮菜单
+
+### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-fab)
+#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 

+ 4 - 2
uni_modules/uni-icons/changelog.md

@@ -1,5 +1,7 @@
-## 2.0.10(2024-06-07)
-- 优化 uni-app x 中,size 属性的类型
+## 2.0.12(2025-08-26)
+- 优化 uni-app x 下 size 类型问题
+## 2.0.11(2025-08-18)
+- 修复 图标点击事件返回
 ## 2.0.9(2024-01-12)
 fix: 修复图标大小默认值错误的问题
 ## 2.0.8(2023-12-14)

+ 79 - 79
uni_modules/uni-icons/components/uni-icons/uni-icons.uvue

@@ -1,91 +1,91 @@
 <template>
-  <text class="uni-icons" :style="styleObj">
-    <slot>{{unicode}}</slot>
-  </text>
+	<text class="uni-icons" :style="styleObj">
+		<slot>{{unicode}}</slot>
+	</text>
 </template>
 
 <script>
-  import { fontData, IconsDataItem } from './uniicons_file'
+	import { fontData, IconsDataItem } from './uniicons_file'
 
-  /**
-   * Icons 图标
-   * @description 用于展示 icon 图标
-   * @tutorial https://ext.dcloud.net.cn/plugin?id=28
-   * @property {Number,String} size 图标大小
-   * @property {String} type 图标图案,参考示例
-   * @property {String} color 图标颜色
-   * @property {String} customPrefix 自定义图标
-   * @event {Function} click 点击 Icon 触发事件
-   */
-  export default {
-    name: "uni-icons",
-    props: {
-      type: {
-        type: String,
-        default: ''
-      },
-      color: {
-        type: String,
-        default: '#333333'
-      },
-      size: {
+	/**
+	 * Icons 图标
+	 * @description 用于展示 icon 图标
+	 * @tutorial https://ext.dcloud.net.cn/plugin?id=28
+	 * @property {Number} size 图标大小
+	 * @property {String} type 图标图案,参考示例
+	 * @property {String} color 图标颜色
+	 * @property {String} customPrefix 自定义图标
+	 * @event {Function} click 点击 Icon 触发事件
+	 */
+	export default {
+		name: "uni-icons",
+		props: {
+			type: {
+				type: String,
+				default: ''
+			},
+			color: {
+				type: String,
+				default: '#333333'
+			},
+			size: {
         type: [Number, String],
         default: 16
-      },
-      fontFamily: {
-        type: String,
-        default: ''
-      }
-    },
-    data() {
-      return {};
-    },
-    computed: {
-      unicode() : string {
-        let codes = fontData.find((item : IconsDataItem) : boolean => { return item.font_class == this.type })
-        if (codes !== null) {
-          return codes.unicode
-        }
-        return ''
-      },
-      iconSize() : string {
-        const size = this.size
-        if (typeof size == 'string') {
-          const reg = /^[0-9]*$/g
-          return reg.test(size as string) ? '' + size + 'px' : '' + size;
-          // return '' + this.size
-        }
-        return this.getFontSize(size as number)
-      },
-      styleObj() : UTSJSONObject {
-        if (this.fontFamily !== '') {
-          return { color: this.color, fontSize: this.iconSize, fontFamily: this.fontFamily }
-        }
-        return { color: this.color, fontSize: this.iconSize }
-      }
-    },
-    created() { },
-    methods: {
-      /**
-       * 字体大小
-       */
-      getFontSize(size : number) : string {
-        return size + 'px';
-      },
-    },
-  }
+			},
+			fontFamily: {
+				type: String,
+				default: ''
+			}
+		},
+		data() {
+			return {};
+		},
+		computed: {
+			unicode() : string {
+				let codes = fontData.find((item : IconsDataItem) : boolean => { return item.font_class == this.type })
+				if (codes !== null) {
+					return codes.unicode
+				}
+				return ''
+			},
+			iconSize() : string {
+				const size = this.size
+				if (typeof size == 'string') {
+				  const reg = /^[0-9]*$/g
+				  return reg.test(size as string) ? '' + size + 'px' : '' + size;
+				  // return '' + this.size
+				}
+				return this.getFontSize(size as number)
+			},
+			styleObj() : UTSJSONObject {
+				if (this.fontFamily !== '') {
+					return { color: this.color, fontSize: this.iconSize, fontFamily: this.fontFamily }
+				}
+				return { color: this.color, fontSize: this.iconSize }
+			}
+		},
+		created() { },
+		methods: {
+			/**
+			 * 字体大小
+			 */
+			getFontSize(size : number) : string {
+				return size + 'px';
+			},
+		},
+	}
 </script>
 
 <style scoped>
-  @font-face {
-    font-family: UniIconsFontFamily;
-    src: url('./uniicons.ttf');
-  }
+	@font-face {
+		font-family: UniIconsFontFamily;
+		src: url('./uniicons.ttf');
+	}
 
-  .uni-icons {
-    font-family: UniIconsFontFamily;
-    font-size: 18px;
-    font-style: normal;
-    color: #333;
-  }
+	.uni-icons {
+		font-family: UniIconsFontFamily;
+		font-size: 18px;
+		font-style: normal;
+		color: #333;
+	}
 </style>

+ 3 - 3
uni_modules/uni-icons/components/uni-icons/uni-icons.vue

@@ -85,8 +85,8 @@
 			}
 		},
 		methods: {
-			_onClick() {
-				this.$emit('click')
+			_onClick(e) {
+				this.$emit('click', e)
 			}
 		}
 	}
@@ -107,4 +107,4 @@
 		text-decoration: none;
 		text-align: center;
 	}
-</style>
+</style>

+ 66 - 44
uni_modules/uni-icons/package.json

@@ -1,7 +1,7 @@
 {
   "id": "uni-icons",
   "displayName": "uni-icons 图标",
-  "version": "2.0.10",
+  "version": "2.0.12",
   "description": "图标组件,用于展示移动端常见的图标,可自定义颜色、大小。",
   "keywords": [
     "uni-ui",
@@ -11,12 +11,14 @@
 ],
   "repository": "https://github.com/dcloudio/uni-ui",
   "engines": {
-    "HBuilderX": "^3.2.14"
+    "HBuilderX": "^3.2.14",
+    "uni-app": "^4.08",
+    "uni-app-x": "^4.61"
   },
   "directories": {
     "example": "../../temps/example_temps"
   },
-"dcloudext": {
+  "dcloudext": {
     "sale": {
       "regular": {
         "price": "0.00"
@@ -34,56 +36,76 @@
       "permissions": "无"
     },
     "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui",
-    "type": "component-vue"
+    "type": "component-vue",
+    "darkmode": "x",
+    "i18n": "x",
+    "widescreen": "x"
   },
   "uni_modules": {
-    "dependencies": ["uni-scss"],
+    "dependencies": [
+      "uni-scss"
+    ],
     "encrypt": [],
     "platforms": {
       "cloud": {
-        "tcb": "y",
-        "aliyun": "y",
-        "alipay": "n"
+        "tcb": "x",
+        "aliyun": "x",
+        "alipay": "x"
       },
       "client": {
-        "App": {
-          "app-vue": "y",
-          "app-nvue": "y",
-          "app-uvue": "y"
+        "uni-app": {
+          "vue": {
+            "vue2": "√",
+            "vue3": "√"
+          },
+          "web": {
+            "safari": "√",
+            "chrome": "√"
+          },
+          "app": {
+            "vue": "√",
+            "nvue": "-",
+            "android": {
+                "extVersion": "",
+                "minVersion": "29"
+            },
+            "ios": "√",
+            "harmony": "√"
+          },
+          "mp": {
+            "weixin": "√",
+            "alipay": "√",
+            "toutiao": "√",
+            "baidu": "√",
+            "kuaishou": "-",
+            "jd": "-",
+            "harmony": "-",
+            "qq": "√",
+            "lark": "-"
+          },
+          "quickapp": {
+            "huawei": "√",
+            "union": "√"
+          }
         },
-        "H5-mobile": {
-          "Safari": "y",
-          "Android Browser": "y",
-          "微信浏览器(Android)": "y",
-          "QQ浏览器(Android)": "y"
-        },
-        "H5-pc": {
-          "Chrome": "y",
-          "IE": "y",
-          "Edge": "y",
-          "Firefox": "y",
-          "Safari": "y"
-        },
-        "小程序": {
-          "微信": "y",
-          "阿里": "y",
-          "百度": "y",
-          "字节跳动": "y",
-          "QQ": "y",
-					"钉钉": "y",
-					"快手": "y",
-					"飞书": "y",
-					"京东": "y"
-        },
-        "快应用": {
-          "华为": "y",
-          "联盟": "y"
-        },
-        "Vue": {
-            "vue2": "y",
-            "vue3": "y"
+        "uni-app-x": {
+          "web": {
+            "safari": "√",
+            "chrome": "√"
+          },
+          "app": {
+            "android": {
+                "extVersion": "",
+                "minVersion": "29"
+            },
+            "ios": "√",
+            "harmony": "√"
+          },
+          "mp": {
+            "weixin": "√"
+          }
         }
       }
     }
   }
-}
+}

+ 16 - 0
uni_modules/uni-tooltip/changelog.md

@@ -0,0 +1,16 @@
+## 0.2.4(2024-04-23)
+- 修复 弹出位置默认值不一致导致的错位
+## 0.2.3(2024-03-20)
+- 修复 弹出位置修正
+## 0.2.2(2024-01-15)
+- 新增 placement支持设置四个方向:top bottom left right
+## 0.2.1(2022-05-09)
+- 修复 content 为空时仍然弹出的bug
+## 0.2.0(2022-05-07)
+**注意:破坏性更新**
+- 更新 text 属性变更为 content
+- 更新 移除 width 属性
+## 0.1.1(2022-04-27)
+- 修复 组件根 text 嵌套组件 warning
+## 0.1.0(2022-04-21)
+- 初始化

+ 108 - 0
uni_modules/uni-tooltip/components/uni-tooltip/uni-tooltip.vue

@@ -0,0 +1,108 @@
+<template>
+	<view class="uni-tooltip">
+		<slot></slot>
+		<view v-if="content || $slots.content" class="uni-tooltip-popup" :style="initPlacement">
+			<slot name="content">
+				{{content}}
+			</slot>
+		</view>
+	</view>
+</template>
+
+
+<script>
+	/**
+	 * Tooltip 提示文字
+	 * @description 常用于展示鼠标 hover 时的提示信息。
+	 * @tutorial https://uniapp.dcloud.io/component/uniui/uni-tooltip
+	 * @property {String} content   弹出层显示的内容
+	 * @property {String}  placement出现位置, 目前支持:left right top bottom
+	 */
+	export default {
+		name: "uni-tooltip",
+		data() {
+			return {
+
+			};
+		},
+		methods: {},
+		computed: {
+			initPlacement() {
+				let style = {};
+				switch (this.placement) {
+					case 'left':
+						style = {
+							top: '50%',
+							transform: 'translateY(-50%)',
+							right: '100%',
+							"margin-right": '10rpx',
+						}
+						break;
+					case 'right':
+						style = {
+							top: '50%',
+							transform: 'translateY(-50%)',
+							left: '100%',
+							"margin-left": '10rpx',
+						}
+						break;
+					case 'top':
+						style = {
+							bottom: '100%',
+							transform: 'translateX(-50%)',
+							left: '50%',
+							"margin-bottom": '10rpx',
+						}
+						break;
+					case 'bottom':
+						style = {
+							top: '100%',
+							transform: 'translateX(-50%)',
+							left: '50%',
+							"margin-top": '10rpx',
+						}
+						break;
+				}
+				return style;
+			}
+		},
+		props: {
+			content: {
+				type: String,
+				default: ''
+			},
+
+			placement: {
+				type: String,
+				default: 'bottom'
+			},
+		}
+	}
+</script>
+
+<style>
+	.uni-tooltip {
+		position: relative;
+		cursor: pointer;
+		display: inline-block;
+	}
+
+	.uni-tooltip-popup {
+		z-index: 1;
+		display: none;
+		position: absolute;
+		background-color: #333;
+		border-radius: 8px;
+		color: #fff;
+		font-size: 12px;
+		text-align: left;
+		line-height: 16px;
+		padding: 12px;
+		overflow: auto;
+	}
+
+
+	.uni-tooltip:hover .uni-tooltip-popup {
+		display: block;
+	}
+</style>

+ 86 - 0
uni_modules/uni-tooltip/package.json

@@ -0,0 +1,86 @@
+{
+  "id": "uni-tooltip",
+  "displayName": "uni-tooltip 提示文字",
+  "version": "0.2.4",
+  "description": "Tooltip 提示文字",
+  "keywords": [
+    "uni-tooltip",
+    "uni-ui",
+    "tooltip",
+    "tip",
+    "文字提示"
+],
+  "repository": "https://github.com/dcloudio/uni-ui",
+  "engines": {
+    "HBuilderX": ""
+  },
+  "directories": {
+    "example": "../../temps/example_temps"
+  },
+"dcloudext": {
+    "sale": {
+      "regular": {
+        "price": "0.00"
+      },
+      "sourcecode": {
+        "price": "0.00"
+      }
+    },
+    "contact": {
+      "qq": ""
+    },
+    "declaration": {
+      "ads": "无 ",
+      "data": "无",
+      "permissions": "无"
+    },
+    "npmurl": "",
+    "type": "component-vue"
+  },
+  "uni_modules": {
+    "dependencies": [],
+    "encrypt": [],
+    "platforms": {
+      "cloud": {
+        "tcb": "y",
+        "aliyun": "y",
+        "alipay": "n"
+      },
+      "client": {
+        "Vue": {
+          "vue2": "y",
+          "vue3": "y"
+        },
+        "App": {
+          "app-vue": "y",
+          "app-nvue": "u"
+        },
+        "H5-mobile": {
+          "Safari": "y",
+          "Android Browser": "y",
+          "微信浏览器(Android)": "y",
+          "QQ浏览器(Android)": "y"
+        },
+        "H5-pc": {
+          "Chrome": "y",
+          "IE": "y",
+          "Edge": "y",
+          "Firefox": "y",
+          "Safari": "y"
+        },
+        "小程序": {
+          "微信": "y",
+          "阿里": "u",
+          "百度": "u",
+          "字节跳动": "u",
+          "QQ": "u",
+          "京东": "u"
+        },
+        "快应用": {
+          "华为": "u",
+          "联盟": "u"
+        }
+      }
+    }
+  }
+}

+ 8 - 0
uni_modules/uni-tooltip/readme.md

@@ -0,0 +1,8 @@
+## Badge 数字角标
+> **组件名:uni-tooltip**
+> 代码块: `uTooltip`
+
+数字角标一般和其它控件(列表、9宫格等)配合使用,用于进行数量提示,默认为实心灰色背景,
+
+### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-tooltip)
+#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839