|
@@ -1,9 +1,27 @@
|
|
|
<template>
|
|
<template>
|
|
|
- <el-row :gutter="20">
|
|
|
|
|
|
|
+ <el-row :gutter="20" class="ptw-page-layout">
|
|
|
<!-- 左侧部门树 -->
|
|
<!-- 左侧部门树 -->
|
|
|
<DeptTree @node-click="handleDeptNodeClick" v-model:collapsed="isLeftContentCollapsed" />
|
|
<DeptTree @node-click="handleDeptNodeClick" v-model:collapsed="isLeftContentCollapsed" />
|
|
|
- <el-col :span="isLeftContentCollapsed ? 24 : 20" :xs="24">
|
|
|
|
|
- <div class="bg-white rounded-sm px-2 py-2">
|
|
|
|
|
|
|
+ <el-col :span="isLeftContentCollapsed ? 24 : 20" :xs="24" class="ptw-main-col">
|
|
|
|
|
+ <div ref="statsContainerRef" class="ptw-stats mb-15px">
|
|
|
|
|
+ <div class="ptw-stats-card" style="position: relative">
|
|
|
|
|
+ <div class="ptw-stats-card__label" style="position: absolute; top: 15px">作业票总数</div>
|
|
|
|
|
+ <div class="ptw-stats-card__value" style="text-align: center">{{ totalStaticCount }}</div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div
|
|
|
|
|
+ class="ptw-stats-chart-card"
|
|
|
|
|
+ style="background: linear-gradient(180deg, #ffffff 0%, #f7faff 100%)">
|
|
|
|
|
+ <div class="ptw-stats-card__label">作业票分类统计</div>
|
|
|
|
|
+ <Echart ref="staticChartRef" :height="200" :options="staticChartOptions" />
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div
|
|
|
|
|
+ class="ptw-stats-chart-card"
|
|
|
|
|
+ style="background: linear-gradient(180deg, #ffffff 0%, #f7faff 100%)">
|
|
|
|
|
+ <div class="ptw-stats-card__label">作业票趋势统计</div>
|
|
|
|
|
+ <Echart ref="timeTrendChartRef" :height="200" :options="timeTrendChartOptions" />
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="bg-white rounded-sm px-2 py-2 shrink-0">
|
|
|
<!-- 搜索工作栏 -->
|
|
<!-- 搜索工作栏 -->
|
|
|
<el-form class="-mb-15px" :model="queryParams" ref="queryFormRef" :inline="true">
|
|
<el-form class="-mb-15px" :model="queryParams" ref="queryFormRef" :inline="true">
|
|
|
<el-form-item label="PTW编号" prop="ptwNo">
|
|
<el-form-item label="PTW编号" prop="ptwNo">
|
|
@@ -14,6 +32,17 @@
|
|
|
<el-input placeholder="请输入PTW序号" v-model="queryParams.ptwXh" />
|
|
<el-input placeholder="请输入PTW序号" v-model="queryParams.ptwXh" />
|
|
|
</el-form-item>
|
|
</el-form-item>
|
|
|
|
|
|
|
|
|
|
+ <el-form-item label="创建日期" prop="createTime">
|
|
|
|
|
+ <el-date-picker
|
|
|
|
|
+ v-model="queryParams.createTime"
|
|
|
|
|
+ value-format="YYYY-MM-DD HH:mm:ss"
|
|
|
|
|
+ type="daterange"
|
|
|
|
|
+ start-placeholder="开始日期"
|
|
|
|
|
+ end-placeholder="结束日期"
|
|
|
|
|
+ :default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
|
|
|
|
|
+ class="!w-240px" />
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+
|
|
|
<el-form-item>
|
|
<el-form-item>
|
|
|
<el-button @click="handleAdd" type="primary" v-hasPermi="['rq:qhse-ptw:create']"
|
|
<el-button @click="handleAdd" type="primary" v-hasPermi="['rq:qhse-ptw:create']"
|
|
|
><Icon icon="ep:plus" class="mr-5px" />新增</el-button
|
|
><Icon icon="ep:plus" class="mr-5px" />新增</el-button
|
|
@@ -37,13 +66,17 @@
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
<!-- 列表 -->
|
|
<!-- 列表 -->
|
|
|
- <ContentWrap class="flex-1 overflow-hidden mt-15px" style="border: none">
|
|
|
|
|
- <zm-table
|
|
|
|
|
- :loading="loading"
|
|
|
|
|
- :data="list"
|
|
|
|
|
- :stripe="true"
|
|
|
|
|
- height="calc(85vh - 130px)"
|
|
|
|
|
- :show-overflow-tooltip="true">
|
|
|
|
|
|
|
+ <ContentWrap
|
|
|
|
|
+ class="ptw-table-wrap flex-1 overflow-hidden mt-15px !mb-0"
|
|
|
|
|
+ style="border: none"
|
|
|
|
|
+ :body-style="tableWrapBodyStyle">
|
|
|
|
|
+ <div class="ptw-table-scroll">
|
|
|
|
|
+ <zm-table
|
|
|
|
|
+ :loading="loading"
|
|
|
|
|
+ :data="list"
|
|
|
|
|
+ :stripe="true"
|
|
|
|
|
+ height="100%"
|
|
|
|
|
+ :show-overflow-tooltip="true">
|
|
|
<zm-table-column :label="t('monitor.serial')" width="70" align="center" fixed="left">
|
|
<zm-table-column :label="t('monitor.serial')" width="70" align="center" fixed="left">
|
|
|
<template #default="scope">
|
|
<template #default="scope">
|
|
|
{{ scope.$index + 1 }}
|
|
{{ scope.$index + 1 }}
|
|
@@ -146,14 +179,17 @@
|
|
|
</el-button>
|
|
</el-button>
|
|
|
</template>
|
|
</template>
|
|
|
</zm-table-column>
|
|
</zm-table-column>
|
|
|
- </zm-table>
|
|
|
|
|
|
|
+ </zm-table>
|
|
|
|
|
+ </div>
|
|
|
|
|
|
|
|
<!-- 分页 -->
|
|
<!-- 分页 -->
|
|
|
- <Pagination
|
|
|
|
|
- :total="total"
|
|
|
|
|
- v-model:page="queryParams.pageNo"
|
|
|
|
|
- v-model:limit="queryParams.pageSize"
|
|
|
|
|
- @pagination="getList" />
|
|
|
|
|
|
|
+ <div class="ptw-pagination">
|
|
|
|
|
+ <Pagination
|
|
|
|
|
+ :total="total"
|
|
|
|
|
+ v-model:page="queryParams.pageNo"
|
|
|
|
|
+ v-model:limit="queryParams.pageSize"
|
|
|
|
|
+ @pagination="getList" />
|
|
|
|
|
+ </div>
|
|
|
</ContentWrap>
|
|
</ContentWrap>
|
|
|
</el-col>
|
|
</el-col>
|
|
|
</el-row>
|
|
</el-row>
|
|
@@ -527,6 +563,7 @@ import DeptTree from '@/views/system/user/DeptTree2.vue'
|
|
|
import { handleTree, defaultProps } from '@/utils/tree'
|
|
import { handleTree, defaultProps } from '@/utils/tree'
|
|
|
import * as DeptApi from '@/api/system/dept'
|
|
import * as DeptApi from '@/api/system/dept'
|
|
|
import { ElMessageBox, ElMessage } from 'element-plus'
|
|
import { ElMessageBox, ElMessage } from 'element-plus'
|
|
|
|
|
+import type { EChartsOption } from 'echarts'
|
|
|
const deptList2 = ref<Tree[]>([]) // 树形结构
|
|
const deptList2 = ref<Tree[]>([]) // 树形结构
|
|
|
import { formatDate } from '@/utils/formatTime'
|
|
import { formatDate } from '@/utils/formatTime'
|
|
|
import { useUserStore } from '@/store/modules/user'
|
|
import { useUserStore } from '@/store/modules/user'
|
|
@@ -551,9 +588,19 @@ const queryParams = reactive({
|
|
|
pageNo: 1,
|
|
pageNo: 1,
|
|
|
pageSize: 10,
|
|
pageSize: 10,
|
|
|
ptwXh: '',
|
|
ptwXh: '',
|
|
|
- ptwNo: ''
|
|
|
|
|
|
|
+ ptwNo: '',
|
|
|
|
|
+ createTime: null,
|
|
|
|
|
+ deptId: ''
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
|
|
+type StaticItem = {
|
|
|
|
|
+ classify: string
|
|
|
|
|
+ count: number
|
|
|
|
|
+ sort?: number
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+type TimeStaticItem = Record<string, number>
|
|
|
|
|
+
|
|
|
const queryParams2 = reactive({
|
|
const queryParams2 = reactive({
|
|
|
pageNo: 1,
|
|
pageNo: 1,
|
|
|
pageSize: 10,
|
|
pageSize: 10,
|
|
@@ -566,6 +613,17 @@ const queryParams3 = reactive({
|
|
|
jsaXh: ''
|
|
jsaXh: ''
|
|
|
})
|
|
})
|
|
|
const queryFormRef = ref(null) // 搜索的表单
|
|
const queryFormRef = ref(null) // 搜索的表单
|
|
|
|
|
+const statsContainerRef = ref<HTMLElement | null>(null)
|
|
|
|
|
+const staticChartRef = ref<any>(null)
|
|
|
|
|
+const timeTrendChartRef = ref<any>(null)
|
|
|
|
|
+let statsResizeObserver: ResizeObserver | null = null
|
|
|
|
|
+const tableWrapBodyStyle = {
|
|
|
|
|
+ padding: '10px',
|
|
|
|
|
+ height: '100%',
|
|
|
|
|
+ display: 'flex',
|
|
|
|
|
+ flexDirection: 'column',
|
|
|
|
|
+ overflow: 'hidden'
|
|
|
|
|
+}
|
|
|
|
|
|
|
|
// 对话框相关
|
|
// 对话框相关
|
|
|
const dialogVisible = ref(false)
|
|
const dialogVisible = ref(false)
|
|
@@ -673,6 +731,8 @@ const handleExport = async () => {
|
|
|
/** 首页处理部门被点击 */
|
|
/** 首页处理部门被点击 */
|
|
|
const handleDeptNodeClick = async (row) => {
|
|
const handleDeptNodeClick = async (row) => {
|
|
|
queryParams.deptId = row.id
|
|
queryParams.deptId = row.id
|
|
|
|
|
+ await getStatic()
|
|
|
|
|
+ await getTimeStaticRes()
|
|
|
await getList()
|
|
await getList()
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -686,6 +746,7 @@ const handleQuery = () => {
|
|
|
const resetQuery = () => {
|
|
const resetQuery = () => {
|
|
|
queryParams.deptId = ''
|
|
queryParams.deptId = ''
|
|
|
queryFormRef.value?.resetFields()
|
|
queryFormRef.value?.resetFields()
|
|
|
|
|
+ getStatic()
|
|
|
handleQuery()
|
|
handleQuery()
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -852,13 +913,241 @@ const extractFileName = (url: string): string => {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+let staticList = ref<StaticItem[]>([])
|
|
|
|
|
+const totalStaticCount = computed(() =>
|
|
|
|
|
+ staticList.value.reduce((sum: number, item: StaticItem) => sum + Number(item.count || 0), 0)
|
|
|
|
|
+)
|
|
|
|
|
+const staticChartOptions = computed<EChartsOption>(() => ({
|
|
|
|
|
+ tooltip: {
|
|
|
|
|
+ trigger: 'axis',
|
|
|
|
|
+ axisPointer: {
|
|
|
|
|
+ type: 'shadow'
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ grid: {
|
|
|
|
|
+ left: 16,
|
|
|
|
|
+ right: 16,
|
|
|
|
|
+ top: 24,
|
|
|
|
|
+ bottom: 24,
|
|
|
|
|
+ containLabel: true
|
|
|
|
|
+ },
|
|
|
|
|
+ xAxis: {
|
|
|
|
|
+ type: 'category',
|
|
|
|
|
+ data: staticList.value.map((item: StaticItem) => item.classify),
|
|
|
|
|
+ axisTick: {
|
|
|
|
|
+ show: false
|
|
|
|
|
+ },
|
|
|
|
|
+ axisLine: {
|
|
|
|
|
+ lineStyle: {
|
|
|
|
|
+ color: '#d9e2ef'
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ axisLabel: {
|
|
|
|
|
+ color: '#6b7280',
|
|
|
|
|
+ fontSize: 12,
|
|
|
|
|
+ interval: 0
|
|
|
|
|
+ // rotate: 30
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ yAxis: {
|
|
|
|
|
+ type: 'value',
|
|
|
|
|
+ axisLine: {
|
|
|
|
|
+ show: false
|
|
|
|
|
+ },
|
|
|
|
|
+ axisTick: {
|
|
|
|
|
+ show: false
|
|
|
|
|
+ },
|
|
|
|
|
+ axisLabel: {
|
|
|
|
|
+ color: '#94a3b8',
|
|
|
|
|
+ fontSize: 12
|
|
|
|
|
+ },
|
|
|
|
|
+ splitLine: {
|
|
|
|
|
+ lineStyle: {
|
|
|
|
|
+ color: '#edf2f7',
|
|
|
|
|
+ type: 'dashed'
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ series: [
|
|
|
|
|
+ {
|
|
|
|
|
+ type: 'bar',
|
|
|
|
|
+ barWidth: 32,
|
|
|
|
|
+ data: staticList.value.map((item: StaticItem) => item.count),
|
|
|
|
|
+ itemStyle: {
|
|
|
|
|
+ borderRadius: [8, 8, 0, 0],
|
|
|
|
|
+ color: {
|
|
|
|
|
+ type: 'linear',
|
|
|
|
|
+ x: 0,
|
|
|
|
|
+ y: 0,
|
|
|
|
|
+ x2: 0,
|
|
|
|
|
+ y2: 1,
|
|
|
|
|
+ colorStops: [
|
|
|
|
|
+ { offset: 0, color: '#79b8ff' },
|
|
|
|
|
+ { offset: 1, color: '#2f78f6' }
|
|
|
|
|
+ ]
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ label: {
|
|
|
|
|
+ show: true,
|
|
|
|
|
+ position: 'top',
|
|
|
|
|
+ color: '#2563eb',
|
|
|
|
|
+ fontSize: 12,
|
|
|
|
|
+ fontWeight: 700
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ ]
|
|
|
|
|
+}))
|
|
|
|
|
+
|
|
|
|
|
+const timeTrendDates = computed(() =>
|
|
|
|
|
+ Object.keys(timeStaticRes.value || {}).sort(
|
|
|
|
|
+ (a, b) => new Date(a).getTime() - new Date(b).getTime()
|
|
|
|
|
+ )
|
|
|
|
|
+)
|
|
|
|
|
+
|
|
|
|
|
+const timeTrendChartOptions = computed<EChartsOption>(() => ({
|
|
|
|
|
+ tooltip: {
|
|
|
|
|
+ trigger: 'axis'
|
|
|
|
|
+ },
|
|
|
|
|
+ grid: {
|
|
|
|
|
+ left: 16,
|
|
|
|
|
+ right: 24,
|
|
|
|
|
+ top: 24,
|
|
|
|
|
+ bottom: 24,
|
|
|
|
|
+ containLabel: true
|
|
|
|
|
+ },
|
|
|
|
|
+ xAxis: {
|
|
|
|
|
+ type: 'category',
|
|
|
|
|
+ data: timeTrendDates.value,
|
|
|
|
|
+ boundaryGap: false,
|
|
|
|
|
+ axisTick: {
|
|
|
|
|
+ show: false
|
|
|
|
|
+ },
|
|
|
|
|
+ axisLine: {
|
|
|
|
|
+ lineStyle: {
|
|
|
|
|
+ color: '#d9e2ef'
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ axisLabel: {
|
|
|
|
|
+ color: '#6b7280',
|
|
|
|
|
+ fontSize: 12
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ yAxis: {
|
|
|
|
|
+ type: 'value',
|
|
|
|
|
+ minInterval: 1,
|
|
|
|
|
+ axisLine: {
|
|
|
|
|
+ show: false
|
|
|
|
|
+ },
|
|
|
|
|
+ axisTick: {
|
|
|
|
|
+ show: false
|
|
|
|
|
+ },
|
|
|
|
|
+ axisLabel: {
|
|
|
|
|
+ color: '#94a3b8',
|
|
|
|
|
+ fontSize: 12
|
|
|
|
|
+ },
|
|
|
|
|
+ splitLine: {
|
|
|
|
|
+ lineStyle: {
|
|
|
|
|
+ color: '#edf2f7',
|
|
|
|
|
+ type: 'dashed'
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ series: [
|
|
|
|
|
+ {
|
|
|
|
|
+ type: 'line',
|
|
|
|
|
+ smooth: true,
|
|
|
|
|
+ symbol: 'circle',
|
|
|
|
|
+ symbolSize: 8,
|
|
|
|
|
+ data: timeTrendDates.value.map((date) => Number(timeStaticRes.value?.[date] || 0)),
|
|
|
|
|
+ lineStyle: {
|
|
|
|
|
+ width: 3,
|
|
|
|
|
+ color: '#10b981'
|
|
|
|
|
+ },
|
|
|
|
|
+ itemStyle: {
|
|
|
|
|
+ color: '#10b981',
|
|
|
|
|
+ borderColor: '#ffffff',
|
|
|
|
|
+ borderWidth: 2
|
|
|
|
|
+ },
|
|
|
|
|
+ areaStyle: {
|
|
|
|
|
+ color: {
|
|
|
|
|
+ type: 'linear',
|
|
|
|
|
+ x: 0,
|
|
|
|
|
+ y: 0,
|
|
|
|
|
+ x2: 0,
|
|
|
|
|
+ y2: 1,
|
|
|
|
|
+ colorStops: [
|
|
|
|
|
+ { offset: 0, color: 'rgba(16, 185, 129, 0.32)' },
|
|
|
|
|
+ { offset: 1, color: 'rgba(16, 185, 129, 0.04)' }
|
|
|
|
|
+ ]
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ ]
|
|
|
|
|
+}))
|
|
|
|
|
+
|
|
|
|
|
+async function getStatic() {
|
|
|
|
|
+ const deptId = queryParams.deptId || userStore.user.deptId
|
|
|
|
|
+ const res = await QHSEPtwApi.getPtwCategory(deptId)
|
|
|
|
|
+ staticList.value = Array.isArray(res)
|
|
|
|
|
+ ? [...res].sort((a: StaticItem, b: StaticItem) => (a.sort || 0) - (b.sort || 0))
|
|
|
|
|
+ : []
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+let timeStaticRes = ref<TimeStaticItem>({})
|
|
|
|
|
+async function getTimeStaticRes() {
|
|
|
|
|
+ const deptId = queryParams.deptId || userStore.user.deptId
|
|
|
|
|
+ const res = await QHSEPtwApi.getPtwCountByDateRange({
|
|
|
|
|
+ createTime: queryParams.createTime,
|
|
|
|
|
+ deptId: deptId
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ timeStaticRes.value = res || {}
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const resizeStatsCharts = () => {
|
|
|
|
|
+ staticChartRef.value?.resize?.()
|
|
|
|
|
+ timeTrendChartRef.value?.resize?.()
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+watch(
|
|
|
|
|
+ () => queryParams.createTime,
|
|
|
|
|
+ () => {
|
|
|
|
|
+ getTimeStaticRes()
|
|
|
|
|
+ }
|
|
|
|
|
+)
|
|
|
|
|
+
|
|
|
|
|
+watch(
|
|
|
|
|
+ () => isLeftContentCollapsed.value,
|
|
|
|
|
+ () => {
|
|
|
|
|
+ nextTick(() => {
|
|
|
|
|
+ resizeStatsCharts()
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+)
|
|
|
|
|
+
|
|
|
onMounted(async () => {
|
|
onMounted(async () => {
|
|
|
getList()
|
|
getList()
|
|
|
deptList2.value = handleTree(await DeptApi.getSimpleDeptList())
|
|
deptList2.value = handleTree(await DeptApi.getSimpleDeptList())
|
|
|
|
|
|
|
|
const users = await getUserProfile()
|
|
const users = await getUserProfile()
|
|
|
userInfo.value = users
|
|
userInfo.value = users
|
|
|
- console.log('xxxxxxxxxxxxxxxxxx', userInfo.value)
|
|
|
|
|
|
|
+ getStatic()
|
|
|
|
|
+ getTimeStaticRes()
|
|
|
|
|
+
|
|
|
|
|
+ nextTick(() => {
|
|
|
|
|
+ resizeStatsCharts()
|
|
|
|
|
+ if (statsContainerRef.value) {
|
|
|
|
|
+ statsResizeObserver = new ResizeObserver(() => {
|
|
|
|
|
+ resizeStatsCharts()
|
|
|
|
|
+ })
|
|
|
|
|
+ statsResizeObserver.observe(statsContainerRef.value)
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+})
|
|
|
|
|
+
|
|
|
|
|
+onBeforeUnmount(() => {
|
|
|
|
|
+ statsResizeObserver?.disconnect()
|
|
|
|
|
+ statsResizeObserver = null
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
let personDialogVisible = ref(false)
|
|
let personDialogVisible = ref(false)
|
|
@@ -1027,6 +1316,88 @@ const handleRadioChange2 = (row: any) => {
|
|
|
</script>
|
|
</script>
|
|
|
|
|
|
|
|
<style scoped>
|
|
<style scoped>
|
|
|
|
|
+.ptw-page-layout {
|
|
|
|
|
+ align-items: stretch;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.ptw-main-col {
|
|
|
|
|
+ height: 85vh;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ flex-direction: column;
|
|
|
|
|
+ min-height: 0;
|
|
|
|
|
+ overflow: hidden;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.ptw-stats {
|
|
|
|
|
+ display: grid;
|
|
|
|
|
+ grid-template-columns: 240px minmax(0, 1fr) minmax(0, 1fr);
|
|
|
|
|
+ gap: 16px;
|
|
|
|
|
+ flex-shrink: 0;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.ptw-table-wrap {
|
|
|
|
|
+ min-height: 0;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ flex-direction: column;
|
|
|
|
|
+ overflow: hidden;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.ptw-table-scroll {
|
|
|
|
|
+ flex: 1;
|
|
|
|
|
+ min-height: 0;
|
|
|
|
|
+ overflow: hidden;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.ptw-pagination {
|
|
|
|
|
+ flex-shrink: 0;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.ptw-stats-card,
|
|
|
|
|
+.ptw-stats-chart-card {
|
|
|
|
|
+ background: #fff;
|
|
|
|
|
+ border-radius: 8px;
|
|
|
|
|
+ padding: 16px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.ptw-stats-card {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ flex-direction: column;
|
|
|
|
|
+ justify-content: center;
|
|
|
|
|
+ min-height: 180px;
|
|
|
|
|
+ background: linear-gradient(135deg, #eff6ff 0%, #dbeafe 100%);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.ptw-stats-card__label {
|
|
|
|
|
+ font-size: 14px;
|
|
|
|
|
+ color: #64748b;
|
|
|
|
|
+ margin-bottom: 12px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.ptw-stats-card__value {
|
|
|
|
|
+ font-size: 42px;
|
|
|
|
|
+ line-height: 1;
|
|
|
|
|
+ font-weight: 700;
|
|
|
|
|
+ color: #1d4ed8;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.ptw-stats-chart-card {
|
|
|
|
|
+ min-width: 0;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+@media (max-width: 768px) {
|
|
|
|
|
+ .ptw-main-col {
|
|
|
|
|
+ height: auto;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .ptw-stats {
|
|
|
|
|
+ grid-template-columns: 1fr;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .ptw-stats-card {
|
|
|
|
|
+ min-height: 130px;
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
.file-name-text {
|
|
.file-name-text {
|
|
|
display: block;
|
|
display: block;
|
|
|
white-space: nowrap;
|
|
white-space: nowrap;
|