|
@@ -1,30 +1,5 @@
|
|
|
<template>
|
|
<template>
|
|
|
<view class="report-page">
|
|
<view class="report-page">
|
|
|
- <view class="filter-bar">
|
|
|
|
|
- <view class="filter-main">
|
|
|
|
|
- <text class="filter-label">统计日期</text>
|
|
|
|
|
- <uni-datetime-picker
|
|
|
|
|
- v-model="selectedDate"
|
|
|
|
|
- class="date-picker"
|
|
|
|
|
- type="date"
|
|
|
|
|
- return-type="string"
|
|
|
|
|
- :clear-icon="false"
|
|
|
|
|
- :border="false"
|
|
|
|
|
- @change="handleDateChange"
|
|
|
|
|
- />
|
|
|
|
|
- </view>
|
|
|
|
|
- <button
|
|
|
|
|
- class="filter-button"
|
|
|
|
|
- type="primary"
|
|
|
|
|
- size="mini"
|
|
|
|
|
- @click="loadList"
|
|
|
|
|
- >
|
|
|
|
|
- 查询
|
|
|
|
|
- </button>
|
|
|
|
|
- </view>
|
|
|
|
|
-
|
|
|
|
|
- <view class="filter-tip">查询范围:{{ queryRangeText }}</view>
|
|
|
|
|
-
|
|
|
|
|
<scroll-view class="report-scroll" scroll-y>
|
|
<scroll-view class="report-scroll" scroll-y>
|
|
|
<view v-if="loading" class="state-card">
|
|
<view v-if="loading" class="state-card">
|
|
|
<text class="state-text">数据加载中...</text>
|
|
<text class="state-text">数据加载中...</text>
|
|
@@ -43,10 +18,9 @@
|
|
|
<view class="card-header">
|
|
<view class="card-header">
|
|
|
<view>
|
|
<view>
|
|
|
<view class="dept-name">{{ item.projectDeptName || "--" }}</view>
|
|
<view class="dept-name">{{ item.projectDeptName || "--" }}</view>
|
|
|
- <view class="dept-meta">排序 {{ item.sort ?? "--" }}</view>
|
|
|
|
|
</view>
|
|
</view>
|
|
|
- <view class="rate-badge">
|
|
|
|
|
- {{ formatPercent(item.utilizationRate) }}
|
|
|
|
|
|
|
+ <view class="rate-badge" @click="openTeamDetail(item)">
|
|
|
|
|
+ 查看队伍明细
|
|
|
</view>
|
|
</view>
|
|
|
</view>
|
|
</view>
|
|
|
|
|
|
|
@@ -64,64 +38,52 @@
|
|
|
}}</text>
|
|
}}</text>
|
|
|
</view>
|
|
</view>
|
|
|
<view class="metric-item">
|
|
<view class="metric-item">
|
|
|
- <text class="metric-label">值班队伍</text>
|
|
|
|
|
|
|
+ <text class="metric-label">施工准备</text>
|
|
|
<text class="metric-value">{{
|
|
<text class="metric-value">{{
|
|
|
formatNumber(item.zbTeamCount)
|
|
formatNumber(item.zbTeamCount)
|
|
|
}}</text>
|
|
}}</text>
|
|
|
</view>
|
|
</view>
|
|
|
<view class="metric-item">
|
|
<view class="metric-item">
|
|
|
- <text class="metric-label">待命队伍</text>
|
|
|
|
|
|
|
+ <text class="metric-label">待命</text>
|
|
|
<text class="metric-value">{{
|
|
<text class="metric-value">{{
|
|
|
formatNumber(item.dmTeamCount)
|
|
formatNumber(item.dmTeamCount)
|
|
|
}}</text>
|
|
}}</text>
|
|
|
</view>
|
|
</view>
|
|
|
<view class="metric-item">
|
|
<view class="metric-item">
|
|
|
- <text class="metric-label">累计天数</text>
|
|
|
|
|
|
|
+ <text class="metric-label">当日运行时效</text>
|
|
|
<text class="metric-value">{{
|
|
<text class="metric-value">{{
|
|
|
- formatNumber(item.cumulativeDays)
|
|
|
|
|
|
|
+ formatPercent(item.hourUtilizationRate)
|
|
|
}}</text>
|
|
}}</text>
|
|
|
</view>
|
|
</view>
|
|
|
<view class="metric-item">
|
|
<view class="metric-item">
|
|
|
- <text class="metric-label">施工天数</text>
|
|
|
|
|
|
|
+ <text class="metric-label">平均运行时效</text>
|
|
|
<text class="metric-value">{{
|
|
<text class="metric-value">{{
|
|
|
- formatNumber(item.constructionDays)
|
|
|
|
|
|
|
+ formatPercent(item.yearHourUtilizationRate)
|
|
|
}}</text>
|
|
}}</text>
|
|
|
</view>
|
|
</view>
|
|
|
</view>
|
|
</view>
|
|
|
|
|
|
|
|
<view class="summary-panel">
|
|
<view class="summary-panel">
|
|
|
<view class="summary-row">
|
|
<view class="summary-row">
|
|
|
- <text class="summary-label">小时利用率</text>
|
|
|
|
|
- <text class="summary-value">
|
|
|
|
|
- {{ formatPercent(item.hourUtilizationRate) }}
|
|
|
|
|
- </text>
|
|
|
|
|
- </view>
|
|
|
|
|
- <view class="summary-row">
|
|
|
|
|
- <text class="summary-label">年小时利用率</text>
|
|
|
|
|
- <text class="summary-value">
|
|
|
|
|
- {{ formatPercent(item.yearHourUtilizationRate) }}
|
|
|
|
|
- </text>
|
|
|
|
|
- </view>
|
|
|
|
|
- <view class="summary-row">
|
|
|
|
|
- <text class="summary-label">注水量</text>
|
|
|
|
|
|
|
+ <text class="summary-label">日注水量(万方)</text>
|
|
|
<text class="summary-value">
|
|
<text class="summary-value">
|
|
|
{{ formatDecimal(item.waterInjection) }}
|
|
{{ formatDecimal(item.waterInjection) }}
|
|
|
</text>
|
|
</text>
|
|
|
</view>
|
|
</view>
|
|
|
<view class="summary-row">
|
|
<view class="summary-row">
|
|
|
- <text class="summary-label">年注水量</text>
|
|
|
|
|
|
|
+ <text class="summary-label">累计注水量(万方)</text>
|
|
|
<text class="summary-value">
|
|
<text class="summary-value">
|
|
|
{{ formatDecimal(item.yearWaterInjection) }}
|
|
{{ formatDecimal(item.yearWaterInjection) }}
|
|
|
</text>
|
|
</text>
|
|
|
</view>
|
|
</view>
|
|
|
<view class="summary-row">
|
|
<view class="summary-row">
|
|
|
- <text class="summary-label">注气量</text>
|
|
|
|
|
|
|
+ <text class="summary-label">日注气量(万方)</text>
|
|
|
<text class="summary-value">
|
|
<text class="summary-value">
|
|
|
{{ formatDecimal(item.gasInjection) }}
|
|
{{ formatDecimal(item.gasInjection) }}
|
|
|
</text>
|
|
</text>
|
|
|
</view>
|
|
</view>
|
|
|
<view class="summary-row">
|
|
<view class="summary-row">
|
|
|
- <text class="summary-label">年注气量</text>
|
|
|
|
|
|
|
+ <text class="summary-label">累计注气量(万方)</text>
|
|
|
<text class="summary-value">
|
|
<text class="summary-value">
|
|
|
{{ formatDecimal(item.yearGasInjection) }}
|
|
{{ formatDecimal(item.yearGasInjection) }}
|
|
|
</text>
|
|
</text>
|
|
@@ -137,6 +99,97 @@
|
|
|
</view>
|
|
</view>
|
|
|
</view>
|
|
</view>
|
|
|
</scroll-view>
|
|
</scroll-view>
|
|
|
|
|
+
|
|
|
|
|
+ <view class="filter-bar">
|
|
|
|
|
+ <view class="filter-main">
|
|
|
|
|
+ <text class="filter-label">统计日期</text>
|
|
|
|
|
+
|
|
|
|
|
+ <uni-datetime-picker
|
|
|
|
|
+ v-model="selectedDate"
|
|
|
|
|
+ class="date-picker"
|
|
|
|
|
+ type="date"
|
|
|
|
|
+ :teleport="true"
|
|
|
|
|
+ return-type="string"
|
|
|
|
|
+ :clear-icon="false"
|
|
|
|
|
+ :border="false"
|
|
|
|
|
+ @change="handleDateChange"
|
|
|
|
|
+ />
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <button
|
|
|
|
|
+ class="filter-button"
|
|
|
|
|
+ type="primary"
|
|
|
|
|
+ size="mini"
|
|
|
|
|
+ @click="loadList"
|
|
|
|
|
+ >
|
|
|
|
|
+ 查询
|
|
|
|
|
+ </button>
|
|
|
|
|
+ </view>
|
|
|
|
|
+
|
|
|
|
|
+ <uni-popup
|
|
|
|
|
+ ref="teamDetailPopup"
|
|
|
|
|
+ type="right"
|
|
|
|
|
+ background-color="#fff"
|
|
|
|
|
+ :is-mask-click="true"
|
|
|
|
|
+ >
|
|
|
|
|
+ <view class="team-drawer">
|
|
|
|
|
+ <view class="team-drawer__header">
|
|
|
|
|
+ <text class="team-drawer__title">
|
|
|
|
|
+ {{ currentDetailTitle || "队伍明细" }}
|
|
|
|
|
+ </text>
|
|
|
|
|
+ <text class="team-drawer__close" @click="closeTeamDetail">关闭</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+
|
|
|
|
|
+ <view class="team-drawer__sub">
|
|
|
|
|
+ 统计日期:{{ selectedDate || "--" }}
|
|
|
|
|
+ </view>
|
|
|
|
|
+
|
|
|
|
|
+ <scroll-view class="team-drawer__scroll" scroll-y>
|
|
|
|
|
+ <view v-if="teamDetailLoading" class="team-state-card">
|
|
|
|
|
+ <text class="team-state-text">数据加载中...</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+
|
|
|
|
|
+ <view
|
|
|
|
|
+ v-else-if="!teamDetailList.length"
|
|
|
|
|
+ class="team-state-card empty"
|
|
|
|
|
+ >
|
|
|
|
|
+ <text class="team-state-text">暂无队伍明细</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+
|
|
|
|
|
+ <view v-else class="team-detail-list">
|
|
|
|
|
+ <view
|
|
|
|
|
+ v-for="(team, index) in teamDetailList"
|
|
|
|
|
+ :key="team.teamName || index"
|
|
|
|
|
+ class="team-detail-card"
|
|
|
|
|
+ >
|
|
|
|
|
+ <view class="team-detail-card__title">
|
|
|
|
|
+ {{ team.teamName || "--" }}
|
|
|
|
|
+ </view>
|
|
|
|
|
+
|
|
|
|
|
+ <view class="team-detail-grid">
|
|
|
|
|
+ <view class="team-detail-item">
|
|
|
|
|
+ <text class="team-detail-label">累计天数</text>
|
|
|
|
|
+ <text class="team-detail-value">{{
|
|
|
|
|
+ formatNumber(team.cumulativeDays)
|
|
|
|
|
+ }}</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <view class="team-detail-item">
|
|
|
|
|
+ <text class="team-detail-label">施工天数</text>
|
|
|
|
|
+ <text class="team-detail-value">{{
|
|
|
|
|
+ formatNumber(team.constructionDays)
|
|
|
|
|
+ }}</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <view class="team-detail-item team-detail-item--full">
|
|
|
|
|
+ <text class="team-detail-label">设备利用率</text>
|
|
|
|
|
+ <text class="team-detail-value">{{
|
|
|
|
|
+ formatPercent(team.utilizationRate)
|
|
|
|
|
+ }}</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </scroll-view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </uni-popup>
|
|
|
</view>
|
|
</view>
|
|
|
</template>
|
|
</template>
|
|
|
|
|
|
|
@@ -144,11 +197,15 @@
|
|
|
import { computed, ref } from "vue";
|
|
import { computed, ref } from "vue";
|
|
|
import { onMounted } from "vue";
|
|
import { onMounted } from "vue";
|
|
|
import dayjs from "dayjs";
|
|
import dayjs from "dayjs";
|
|
|
-import { getRhRate } from "@/api/ruiHengReport";
|
|
|
|
|
|
|
+import { getRhRate, getRhTeamDetail } from "@/api/ruiHengReport";
|
|
|
|
|
|
|
|
const selectedDate = ref(dayjs().subtract(1, "day").format("YYYY-MM-DD"));
|
|
const selectedDate = ref(dayjs().subtract(1, "day").format("YYYY-MM-DD"));
|
|
|
const dataList = ref([]);
|
|
const dataList = ref([]);
|
|
|
const loading = ref(false);
|
|
const loading = ref(false);
|
|
|
|
|
+const teamDetailPopup = ref(null);
|
|
|
|
|
+const teamDetailLoading = ref(false);
|
|
|
|
|
+const teamDetailList = ref([]);
|
|
|
|
|
+const currentDetailTitle = ref("");
|
|
|
|
|
|
|
|
const buildQueryParams = () => {
|
|
const buildQueryParams = () => {
|
|
|
const baseDate = selectedDate.value
|
|
const baseDate = selectedDate.value
|
|
@@ -163,11 +220,6 @@ const buildQueryParams = () => {
|
|
|
};
|
|
};
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
-const queryRangeText = computed(() => {
|
|
|
|
|
- const [startTime, endTime] = buildQueryParams().createTime;
|
|
|
|
|
- return `${startTime} - ${endTime}`;
|
|
|
|
|
-});
|
|
|
|
|
-
|
|
|
|
|
const sortedList = computed(() =>
|
|
const sortedList = computed(() =>
|
|
|
[...dataList.value].sort((a, b) => (a.sort ?? 999999) - (b.sort ?? 999999)),
|
|
[...dataList.value].sort((a, b) => (a.sort ?? 999999) - (b.sort ?? 999999)),
|
|
|
);
|
|
);
|
|
@@ -192,6 +244,33 @@ const handleDateChange = () => {
|
|
|
loadList();
|
|
loadList();
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
|
|
+const openTeamDetail = async (item) => {
|
|
|
|
|
+ currentDetailTitle.value = `${item.projectDeptName || "--"} 队伍明细`;
|
|
|
|
|
+ teamDetailList.value = [];
|
|
|
|
|
+ teamDetailLoading.value = true;
|
|
|
|
|
+ teamDetailPopup.value?.open();
|
|
|
|
|
+
|
|
|
|
|
+ try {
|
|
|
|
|
+ const response = await getRhTeamDetail({
|
|
|
|
|
+ deptId: item.projectDeptId,
|
|
|
|
|
+ createTime: buildQueryParams().createTime,
|
|
|
|
|
+ });
|
|
|
|
|
+ teamDetailList.value = Array.isArray(response?.data) ? response.data : [];
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ teamDetailList.value = [];
|
|
|
|
|
+ uni.showToast({
|
|
|
|
|
+ title: "队伍明细加载失败",
|
|
|
|
|
+ icon: "none",
|
|
|
|
|
+ });
|
|
|
|
|
+ } finally {
|
|
|
|
|
+ teamDetailLoading.value = false;
|
|
|
|
|
+ }
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+const closeTeamDetail = () => {
|
|
|
|
|
+ teamDetailPopup.value?.close();
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
const formatNumber = (value) => {
|
|
const formatNumber = (value) => {
|
|
|
if (value === null || value === undefined || value === "") return "--";
|
|
if (value === null || value === undefined || value === "") return "--";
|
|
|
return value;
|
|
return value;
|
|
@@ -217,13 +296,6 @@ onMounted(() => {
|
|
|
min-height: 100vh;
|
|
min-height: 100vh;
|
|
|
padding: 12px;
|
|
padding: 12px;
|
|
|
box-sizing: border-box;
|
|
box-sizing: border-box;
|
|
|
- background:
|
|
|
|
|
- radial-gradient(
|
|
|
|
|
- circle at top left,
|
|
|
|
|
- rgba(0, 64, 152, 0.14),
|
|
|
|
|
- transparent 28%
|
|
|
|
|
- ),
|
|
|
|
|
- linear-gradient(180deg, #f4f7fb 0%, #eef2f8 100%);
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
.filter-bar {
|
|
.filter-bar {
|
|
@@ -240,6 +312,10 @@ onMounted(() => {
|
|
|
.filter-main {
|
|
.filter-main {
|
|
|
flex: 1;
|
|
flex: 1;
|
|
|
min-width: 0;
|
|
min-width: 0;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ justify-content: space-between;
|
|
|
|
|
+ gap: 10rpx;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
.filter-label {
|
|
.filter-label {
|
|
@@ -259,13 +335,11 @@ onMounted(() => {
|
|
|
|
|
|
|
|
.filter-button {
|
|
.filter-button {
|
|
|
width: 76px;
|
|
width: 76px;
|
|
|
- height: 40px;
|
|
|
|
|
|
|
+ height: 38px;
|
|
|
line-height: 40px;
|
|
line-height: 40px;
|
|
|
- margin: 20px 0 0;
|
|
|
|
|
border-radius: 12px;
|
|
border-radius: 12px;
|
|
|
background: linear-gradient(135deg, #004098 0%, #1f68d8 100%);
|
|
background: linear-gradient(135deg, #004098 0%, #1f68d8 100%);
|
|
|
font-size: 14px;
|
|
font-size: 14px;
|
|
|
- box-shadow: 0 10px 22px rgba(0, 64, 152, 0.22);
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
.filter-tip {
|
|
.filter-tip {
|
|
@@ -417,4 +491,118 @@ onMounted(() => {
|
|
|
color: #73839a;
|
|
color: #73839a;
|
|
|
font-size: 14px;
|
|
font-size: 14px;
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+.team-drawer {
|
|
|
|
|
+ width: 82vw;
|
|
|
|
|
+ max-width: 620rpx;
|
|
|
|
|
+ height: 100vh;
|
|
|
|
|
+ padding: 18px 14px calc(18px + env(safe-area-inset-bottom));
|
|
|
|
|
+ box-sizing: border-box;
|
|
|
|
|
+ background: linear-gradient(180deg, #f8fbff 0%, #eef4fb 100%);
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ flex-direction: column;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.team-drawer__header {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ justify-content: space-between;
|
|
|
|
|
+ gap: 12px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.team-drawer__title {
|
|
|
|
|
+ min-width: 0;
|
|
|
|
|
+ color: #122033;
|
|
|
|
|
+ font-size: 18px;
|
|
|
|
|
+ font-weight: 700;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.team-drawer__close {
|
|
|
|
|
+ flex-shrink: 0;
|
|
|
|
|
+ color: #004098;
|
|
|
|
|
+ font-size: 14px;
|
|
|
|
|
+ font-weight: 600;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.team-drawer__sub {
|
|
|
|
|
+ margin-top: 8px;
|
|
|
|
|
+ margin-bottom: 14px;
|
|
|
|
|
+ color: #70819a;
|
|
|
|
|
+ font-size: 12px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.team-drawer__scroll {
|
|
|
|
|
+ flex: 1;
|
|
|
|
|
+ min-height: 0;
|
|
|
|
|
+ padding-bottom: 50rpx;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.team-detail-list {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ flex-direction: column;
|
|
|
|
|
+ gap: 12px;
|
|
|
|
|
+ padding-bottom: 10px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.team-detail-card {
|
|
|
|
|
+ padding: 14px;
|
|
|
|
|
+ border-radius: 16px;
|
|
|
|
|
+ background: rgba(255, 255, 255, 0.96);
|
|
|
|
|
+ box-shadow: 0 10px 24px rgba(31, 57, 90, 0.08);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.team-detail-card__title {
|
|
|
|
|
+ margin-bottom: 12px;
|
|
|
|
|
+ color: #18273b;
|
|
|
|
|
+ font-size: 16px;
|
|
|
|
|
+ font-weight: 700;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.team-detail-grid {
|
|
|
|
|
+ display: grid;
|
|
|
|
|
+ grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
|
|
|
+ gap: 10px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.team-detail-item {
|
|
|
|
|
+ padding: 10px 12px;
|
|
|
|
|
+ border-radius: 12px;
|
|
|
|
|
+ background: #f6f9fd;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.team-detail-item--full {
|
|
|
|
|
+ grid-column: 1 / -1;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.team-detail-label {
|
|
|
|
|
+ display: block;
|
|
|
|
|
+ color: #6e7e95;
|
|
|
|
|
+ font-size: 12px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.team-detail-value {
|
|
|
|
|
+ display: block;
|
|
|
|
|
+ margin-top: 6px;
|
|
|
|
|
+ color: #18273b;
|
|
|
|
|
+ font-size: 18px;
|
|
|
|
|
+ font-weight: 700;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.team-state-card {
|
|
|
|
|
+ margin-top: 8px;
|
|
|
|
|
+ padding: 36px 20px;
|
|
|
|
|
+ border-radius: 16px;
|
|
|
|
|
+ background: rgba(255, 255, 255, 0.92);
|
|
|
|
|
+ text-align: center;
|
|
|
|
|
+ box-shadow: 0 12px 28px rgba(30, 54, 88, 0.06);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.team-state-card.empty {
|
|
|
|
|
+ border: 1px dashed rgba(0, 64, 152, 0.16);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.team-state-text {
|
|
|
|
|
+ color: #73839a;
|
|
|
|
|
+ font-size: 14px;
|
|
|
|
|
+}
|
|
|
</style>
|
|
</style>
|