基于 Vue3 + Flask + ECharts 构建政务 OA 系统门户

本项目将带领大家从零搭建一个类似截图中的政务 OA 系统首页。前端采用 Vue 3 (Composition API) 结合现代化 UI 布局,后端使用轻量级的 Flask 提供 RESTful API,并利用 ECharts 为统计卡片注入动态数据可视化效果。

一、 需求与 UI 拆解分析

我们将页面拆分为以下几个核心组件:

  1. 顶部导航栏 (Navbar):深蓝色背景,包含 Logo、菜单、多语言切换及登录/管理后台入口。

  2. 首屏信息区 (Hero Section):左侧为渐变蓝色的系统介绍区,右侧为系统版本、服务范围等基础信息卡片。

  3. 数据统计面板 (Stats Cards) :5个带有 Icon 和数字的卡片(内设机构、在职人员、通知公告等)。(此处是我们引入 ECharts 的重点)

  4. 底部内容区:左侧为"通知公告"列表,右侧为"政务公开"的快捷宫格(机构职能、政策文件等)。

二、 后端搭建:Flask 提供数据接口

首先,我们需要一个简单的后端来提供页面所需的模拟数据。

  1. 初始化 Flask 项目:

    bash

    复制代码
    mkdir flask-backend && cd flask-backend
    python -m venv venv
    source venv/bin/activate  # Windows使用 venv\Scripts\activate
    pip install Flask flask-cors
  2. 编写 app.py:

    python

    复制代码
    from flask import Flask, jsonify
    from flask_cors import CORS
    from datetime import datetime
    
    app = Flask(__name__)
    CORS(app) # 允许跨域请求
    
    @app.route('/api/dashboard', methods=['GET'])
    def get_dashboard_data():
        # 模拟统计数据 (包含用于ECharts的趋势数据)
        stats = [
            {"id": 1, "title": "内设机构", "count": 10, "icon": "org", "color": "#2563eb", "trend": [5, 7, 6, 8, 9, 10, 10]},
            {"id": 2, "title": "在职人员", "count": 22, "icon": "user", "color": "#10b981", "trend": [18, 19, 20, 20, 21, 22, 22]},
            {"id": 3, "title": "通知公告", "count": 8, "icon": "bell", "color": "#ef4444", "trend": [2, 3, 3, 5, 6, 7, 8]},
            {"id": 4, "title": "累计公文", "count": 6, "icon": "doc", "color": "#d97706", "trend": [1, 2, 2, 4, 5, 5, 6]},
            {"id": 5, "title": "会议安排", "count": 6, "icon": "calendar", "color": "#8b5cf6", "trend": [0, 1, 3, 3, 4, 6, 6]}
        ]
    
        # 模拟通知公告
        notices = [
            {"title": "关于做好2026年度政务数字化评估工作的通知", "date": "10-17", "is_top": True},
            {"title": "新版公文处理办法(试行)印发", "date": "10-15", "is_top": True},
            {"title": "机关食堂冬季供餐时间调整公告", "date": "10-16", "is_top": False},
        ]
    
        # 模拟政务公开宫格
        public_info = [
            {"title": "机构职能", "desc": "内设机构与职责"},
            {"title": "通知公告", "desc": "通知 / 公告 / 制度"},
            {"title": "政策文件", "desc": "规范性文件发布"},
            {"title": "人事任免", "desc": "干部任免信息"}
        ]
    
        return jsonify({
            "date": datetime.now().strftime("%Y年%m月%d日"),
            "stats": stats,
            "notices": notices,
            "public_info": public_info
        })
    
    if __name__ == '__main__':
        app.run(debug=True, port=5000)

三、 前端搭建:Vue3 页面与 ECharts 融合

使用 Vite 快速创建 Vue3 项目,并安装 Axios 和 ECharts。

bash

复制代码
npm create vite@latest vue-oa-frontend -- --template vue
cd vue-oa-frontend
npm install axios echarts vue-echarts

1. 封装 ECharts 微型图表组件 (StatsCard.vue)

截图中的统计卡片只有数字。为了让 ECharts 发挥作用,我们可以为每个卡片底部添加一个迷你的折线图(Sparkline),展示该指标近7天的发展趋势。

vue

