|
@@ -1,105 +1,16 @@
|
|
|
<script setup lang="ts">
|
|
<script setup lang="ts">
|
|
|
import DeptTree2 from '@/views/pms/iotrhdailyreport/DeptTree2.vue'
|
|
import DeptTree2 from '@/views/pms/iotrhdailyreport/DeptTree2.vue'
|
|
|
-import quarterOfYear from 'dayjs/plugin/quarterOfYear'
|
|
|
|
|
import dayjs from 'dayjs'
|
|
import dayjs from 'dayjs'
|
|
|
import { IotRhDailyReportApi } from '@/api/pms/iotrhdailyreport'
|
|
import { IotRhDailyReportApi } from '@/api/pms/iotrhdailyreport'
|
|
|
import { useDebounceFn } from '@vueuse/core'
|
|
import { useDebounceFn } from '@vueuse/core'
|
|
|
import CountTo from '@/components/count-to1.vue'
|
|
import CountTo from '@/components/count-to1.vue'
|
|
|
|
|
+import * as echarts from 'echarts'
|
|
|
|
|
+import UnfilledReportDialog from './UnfilledReportDialog.vue'
|
|
|
|
|
|
|
|
-dayjs.extend(quarterOfYear)
|
|
|
|
|
|
|
+import { Motion, AnimatePresence } from 'motion-v'
|
|
|
|
|
|
|
|
-const rangeShortcuts = [
|
|
|
|
|
- {
|
|
|
|
|
- text: '今天',
|
|
|
|
|
- value: () => {
|
|
|
|
|
- const today = dayjs()
|
|
|
|
|
- return [today.startOf('day').toDate(), today.endOf('day').toDate()]
|
|
|
|
|
- }
|
|
|
|
|
- },
|
|
|
|
|
- {
|
|
|
|
|
- text: '昨天',
|
|
|
|
|
- value: () => {
|
|
|
|
|
- const yesterday = dayjs().subtract(1, 'day')
|
|
|
|
|
- return [yesterday.startOf('day').toDate(), yesterday.endOf('day').toDate()]
|
|
|
|
|
- }
|
|
|
|
|
- },
|
|
|
|
|
- {
|
|
|
|
|
- text: '本周',
|
|
|
|
|
- value: () => {
|
|
|
|
|
- return [dayjs().startOf('week').toDate(), dayjs().endOf('week').toDate()]
|
|
|
|
|
- }
|
|
|
|
|
- },
|
|
|
|
|
- {
|
|
|
|
|
- text: '上周',
|
|
|
|
|
- value: () => {
|
|
|
|
|
- const lastWeek = dayjs().subtract(1, 'week')
|
|
|
|
|
- return [lastWeek.startOf('week').toDate(), lastWeek.endOf('week').toDate()]
|
|
|
|
|
- }
|
|
|
|
|
- },
|
|
|
|
|
- {
|
|
|
|
|
- text: '本月',
|
|
|
|
|
- value: () => {
|
|
|
|
|
- return [dayjs().startOf('month').toDate(), dayjs().endOf('month').toDate()]
|
|
|
|
|
- }
|
|
|
|
|
- },
|
|
|
|
|
- {
|
|
|
|
|
- text: '上月',
|
|
|
|
|
- value: () => {
|
|
|
|
|
- const lastMonth = dayjs().subtract(1, 'month')
|
|
|
|
|
- return [lastMonth.startOf('month').toDate(), lastMonth.endOf('month').toDate()]
|
|
|
|
|
- }
|
|
|
|
|
- },
|
|
|
|
|
- {
|
|
|
|
|
- text: '本季度',
|
|
|
|
|
- value: () => {
|
|
|
|
|
- return [dayjs().startOf('quarter').toDate(), dayjs().endOf('quarter').toDate()]
|
|
|
|
|
- }
|
|
|
|
|
- },
|
|
|
|
|
- {
|
|
|
|
|
- text: '上季度',
|
|
|
|
|
- value: () => {
|
|
|
|
|
- const lastQuarter = dayjs().subtract(1, 'quarter')
|
|
|
|
|
- return [lastQuarter.startOf('quarter').toDate(), lastQuarter.endOf('quarter').toDate()]
|
|
|
|
|
- }
|
|
|
|
|
- },
|
|
|
|
|
- {
|
|
|
|
|
- text: '今年',
|
|
|
|
|
- value: () => {
|
|
|
|
|
- return [dayjs().startOf('year').toDate(), dayjs().endOf('year').toDate()]
|
|
|
|
|
- }
|
|
|
|
|
- },
|
|
|
|
|
- {
|
|
|
|
|
- text: '去年',
|
|
|
|
|
- value: () => {
|
|
|
|
|
- const lastYear = dayjs().subtract(1, 'year')
|
|
|
|
|
- return [lastYear.startOf('year').toDate(), lastYear.endOf('year').toDate()]
|
|
|
|
|
- }
|
|
|
|
|
- },
|
|
|
|
|
- {
|
|
|
|
|
- text: '最近7天',
|
|
|
|
|
- value: () => {
|
|
|
|
|
- return [dayjs().subtract(6, 'day').toDate(), dayjs().toDate()]
|
|
|
|
|
- }
|
|
|
|
|
- },
|
|
|
|
|
- {
|
|
|
|
|
- text: '最近30天',
|
|
|
|
|
- value: () => {
|
|
|
|
|
- return [dayjs().subtract(29, 'day').toDate(), dayjs().toDate()]
|
|
|
|
|
- }
|
|
|
|
|
- },
|
|
|
|
|
- {
|
|
|
|
|
- text: '最近90天',
|
|
|
|
|
- value: () => {
|
|
|
|
|
- return [dayjs().subtract(89, 'day').toDate(), dayjs().toDate()]
|
|
|
|
|
- }
|
|
|
|
|
- },
|
|
|
|
|
- {
|
|
|
|
|
- text: '最近一年',
|
|
|
|
|
- value: () => {
|
|
|
|
|
- return [dayjs().subtract(1, 'year').toDate(), dayjs().toDate()]
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-]
|
|
|
|
|
|
|
+import { rangeShortcuts } from '@/utils/formatTime'
|
|
|
|
|
+import download from '@/utils/download'
|
|
|
|
|
|
|
|
interface Query {
|
|
interface Query {
|
|
|
pageNo: number
|
|
pageNo: number
|
|
@@ -116,37 +27,43 @@ const query = ref<Query>({
|
|
|
pageNo: 1,
|
|
pageNo: 1,
|
|
|
pageSize: 10,
|
|
pageSize: 10,
|
|
|
deptId: 157,
|
|
deptId: 157,
|
|
|
- createTime: []
|
|
|
|
|
|
|
+ createTime: [
|
|
|
|
|
+ ...rangeShortcuts[2].value().map((item) => dayjs(item).format('YYYY-MM-DD HH:mm:ss'))
|
|
|
|
|
+ ]
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
-const totalWorkKeys = [
|
|
|
|
|
- ['totalCount', '个', '总数', 'i-tabler:report-analytics text-sky'],
|
|
|
|
|
|
|
+const totalWorkKeys: [string, string, string, string, number][] = [
|
|
|
|
|
+ ['totalCount', '个', '总数', 'i-tabler:report-analytics text-sky', 0],
|
|
|
[
|
|
[
|
|
|
'alreadyReported',
|
|
'alreadyReported',
|
|
|
'个',
|
|
'个',
|
|
|
'已填报',
|
|
'已填报',
|
|
|
- 'i-material-symbols:check-circle-outline-rounded text-emerald'
|
|
|
|
|
|
|
+ 'i-material-symbols:check-circle-outline-rounded text-emerald',
|
|
|
|
|
+ 0
|
|
|
],
|
|
],
|
|
|
- ['notReported', '个', '未填报', 'i-material-symbols:cancel-outline-rounded text-rose'],
|
|
|
|
|
|
|
+ ['notReported', '个', '未填报', 'i-material-symbols:cancel-outline-rounded text-rose', 0],
|
|
|
[
|
|
[
|
|
|
'totalFuelConsumption',
|
|
'totalFuelConsumption',
|
|
|
'L',
|
|
'L',
|
|
|
'累计油耗',
|
|
'累计油耗',
|
|
|
- 'i-material-symbols:directions-car-outline-rounded text-sky'
|
|
|
|
|
|
|
+ 'i-material-symbols:directions-car-outline-rounded text-sky',
|
|
|
|
|
+ 2
|
|
|
],
|
|
],
|
|
|
[
|
|
[
|
|
|
'totalPowerConsumption',
|
|
'totalPowerConsumption',
|
|
|
'KWH',
|
|
'KWH',
|
|
|
'累计用电量',
|
|
'累计用电量',
|
|
|
- 'i-material-symbols:electric-bolt-outline-rounded text-sky'
|
|
|
|
|
|
|
+ 'i-material-symbols:electric-bolt-outline-rounded text-sky',
|
|
|
|
|
+ 2
|
|
|
],
|
|
],
|
|
|
[
|
|
[
|
|
|
'totalWaterInjection',
|
|
'totalWaterInjection',
|
|
|
'方',
|
|
'方',
|
|
|
'累计注水量',
|
|
'累计注水量',
|
|
|
- 'i-material-symbols:water-drop-outline-rounded text-sky'
|
|
|
|
|
|
|
+ 'i-material-symbols:water-drop-outline-rounded text-sky',
|
|
|
|
|
+ 2
|
|
|
],
|
|
],
|
|
|
- ['totalGasInjection', '万方', '累计注气量', 'i-material-symbols:cloud-outline text-sky']
|
|
|
|
|
|
|
+ ['totalGasInjection', '万方', '累计注气量', 'i-material-symbols:cloud-outline text-sky', 4]
|
|
|
]
|
|
]
|
|
|
|
|
|
|
|
const totalWork = ref({
|
|
const totalWork = ref({
|
|
@@ -170,7 +87,8 @@ const getTotal = useDebounceFn(async () => {
|
|
|
let res1: any[]
|
|
let res1: any[]
|
|
|
if (query.value.createTime.length !== 0) {
|
|
if (query.value.createTime.length !== 0) {
|
|
|
res1 = await IotRhDailyReportApi.rhDailyReportStatistics({
|
|
res1 = await IotRhDailyReportApi.rhDailyReportStatistics({
|
|
|
- createTime: query.value.createTime
|
|
|
|
|
|
|
+ createTime: query.value.createTime,
|
|
|
|
|
+ deptId: query.value.deptId
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
totalWork.value.totalCount = res1[0].count
|
|
totalWork.value.totalCount = res1[0].count
|
|
@@ -201,8 +119,6 @@ interface List {
|
|
|
transitTime: number | null
|
|
transitTime: number | null
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-const total = ref<number>(1000)
|
|
|
|
|
-
|
|
|
|
|
const list = ref<List[]>([])
|
|
const list = ref<List[]>([])
|
|
|
|
|
|
|
|
const type = ref('2')
|
|
const type = ref('2')
|
|
@@ -241,31 +157,180 @@ const formatter = (row: List, column: any) => {
|
|
|
const getList = useDebounceFn(async () => {
|
|
const getList = useDebounceFn(async () => {
|
|
|
listLoading.value = true
|
|
listLoading.value = true
|
|
|
try {
|
|
try {
|
|
|
- const res = (await IotRhDailyReportApi.getIotRhDailyReportSummary(query.value)) as {
|
|
|
|
|
- total: number
|
|
|
|
|
- list: any[]
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ const res = await IotRhDailyReportApi.getIotRhDailyReportSummary(query.value)
|
|
|
|
|
|
|
|
- const { total: resTotal, list: resList } = res
|
|
|
|
|
|
|
+ const { list: reslist } = res
|
|
|
|
|
|
|
|
- total.value = resTotal
|
|
|
|
|
|
|
+ type.value = reslist[0]?.type || '2'
|
|
|
|
|
|
|
|
- type.value = resList[0]?.type || '2'
|
|
|
|
|
-
|
|
|
|
|
- list.value = resList.map(
|
|
|
|
|
- ({ id, projectDeptIa, projectDeptName, teamId, teamName, sort, taskId, type, ...other }) => ({
|
|
|
|
|
- id: type === '2' ? projectDeptIa : teamId,
|
|
|
|
|
- name: type === '2' ? projectDeptName : teamName,
|
|
|
|
|
- ...other,
|
|
|
|
|
- cumulativeGasInjection: (other.cumulativeGasInjection || 0) / 10000
|
|
|
|
|
- })
|
|
|
|
|
|
|
+ list.value = reslist.map(
|
|
|
|
|
+ ({ id, projectDeptId, projectDeptName, teamId, teamName, sort, taskId, type, ...other }) => {
|
|
|
|
|
+ return {
|
|
|
|
|
+ id: type === '2' ? projectDeptId : teamId,
|
|
|
|
|
+ name: type === '2' ? projectDeptName : teamName,
|
|
|
|
|
+ ...other,
|
|
|
|
|
+ cumulativeGasInjection: (other.cumulativeGasInjection || 0) / 10000
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
)
|
|
)
|
|
|
} finally {
|
|
} finally {
|
|
|
listLoading.value = false
|
|
listLoading.value = false
|
|
|
}
|
|
}
|
|
|
}, 1000)
|
|
}, 1000)
|
|
|
|
|
|
|
|
|
|
+const tab = ref<'表格' | '看板'>('表格')
|
|
|
|
|
+
|
|
|
|
|
+const currentTab = ref<'表格' | '看板'>('表格')
|
|
|
|
|
+
|
|
|
|
|
+const deptName = ref('瑞恒兴域')
|
|
|
|
|
+
|
|
|
|
|
+const direction = ref<'left' | 'right'>('right')
|
|
|
|
|
+
|
|
|
|
|
+const handleSelectTab = (val: '表格' | '看板') => {
|
|
|
|
|
+ tab.value = val
|
|
|
|
|
+ direction.value = val === '看板' ? 'right' : 'left'
|
|
|
|
|
+ nextTick(() => {
|
|
|
|
|
+ currentTab.value = val
|
|
|
|
|
+ setTimeout(() => {
|
|
|
|
|
+ render()
|
|
|
|
|
+ })
|
|
|
|
|
+ })
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const chartRef = ref<HTMLDivElement | null>(null)
|
|
|
|
|
+let chart: echarts.ECharts | null = null
|
|
|
|
|
+
|
|
|
|
|
+const xAxisData = ref<string[]>([])
|
|
|
|
|
+
|
|
|
|
|
+const legend = ref<string[][]>([
|
|
|
|
|
+ ['累计油耗 (吨)', 'cumulativeFuelConsumption'],
|
|
|
|
|
+ ['累计注气量 (万方)', 'cumulativeGasInjection'],
|
|
|
|
|
+ ['累计用电量 (KWH)', 'cumulativePowerConsumption'],
|
|
|
|
|
+ ['累计注水量 (方)', 'cumulativeWaterInjection'],
|
|
|
|
|
+ ['平均时效 (%)', 'transitTime']
|
|
|
|
|
+])
|
|
|
|
|
+
|
|
|
|
|
+const chartData = ref<Record<string, number[]>>({
|
|
|
|
|
+ cumulativeFuelConsumption: [],
|
|
|
|
|
+ cumulativeGasInjection: [],
|
|
|
|
|
+ cumulativePowerConsumption: [],
|
|
|
|
|
+ cumulativeWaterInjection: [],
|
|
|
|
|
+ transitTime: []
|
|
|
|
|
+})
|
|
|
|
|
+
|
|
|
|
|
+let chartLoading = ref(false)
|
|
|
|
|
+
|
|
|
|
|
+const getChart = useDebounceFn(async () => {
|
|
|
|
|
+ chartLoading.value = true
|
|
|
|
|
+
|
|
|
|
|
+ try {
|
|
|
|
|
+ const res = await IotRhDailyReportApi.getIotRhDailyReportSummaryPolyline(query.value)
|
|
|
|
|
+
|
|
|
|
|
+ chartData.value = {
|
|
|
|
|
+ cumulativeFuelConsumption: res.map((item) => item.cumulativeFuelConsumption || 0),
|
|
|
|
|
+ cumulativeGasInjection: res.map((item) => (item.cumulativeGasInjection || 0) / 10000),
|
|
|
|
|
+ cumulativePowerConsumption: res.map((item) => item.cumulativePowerConsumption || 0),
|
|
|
|
|
+ cumulativeWaterInjection: res.map((item) => item.cumulativeWaterInjection || 0),
|
|
|
|
|
+ transitTime: res.map((item) => (item.transitTime || 0) * 100)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ xAxisData.value = res.map((item) => item.reportDate || '')
|
|
|
|
|
+ } finally {
|
|
|
|
|
+ chartLoading.value = false
|
|
|
|
|
+ }
|
|
|
|
|
+}, 1000)
|
|
|
|
|
+
|
|
|
|
|
+const render = () => {
|
|
|
|
|
+ if (!chartRef.value) return
|
|
|
|
|
+
|
|
|
|
|
+ chart = echarts.init(chartRef.value, undefined, { renderer: 'canvas' })
|
|
|
|
|
+
|
|
|
|
|
+ const values: number[] = []
|
|
|
|
|
+
|
|
|
|
|
+ for (const [_name, key] of legend.value) {
|
|
|
|
|
+ values.push(...(chartData.value[key] || []))
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const maxVal = values.length === 0 ? 10000 : Math.max(...values)
|
|
|
|
|
+ const minVal = values.length === 0 ? 0 : Math.min(...values)
|
|
|
|
|
+
|
|
|
|
|
+ const maxDigits = (Math.floor(maxVal) + '').length
|
|
|
|
|
+ const minDigits = (Math.floor(Math.abs(minVal)) + '').length
|
|
|
|
|
+ const interval = Math.max(maxDigits, minDigits)
|
|
|
|
|
+
|
|
|
|
|
+ const maxInterval = interval
|
|
|
|
|
+ const minInterval = minDigits
|
|
|
|
|
+
|
|
|
|
|
+ const intervalArr = [0]
|
|
|
|
|
+ for (let i = 1; i <= interval; i++) {
|
|
|
|
|
+ intervalArr.push(Math.pow(10, i))
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ chart.setOption({
|
|
|
|
|
+ tooltip: {
|
|
|
|
|
+ trigger: 'axis',
|
|
|
|
|
+ axisPointer: {
|
|
|
|
|
+ type: 'line'
|
|
|
|
|
+ },
|
|
|
|
|
+ formatter: (params) => {
|
|
|
|
|
+ let d = `${params[0].axisValueLabel}<br>`
|
|
|
|
|
+ let item = params.map((el) => {
|
|
|
|
|
+ return `<div class="flex items-center justify-between mt-1 gap-1">
|
|
|
|
|
+ <span>${el.marker} ${el.seriesName}</span>
|
|
|
|
|
+ <span>${chartData.value[legend.value[el.componentIndex][1]][el.dataIndex].toFixed(2)} ${el.seriesName.split(' ')[1]}</span>
|
|
|
|
|
+ </div>`
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ return d + item.join('')
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ legend: {
|
|
|
|
|
+ data: legend.value.map(([name]) => name),
|
|
|
|
|
+ show: true
|
|
|
|
|
+ },
|
|
|
|
|
+ xAxis: {
|
|
|
|
|
+ type: 'category',
|
|
|
|
|
+ data: xAxisData.value
|
|
|
|
|
+ },
|
|
|
|
|
+ yAxis: {
|
|
|
|
|
+ type: 'value',
|
|
|
|
|
+ min: minInterval,
|
|
|
|
|
+ max: maxInterval,
|
|
|
|
|
+ interval: 1,
|
|
|
|
|
+ axisLabel: {
|
|
|
|
|
+ formatter: (v) => {
|
|
|
|
|
+ const num = v === 0 ? 0 : v > 0 ? Math.pow(10, v) : -Math.pow(10, -v)
|
|
|
|
|
+
|
|
|
|
|
+ return num.toLocaleString()
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ series: legend.value.map(([name, key]) => ({
|
|
|
|
|
+ name,
|
|
|
|
|
+ type: 'line',
|
|
|
|
|
+ smooth: true,
|
|
|
|
|
+ showSymbol: true,
|
|
|
|
|
+ data: chartData.value[key].map((value) => {
|
|
|
|
|
+ // return value
|
|
|
|
|
+ if (value === 0) return 0
|
|
|
|
|
+
|
|
|
|
|
+ const isPositive = value > 0
|
|
|
|
|
+ const absItem = Math.abs(value)
|
|
|
|
|
+
|
|
|
|
|
+ const min_value = Math.max(...intervalArr.filter((v) => v <= absItem))
|
|
|
|
|
+ const min_index = intervalArr.findIndex((v) => v === min_value)
|
|
|
|
|
+
|
|
|
|
|
+ const new_value =
|
|
|
|
|
+ (absItem - min_value) / (intervalArr[min_index + 1] - intervalArr[min_index]) + min_index
|
|
|
|
|
+
|
|
|
|
|
+ return isPositive ? new_value : -new_value
|
|
|
|
|
+ })
|
|
|
|
|
+ }))
|
|
|
|
|
+ })
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
const handleDeptNodeClick = (node: any) => {
|
|
const handleDeptNodeClick = (node: any) => {
|
|
|
|
|
+ deptName.value = node.name
|
|
|
query.value.deptId = node.id
|
|
query.value.deptId = node.id
|
|
|
handleQuery()
|
|
handleQuery()
|
|
|
}
|
|
}
|
|
@@ -274,8 +339,11 @@ const handleQuery = (setPage = true) => {
|
|
|
if (setPage) {
|
|
if (setPage) {
|
|
|
query.value.pageNo = 1
|
|
query.value.pageNo = 1
|
|
|
}
|
|
}
|
|
|
- getTotal()
|
|
|
|
|
|
|
+ getChart().then(() => {
|
|
|
|
|
+ render()
|
|
|
|
|
+ })
|
|
|
getList()
|
|
getList()
|
|
|
|
|
+ getTotal()
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
const resetQuery = () => {
|
|
const resetQuery = () => {
|
|
@@ -298,6 +366,73 @@ watch(
|
|
|
onMounted(() => {
|
|
onMounted(() => {
|
|
|
handleQuery()
|
|
handleQuery()
|
|
|
})
|
|
})
|
|
|
|
|
+
|
|
|
|
|
+const exportChart = () => {
|
|
|
|
|
+ if (!chart) return
|
|
|
|
|
+ let img = new Image()
|
|
|
|
|
+ img.src = chart.getDataURL({
|
|
|
|
|
+ type: 'png',
|
|
|
|
|
+ pixelRatio: 1,
|
|
|
|
|
+ backgroundColor: '#fff'
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ img.onload = function () {
|
|
|
|
|
+ let canvas = document.createElement('canvas')
|
|
|
|
|
+ canvas.width = img.width
|
|
|
|
|
+ canvas.height = img.height
|
|
|
|
|
+ let ctx = canvas.getContext('2d')
|
|
|
|
|
+ ctx?.drawImage(img, 0, 0)
|
|
|
|
|
+ let dataURL = canvas.toDataURL('image/png')
|
|
|
|
|
+
|
|
|
|
|
+ let a = document.createElement('a')
|
|
|
|
|
+
|
|
|
|
|
+ let event = new MouseEvent('click')
|
|
|
|
|
+
|
|
|
|
|
+ a.href = dataURL
|
|
|
|
|
+ a.download = `瑞恒日报统计数据.png`
|
|
|
|
|
+ a.dispatchEvent(event)
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const exportData = async () => {
|
|
|
|
|
+ const res = await IotRhDailyReportApi.exportRhDailyReportStatistics(query.value)
|
|
|
|
|
+
|
|
|
|
|
+ download.excel(res, '瑞恒日报统计数据.xlsx')
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const exportAll = async () => {
|
|
|
|
|
+ if (tab.value === '看板') exportChart()
|
|
|
|
|
+ else exportData()
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const message = useMessage()
|
|
|
|
|
+
|
|
|
|
|
+const unfilledDialogRef = ref()
|
|
|
|
|
+
|
|
|
|
|
+const openUnfilledDialog = () => {
|
|
|
|
|
+ // 检查是否选择了创建时间
|
|
|
|
|
+ if (!query.value.createTime || query.value.createTime.length === 0) {
|
|
|
|
|
+ message.warning('请先选择创建时间范围')
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 打开弹窗
|
|
|
|
|
+ unfilledDialogRef.value?.open()
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const router = useRouter()
|
|
|
|
|
+
|
|
|
|
|
+const tolist = (id: number) => {
|
|
|
|
|
+ const { pageNo, pageSize, ...rest } = query.value
|
|
|
|
|
+
|
|
|
|
|
+ router.push({
|
|
|
|
|
+ path: '/iotdayilyreport/IotRhDailyReport',
|
|
|
|
|
+ query: {
|
|
|
|
|
+ ...rest,
|
|
|
|
|
+ deptId: id
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+}
|
|
|
</script>
|
|
</script>
|
|
|
|
|
|
|
|
<template>
|
|
<template>
|
|
@@ -305,7 +440,7 @@ onMounted(() => {
|
|
|
<div class="bg-white dark:bg-[#1d1e1f] rounded-lg shadow p-4">
|
|
<div class="bg-white dark:bg-[#1d1e1f] rounded-lg shadow p-4">
|
|
|
<DeptTree2 :deptId="id" @node-click="handleDeptNodeClick" />
|
|
<DeptTree2 :deptId="id" @node-click="handleDeptNodeClick" />
|
|
|
</div>
|
|
</div>
|
|
|
- <div class="grid grid-rows-[62px_164px_1fr] h-full gap-5">
|
|
|
|
|
|
|
+ <div class="grid grid-rows-[62px_164px_1fr] h-full gap-4">
|
|
|
<el-form
|
|
<el-form
|
|
|
size="default"
|
|
size="default"
|
|
|
class="bg-white dark:bg-[#1d1e1f] rounded-lg shadow px-8 gap-8 flex items-center justify-between"
|
|
class="bg-white dark:bg-[#1d1e1f] rounded-lg shadow px-8 gap-8 flex items-center justify-between"
|
|
@@ -352,46 +487,111 @@ onMounted(() => {
|
|
|
<div
|
|
<div
|
|
|
v-for="info in totalWorkKeys"
|
|
v-for="info in totalWorkKeys"
|
|
|
:key="info[0]"
|
|
:key="info[0]"
|
|
|
- class="bg-white dark:bg-[#1d1e1f] rounded-lg shadow p-4 flex flex-col items-center justify-center gap-2"
|
|
|
|
|
|
|
+ class="bg-white dark:bg-[#1d1e1f] rounded-lg shadow p-1 flex flex-col items-center justify-center gap-1"
|
|
|
>
|
|
>
|
|
|
<div class="size-7.5" :class="info[3]"></div>
|
|
<div class="size-7.5" :class="info[3]"></div>
|
|
|
- <count-to class="text-2xl font-medium" :start-val="0" :end-val="totalWork[info[0]]">
|
|
|
|
|
|
|
+ <count-to
|
|
|
|
|
+ class="text-2xl font-medium"
|
|
|
|
|
+ :start-val="0"
|
|
|
|
|
+ :end-val="totalWork[info[0]]"
|
|
|
|
|
+ :decimals="info[4]"
|
|
|
|
|
+ @click="info[2] === '未填报' ? openUnfilledDialog() : ''"
|
|
|
|
|
+ >
|
|
|
<span class="text-xs leading-8 text-[var(--el-text-color-regular)]">暂无数据</span>
|
|
<span class="text-xs leading-8 text-[var(--el-text-color-regular)]">暂无数据</span>
|
|
|
</count-to>
|
|
</count-to>
|
|
|
<div class="text-xs font-medium text-[var(--el-text-color-regular)]">{{ info[1] }}</div>
|
|
<div class="text-xs font-medium text-[var(--el-text-color-regular)]">{{ info[1] }}</div>
|
|
|
<div class="text-sm font-medium text-[var(--el-text-color-regular)]">{{ info[2] }}</div>
|
|
<div class="text-sm font-medium text-[var(--el-text-color-regular)]">{{ info[2] }}</div>
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
- <div class="bg-white dark:bg-[#1d1e1f] rounded-lg shadow pt-4 px-8">
|
|
|
|
|
- <el-table
|
|
|
|
|
- v-loading="listLoading"
|
|
|
|
|
- :data="list"
|
|
|
|
|
- :stripe="true"
|
|
|
|
|
- :style="{ width: '100%' }"
|
|
|
|
|
- max-height="600"
|
|
|
|
|
- class="min-h-143"
|
|
|
|
|
- show-overflow-tooltip
|
|
|
|
|
- >
|
|
|
|
|
- <el-table-column
|
|
|
|
|
- v-for="item in columns(type)"
|
|
|
|
|
- :key="item.prop"
|
|
|
|
|
- :label="item.label"
|
|
|
|
|
- :prop="item.prop"
|
|
|
|
|
- align="center"
|
|
|
|
|
- :formatter="formatter"
|
|
|
|
|
- />
|
|
|
|
|
- </el-table>
|
|
|
|
|
-
|
|
|
|
|
- <Pagination
|
|
|
|
|
- class="mt-8"
|
|
|
|
|
- :total="total"
|
|
|
|
|
- v-model:page="query.pageNo"
|
|
|
|
|
- v-model:limit="query.pageSize"
|
|
|
|
|
- @pagination="getList"
|
|
|
|
|
- />
|
|
|
|
|
|
|
+ <div
|
|
|
|
|
+ class="bg-white dark:bg-[#1d1e1f] rounded-lg shadow px-8 py-4 grid grid-rows-[48px_1fr] gap-2"
|
|
|
|
|
+ >
|
|
|
|
|
+ <div class="flex items-center justify-between">
|
|
|
|
|
+ <el-button-group>
|
|
|
|
|
+ <el-button
|
|
|
|
|
+ size="default"
|
|
|
|
|
+ :type="tab === '表格' ? 'primary' : 'default'"
|
|
|
|
|
+ @click="handleSelectTab('表格')"
|
|
|
|
|
+ >表格
|
|
|
|
|
+ </el-button>
|
|
|
|
|
+ <el-button
|
|
|
|
|
+ size="default"
|
|
|
|
|
+ :type="tab === '看板' ? 'primary' : 'default'"
|
|
|
|
|
+ @click="handleSelectTab('看板')"
|
|
|
|
|
+ >看板
|
|
|
|
|
+ </el-button>
|
|
|
|
|
+ </el-button-group>
|
|
|
|
|
+ <h3 class="text-xl font-medium">{{ `${deptName}-${tab}` }}</h3>
|
|
|
|
|
+ <el-button size="default" type="primary" @click="exportAll">导出</el-button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <el-auto-resizer>
|
|
|
|
|
+ <template #default="{ height, width }">
|
|
|
|
|
+ <Motion
|
|
|
|
|
+ as="div"
|
|
|
|
|
+ :style="{ position: 'relative', overflow: 'hidden' }"
|
|
|
|
|
+ :animate="{ height: `${height}px`, width: `${width}px` }"
|
|
|
|
|
+ :transition="{ type: 'spring', stiffness: 200, damping: 25, duration: 0.3 }"
|
|
|
|
|
+ >
|
|
|
|
|
+ <AnimatePresence :initial="false" mode="sync">
|
|
|
|
|
+ <Motion
|
|
|
|
|
+ :key="currentTab"
|
|
|
|
|
+ as="div"
|
|
|
|
|
+ :initial="{ x: direction === 'left' ? '-100%' : '100%', opacity: 0 }"
|
|
|
|
|
+ :animate="{ x: '0%', opacity: 1 }"
|
|
|
|
|
+ :exit="{ x: direction === 'left' ? '50%' : '-50%', opacity: 0 }"
|
|
|
|
|
+ :transition="{ type: 'tween', stiffness: 300, damping: 30, duration: 0.3 }"
|
|
|
|
|
+ :style="{ position: 'absolute', left: 0, right: 0, top: 0 }"
|
|
|
|
|
+ >
|
|
|
|
|
+ <div :style="{ width: `${width}px`, height: `${height}px` }">
|
|
|
|
|
+ <el-table
|
|
|
|
|
+ v-if="currentTab === '表格'"
|
|
|
|
|
+ v-loading="listLoading"
|
|
|
|
|
+ :data="list"
|
|
|
|
|
+ :stripe="true"
|
|
|
|
|
+ :width="width"
|
|
|
|
|
+ :max-height="height"
|
|
|
|
|
+ show-overflow-tooltip
|
|
|
|
|
+ >
|
|
|
|
|
+ <template v-for="item in columns(type)" :key="item.prop">
|
|
|
|
|
+ <el-table-column
|
|
|
|
|
+ v-if="item.prop !== 'name'"
|
|
|
|
|
+ :label="item.label"
|
|
|
|
|
+ :prop="item.prop"
|
|
|
|
|
+ align="center"
|
|
|
|
|
+ :formatter="formatter"
|
|
|
|
|
+ />
|
|
|
|
|
+ <el-table-column
|
|
|
|
|
+ v-else
|
|
|
|
|
+ :label="item.label"
|
|
|
|
|
+ :prop="item.prop"
|
|
|
|
|
+ align="center"
|
|
|
|
|
+ >
|
|
|
|
|
+ <template #default="{ row }">
|
|
|
|
|
+ <el-button text type="primary" @click.prevent="tolist(row.id)">{{
|
|
|
|
|
+ row.name
|
|
|
|
|
+ }}</el-button>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </el-table-column>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </el-table>
|
|
|
|
|
+ <div
|
|
|
|
|
+ ref="chartRef"
|
|
|
|
|
+ v-loading="chartLoading"
|
|
|
|
|
+ :key="dayjs().valueOf()"
|
|
|
|
|
+ v-else
|
|
|
|
|
+ :style="{ width: `${width}px`, height: `${height}px` }"
|
|
|
|
|
+ >
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </Motion>
|
|
|
|
|
+ </AnimatePresence>
|
|
|
|
|
+ </Motion>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </el-auto-resizer>
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
+ <UnfilledReportDialog ref="unfilledDialogRef" :query-params="query" />
|
|
|
</template>
|
|
</template>
|
|
|
|
|
|
|
|
<style scoped>
|
|
<style scoped>
|
|
@@ -404,7 +604,7 @@ onMounted(() => {
|
|
|
border-top-left-radius: 8px;
|
|
border-top-left-radius: 8px;
|
|
|
|
|
|
|
|
.el-table__cell {
|
|
.el-table__cell {
|
|
|
- height: 52px;
|
|
|
|
|
|
|
+ height: 40px;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
.el-table__header-wrapper {
|
|
.el-table__header-wrapper {
|