Flask+Vue河南庙会数字化展示与定制项目实战解析
2026/9/20 10:47:44 网站建设 项目流程

做河南庙会数字化这个项目,前后折腾了快一个月,踩了不少坑,也攒了不少能直接套用的经验。今天就把整个“基于Flask的河南庙会文化艺术展示与定制”项目拆开聊一聊,从技术选型到Vue前端互动,再到PyCharm环境配置和实际部署,把关键环节都过一遍。这套东西适合两类人:一是想拿非遗文化、地方活动做成线上展示产品的朋友,二是刚好在学Flask、Vue、Django这几个技术栈,想找一个综合项目练手的人。

我最早想得很简单,做一个网页,把庙会的时间、地点、节目单放上去就行。真做起来才发现,庙会文化的核心不只是“哪天有会”,而是那些传承了很多年的民间艺术:浚县泥咕咕、淮阳泥泥狗、朱仙镇木版年画、豫剧清唱、高跷、旱船、打铁花……这些内容形态极多,有图片、有视频、有传承人故事,如果只是堆在页面上,用户根本看不下去。所以后来把项目定位改成“展示 + 定制”:一方面把庙会和非遗项目做成结构化的浏览体验,另一方面让用户能挑选喜欢的文化元素,生成一张带自己署名的纪念海报或文创卡片。这个“定制”功能,反而成了整个项目里最有意思的部分。

下面我就按实际开发的顺序,把项目整个拆开讲。

1. 为什么是Flask + Vue:项目整体设计与技术选型心路

1.1 先聊选型:Flask比Django更适合这个项目的原因

项目标题里有“基于flask”,但也有不少人第一反应是:为什么不用Django?毕竟Django在做内容管理、后台数据录入方面确实方便,热搜词里也有一堆和Django相关的内容,比如“django项目实战新手”“django创建app”“django之MTV模式有什么作用”。这问题我在项目初期也认真纠结过。

Django的优势是“全家桶”:自带ORM、Admin后台、认证系统、表单处理,MTV模式更是把“数据模型-模板渲染-视图控制”理得很顺。但如果你的团队不大、项目边界清晰,Django反而会有一种“被框架拖着走”的感觉。尤其是我这个项目的核心是“文化展示接口 + 定制服务”,后台管理只是辅助,不会出现几十个复杂权限角色。用Flask这种微框架,我可以把所有代码都捏在自己手里,每个视图函数都看得懂,遇到需要改的地方直接在app.py或blueprint里改,而不需要翻Django的settings和各种中间件。

另外,Flask和SQLAlchemy的组合足够覆盖这个项目的持久化需求。对于“庙会活动表”“非遗项目表”“定制订单表”这种关系相对简单、查询也不复杂的场景,Flask-SQLAlchemy的表现完全不输Django ORM。所以我的结论是:项目体量不大、追求灵活可控,选Flask;项目一上来就有复杂的权限体系、成熟的内容工作流,才更需要Django这种重型框架。这个项目显然属于前者。

1.2 四条业务线:展示、搜索、定制、管理怎么划分

在动手写代码前,我先把项目功能拆成了四条业务线,这也是Flask蓝图(Blueprint)的划分依据:

  • 展示线:庙会日历、非遗项目库、图片库、视频库,核心是让用户“逛得明白”。用户进入首页,能看到近期庙会活动列表,点进详情页能看到庙会对应的非遗艺术、传承人故事、现场图片和视频。
  • 搜索线:支持按庙会名称、地区、非遗类型三个维度筛选。比如用户搜索“浚县”,就能把浚县正月庙会、泥咕咕项目全部带出来。
  • 定制线:这是项目的亮点。用户在看某个非遗项目时,如果喜欢对应的纹样或图片,可以选择底图、填写祝福文案、选择卡片风格,系统在服务端生成一张定制海报,提供高清PNG下载。
  • 管理线:后台只保留最基础的数据维护能力:新增庙会、新增非遗项目、删除下架内容。我直接用Flask-Admin实现,五分钟搭一个后台,后面在“常见问题”里会说清楚为什么不用自写后台。

