From 90ab562f5eac24e0e3b334335f6d76438236f305 Mon Sep 17 00:00:00 2001
From: spring <2396852758@qq.com>
Date: 星期四, 21 八月 2025 10:04:30 +0800
Subject: [PATCH] 完成档案统计
---
src/api/fileManagement/statistics.js | 75 ++++++
src/views/fileManagement/borrow/index.vue | 12
src/views/fileManagement/statistics/index.vue | 539 +++++++++++++++++++++++++++++++++++++++++++++++++
src/api/fileManagement/document.js | 25 ++
4 files changed, 644 insertions(+), 7 deletions(-)
diff --git a/src/api/fileManagement/document.js b/src/api/fileManagement/document.js
index 49b156e..f3d5f4f 100644
--- a/src/api/fileManagement/document.js
+++ b/src/api/fileManagement/document.js
@@ -162,3 +162,28 @@
data: ids,
});
}
+
+// 缁熻鐩稿叧鎺ュ彛
+// 鑾峰彇鎬讳綋缁熻鏁版嵁
+export function getDocumentationOverview() {
+ return request({
+ url: "/documentation/overview",
+ method: "get",
+ });
+}
+
+// 鑾峰彇鍒嗙被缁熻鏁版嵁
+export function getDocumentationCategoryStats() {
+ return request({
+ url: "/documentation/category",
+ method: "get",
+ });
+}
+
+// 鑾峰彇鐘舵�佺粺璁℃暟鎹�
+export function getDocumentationStatusStats() {
+ return request({
+ url: "/documentation/status",
+ method: "get",
+ });
+}
diff --git a/src/api/fileManagement/statistics.js b/src/api/fileManagement/statistics.js
new file mode 100644
index 0000000..d77375c
--- /dev/null
+++ b/src/api/fileManagement/statistics.js
@@ -0,0 +1,75 @@
+import request from "@/utils/request";
+
+// 鑾峰彇妗f鎬讳綋缁熻
+export function getDocumentStatistics() {
+ return request({
+ url: "/fileManagement/statistics/overview",
+ method: "get",
+ });
+}
+
+// 鑾峰彇妗f鍒嗙被缁熻
+export function getCategoryStatistics() {
+ return request({
+ url: "/fileManagement/statistics/category",
+ method: "get",
+ });
+}
+
+// 鑾峰彇妗f鐘舵�佺粺璁�
+export function getStatusStatistics() {
+ return request({
+ url: "/fileManagement/statistics/status",
+ method: "get",
+ });
+}
+
+// 鑾峰彇妗f鍊熼槄缁熻
+export function getBorrowStatistics() {
+ return request({
+ url: "/fileManagement/statistics/borrow",
+ method: "get",
+ });
+}
+
+// 鑾峰彇妗f骞村害缁熻
+export function getYearStatistics() {
+ return request({
+ url: "/fileManagement/statistics/year",
+ method: "get",
+ });
+}
+
+// 鑾峰彇妗f浣嶇疆缁熻
+export function getLocationStatistics() {
+ return request({
+ url: "/fileManagement/statistics/location",
+ method: "get",
+ });
+}
+
+// 鑾峰彇妗f瓒嬪娍缁熻
+export function getTrendStatistics(params) {
+ return request({
+ url: "/fileManagement/statistics/trend",
+ method: "get",
+ params: params,
+ });
+}
+
+// 鑾峰彇妗f鍊熼槄鎺掕
+export function getBorrowRanking() {
+ return request({
+ url: "/fileManagement/statistics/borrowRanking",
+ method: "get",
+ });
+}
+
+// 鑾峰彇妗f鍒嗙被璇︽儏缁熻
+export function getCategoryDetailStatistics(categoryId) {
+ return request({
+ url: `/fileManagement/statistics/categoryDetail/${categoryId}`,
+ method: "get",
+ });
+}
+
diff --git a/src/views/fileManagement/borrow/index.vue b/src/views/fileManagement/borrow/index.vue
index 9a9ee81..6875571 100644
--- a/src/views/fileManagement/borrow/index.vue
+++ b/src/views/fileManagement/borrow/index.vue
@@ -233,13 +233,8 @@
const tableColumns = ref([
{
label: '鏂囨。鍚嶇О',
- prop: 'documentationId',
+ prop: 'docName',
width: '200',
- formatData: (params) => {
- if (!params) return '-';
- const doc = documentList.value.find(item => item.id === params);
- return doc ? (doc.docName || doc.name) : params;
- }
},
{ label: '鍊熼槄浜�', prop: 'borrower' },
{ label: '鍊熼槄鐩殑', prop: 'borrowPurpose' },
@@ -374,7 +369,10 @@
};
// 鎵撳紑鍊熼槄寮规
-const openBorrowDia = (type, data) => {
+const openBorrowDia = async (type, data) => {
+ // 鍏堝埛鏂版枃妗e垪琛�
+ await loadDocumentList();
+
borrowOperationType.value = type;
borrowDia.value = true;
diff --git a/src/views/fileManagement/statistics/index.vue b/src/views/fileManagement/statistics/index.vue
new file mode 100644
index 0000000..42b81e4
--- /dev/null
+++ b/src/views/fileManagement/statistics/index.vue
@@ -0,0 +1,539 @@
+<template>
+ <div class="app-container statistics-container">
+
+ <!-- 鎬讳綋缁熻鍗$墖 -->
+ <el-row :gutter="20" class="statistics-cards">
+ <el-col :span="6" v-for="(item, index) in overviewData" :key="index">
+ <el-card class="statistics-card" :class="item.type">
+ <div class="card-content">
+ <div class="card-icon">
+ <el-icon :size="32">
+ <component :is="item.icon" />
+ </el-icon>
+ </div>
+ <div class="card-info">
+ <div class="card-number">
+ <el-skeleton-item v-if="loading" variant="text" style="width: 60px; height: 32px;" />
+ <span v-else>{{ item.value }}</span>
+ </div>
+ <div class="card-label">{{ item.label }}</div>
+ </div>
+ </div>
+ </el-card>
+ </el-col>
+ </el-row>
+
+ <!-- 鍥捐〃鍖哄煙 -->
+ <el-row :gutter="20" class="charts-section">
+ <el-col :span="12">
+ <el-card class="chart-card">
+ <template #header>
+ <div class="card-header">
+ <span>妗f鍒嗙被缁熻</span>
+ </div>
+ </template>
+ <div class="chart-container">
+ <div ref="categoryChartRef" class="chart"></div>
+ </div>
+ </el-card>
+ </el-col>
+
+ <el-col :span="12">
+ <el-card class="chart-card">
+ <template #header>
+ <div class="card-header">
+ <span>妗f鐘舵�佺粺璁�</span>
+ </div>
+ </template>
+ <div class="chart-container">
+ <div ref="statusChartRef" class="chart"></div>
+ </div>
+ </el-card>
+ </el-col>
+ </el-row>
+ </div>
+</template>
+
+<script setup>
+import { ref, onMounted, nextTick, onUnmounted } from "vue";
+import { ElMessage } from "element-plus";
+import { Refresh } from "@element-plus/icons-vue";
+import * as echarts from "echarts";
+import {
+ getDocumentationOverview,
+ getDocumentationCategoryStats,
+ getDocumentationStatusStats
+} from "@/api/fileManagement/document";
+import {
+ Document,
+ Folder,
+ Tickets,
+ Calendar
+} from "@element-plus/icons-vue";
+
+// 鍝嶅簲寮忔暟鎹�
+const overviewData = ref([
+ {
+ label: "鎬绘。妗堟暟",
+ value: 0,
+ icon: "Document",
+ type: "primary",
+ },
+ {
+ label: "鍒嗙被鏁伴噺",
+ value: 0,
+ icon: "Folder",
+ type: "success",
+ },
+ {
+ label: "鍊熷嚭妗f",
+ value: 0,
+ icon: "Tickets",
+ type: "warning",
+ },
+ {
+ label: "鏈湀鏂板",
+ value: 0,
+ icon: "Calendar",
+ type: "info",
+ },
+]);
+
+const categoryChartRef = ref(null);
+const statusChartRef = ref(null);
+
+// 鍥捐〃瀹炰緥
+let categoryChart = null;
+let statusChart = null;
+
+// 鍔犺浇鐘舵��
+const loading = ref(false);
+const autoRefreshInterval = ref(null);
+
+// 鑷姩鍒锋柊寮�鍏�
+const autoRefreshEnabled = ref(true);
+
+// 鑷姩鍒锋柊闂撮殧锛�5鍒嗛挓锛�
+const AUTO_REFRESH_INTERVAL = 5 * 60 * 1000;
+
+// 鍚姩鑷姩鍒锋柊
+const startAutoRefresh = () => {
+ if (autoRefreshInterval.value) {
+ clearInterval(autoRefreshInterval.value);
+ }
+ if (autoRefreshEnabled.value) {
+ autoRefreshInterval.value = setInterval(() => {
+ refreshData();
+ }, AUTO_REFRESH_INTERVAL);
+ }
+};
+
+// 鍋滄鑷姩鍒锋柊
+const stopAutoRefresh = () => {
+ if (autoRefreshInterval.value) {
+ clearInterval(autoRefreshInterval.value);
+ autoRefreshInterval.value = null;
+ }
+};
+
+// 鍒囨崲鑷姩鍒锋柊鐘舵��
+const toggleAutoRefresh = (value) => {
+ if (value) {
+ startAutoRefresh();
+ } else {
+ stopAutoRefresh();
+ }
+};
+
+// 鍔犺浇鎬讳綋缁熻鏁版嵁
+const loadOverviewData = async () => {
+ try {
+ const response = await getDocumentationOverview();
+ if (response.code === 200) {
+ const data = response.data;
+ overviewData.value[0].value = data.totalDocsCount || 0;
+ overviewData.value[1].value = data.categoryNumCount || 0;
+ overviewData.value[2].value = data.borrowedDocsCount || 0;
+ overviewData.value[3].value = data.monthlyAddedDocsCount || 0;
+ }
+ } catch (error) {
+ console.error('鍔犺浇鎬讳綋缁熻鏁版嵁澶辫触:', error);
+ ElMessage.error('鍔犺浇鎬讳綋缁熻鏁版嵁澶辫触');
+ }
+};
+
+// 鍔犺浇鍒嗙被缁熻鏁版嵁
+const loadCategoryData = async () => {
+ try {
+ const response = await getDocumentationCategoryStats();
+ if (response.code === 200) {
+ renderCategoryChart(response.data);
+ }
+ } catch (error) {
+ console.error('鍔犺浇鍒嗙被缁熻鏁版嵁澶辫触:', error);
+ ElMessage.error('鍔犺浇鍒嗙被缁熻鏁版嵁澶辫触');
+ }
+};
+
+// 鍔犺浇鐘舵�佺粺璁℃暟鎹�
+const loadStatusData = async () => {
+ try {
+ const response = await getDocumentationStatusStats();
+ if (response.code === 200) {
+ renderStatusChart(response.data);
+ }
+ } catch (error) {
+ console.error('鍔犺浇鐘舵�佺粺璁℃暟鎹け璐�:', error);
+ ElMessage.error('鍔犺浇鐘舵�佺粺璁℃暟鎹け璐�');
+ }
+};
+
+// 鍒锋柊鏁版嵁
+const refreshData = async () => {
+ loading.value = true;
+ try {
+ await Promise.all([
+ loadOverviewData(),
+ loadCategoryData(),
+ loadStatusData()
+ ]);
+ ElMessage.success('鏁版嵁鍒锋柊鎴愬姛');
+ } catch (error) {
+ console.error('鍒锋柊鏁版嵁澶辫触:', error);
+ ElMessage.error('鍒锋柊鏁版嵁澶辫触');
+ } finally {
+ loading.value = false;
+ }
+};
+
+// 鍒濆鍖栧浘琛�
+const initCharts = () => {
+ // 寤惰繜鍒濆鍖栵紝纭繚DOM鍏冪礌宸茬粡娓叉煋
+ setTimeout(() => {
+ if (categoryChartRef.value) {
+ categoryChart = echarts.init(categoryChartRef.value);
+ }
+
+ if (statusChartRef.value) {
+ statusChart = echarts.init(statusChartRef.value);
+ }
+
+ // 鍒濆鍖栧畬鎴愬悗鍔犺浇鏁版嵁
+ loadCategoryData();
+ loadStatusData();
+ }, 300);
+};
+
+// 娓叉煋鍒嗙被缁熻鍥捐〃
+const renderCategoryChart = (data) => {
+ if (!categoryChart) return;
+ let newData = data.map(item => {
+ return {
+ name: item.category,
+ value: item.count
+ }
+ })
+
+ const option = {
+ title: {
+ text: "妗f鍒嗙被鍒嗗竷",
+ left: "center",
+ textStyle: {
+ fontSize: 16,
+ fontWeight: "normal",
+ },
+ },
+ tooltip: {
+ trigger: "item",
+ formatter: "{a} <br/>{b}: {c} ({d}%)",
+ },
+ legend: {
+ orient: "vertical",
+ left: "left",
+ top: "middle",
+ },
+ series: [
+ {
+ name: "妗f鏁伴噺",
+ type: "pie",
+ radius: ["40%", "70%"],
+ center: ["60%", "50%"],
+ data: newData || [
+ { name: "鎶�鏈枃妗�", value: 450 },
+ { name: "绠$悊鏂囨。", value: 320 },
+ { name: "璐㈠姟鏂囨。", value: 280 },
+ { name: "浜轰簨鏂囨。", value: 200 },
+ ],
+ emphasis: {
+ itemStyle: {
+ shadowBlur: 10,
+ shadowOffsetX: 0,
+ shadowColor: "rgba(0, 0, 0, 0.5)",
+ },
+ },
+ },
+ ],
+ };
+
+ try {
+ categoryChart.setOption(option);
+ } catch (error) {
+ console.error('鍒嗙被鍥捐〃娓叉煋澶辫触:', error);
+ }
+};
+
+// 娓叉煋鐘舵�佺粺璁″浘琛�
+const renderStatusChart = (data) => {
+ if (!statusChart) return;
+ let newData = data.map(item => {
+ return {
+ name: item.docStatus,
+ value: item.count
+ }
+ })
+ const option = {
+ title: {
+ text: "妗f鐘舵�佸垎甯�",
+ left: "center",
+ textStyle: {
+ fontSize: 16,
+ fontWeight: "normal",
+ },
+ },
+ tooltip: {
+ trigger: "item",
+ formatter: "{a} <br/>{b}: {c} ({d}%)",
+ },
+ legend: {
+ orient: "vertical",
+ left: "left",
+ top: "middle",
+ },
+ series: [
+ {
+ name: "妗f鏁伴噺",
+ type: "pie",
+ radius: ["40%", "70%"],
+ center: ["60%", "50%"],
+ roseType: false,
+ data: newData || [
+ { name: "姝e父", value: 1150, itemStyle: { color: "#67C23A" } },
+ { name: "鍊熷嚭", value: 89, itemStyle: { color: "#E6A23C" } },
+ { name: "涓㈠け", value: 8, itemStyle: { color: "#F56C6C" } },
+ { name: "鎹熷潖", value: 4, itemStyle: { color: "#909399" } },
+ ],
+ emphasis: {
+ itemStyle: {
+ shadowBlur: 10,
+ shadowOffsetX: 0,
+ shadowColor: "rgba(0, 0, 0, 0.5)",
+ },
+ },
+ },
+ ],
+ };
+
+ try {
+ statusChart.setOption(option);
+ } catch (error) {
+ console.error('鐘舵�佸浘琛ㄦ覆鏌撳け璐�:', error);
+ }
+};
+
+onMounted(() => {
+ loadOverviewData();
+ initCharts();
+ startAutoRefresh();
+});
+
+// 缁勪欢鍗歌浇鏃舵竻鐞嗗畾鏃跺櫒
+onUnmounted(() => {
+ stopAutoRefresh();
+});
+</script>
+
+<style scoped>
+.statistics-container {
+ padding: 20px;
+ background-color: #f5f7fa;
+ min-height: 100vh;
+}
+
+.page-header {
+ text-align: center;
+ margin-bottom: 30px;
+ padding: 20px;
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+ border-radius: 12px;
+ color: white;
+}
+
+.page-header h2 {
+ color: white;
+ margin-bottom: 10px;
+ font-size: 28px;
+ font-weight: 600;
+}
+
+.page-header p {
+ color: rgba(255, 255, 255, 0.9);
+ font-size: 14px;
+ margin: 0 0 15px 0;
+}
+
+.header-controls {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ margin-top: 10px;
+ gap: 20px;
+}
+
+.refresh-btn {
+ margin-left: 20px;
+}
+
+.statistics-cards {
+ margin-bottom: 30px;
+}
+
+.statistics-card {
+ border-radius: 12px;
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
+ transition: all 0.3s ease;
+ border: none;
+ overflow: hidden;
+}
+
+.statistics-card:hover {
+ transform: translateY(-5px);
+ box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15);
+}
+
+.statistics-card.primary {
+ border-left: 4px solid #409EFF;
+ background: linear-gradient(135deg, #409EFF 0%, #36a3f7 100%);
+}
+
+.statistics-card.success {
+ border-left: 4px solid #67C23A;
+ background: linear-gradient(135deg, #67C23A 0%, #85ce61 100%);
+}
+
+.statistics-card.warning {
+ border-left: 4px solid #E6A23C;
+ background: linear-gradient(135deg, #E6A23C 0%, #ebb563 100%);
+}
+
+.statistics-card.info {
+ border-left: 4px solid #909399;
+ background: linear-gradient(135deg, #909399 0%, #a6a9ad 100%);
+}
+
+.card-content {
+ display: flex;
+ align-items: center;
+ padding: 20px;
+}
+
+.card-icon {
+ margin-right: 20px;
+ color: white;
+}
+
+.card-info {
+ flex: 1;
+}
+
+.card-number {
+ font-size: 32px;
+ font-weight: 600;
+ color: white;
+ margin-bottom: 5px;
+}
+
+.card-label {
+ font-size: 14px;
+ color: rgba(255, 255, 255, 0.9);
+}
+
+.charts-section {
+ margin-bottom: 30px;
+}
+
+.chart-card {
+ border-radius: 12px;
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
+ border: none;
+}
+
+.card-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ font-weight: 600;
+ color: #303133;
+ padding: 15px 20px;
+ border-bottom: 1px solid #ebeef5;
+}
+
+.chart-container {
+ height: 400px;
+ padding: 20px;
+}
+
+.chart {
+ width: 100%;
+ height: 100%;
+}
+
+/* 鍝嶅簲寮忚璁� */
+@media (max-width: 768px) {
+ .statistics-container {
+ padding: 10px;
+ }
+
+ .page-header {
+ padding: 15px;
+ }
+
+ .page-header h2 {
+ font-size: 24px;
+ }
+
+ .header-controls {
+ flex-direction: column;
+ gap: 15px;
+ }
+
+ .refresh-btn {
+ margin-left: 0;
+ }
+
+ .statistics-cards .el-col {
+ margin-bottom: 15px;
+ }
+
+ .charts-section .el-col {
+ margin-bottom: 20px;
+ }
+
+ .chart-container {
+ height: 300px;
+ }
+}
+
+@media (max-width: 480px) {
+ .page-header h2 {
+ font-size: 20px;
+ }
+
+ .card-number {
+ font-size: 24px;
+ }
+
+ .chart-container {
+ height: 250px;
+ }
+}
+</style>
--
Gitblit v1.9.3