yanghao 1 месяц назад
Родитель
Сommit
ad492d3bcd

+ 7 - 0
src/api/pms/iotrydailyreport/index.ts

@@ -90,6 +90,13 @@ export const IotRyDailyReportApi = {
     return await request.get({ url: `/pms/iot-ry-daily-report/nptStatistics`, params })
   },
 
+  exportNptStatistics: async (params: any) => {
+    return await request.download({
+      url: `/pms/iot-ry-daily-report/exportNptStatistics`,
+      params
+    })
+  },
+
   // 累计工作量统计
   totalWorkload: async (params: any) => {
     return await request.get({ url: `/pms/iot-ry-daily-report/totalWorkload`, params })

+ 1 - 1
src/views/pms/iotrydailyreport/components/NonProductionEfficiency.vue

@@ -406,7 +406,7 @@ const { ZmTable, ZmTableColumn } = useTableComponents<ListItem>()
             @row-click="handleRowClick">
             <zm-table-column
               prop="name"
-              label="队伍"
+              label="部门"
               min-width="120"
               fixed="left"
               cover-formatter

+ 1 - 1
src/views/pms/iotrydailyreport/components/XjNonProductionEfficiency.vue

@@ -406,7 +406,7 @@ const { ZmTable, ZmTableColumn } = useTableComponents<ListItem>()
             @row-click="handleRowClick">
             <zm-table-column
               prop="name"
-              label="队伍"
+              label="部门"
               min-width="120"
               fixed="left"
               cover-formatter

+ 63 - 0
src/views/pms/iotrydailyreport/summary.vue

@@ -1,5 +1,7 @@
 <script setup lang="ts">
 import dayjs from 'dayjs'
+import { IotRyDailyReportApi } from '@/api/pms/iotrydailyreport'
+import download from '@/utils/download'
 import { rangeShortcuts } from '@/utils/formatTime'
 import { resolveDailyReportDeptId } from '@/utils/dailyReportDept'
 import { useUserStore } from '@/store/modules/user'
@@ -10,6 +12,9 @@ defineOptions({
   name: 'IotRyDailyReportSummary'
 })
 
+const route = useRoute()
+const pageRouteName = route.name
+const message = useMessage()
 const TOP_DEPT_ID = 158
 const deptId = resolveDailyReportDeptId(useUserStore().getUser.deptId, TOP_DEPT_ID)
 
@@ -41,6 +46,33 @@ const query = ref<Query>(createDefaultQuery())
 const activeTab = ref<'日报统计' | '非生产时效'>('日报统计')
 const deptName = ref('瑞鹰国际钻井')
 const refreshKey = ref(0)
+const exportLoading = ref(false)
+
+const getRouteCreateTime = () => {
+  const createTime = route.query.createTime
+
+  if (Array.isArray(createTime)) {
+    const values = createTime.filter((item): item is string => typeof item === 'string')
+    return values.length === 2 ? values : undefined
+  }
+
+  if (typeof createTime === 'string') {
+    const values = createTime.split(',').filter(Boolean)
+    return values.length === 2 ? values : undefined
+  }
+
+  return undefined
+}
+
+const syncQueryFromRoute = () => {
+  if (route.name !== pageRouteName) return
+
+  const createTime = getRouteCreateTime()
+  if (createTime) query.value.createTime = createTime
+
+  activeTab.value = route.query.activeTab === '非生产时效' ? '非生产时效' : '日报统计'
+  handleQuery()
+}
 
 const handleDeptNodeClick = (node: any) => {
   deptName.value = node.name
@@ -57,6 +89,27 @@ const resetQuery = () => {
   deptName.value = '瑞鹰国际钻井'
   handleQuery()
 }
+
+const handleExport = async () => {
+  try {
+    await message.exportConfirm()
+
+    exportLoading.value = true
+    const { pageNo: _pageNo, pageSize: _pageSize, ...params } = query.value
+
+    void _pageNo
+    void _pageSize
+
+    const data = await IotRyDailyReportApi.exportNptStatistics(params)
+    download.excel(data, '瑞鹰钻井非生产时效统计.xlsx')
+  } finally {
+    exportLoading.value = false
+  }
+}
+
+onMounted(syncQueryFromRoute)
+
+watch(() => [route.query.createTime, route.query.activeTab], syncQueryFromRoute)
 </script>
 
 <template>
@@ -108,6 +161,16 @@ const resetQuery = () => {
         <el-button size="small" @click="resetQuery">
           <Icon icon="ep:refresh" class="mr-5px" /> 重置
         </el-button>
+        <el-button
+          v-if="activeTab === '非生产时效'"
+          v-hasPermi="['pms:iot-ry-daily-report:export']"
+          size="small"
+          type="success"
+          plain
+          :loading="exportLoading"
+          @click="handleExport">
+          <Icon icon="ep:download" class="mr-5px" /> 导出
+        </el-button>
       </el-form-item>
     </el-form>
 

+ 63 - 0
src/views/pms/iotrydailyreport/xsummary.vue

@@ -1,5 +1,7 @@
 <script setup lang="ts">
 import dayjs from 'dayjs'
+import { IotRyDailyReportApi } from '@/api/pms/iotrydailyreport'
+import download from '@/utils/download'
 import { rangeShortcuts } from '@/utils/formatTime'
 import { resolveDailyReportDeptId } from '@/utils/dailyReportDept'
 import { useUserStore } from '@/store/modules/user'
@@ -10,6 +12,9 @@ defineOptions({
   name: 'IotRyXjDailyReportSummary'
 })
 
+const route = useRoute()
+const pageRouteName = route.name
+const message = useMessage()
 const TOP_DEPT_ID = 158
 const deptId = resolveDailyReportDeptId(useUserStore().getUser.deptId, TOP_DEPT_ID)
 
@@ -41,6 +46,33 @@ const query = ref<Query>(createDefaultQuery())
 const activeTab = ref<'日报统计' | '非生产时效'>('日报统计')
 const deptName = ref('瑞鹰国际修井')
 const refreshKey = ref(0)
+const exportLoading = ref(false)
+
+const getRouteCreateTime = () => {
+  const createTime = route.query.createTime
+
+  if (Array.isArray(createTime)) {
+    const values = createTime.filter((item): item is string => typeof item === 'string')
+    return values.length === 2 ? values : undefined
+  }
+
+  if (typeof createTime === 'string') {
+    const values = createTime.split(',').filter(Boolean)
+    return values.length === 2 ? values : undefined
+  }
+
+  return undefined
+}
+
+const syncQueryFromRoute = () => {
+  if (route.name !== pageRouteName) return
+
+  const createTime = getRouteCreateTime()
+  if (createTime) query.value.createTime = createTime
+
+  activeTab.value = route.query.activeTab === '非生产时效' ? '非生产时效' : '日报统计'
+  handleQuery()
+}
 
 const handleDeptNodeClick = (node: any) => {
   deptName.value = node.name
@@ -57,6 +89,27 @@ const resetQuery = () => {
   deptName.value = '瑞鹰国际修井'
   handleQuery()
 }
+
+const handleExport = async () => {
+  try {
+    await message.exportConfirm()
+
+    exportLoading.value = true
+    const { pageNo: _pageNo, pageSize: _pageSize, ...params } = query.value
+
+    void _pageNo
+    void _pageSize
+
+    const data = await IotRyDailyReportApi.exportNptStatistics(params)
+    download.excel(data, '瑞鹰修井非生产时效统计.xlsx')
+  } finally {
+    exportLoading.value = false
+  }
+}
+
+onMounted(syncQueryFromRoute)
+
+watch(() => [route.query.createTime, route.query.activeTab], syncQueryFromRoute)
 </script>
 
 <template>
@@ -108,6 +161,16 @@ const resetQuery = () => {
         <el-button size="small" @click="resetQuery">
           <Icon icon="ep:refresh" class="mr-5px" /> 重置
         </el-button>
+        <el-button
+          v-if="activeTab === '非生产时效'"
+          v-hasPermi="['pms:iot-ry-daily-report:export']"
+          size="small"
+          type="success"
+          plain
+          :loading="exportLoading"
+          @click="handleExport">
+          <Icon icon="ep:download" class="mr-5px" /> 导出
+        </el-button>
       </el-form-item>
     </el-form>
 

+ 4 - 4
src/views/pms/stat/rhkb/assetValue.vue

@@ -6,10 +6,10 @@ const chartRef = ref<HTMLDivElement>()
 let chart: echarts.ECharts | null = null
 
 const assetValueData = [
-  { name: '原值', value: 85000.77 },
-  { name: '本期计提', value: 512.61 },
-  { name: '累计折旧', value: 57722.06 },
-  { name: '净值', value: 27152.6 }
+  { name: '原值', value: 86405.45 },
+  { name: '本期计提', value: 499.82 },
+  { name: '累计折旧', value: 58556.74 },
+  { name: '净值', value: 27848.72 }
 ]
 
 function getChartOption(): echarts.EChartsOption {

+ 94 - 2
src/views/pms/stat/rykb/exception-prompt.vue

@@ -10,11 +10,20 @@ type TimeRange = 'day' | 'month' | 'quarter' | 'year'
 type NptCountItem = {
   nptName: string
   teamCount: number
+  classificationPair: {
+    zj: number
+    xj: number
+  }
 }
 
+type ProjectClassification = 'zj' | 'xj'
+
+const router = useRouter()
 const activeTimeRange = ref<TimeRange>('day')
 const loading = ref(false)
 const productionList = ref<NptCountItem[]>([])
+const classificationDialogVisible = ref(false)
+const pendingProductionItem = ref<NptCountItem | null>(null)
 
 const timeOptions: Array<{ label: string; value: TimeRange }> = [
   { label: '本日', value: 'day' },
@@ -34,6 +43,48 @@ function getCreateTime(type: TimeRange) {
   return [start.format('YYYY-MM-DD HH:mm:ss'), end.format('YYYY-MM-DD HH:mm:ss')]
 }
 
+function hasClassification(item: NptCountItem, classification: ProjectClassification) {
+  return item.classificationPair[classification] > 0
+}
+
+function navigateToSummary(item: NptCountItem, classification: ProjectClassification) {
+  classificationDialogVisible.value = false
+  pendingProductionItem.value = null
+
+  router.push({
+    name:
+      classification === 'zj' ? 'IotRyDailyReportSummary' : 'IotRyXjDailyReportSummary',
+    query: {
+      activeTab: '非生产时效',
+      createTime: getCreateTime(activeTimeRange.value),
+      nptName: item.nptName
+    }
+  })
+}
+
+function handleProductionClick(item: NptCountItem) {
+  const hasZj = hasClassification(item, 'zj')
+  const hasXj = hasClassification(item, 'xj')
+
+  if (hasZj && hasXj) {
+    pendingProductionItem.value = item
+    classificationDialogVisible.value = true
+    return
+  }
+
+  if (hasZj) {
+    navigateToSummary(item, 'zj')
+  } else if (hasXj) {
+    navigateToSummary(item, 'xj')
+  }
+}
+
+function handleClassificationSelect(classification: ProjectClassification) {
+  if (!pendingProductionItem.value) return
+
+  navigateToSummary(pendingProductionItem.value, classification)
+}
+
 async function loadData() {
   loading.value = true
   try {
@@ -44,7 +95,11 @@ async function loadData() {
     productionList.value = Array.isArray(res)
       ? res.map((item) => ({
           nptName: item.nptName || '-',
-          teamCount: Number(item.teamCount || 0)
+          teamCount: Number(item.teamCount || 0),
+          classificationPair: {
+            zj: Number(item.classificationPair?.zj || 0),
+            xj: Number(item.classificationPair?.xj || 0)
+          }
         }))
       : []
   } catch (error) {
@@ -92,7 +147,12 @@ onMounted(() => {
           <div
             v-for="(item, index) in productionTopList"
             :key="item.nptName"
-            class="production-item">
+            class="production-item"
+            :class="{
+              'production-item--clickable':
+                hasClassification(item, 'zj') || hasClassification(item, 'xj')
+            }"
+            @click="handleProductionClick(item)">
             <span class="production-item__rank">{{ index + 1 }}</span>
             <span class="production-item__name">{{ item.nptName }}</span>
             <span class="production-item__count">
@@ -118,6 +178,21 @@ onMounted(() => {
         </div>
       </section>
     </div>
+
+    <el-dialog
+      v-model="classificationDialogVisible"
+      title="选择业务类型"
+      width="360px"
+      append-to-body>
+      <div class="classification-dialog__content">
+        “{{ pendingProductionItem?.nptName }}”同时包含钻井和修井数据,请选择要查看的类型。
+      </div>
+      <template #footer>
+        <el-button @click="classificationDialogVisible = false">取消</el-button>
+        <el-button type="primary" @click="handleClassificationSelect('zj')">查看钻井</el-button>
+        <el-button type="primary" @click="handleClassificationSelect('xj')">查看修井</el-button>
+      </template>
+    </el-dialog>
   </div>
 </template>
 
@@ -232,6 +307,23 @@ onMounted(() => {
   gap: calc(6px * var(--kb-scale, 1));
 }
 
+.production-item--clickable {
+  cursor: pointer;
+  transition:
+    border-color 0.2s,
+    transform 0.2s;
+
+  &:hover {
+    border-color: rgb(45 124 248 / 38%);
+    transform: translateY(calc(-1px * var(--kb-scale, 1)));
+  }
+}
+
+.classification-dialog__content {
+  line-height: 1.7;
+  color: var(--el-text-color-regular);
+}
+
 .production-item__rank {
   display: inline-flex;
   width: calc(18px * var(--kb-scale, 1));