四条线互不干扰,恰好对应四个Flask蓝图:catalogsearchcustomizeadmin。开发时每一条线都能独立调试,不用在几百行的路由文件里找代码。

2. 庙会文化数据的结构设计:非遗内容如何变成可管理的数据

2.1 数据模型拆分:从“庙会”到“文化要素”的三层结构

文化类项目最容易犯的错,就是把所有信息塞进一个大表格。比如一个“庙会活动表”里既有庙会名称、时间、地点,又把表演项目、非遗图片全用JSON字段塞进去。短时间看是省事,后面做筛选、定制、推荐的时候,查数据会非常痛苦。

我最终拆成了三个核心模型:

  • Festival(庙会活动):字段包括id、名称、地区、开始日期、结束日期、简介、主图地址。一个庙会可以关联多个非遗项目,新的庙会信息由后台维护人员录入。
  • HeritageItem(非遗项目):字段包括id、名称、类型、所属地区、传承人、介绍、图片地址、视频地址。这里用festival_id做外键,表示这个项目属于哪一场庙会,同时允许一个项目出现在多个庙会中,用关联表维护多对多关系。
  • CustomOrder(定制订单):字段包括id、底图地址、文字内容、风格参数、生成结果地址、创建时间。这个表的用途是记录每一次定制行为,方便后面统计哪个纹样最受欢迎。

选这段结构的关键原因是:用户浏览路径是“庙会 → 非遗项目 → 定制文创”,数据模型必须跟着这个路径走,才能保证前端请求一次就能把详情页需要的所有数据拼出来。如果把图片、视频、项目介绍全堆在同一张表里,后面做定制功能的材料匹配时,就得反复拆JSON,非常难维护。

2.2 中文标签与分类体系:如何避免“类型”字段失控

河南庙会里的非遗内容五花八门,如果分类做得太粗,比如只分“表演类”“手工艺类”,那泥咕咕和朱仙镇年画会被堆在一起,用户搜索时无法精准命中;如果做得太细,比如按每个村的手艺分,后台维护成本又太高。

我的做法是“两级分类”:

  • 一级分类(category):固定几个大项,表演艺术、传统技艺、民俗活动、传统美食。
  • 二级分类(tags):用逗号分隔的标签,比如“泥塑、彩绘”“戏曲、豫剧”“社火、高跷”。一级分类用字典表约束,二级分类允许自定义。

这个方案兼顾了结构化查询和灵活性。写SQLAlchemy查询时,按一级分类过滤非常快;按标签搜索时,直接用LIKE '%泥咕咕%'或者加了简单的倒排缓存,也能满足小规模访问量。如果你一上来就上Elasticsearch,反而有点杀鸡用牛刀。

2.3 图片/视频文件规划:m3u8播放背后的存储约定

庙会现场的视频大多是长视频,如果直接放MP4,用户加载起来很慢。这里就要提到热搜词里反复出现的“vue播放m3u8、vue播放m3u8免安装”。m3u8是HLS流媒体协议里的索引文件,视频被切成很多个.ts小片段,播放器通过索引文件按需加载,它的好处是加载快、支持拖动、对服务器带宽压力小。

我在项目里对文件目录做了统一约定:

media/ festivals/ {festival_id}/ cover.jpg images/ videos/ index.m3u8 ts/

视频上传后,用FFmpeg把MP4转成HLS切片:

ffmpeg -i input.mp4 -codec copy -hls_time 6 -hls_list_size 0 -hls_segment_filename ts/output_%03d.ts output.m3u8

这条命令的意思是:不重新编码(-codec copy)以节省CPU,每个切片6秒,生成output.m3u8索引文件和按顺序编号的ts切片。-hls_list_size 0表示保留所有切片,不生成滚动播放列表。