复制代码
<!-- components/StatsCard.vue -->
<template>
  <div class="stats-card">
    <div class="card-header">
      <div class="icon-box" :style="{ backgroundColor: color + '20', color: color }">
        <!-- 此处省略具体图标 SVG,实际开发可用 ElementPlus/Icon 库 -->
        <i class="icon">{{ icon }}</i>
      </div>
      <div class="info">
        <h3>{{ count }}</h3>
        <p>{{ title }}</p>
      </div>
    </div>
    <!-- ECharts 迷你图表容器 -->
    <div class="chart-container" ref="chartRef"></div>
  </div>
</template>

<script setup>
import { ref, onMounted, watch } from 'vue'
import * as echarts from 'echarts'

const props = defineProps({
  title: String,
  count: Number,
  icon: String,
  color: String,
  trend: Array
})

const chartRef = ref(null)
let chartInstance = null

const initChart = () => {
  if (!chartRef.value) return
  chartInstance = echarts.init(chartRef.value)
  
  const option = {
    grid: { top: 5, bottom: 5, left: 0, right: 0 },
    xAxis: { type: 'category', show: false, data: ['1','2','3','4','5','6','7'] },
    yAxis: { type: 'value', show: false },
    series: [{
      data: props.trend,
      type: 'line',
      smooth: true,
      symbol: 'none',
      lineStyle: { color: props.color, width: 2 },
      areaStyle: {
        color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
          { offset: 0, color: props.color + '80' }, // 渐变色
          { offset: 1, color: props.color + '00' }
        ])
      }
    }]
  }
  chartInstance.setOption(option)
}

onMounted(() => {
  initChart()
  window.addEventListener('resize', () => chartInstance?.resize())
})

watch(() => props.trend, () => {
  if (chartInstance) {
    chartInstance.setOption({ series: [{ data: props.trend }] })
  }
})
</script>

