202 lines
5.5 KiB
Vue
202 lines
5.5 KiB
Vue
<template>
|
|
<div class="page-container">
|
|
<n-card class="page-layout">
|
|
<!-- 搜索表单 -->
|
|
<div class="search-form">
|
|
<n-form inline :model="searchForm" label-placement="left">
|
|
<n-form-item label="统计方式">
|
|
<n-select
|
|
v-model:value="searchForm.type"
|
|
:options="statisticTypeOptions"
|
|
placeholder="请选择统计方式"
|
|
clearable
|
|
style="width: 200px"
|
|
/>
|
|
</n-form-item>
|
|
<n-form-item label="统计时间">
|
|
<n-date-picker
|
|
v-model:formatted-value="searchForm.timestamp"
|
|
format="yyyy-MM-dd"
|
|
type="date"
|
|
clearable
|
|
style="width: 200px"
|
|
/>
|
|
</n-form-item>
|
|
|
|
<n-form-item>
|
|
<n-space>
|
|
<n-button type="primary" @click="handleSearch">
|
|
<template #icon><n-icon><SearchOutline /></n-icon></template>
|
|
搜索
|
|
</n-button>
|
|
<n-button @click="handleReset">
|
|
<template #icon><n-icon><RefreshOutline /></n-icon></template>
|
|
重置
|
|
</n-button>
|
|
</n-space>
|
|
</n-form-item>
|
|
</n-form>
|
|
</div>
|
|
<div class="stats-section">
|
|
<div class="stats-cards">
|
|
<n-card class="stat-card" size="small" :bordered="false" content-style="background: transparent">
|
|
<div class="stat-item">
|
|
<span class="stat-label">提交总数</span>
|
|
<span class="stat-value">{{ stats.totalQty }}</span>
|
|
</div>
|
|
</n-card>
|
|
<n-card class="stat-card success" size="small" :bordered="false" content-style="background: transparent">
|
|
<div class="stat-item">
|
|
<span class="stat-label">合格</span>
|
|
<span class="stat-value">{{ stats.successQty }}</span>
|
|
</div>
|
|
</n-card>
|
|
<n-card class="stat-card fail" size="small" :bordered="false" content-style="background: transparent">
|
|
<div class="stat-item">
|
|
<span class="stat-label">报废</span>
|
|
<span class="stat-value">{{ stats.scrapQty }}</span>
|
|
</div>
|
|
</n-card>
|
|
</div>
|
|
<n-grid :cols="1" :x-gap="24" class="stats-charts">
|
|
<n-gi>
|
|
<n-card title="汇报统计图" size="small" :bordered="false" content-style="background: transparent">
|
|
<div ref="statisticChartRef" class="chart-box"></div>
|
|
</n-card>
|
|
</n-gi>
|
|
</n-grid>
|
|
</div>
|
|
</n-card>
|
|
</div>
|
|
</template>
|
|
<script setup lang="ts">
|
|
import { ref, reactive, h, onMounted } from 'vue'
|
|
import { dictDataApi } from '@/api/org'
|
|
import { submitLogApi } from '@/api/submitLog'
|
|
|
|
//字典数据
|
|
const statisticTypeOptions = ref<{ label: string; value: any }[]>([])
|
|
|
|
//统计属性
|
|
const searchForm =reactive({
|
|
type: null as string,
|
|
timestamp: null as string
|
|
})
|
|
|
|
const stats = reactive({
|
|
totalQty:0,
|
|
successQty:0,
|
|
scrapQty:0,
|
|
outputRate:0,
|
|
scrapRate:0,
|
|
})
|
|
|
|
const statisticChartRef = ref<HTMLElement | null>(null)
|
|
|
|
let statisticChart: any = null
|
|
|
|
function handleSearch() {
|
|
loadStatistics()
|
|
}
|
|
|
|
function handleReset(){
|
|
searchForm.type = null
|
|
searchForm.timestamp = null
|
|
handleSearch()
|
|
}
|
|
|
|
async function loadStatistics() {
|
|
try{
|
|
const res = await submitLogApi.statistics({
|
|
type: searchForm.type,
|
|
time: searchForm.timestamp
|
|
})
|
|
|
|
Object.assign(stats,res)
|
|
|
|
updateCharts()
|
|
}catch{}
|
|
}
|
|
|
|
|
|
function updateCharts(){
|
|
import('echarts').then((echarts)=>{
|
|
if(statisticChartRef.value) {
|
|
if (!statisticChart) statisticChart = echarts.init(statisticChartRef.value)
|
|
|
|
const data = [{name:"产出率",value:stats.outputRate},{name:"报废率",value:stats.scrapRate}]
|
|
statisticChart.setOption({
|
|
tooltip: { trigger: 'item' },
|
|
series: [{ type: 'pie', radius: '60%', data }]
|
|
})
|
|
}
|
|
})
|
|
}
|
|
|
|
|
|
//加载字典选项
|
|
async function loadDictOptions() {
|
|
|
|
try{
|
|
const data = await dictDataApi.listByType('statistic_type')
|
|
statisticTypeOptions.value = data.map(d=>({label:d.dictLabel,value: (Number(d.dictValue) || d.dictValue) }))
|
|
}catch{}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
onMounted(()=>{
|
|
loadStatistics()
|
|
loadDictOptions()
|
|
})
|
|
|
|
|
|
</script>
|
|
<style lang="scss" scoped>
|
|
.page-layout {
|
|
.stats-section {
|
|
margin-bottom: 20px;
|
|
}
|
|
.stats-cards {
|
|
display: flex;
|
|
gap: 16px;
|
|
margin-bottom: 16px;
|
|
}
|
|
.stat-card {
|
|
flex: 1;
|
|
.stat-item {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 4px;
|
|
.stat-value {
|
|
font-size: 24px;
|
|
font-weight: 700;
|
|
color: #111827;
|
|
}
|
|
.stat-label {
|
|
font-size: 13px;
|
|
color: #6b7280;
|
|
}
|
|
}
|
|
&.success .stat-value { color: #18a058; }
|
|
&.fail .stat-value { color: #d03050; }
|
|
}
|
|
.stats-charts {
|
|
margin-bottom: 16px;
|
|
}
|
|
.chart-box {
|
|
height: 260px;
|
|
text-align: center;
|
|
}
|
|
.search-form {
|
|
margin-bottom: 16px;
|
|
}
|
|
.pagination-container {
|
|
display: flex;
|
|
justify-content: flex-end;
|
|
margin-top: 12px;
|
|
}
|
|
}
|
|
</style> |