内部资源站
概述
一个基于 Python Flask 开发的轻量级内部资源导航站,引用 Element UI 样式,方便管理和快速查找公司内部各种工具和平台链接。
核心功能:
- 分类展示:按研发工具、运维工具、云平台等分组展示资源
- 卡片式布局:每个资源以卡片形式展示图标和名称
- 多环境切换:支持同一资源多个环境(内网/生产/测试)的弹窗选择
- 数据驱动:通过 JSON 配置文件动态渲染页面,增删改无需修改代码
适用场景:
- 公司内部导航页
- 团队工具集汇总
- 运维/开发资源入口
部署
目录结构
./
│ data-v2.json # 配置文件
│ main.py # Python 主程序
│ README.md
│
├───static
│ │ favicon.ico
│ │ index.css # Element UI 样式
│ │ index.js # Element UI 脚本
│ │ vue.js # Vue.js 框架
│ │
│ └───img
│ 1panel.png
│ aliyun.png
│ archery.png
│ arweb.png
│ baota.png
│ cdn.png
│ jenkins.png
│ zabbix.png
│
└───templates
index.html # 页面模板Python 主程序(main.py)
from flask import Flask, render_template, jsonify
import json
app = Flask(__name__)
# 修改 Jinja2 默认语法,避免与 Vue.js 的 {{ }} 冲突
app.jinja_env.variable_start_string = '[['
app.jinja_env.variable_end_string = ']]'
# 读取并返回 data-v2.json 文件内容
@app.route('/data')
def get_data():
try:
with open('data-v2.json', 'r', encoding='utf-8') as file:
data = json.load(file)
return jsonify(data)
except FileNotFoundError:
return jsonify({"error": "File not found"}), 404
except json.JSONDecodeError:
return jsonify({"error": "Error decoding JSON"}), 500
# 渲染 index.html 页面
@app.route('/')
def index():
return render_template('index.html')
if __name__ == '__main__':
app.run(host="0.0.0.0", port=8080)HTML 页面模板(templates/index.html)
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>内部资源站</title>
<link rel="icon" href="static/favicon.ico">
<!-- 引入 Element UI 样式 -->
<link rel="stylesheet" href="static/index.css">
<script src="static/vue.js"></script>
<script src="static/index.js"></script>
</head>
<body>
<div id="app">
<div class="list" v-for="(item, itemindex) in database" :key="itemindex">
<h3 style="padding-left: 10px;">{{ item.indexname }}</h3>
<div class="table">
<el-card class="card" shadow="hover" v-for="app in item.appgroup" :key="app.urlname">
<template v-if="app.lables">
<img :src="app.img" class="image">
<el-popover placement="right" trigger="click">
<el-table :data="app.env" max-height="450">
<el-table-column prop="urlname" label="环境">
<template slot-scope="scope">
<el-link :href="scope.row && scope.row.url" target="_blank">
{{ scope.row.urlname }}
</el-link>
</template>
</el-table-column>
</el-table>
<el-button round slot="reference" size="mini">{{ app.urlname }}</el-button>
</el-popover>
</template>
<template v-else>
<img :src="app.img" class="image" @click="urljump(app.url)">
<div align="center"><span>{{ app.urlname }}</span></div>
</template>
</el-card>
</div>
</div>
</div>
<script>
new Vue({
el: '#app',
data() {
return {
database: []
};
},
created() {
fetch('/data')
.then(response => response.json())
.then(data => {
this.database = data;
})
.catch(error => console.error('Error fetching data:', error));
},
methods: {
urljump(url) {
window.open(url);
}
}
});
</script>
<style>
.table {
display: flex;
flex-wrap: wrap;
}
.card {
margin: 10px;
box-sizing: border-box;
width: 120px;
}
.el-card__body {
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
padding: 10px !important;
}
.image {
width: 70px;
height: 70px;
display: inline-block;
border-radius: 10px;
margin-bottom: 10px;
cursor: pointer;
}
</style>
</body>
</html>配置文件(data-v2.json)
根据配置文件数据格式展示内容,添加配置项即为添加展示的资源卡片:
[
{
"indexname": "研发工具",
"appgroup": [
{
"urlname": "Jenkins",
"img": "static/img/jenkins.png",
"lables": true,
"env": [
{
"urlname": "内网",
"url": "http://内网地址"
},
{
"urlname": "生产",
"url": "http://生产地址"
},
{
"urlname": "测试",
"url": "http://测试地址"
}
]
},
{
"urlname": "IT Tools",
"img": "static/img/it-tools.png",
"url": "static/it-tools/"
}
]
},
{
"indexname": "运维工具",
"appgroup": [
{
"urlname": "Zabbix",
"img": "static/img/zabbix.png",
"url": "http://zabbix地址"
}
]
},
{
"indexname": "云平台",
"appgroup": [
{
"urlname": "腾讯云",
"img": "static/img/tx.png",
"url": "https://console.cloud.tencent.com"
},
{
"urlname": "阿里云",
"img": "static/img/aliyun.png",
"url": "https://console.aliyun.com"
}
]
}
]启动服务
安装依赖
pip install flask运行服务
python main.py访问地址
http://<服务器IP>:8080后台运行(推荐)
# 使用 nohup
nohup python main.py > /dev/null 2>&1 &
# 或使用 screen / tmux 保持会话配置文件字段说明
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
indexname |
string | ✅ | 分组名称,如"研发工具" |
appgroup |
array | ✅ | 该分组下的应用列表 |
appgroup.urlname |
string | ✅ | 应用显示名称 |
appgroup.img |
string | ✅ | 应用图标路径(相对路径) |
appgroup.lables |
boolean | ❌ | 是否启用多环境弹窗(true/false) |
appgroup.env |
array | ❌ | 多环境列表(lables 为 true 时生效) |
appgroup.env.urlname |
string | ✅ | 环境名称,如"内网"、“生产” |
appgroup.env.url |
string | ✅ | 环境对应的访问地址 |
appgroup.url |
string | ❌ | 单链接地址(lables 为 false 或不设置时使用) |
常见问题与注意事项
Vue.js 与 Jinja2 语法冲突
Flask 的 Jinja2 模板引擎默认使用 {{ }},与 Vue.js 的插值语法冲突。代码中已修改 Jinja2 分隔符:
app.jinja_env.variable_start_string = '[['
app.jinja_env.variable_end_string = ']]'图标不显示
- 确认图片文件存在于
static/img/目录下 - 检查配置文件中的
img路径是否正确(如static/img/xxx.png) - 确认文件名大小写与实际文件一致
新增资源不显示
- 确认
data-v2.json文件格式正确(使用 JSON 校验工具检查) - 检查是否有多余的逗号或缺少引号
- 重启服务使配置生效
端口被占用
# 查看端口占用
netstat -tlnp | grep 8080
# 修改 main.py 中的端口
app.run(host="0.0.0.0", port=8090)访问页面空白
- 确认
templates/index.html存在 - 检查静态资源(
static/目录)是否完整 - 打开浏览器开发者工具,查看 Console 是否有报错
多环境弹窗不显示
确认配置中 lables 字段为 true,且 env 数组不为空:
{
"urlname": "Jenkins",
"img": "static/img/jenkins.png",
"lables": true,
"env": [
{ "urlname": "内网", "url": "http://xxx" },
{ "urlname": "生产", "url": "http://xxx" }
]
}