文件存储做好约定之后,Flask和Vue只需要按路径规则访问就行。前端播放m3u8的细节我在后面专门讲。

3. 从零搭起项目:PyCharm、Flask后端与Vue前端的完整实操

3.1 PyCharm里创建Flask工程并启动的详细步骤

如果你在热搜里搜过“pycharm安装教程”“pycharm安装flask”,说明你大概率卡在了环境配置这一步。这里我说一套最省心的流程,用PyCharm专业版或社区版都能走通。

第一步,打开PyCharm,选择New Project,左侧选Flask。注意,PyCharm新建Flask项目时会自动创建一个app.py和一个templates目录,这对小项目没问题,但我们后面要拆蓝图,所以只把它当成脚手架。社区版也可以,只是少了Flask模板的快捷入口,手动建目录也一样。

第二步,在PyCharm底部的Terminal里激活虚拟环境并安装依赖。如果有venv目录,Windows下用:

venv\Scripts\activate

然后安装核心依赖:

pip install flask flask-sqlalchemy flask-cors flask-admin pillow waitress

这里解释一下每个包的用途:

  • flask:基础Web框架。
  • flask-sqlalchemy:把SQLAlchemy集成到Flask,负责ORM映射。
  • flask-cors:解决前端Vue开发服务器和后端Flask端口不一致时的跨域问题。
  • flask-admin:做成管理后台,不用自己写增删改查页面。
  • pillow:定制海报时做图片合成。
  • waitress:Windows下常用的生产级WSGI服务器,后面部署讲。

第三步,写最简单的启动入口,确认环境没问题:

from flask import Flask app = Flask(__name__) @app.route("/") def index(): return "庙会文化项目启动成功" if __name__ == "__main__": app.run(debug=True, port=5000)

在PyCharm里直接右键运行,浏览器访问http://127.0.0.1:5000,看到提示文字就说明环境通了。这一步看起来很简单,但我见过很多新手卡在“为什么pip install之后PyCharm还是找不到flask”——原因基本都是PyCharm当前解释器没指向虚拟环境,需要在Settings -> Project -> Python Interpreter里手动选择venv下的Python。

3.2 Flask后端:REST风格接口和跨域处理

项目到了正式写接口的阶段,我按REST风格定义了最核心的几个API端点:

方法路径说明
GET/api/festivals庙会列表,支持地区、日期筛选
GET/api/festivals/ int:id庙会详情,附带关联非遗项目
GET/api/heritage/ int:id非遗项目详情
POST/api/custom/orders提交定制订单
GET/api/custom/orders/ int:id查询定制订单状态和下载地址

写接口时有个关键点:前端Vue的开发服务器默认跑在5173端口,Flask跑在5000端口,两者端口不同,浏览器默认会拦截跨域请求。解决办法是加flask-cors

from flask_cors import CORS app = Flask(__name__) CORS(app, resources={r"/api/*": {"origins": "*"}})

origins这里开发阶段用通配符*图省事,部署上线后建议改成前端真实域名。我遇到过因为跨域配置太宽松,被临时部署的测试域名白嫖接口的情况,所以生产环境一定不要图方便。

另外,Flask接口返回数据时统一用jsonify,并且给每个接口都套一层codedatamessage结构。这样前端拿到响应后,先判断code再取数据,后面写Vue代码会清爽很多。

3.3 Vue前端工程:axios、路由和庙会详情页

前端部分我用Vue 3 + Vite。创建项目之前先确认Node环境装好,然后在PyCharm终端里执行:

npm create vue@latest

这里会问你需不需要TypeScript、Vue Router、Pinia等,按需选择。我建议新手第一次做项目可以直接全选默认,先跑通再逐个剥掉。项目创建完进入目录,安装依赖:

npm install npm install axios

安装依赖慢是另一个常见痛点,Windows下如果遇到网络问题,把npm镜像切到国内源会明显改善。切源的方法很简单,在项目根目录建一个.npmrc文件,写入:

