2024 年年底已经到了,看到网易云音乐和QQ音乐的年度报告,突然觉得,我也可以给同事们安排一个。
实际上这个想法 2023 年就已经有了,并且也确实做了一些工作,但是因为不懂 css 和 javascript ,所以很难做出来漂亮美观的页面。
在大概 8 年前,我尝试过用 WordPress 来搭建博客和个人网站,虽然后来没有再使用,但是对 WordPress + Elementor 这种直接可视化拖动小部件来搭建网页的方式,印象非常深刻。能让一个完全不懂前端的人,快速的地搭建一个相对美观的网页。
所以,我在今年尝试了新的技术方案,以便于我能够在对前端一无所知的情况下,也能构建出来一个美观的、带有简单动画的年度报告页面,那就是利用 WordPress 来可视化地制作网页,具体的技术思路如下:
最终效果如下:
直接使用 Docker Desktop 部署 WordPress:
services:
wordpress:
image: wordpress:php8.3
restart: always
volumes:
- './html/plugins:/var/www/html/wp-content/plugins'
- './html/uploads:/var/www/html/wp-content/uploads'
- './html/public_static:/var/www/html/public_static'
- './php/php.ini:/usr/local/etc/php/php.ini'
ports:
- '60080:80'
environment:
TZ: "Asia/Shanghai"
WORDPRESS_DB_NAME: 'wordpress'
WORDPRESS_DB_USER: 'root'
WORDPRESS_DB_PASSWORD: '********'
WORDPRESS_DB_HOST: 'mysql'
WORDPRESS_CONFIG_EXTRA: |
define( 'WP_MEMORY_LIMIT', '1024M' );
define( 'WP_MAX_MEMORY_LIMIT', '2048M' );
define( 'WP_SITEURL', 'https://<your-domian>' );
define( 'WP_HOME', https://<your-domian>' );
define( 'WP_CACHE', true );
define( 'WP_ENVIRONMENT_TYPE', 'production' );
mysql:
image: mysql:latest
restart: always
volumes:
- 'mysql-data:/var/lib/mysql'
environment:
MYSQL_DATABASE: 'wordpress'
MYSQL_ROOT_PASSWORD: '********'
volumes:
mysql-data:
/var/www/html/wp-content/plugins
、附件上传目录 /var/www/html/wp-content/uploads
即可,有必要时还可以挂载主题目录 /var/www/html/wp-content/themes
。wp-config.php
文件,在容器启动时,会根据传入的环境变量自动生成。php.ini
并挂载到 /usr/local/etc/php/php.ini
,有利于更改内存限制和最大文件上传大小。./html/public_static:/var/www/html/public_static
是给 Simply Static 准备的静态页面存储位置。将访问端口接入到腾讯云EdgeOne来得到可以公网访问的域名并添加 SSL 证书,并将域名配置到 WORDPRESS_CONFIG_EXTRA
环境变量中,然后部署容器即可。
Pro 版本可以直接在淘宝搜索购买,价格在 18元 ~ 30元 不等,一般都需要发送页面的公网访问地址、管理员用户名、管理员密码给商家进行激活。
直接在插件商店搜索安装即可。也可以在 Simply Static - the best WordPress static site generator. 官网下载安装,然后将其放到 ./html/plugins
目录下,然后在插件管理页面开启。
还可以安装其他 Elementor 相关插件,获得更加丰富的页面小部件。
在页面菜单中,用 Elementor 构建器创建编辑一个页面,并将其设置为站点主页。
由于我是用来作为年度报告,所以不希望页面有导航栏、菜单栏,所以我在页面设置中,将 页面布局
设置为 Elementor 画布
,然后开始使用各种小部件来搭建页面。
通过设置板块最小高度为 100vh 来让每一个页面部分都能动态适应各种型号的手机屏幕满屏显示,这样截图时才能够更加美观。
通过将数据部分写成 {{data}} 占位符,我在转换成静态页面之后,能轻松地搜索这些占位符,替换成 jinja2 模板用到的实际字段名。
在完成页面构建,预览效果满意后,就可以使用 Simply Static 插件来转换。
Settings-General
页面设置为 Offline Usage
Settings-Deploy
页面设置为 Local Diretory
并配置导出目录为 /var/www/html/public_static
Generate Static Files
按钮,并切换到 Activity Log
查看日志,耐心等待导出完成。出现 Done! Finished in <time>
字样说明导出成功。Diagnostics
诊断工具页面检查是否可以转换、是否存在不兼容插件等情况。从 ./html/public_static
目录将所有生成的静态文件复制到一个新的项目文件夹。
在清理完不用的静态文件后,我们可以编写一个 python 脚本。
以下脚本是一个示例,它加载 jinja2 模板,从 postgresql 数据库中查询数据,并渲染成本地的静态页面。注意,在渲染前,必须将 index.html 中的 {{data}} 占位符改为实际数据字段名,本例中为 name
,date
, some_data
三个字段。
from datetime import datetime
import psycopg
from pydantic import BaseModel
from jinja2 import Environment, FileSystemLoader
import os
teachers = ["张三", "李四", "王五"]
template_dir = os.path.join(os.path.dirname(__file__), 'static')
env = Environment(loader=FileSystemLoader(template_dir))
template = env.get_template('index.html')
class ReportData(BaseModel):
name:str
date:str = datetime.now().strftime("%Y-%m-%d %H:%M")
some_data:int = 0
conn = psycopg.connect("postgresql://<user>:<pwd>@<host>:5432/<database>")
cur = conn.cursor()
for teacher in teachers:
report_data = ReportData(name=teacher)
cur.execute("""
SELECT .... AS some_data FROM table
WHERE "teacherName" = %s AND "startTime" BETWEEN '2024-01-01' AND '2024-12-31';
""", [teacher])
record = cur.fetchone()
if record:
report_data.some_data = record[0]
output = template.render(report_data.model_dump())
with open(f'static/{teacher}.html', 'w', encoding="utf-8") as f:
f.write(output)
cur.close()
运行脚本,可以生成 张三.html
李四.html
王五.html
三个年度报告,然后打开确认,如果有需要调整的地方,可以直接调整 index.html
模板,或者重新编辑 WordPress 页面并生成静态文件。
我们首先在腾讯云对象存储中创建一个新的私有读写的桶,然后开启桶的静态网站功能。
然后使用腾讯云 EdgeOne 配置一个域名,并将存储桶接入。
然后可以将静态资源上传到对象存储根目录,然后访问 http://your-domain/index.html 来查看配置是否正确。
在上一步中,我们使用 for 循环遍历teachers
列表,生成了一些 html 页面,用于本地预览。
但是实际上线时,可能不希望一个人能查看另一个人的年度报告,所以,文件名就不能简单地使用人名。
我们可以引入 uuid
库,然后为每个老师生成一个随机的 uuid
,并截取后 12 位作为文件名,将其上传到腾讯云对象存储中。
同时,还需要为每个人推送他们的报告,我使用了钉钉工作通知。
... # 省略重复代码
import uuid
from qcloud_cos import CosConfig, CosS3Client
from dagster_dingtalk import DingTalkAppClient
cos_client = CosS3Client(
CosConfig(Region="ap-guangzhou", SecretId="<your-secretid>", SecretKey="<your-secretkey>")
)
dt_client = DingTalkAppClient(
app_id="<your-app_id>",
client_id="<your-client_id>",
client_secret="<your-client_secret>"
)
teachers = ["张三", "李四", "王五"]
... # 省略重复代码
for teacher in teachers:
... # 省略重复代码
output = template.render(report_data.model_dump())
uuid_str = str(uuid.uuid4())[-12:]
cos_client.put_object(
Bucket='<your-bucket>',
Body=output.encode('utf-8'),
Key=f'{uuid_str}.html',
EnableMD5=False
)
msg = {
"msgtype": "action_card",
"action_card": {
"title": "你的年度教学报告来啦!快来查看吧!",
"markdown": "\n"
f"\n### {teacher}老师,您的2024年度教学报告已生成!快来查看吧!",
"single_title": "点击查看",
"single_url": f"https://<your-domain>/{uuid_str}.html"
}
}
dt_client.即时通信.工作通知.发送工作通知(to_all_user=False, user_list=[user_id], msg=msg)
... # 省略重复代码
至此,搞定完工!