<style scoped>
.stats-card {
  background: #fff;
  border-radius: 12px;
  padding: 16px;
  box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.05);
  display: flex;
  flex-direction: column;
  position: relative;
  overflow: hidden;
}
.card-header {
  display: flex;
  align-items: center;
  gap: 12px;
  z-index: 2;
}
.icon-box {
  width: 40px;
  height: 40px;
  border-radius: 8px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 20px;
  font-weight: bold;
}
.info h3 { margin: 0; font-size: 24px; color: #1e293b; }
.info p { margin: 0; font-size: 12px; color: #64748b; }
.chart-container {
  height: 30px;
  width: 100%;
  margin-top: 10px;
  z-index: 1;
}
</style>

2. 构建主页面 (App.vue)

参考截图布局,使用 Flex/Grid 进行排版,并请求 Flask 数据。

vue

复制代码
<template>
  <div class="oa-layout">
    <!-- 1. 顶部导航 -->
    <header class="navbar">
      <div class="logo">政务 OA 系统 <span>GOVERNMENT OA SYSTEM</span></div>
      <nav>
        <a href="#" class="active">首页</a>
        <a href="#">政务公开</a>
      </nav>
      <div class="actions">
        <button>中文 / EN</button>
        <button>员工门户</button>
        <button>管理后台</button>
      </div>
    </header>

    <main class="container">
      <!-- 2. 首屏信息 -->
      <section class="hero-section">
        <div class="hero-left">
          <h1>政务 OA 系统</h1>
          <p>通知公告 · 公文流转 · 会议管理 · 行政审批 · 日程安排</p>
          <p>考勤统计 · 内部消息 · 通讯录 —— 员工门户与管理后台一体化办公</p>
          <div class="buttons">
            <button class="primary">进入员工门户</button>
            <button class="secondary">浏览政务公开</button>
          </div>
        </div>
        <div class="hero-right">
          <h2>{{ dashboardData.date }}</h2>
          <ul>
            <li><span>系统版本</span> <strong>v1.0.0</strong></li>
            <li><span>服务范围</span> <strong>全体干部职工</strong></li>
            <li><span>运维支持</span> <strong>局办公室 · 信息中心</strong></li>
          </ul>
        </div>
      </section>

      <!-- 3. 数据统计卡片 (结合 ECharts) -->
      <section class="stats-grid">
        <StatsCard 
          v-for="stat in dashboardData.stats" 
          :key="stat.id"
          v-bind="stat"
        />
      </section>

      <!-- 4. 底部内容区 -->
      <section class="content-grid">
        <!-- 通知公告 -->
        <div class="panel">
          <div class="panel-header">
            <h3>通知公告</h3>
            <a href="#">更多 →</a>
          </div>
          <ul class="notice-list">
            <li v-for="(notice, index) in dashboardData.notices" :key="index">
              <span v-if="notice.is_top" class="tag">置顶</span>
              <a href="#">{{ notice.title }}</a>
              <span class="date">{{ notice.date }}</span>
            </li>
          </ul>
        </div>

        <!-- 政务公开 -->
        <div class="panel">
          <div class="panel-header">
            <h3>政务公开</h3>
            <a href="#">进入专栏 →</a>
          </div>
          <div class="public-grid">
            <div class="public-item" v-for="(item, index) in dashboardData.public_info" :key="index">
              <h4>{{ item.title }}</h4>
              <p>{{ item.desc }}</p>
            </div>
          </div>
        </div>
      </section>
    </main>
  </div>
</template>

<script setup>
import { ref, onMounted } from 'vue'
import axios from 'axios'
import StatsCard from './components/StatsCard.vue'

const dashboardData = ref({
  date: '',
  stats: [],
  notices: [],
  public_info: []
})

onMounted(async () => {
  try {
    // 请求 Flask 后端
    const res = await axios.get('http://127.0.0.1:5000/api/dashboard')
    dashboardData.value = res.data
  } catch (error) {
    console.error('获取数据失败:', error)
  }
})
</script>

<style>
/* 此处仅为关键样式示意,完整样式需配合 UI 设计稿 */
body { background-color: #f1f5f9; margin: 0; font-family: sans-serif; }
.navbar { background: #1e3a8a; color: white; padding: 16px 32px; display: flex; justify-content: space-between; align-items: center; }
.container { max-width: 1200px; margin: 24px auto; padding: 0 16px; }

/* Hero 区域 */
.hero-section { display: grid; grid-template-columns: 2fr 1fr; gap: 24px; margin-bottom: 24px; }
.hero-left { background: linear-gradient(135deg, #1e3a8a, #3b82f6); color: white; padding: 32px; border-radius: 12px; }
.hero-right { background: white; padding: 32px; border-radius: 12px; }

/* ECharts 卡片网格 */
.stats-grid { display: grid; grid-template-columns: repeat(5, 1fr); gap: 16px; margin-bottom: 24px; }

/* 底部面板 */
.content-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 24px; }
.panel { background: white; padding: 24px; border-radius: 12px; }
.public-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
.public-item { border: 1px solid #e2e8f0; padding: 16px; border-radius: 8px; }
</style>

四、 项目启动与联调

  1. 启动后端:进入 flask-backend 目录,运行 python app.py。

  2. 启动前端:进入 vue-oa-frontend 目录,运行 npm run dev。

  3. 打开浏览器访问 Vite 提供的本地地址,你将看到一个完美复刻截图 UI,并且卡片下方带有 ECharts 动态趋势线的政务 OA 系统门户。

总结

通过 Vue3 的组件化开发,我们快速还原了复杂的政务 OA 首页;Flask 提供了轻量快捷的 API 支撑;而 ECharts 的引入,则让原本静态的数字面板(内设机构、在职人员等)拥有了直观的数据趋势展示能力。这种**"传统政务UI + 微图表"**的设计,不仅保留了庄重感,更增加了数据可视化的现代感。后续您可以继续使用 ECharts 在"管理后台"模块渲染大屏统计饼图和柱状图,进一步完善系统。

下载地址

https://download.csdn.net/download/suny8/93711804

相关推荐
何何____1 小时前
TypeScript 函数类型兼容性
前端·javascript·typescript
雪芽蓝域zzs1 小时前
json数据转化成interface属性
前端·javascript·json
Java小卷1 小时前
Vue3 动态表单支持弹框组件
前端·vue.js·低代码
溪语流沙2 小时前
【Web全栈进阶】前端对接新契约:信封解包 + 统一错误处理
前端·vue.js·python·状态模式·fastapi
国服第二切图仔2 小时前
Chrome 插件开发实战指南:从入门到发布
前端·chrome
鬼手点金2 小时前
ReAct 循环
linux·服务器·前端·javascript·react.js·前端框架
鬼手点金2 小时前
上下文:Agent 的眼睛
java·前端·人工智能·git·深度学习
雪芽蓝域zzs2 小时前
Vue导入方法三种方式对比
前端·javascript·vue.js