registry=https://registry.npmmirror.com

前端页面结构并不复杂,核心是三个视图:HomeView.vue展示庙会列表,FestivalDetail.vue展示庙会详情和关联的非遗项目,CustomizeView.vue承载定制功能。路由用Vue Router管理,典型配置:

import { createRouter, createWebHistory } from 'vue-router' import HomeView from '../views/HomeView.vue' import FestivalDetail from '../views/FestivalDetail.vue' import CustomizeView from '../views/CustomizeView.vue' const router = createRouter({ history: createWebHistory(), routes: [ { path: '/', name: 'home', component: HomeView }, { path: '/festival/:id', name: 'festival-detail', component: FestivalDetail }, { path: '/customize/:heritageId', name: 'customize', component: CustomizeView } ] }) export default router

这里有个容易踩的坑:用createWebHistory时,在Vite开发服务器没问题,但打包后部署到Nginx,如果用户直接刷新/festival/1这个地址,Nginx会返回404。原因是没有配置前端路由回退,需要把Nginx的try_files指到index.html。这个我在后面部署章节会详细说。

调用后端接口时,统一封装一个request.js,用axios实例的方式:

import axios from 'axios' const request = axios.create({ baseURL: 'http://127.0.0.1:5000/api', timeout: 10000 }) request.interceptors.response.use( response => response.data, error => { console.error('请求出错', error) return Promise.reject(error) } ) export default request

这样每个页面里只需要写:

import request from '@/utils/request' const res = await request.get(`/festivals/${route.params.id}`)

把baseURL统一管理之后,后面前端联调或者部署换地址,只改一个文件,不用在几十个组件里搜API链接。

3.4 庙会视频的m3u8在Vue中播放的完整配置

前面说了视频会转成m3u8格式,前端播放是另一个容易卡住很多人的地方。Vue里播放m3u8,我推荐用hls.js,因为流程简单、兼容性好。

先安装:

npm install hls.js

然后在组件里写一个播放封装:

<template> <video ref="videoRef" controls class="video-player"></video> </template> <script setup> import { ref, onMounted, watch } from 'vue' import Hls from 'hls.js' const props = defineProps({ src: { type: String, required: true } }) const videoRef = ref(null) function playHls(url) { const video = videoRef.value if (Hls.isSupported()) { if (hlsInstance) { hlsInstance.destroy() } hlsInstance = new Hls() hlsInstance.loadSource(url) hlsInstance.attachMedia(video) hlsInstance.on(Hls.Events.MANIFEST_PARSED, () => { video.play() }) } else if (video.canPlayType('application/vnd.apple.mpegurl')) { // Safari直接支持HLS video.src = url video.play() } } onMounted(() => { if (props.src) { playHls(props.src) } }) watch(() => props.src, (newVal) => { if (newVal) { playHls(newVal) } }) </script>

这段代码里有一个细节值得注意:hlsInstance必须存储在组件作用域里,避免音频和视频播放下一个文件时,上一个Hls实例还在后台占用内存和请求。我在调试时遇到过连续切换几个庙会视频后页面卡死的现象,后来发现就是Hls实例没有销毁。

还有一个和m3u8播放强相关的问题:CORS。m3u8索引文件后面跟着几十个ts片段,浏览器会逐个请求这些ts文件,如果服务器没给ts文件设置正确的CORS头,就算m3u8能加载,视频画面也黑屏。Flask里处理办法是给媒体目录单独设置响应头:

@app.after_request def add_media_cors_headers(response): if request.path.startswith('/media/'): response.headers['Access-Control-Allow-Origin'] = '*' return response

这个坑非常隐蔽,因为浏览器控制台可能只报一个Failed to load resource,不会明确告诉你是哪个ts文件被CORS拦截了。排查的时候要先打开Network面板,过滤m3u8ts请求,看响应头里有没有Access-Control-Allow-Origin

4. 定制功能落地:从“浏览文化”到“带走文化”

4.1 定制表单与订单状态机设计

“定制”听起来很玄,落到功能上就是三步:选底图、填文案、生成图片。

定制页面的表单字段我设计成:

  • heritageId:当前正在浏览的非遗项目ID,决定有哪些纹样素材可选用。
  • templateId:用户选择的底图模板ID,模板决定了海报的整体色调和构图。
  • senderName:署名,比如“张三 敬献”。
  • message:一句祝福或介绍文字,比如“中原遗韵,匠心永传”。
  • style:配色风格,比如“绛红”“墨青”“宣纸黄”。

订单表里除了这些字段,还要有一个状态字段status,我用字符串表示:

  • pending:刚提交,服务端还没处理。
  • processing:服务端正在生成图片。
  • done:图片生成完成,提供下载地址。
  • failed:生成失败,记录了失败原因。

状态机看着简单,实际开发时很有价值。因为图片合成是耗时操作,如果直接在请求里同步生成,用户等待时间可能超过5秒,前端axios默认超时时间不够,体验极差。我的做法是:提交订单后立刻返回orderIdpending状态,前端轮询查询接口,等后端把图片合成好后返回done。这样用户界面可以先显示“正在排版,请稍候”,而不是卡在请求里。

4.2 用Pillow生成定制海报的轻量实现

定制功能的核心是服务端用Pillow合成图片。我把整个流程放在一个独立模块poster.py里,关键代码如下:

from PIL import Image, ImageDraw, ImageFont def generate_poster(order): base = Image.open(order.base_image_path).convert("RGB") draw = ImageDraw.Draw(base) # 在底图下方叠加半透明色带,保证文字可读性 overlay = Image.new("RGBA", base.size, (0, 0, 0, 0)) overlay_draw = ImageDraw.Draw(overlay) width, height = base.size overlay_draw.rectangle( [0, int(height * 0.75), width, height], fill=(0, 0, 0, 160) ) base = Image.alpha_composite(base.convert("RGBA"), overlay).convert("RGB") draw = ImageDraw.Draw(base) # 写文案 font_path = "fonts/SourceHanSerifSC-Regular.otf" title_font = ImageFont.truetype(font_path, 48) text_font = ImageFont.truetype(font_path, 32) draw.text((50, int(height * 0.78)), order.message, fill=(255, 255, 255), font=title_font) draw.text((50, int(height * 0.78) + 80), f"{order.sender_name} 敬献", fill=(230, 220, 200), font=text_font) output_path = f"output/poster_{order.id}.png" base.save(output_path) return output_path

这里有两个特别需要注意的坑:

一是字体问题。Pillow默认字体不支持中文,如果直接画中文会得到一堆方框。必须准备中文字体文件,思源宋体、思源黑体都行,而且路径要放到项目里随代码一起管理,不要依赖操作系统的字体目录,否则服务器上一换环境中文就乱码。

二是图片合成时的模式转换。底图可能是JPEG,没有Alpha通道,而半透明遮罩是RGBA,直接粘贴会报错。必须先convert("RGBA")做合成,再转回RGB保存为PNG。我第一次写的时候忘了转回RGB,保存出来的图背景是全黑的,排查了半天才发现是模式问题。

定制结果生成以后,Flask提供一个下载接口,这里需要设置Content-Disposition头,让浏览器把响应当附件下载而不是直接打开:

from flask import send_file @app.route("/api/custom/orders/<int:order_id>/download") def download_custom_order(order_id): order = CustomOrder.query.get_or_404(order_id) if order.status != "done": return jsonify(code=400, message="订单尚未完成"), 400 return send_file( order.result_path, mimetype="image/png", as_attachment=True, download_name=f"poster_{order_id}.png" )

如果你在热搜里看到“django streaminghttpresponse 参数content_type和content-disposition”,其实解决的就是同一个问题:文件下载时,响应头里Content-Type告诉浏览器文件类型,Content-Disposition里的attachment告诉浏览器是附件而不是内联展示。Flask的send_fileas_attachment=Truedownload_name就能完成,底层道理和Django是一模一样的。

4.3 管理后台:Flask-Admin还是自写?

项目后台只需要录入数据,我直接用Flask-Admin,十分钟就能跑起来:

from flask_admin import Admin from flask_admin.contrib.sqla import ModelView admin = Admin(app, name="庙会文化管理后台") admin.add_view(ModelView(Festival, db.session)) admin.add_view(ModelView(HeritageItem, db.session)) admin.add_view(ModelView(CustomOrder, db.session))

ModelView会自动根据SQLAlchemy模型生成列表页、新增页、编辑页、删除按钮。对于运营人员来说,能录入数据、能改信息就够了,没必要费劲用Vue写一个独立后台。

但这里要提醒一点:如果把CustomOrder也交给Flask-Admin管理,默认情况下运营人员可以直接看到用户的署名、联系方式(如果有的话),也能编辑订单状态。这在内部使用没问题,一旦项目要对外开放并且涉及用户敏感信息,就必须对后台做权限控制,至少加一个简单的登录认证。Flask-Admin支持用flask-login包装一层,但能查看到什么字段、能不能导出,建议单独配置成只读视图,避免误操作。

5. 常见问题与排查技巧实录

5.1 PyCharm与Vue环境配置的经典报错

这个项目开发环境里最密集的问题,基本都集中在“环境配置”而不是业务逻辑上。

第一个高频报错是pip install flask时报错Microsoft Visual C++ 14.0 is required。这个通常是某些Python包需要编译C扩展,Windows下没有C++构建工具。解决方法是下载“Microsoft C++ Build Tools”安装,或者尽量选有预编译wheel文件的包版本。在安装pillow时如果遇到这个报错,直接去PyPI下载对应Python版本的pillow.whl手动安装,能绕开编译过程。

第二个高频问题是PyCharm里安装Flask后,代码还是飘红。这时要检查右下角解释器状态,确保当前项目使用的是虚拟环境里的Python,而不是PyCharm自带的系统解释器。具体路径是File -> Settings -> Project -> Python Interpreter,选择venv目录下的Python.exe。

第三个高频问题是Vue安装依赖时卡在npm install。除了切换镜像源,还可以试一下npm install --registry=https://registry.npmmirror.com。如果node_modules已经装了一半,先删除node_modulespackage-lock.json再重新安装,不要直接在失败现场反复install。

5.2 m3u8播放黑屏与请求拦截图解

我在庙会详情页里嵌了视频播放,自测时发现一个诡异现象:m3u8本身能加载出来,播放器初始化正常,但画面始终黑,进度条可以拖动,就是没有声音没有画面。

排查步骤:

  1. 打开浏览器Network面板,筛选m3u8,确认索引请求返回200。
  2. 筛选ts,发现大量ts请求返回200,但看Response响应体长度,有些是0。
  3. 直接复制一个ts链接到新标签页打开,发现能正常播放。
  4. 查看这个ts请求的响应头,发现没有Access-Control-Allow-Origin

问题就出在ts文件的CORS响应头上。Flask里对/media/的请求加了after_request处理,生产环境用Nginx托管媒体文件时,又需要再检查Nginx配置里的add_header是否写对了位置。如果你在Flask里加了CORS头但Nginx又托管了媒体目录,那Nginx很可能把Flask的响应头直接覆盖掉了。正确做法是在Nginx的location /media/块里单独加:

location /media/ { add_header Access-Control-Allow-Origin *; }

另外,如果你在开发环境把m3u8索引和ts切片放在不同域名下,情况会更复杂,因为浏览器对每个资源都会单独发CORS预检。最省事的方案就是让m3u8和ts同源。

5.3 部署上线:waitress + Nginx的组合

项目开发完成后的部署,我选了waitress + Nginx。热搜词里也有“python django windows10 waitress+nginx部署”,其实waitress不区分Flask还是Django,它就是一个纯Python的WSGI服务器,Windows下用起来比gunicorn省心得多,因为gunicorn在Windows上支持并不好,经常需要借助WSL才能跑。

生产启动命令很简单:

waitress-serve --host=0.0.0.0 --port=8000 app:app

app:app表示从app.py中导入app这个Flask实例。注意,生产环境一定不要再开Flask的debug模式,那会带来很大的安全风险,我早期有一次忘了关debug,直接把项目搁在一个公网测试服务器上,结果后台暴露了调试器,点开哪个请求都能看到服务端完整报错信息,包括文件路径和部分代码片段。好在只是测试环境,不然后果很麻烦。

Nginx在部署里的职责主要是两个:一是托管dist目录下的Vue静态文件,二是把/api请求反向代理到waitress的8000端口。

前端Vue打包先执行:

npm run build

会生成dist目录。Nginx配置核心块:

server { listen 80; server_name your-domain.com; root /path/to/dist; index index.html; location / { try_files $uri $uri/ /index.html; } location /api/ { proxy_pass http://127.0.0.1:8000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } location /media/ { alias /path/to/media/; } }

这段配置里最关键的是try_files $uri $uri/ /index.html。没有这一行,用户在前端路由下刷新页面就会Nginx 404,这正是Vue Router使用HTML5 History模式时的经典问题。

5.4 缓存策略与海报下载文件名乱码

还有一个我踩过的小坑:定制海报下载时,如果用户直接点send_file返回的接口,文件名可能是poster_12.png这种格式,浏览器下载时没问题。但如果文件名里有中文,比如我想让用户下载的是“浚县泥咕咕定制海报.png”,必须给download_name传带UTF-8编码的值,同时要确保响应头里的Content-Disposition正确编码。Flask的send_file已经处理了download_name的URL编码,但我遇到过前端拿到下载地址后用window.open打开,某些浏览器会把中文文件名变成一串百分号。

后来我干脆写了一个前端下载辅助函数,先用fetch拿到Blob,再用本地临时URL触发下载,这样文件名由前端决定,不再依赖服务器的Content-Disposition:

async function downloadPoster(orderId) { const res = await fetch(`/api/custom/orders/${orderId}/download`) const blob = await res.blob() const url = URL.createObjectURL(blob) const a = document.createElement('a') a.href = url a.download = '定制海报.png' a.click() URL.revokeObjectURL(url) }

这个方法虽然多了一步,但能确保中文文件名在所有浏览器里都正常。如果你既想让浏览器直接下载,又希望文件名是中文,那也可以继续用send_filedownload_name,前提是客户端不额外处理响应。

项目做下来的三点体会

如果回头复盘这个项目,我最想强调的不是Flask怎么写、Vue怎么用,而是“文化数据的颗粒度”要先想清楚。庙会文化不是几个字段能装完的,但也不能一开始就把模型设计得无比复杂。先用“庙会-非遗项目-定制订单”这三张表把核心链路跑通,后面加传承人、加展演日历、加数字藏品,都是在稳定骨架上长肉。

另外一点,就是前后端联调时一定要尽早把接口返回结构定下来,不要前端一套、后端一套。我在项目初期吃够了乱改接口的亏,后来把所有API响应统一成{ code, data, message },前后端各写一份接口文档,问题立刻少了大半。

最后说一个可以继续扩展的方向。现在的定制功能只支持静态海报和图片下载,后续可以考虑把定制结果做成动态页面,生成带独立链接的H5分享页,用户在手机上打开就能看到自己定制的庙会艺术卡片,还能转发给朋友。技术上并不复杂,就是在订单完成后多生成一个HTML模板,用Vue做分享页渲染,Flask只需要提供一个公开只读的分享接口。这样整个项目就从“展示 + 定制”进一步升级成了“展示 + 定制 + 分享”,文化传播的链条才算真正闭合。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询