ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

Flask+Vue企业任务分配系统实战:从架构设计到算法实现

Flask+Vue企业任务分配系统实战:从架构设计到算法实现 好久没写项目复盘了最近正好给一个中型制造业企业做了一套内部管理工具核心模块就是任务分配和进度跟踪。技术选型上我直接定了Python Flask加Vue这套组合标题里写的“企业项目管任务分配”其实就是“企业项目管理中任务分配子系统”的意思后台接口用Flask前端页面用Vue全家桶前后端分离开发。这篇文章我把整套方案的思路、关键代码、踩坑记录都整理出来给准备用FlaskVue做企业内部系统的朋友一个完整的参考。先说一下这套方案适合什么人。如果你是要给公司做内部工具、给学校做课程设计、或者自己接外包做管理类系统而且团队里Python基础比Java好那Flask一定是比Spring Boot更顺手的选项。它轻、灵活、生态齐全配合Vue做前端一个人也能在两周内从零拉出一个能用的系统。如果你从来没接触过前后端分离这篇文章也可以当入门实战教程看我会把每个环节为什么这么做讲清楚。1. 整体设计与技术选型思路1.1 为什么选Flask而不是Django很多人一听说用Python写Web后端第一反应就是Django。确实Django自带Admin后台、ORM、认证体系开箱即用但如果你做的是企业任务管理这种业务逻辑不算特别复杂、但定制化程度很高的系统Django反而有点笨重。Flask的核心优势是“微框架”它不会替你决定用哪个数据库、用哪种模板、用哪种认证方式所有东西都能自己组装。这套任务分配系统里我需要的核心能力其实是提供RESTful API给前端Vue调用、管理用户和任务数据、实现简单的权限控制。Flask加Flask-SQLAlchemy、Flask-CORS、PyJWT这几个扩展已经完全够了。整个后端代码加起来不超过2000行结构非常清晰后面要加功能也方便。打个比方Django像是一套精装修的房子拎包入住但想改墙就得把装修砸了Flask像是毛坯房水电管线都给你预埋好了想怎么隔断、怎么装完全由你决定。做企业内部管理工具业务变化非常快今天要加个任务标签明天要加个工时统计Flask这种高度可控的架构反而更省心。1.2 前端为什么用Vue而不是其他框架Vue在国内企业项目里的普及率这几年一直很高主要原因有三个。第一是上手门槛低模板语法简单后端开发人员也能快速看懂第二是组件化开发非常适合管理后台这种页面布局第三是Vue生态里的Element UI、Ant Design Vue这些组件库能直接把表格、表单、弹窗、树形控件拖出来用开发效率极高。这套系统里我用了Vue 3加Vue Router加Axios的组合。Vue 3的组合式API写业务逻辑比Vue 2的选项式API更灵活特别是任务分配这种涉及多个状态联动的场景用ref和computed管理状态会很顺手。热词里也提到了vue选项式和组合式区别这个后面我会专门展开讲一下我实际切换过来的体会。有人可能会问为什么不直接服务端渲染用Flask的Jinja2模板把页面渲染出来。说实话纯内部系统确实能用模板渲染但任务分配这个场景对交互要求很高比如拖拽变更任务状态、实时筛选任务列表、多人同时刷新数据这些用服务端渲染做起来非常痛苦。前后端分离之后Flask只干一件事——处理数据Vue只干一件事——渲染页面职责清晰出问题也好排查。1.3 架构设计与数据库选型整个系统采用经典的前后端分离架构Vue前端端口8080 -- Axios HTTP请求 -- Flask后端端口5000 -- MySQL数据库前端的页面路由由Vue Router管理后端只提供/api/xxx格式的JSON接口。开发环境下我用Vue CLI的代理功能解决跨域问题生产环境则直接用Nginx把前端静态文件和后端接口代理到同一个域名下这样浏览器不会产生跨域限制。数据库我选的是MySQL原因是这套任务分配系统后续要对接企业里已有的员工数据MySQL在企业里几乎是无处不在的标配。如果你做课程设计或者小团队内部用精简开发复杂度可以直接换成SQLite代码层面只需要改一行数据库连接配置Flask-SQLAlchemy的ORM模型完全不用动。2. Flask后端核心模块设计与实现2.1 项目目录结构规划后端代码我习惯按模块拆分而不是把所有路由写在同一个app.py里。虽然Flask允许你写一个超级文件搞定一切但任务分配系统至少包含用户、项目、任务、统计这四个业务模块全部堆一起到后面改一个地方手抖就可能影响到别的地方。我推荐的结构是这样的backend/ ├── app.py # 应用入口注册所有蓝图 ├── config.py # 配置文件数据库连接等 ├── models/ │ ├── __init__.py # 初始化db对象 │ ├── user.py # 用户模型 │ ├── project.py # 项目模型 │ └── task.py # 任务模型 ├── apis/ │ ├── __init__.py # 蓝图注册 │ ├── auth.py # 登录认证接口 │ ├── project_api.py # 项目相关接口 │ ├── task_api.py # 任务相关接口 │ └── stats_api.py # 统计报表接口 ├── utils/ │ ├── decorators.py # 登录校验装饰器 │ └── response.py # 统一返回格式封装 └── requirements.txt这种结构下每个业务模块独立成文件路由函数只负责参数校验和调用业务方法数据操作逻辑放在模型层的类方法里。比如Task模型类下写一个assign_task()方法专门处理任务分配的业务逻辑路由函数调用它就行。这样做的好处是以后写单元测试会非常舒服直接实例化模型类调用方法就能验证逻辑。2.2 数据模型设计用户、项目、任务三张表任务分配系统的核心数据模型就三张表但设计的时候有几个细节值得注意。第一张是用户表。除了基本的id、username、password_hash之外我加了一个role字段用来区分管理员、项目经理、普通成员三种角色。密码字段我存的是werkzeug.security生成的哈希值绝对不存明文密码这是一个基本的安全底线。第二张是项目表。字段包括id、name、description、owner_id项目负责人、status、created_at。这里的owner_id是一个外键指向用户表的id代表“这个项目是谁负责的”。任务分配的时候需要优先把任务分给项目负责人所以这个外键关系很重要。第三张是任务表结构会复杂一些class Task(db.Model): __tablename__ task id db.Column(db.Integer, primary_keyTrue) title db.Column(db.String(200), nullableFalse) description db.Column(db.Text) project_id db.Column(db.Integer, db.ForeignKey(project.id)) assignee_id db.Column(db.Integer, db.ForeignKey(user.id)) creator_id db.Column(db.Integer, db.ForeignKey(user.id)) priority db.Column(db.Integer, default1) # 1低 2中 3高 4紧急 status db.Column(db.String(20), defaulttodo) # todo doing done estimate_hours db.Column(db.Float, default0) actual_hours db.Column(db.Float, default0) due_date db.Column(db.Date) created_at db.Column(db.DateTime, defaultdatetime.utcnow)任务表里的assignee_id就是“被分配人”这是整个任务分配功能的核心字段。priority用来区分任务的紧急程度estimate_hours是预估工时这两个字段在后面的自动分配算法里都会用到。为了简化这里用字符串表示任务状态todo是待办、doing是进行中、done是已完成。注意实际开发中我一般会给status字段建立索引因为任务列表页经常按状态筛选数据。如果团队规模将来变大任务表数据量到几十万条的时候没有索引的查询会明显变慢。2.3 登录认证与权限控制企业内部系统虽然不像互联网产品那样面临巨大的安全攻击压力但基础的认证还是必须有的。我用的是JWTJSON Web Token方案前端登录成功后拿到一个token之后每次请求都在请求头带上Authorization: Bearer token后端通过装饰器校验token并解析出当前用户信息。def token_required(f): wraps(f) def decorated(*args, **kwargs): token request.headers.get(Authorization) if not token: return jsonify({code: 401, msg: 未登录}), 401 try: token token.replace(Bearer , ) payload jwt.decode(token, app.config[SECRET_KEY], algorithms[HS256]) current_user User.query.get(payload[uid]) except jwt.ExpiredSignatureError: return jsonify({code: 401, msg: 登录已过期}), 401 except Exception: return jsonify({code: 401, msg: 无效的token}), 401 return f(current_user, *args, **kwargs) return decorated这个装饰器写好后所有需要登录才能访问的接口只要在路由函数上加一行token_required就行。权限控制分两层一是接口层管理员才能调用的接口单独加admin_required装饰器二是数据层比如普通用户只能看到自己参与的任务这需要在查询时根据current_user.id做过滤。有个容易踩的坑是JWT的过期时间。我一开始设的是24小时过期结果公司同事反映第二天早上打开系统就提示重新登录体验很差。后来改成了72小时并且在用户勾选“记住我”的时候生成一个刷新token才算解决。2.4 核心任务接口编写任务相关的接口我设计了六个接口方法功能/api/tasksGET任务列表支持按项目、状态、负责人筛选/api/tasks/idGET任务详情/api/tasksPOST新建任务/api/tasks/idPUT更新任务包括重新分配负责人/api/tasks/idDELETE删除任务/api/tasks/id/statusPUT变更任务状态列表接口的筛选逻辑看起来简单但参数组合多了就乱。我建议直接用query.filter_by()链式调用根据前端传来的参数动态拼接查询条件task_api.route(/tasks, methods[GET]) token_required def get_tasks(current_user): query Task.query project_id request.args.get(project_id) status request.args.get(status) assignee_id request.args.get(assignee_id) if project_id: query query.filter_by(project_idproject_id) if status: query query.filter_by(statusstatus) if assignee_id: query query.filter_by(assignee_idassignee_id) else: if current_user.role ! admin: query query.filter_by(assignee_idcurrent_user.id) tasks query.order_by(Task.priority.desc(), Task.due_date.asc()).all() return success_response([task.to_dict() for task in tasks])注意一个逻辑如果前端没有传assignee_id而且当前用户不是管理员就默认只看自己负责的任务。这个设计保证了普通用户打开任务列表不会看到公司所有任务避免信息过载也天然实现了数据隔离。3. Vue前端实战从脚手架到任务看板3.1 环境配置与项目初始化Vue前端这部分热词里能看到很多人问vue安装及环境配置、vue安装依赖、vscode python环境配置我先把手顺捋一遍。首先确保本地装了Node.js推荐14以上版本然后全局安装Vue CLInpm install -g vue/cli vue --version vue create task-frontend创建项目时选择Vue 3预设包管理器我习惯用npm。进入项目目录后安装需要的依赖npm install axios vue-router4 element-plus npm install sass sass-loader --save-dev这里用Element Plus作为UI组件库因为它是Element UI的Vue 3版本表格、表单、下拉选择、日期选择器这些都是现成的做管理后台能省下大量样式开发时间。vscode python环境配置这个热词也顺带提一嘴我开发时是VSCode一个窗口开后端一个窗口开前端。VSCode里装好Python扩展和Vue Language Features扩展Python解释器选择虚拟环境里那个路径Flask代码就能获得自动补全和断点调试能力。前端代码的调试通过Vue Devtools浏览器插件解决。3.2 路由设计与页面结构前端页面我规划了五个路由路径页面功能/login登录页用户登录/dashboard仪表盘任务统计总览/projects项目列表查看和管理项目/tasks任务看板核心的任务分配与管理页面/users用户管理管理员维护用户信息路由配置里我加了一个前置守卫没有登录就跳转到登录页router.beforeEach((to, from, next) { const token localStorage.getItem(token) if (to.path ! /login !token) { next(/login) } else { next() } })任务看板页面是这套系统最重要的界面。我用Element Plus的el-table展示任务列表每一行数据包括标题、优先级、负责人、截止日期、状态。状态列用el-tag组件展示不同颜色待办是灰色、进行中是蓝色、已完成是绿色。表格上方放一排筛选条件包括项目下拉框、状态下拉框、负责人下拉框选择后立即触发重新查询。3.3 Axios封装和前后端联调Axios不能直接在组件里到处写我封装了一个统一的请求模块import axios from axios import { ElMessage } from element-plus import router from ../router const service axios.create({ baseURL: /api, timeout: 10000 }) service.interceptors.request.use(config { const token localStorage.getItem(token) if (token) { config.headers[Authorization] Bearer token } return config }) service.interceptors.response.use( response { const res response.data if (res.code ! 200) { ElMessage.error(res.msg || 请求失败) return Promise.reject(new Error(res.msg)) } return res }, error { if (error.response error.response.status 401) { localStorage.removeItem(token) router.push(/login) } ElMessage.error(error.message || 网络异常) return Promise.reject(error) } ) export default servicebaseURL设为/api开发环境下需要通过Vue CLI的代理配置把请求转发到Flask的5000端口。在项目根目录创建vue.config.jsmodule.exports { devServer: { port: 8080, proxy: { /api: { target: http://localhost:5000, changeOrigin: true } } } }这样前端发/api/tasks请求时开发服务器会转发到http://localhost:5000/api/tasks完美解决跨域问题。生产部署时Nginx也需要配置类似的location /api代理。注意我见过很多新手在开发环境直接写axios.get(http://localhost:5000/api/tasks)来请求后端虽然能通但会触发CORS跨域机制Flask那边必须额外加flask-cors扩展处理。而通过代理方式请求浏览器看到的是同源请求完全不需要处理跨域省很多事。3.4 任务分配的前端交互设计任务看板页面上管理员有两种分配任务的方式。第一种是在新建任务弹窗里直接选择负责人这个比较简单第二种是在任务列表里点击“重新分配”弹出一个下拉框从项目成员列表里选择新的负责人。下拉框的数据是动态加载的选完项目以后前端根据project_id调/api/projects/id/members接口拿到成员列表。这个交互细节很重要如果不按项目过滤成员用户会看到全公司的通讯录不仅容易选错人也不符合企业内部最小授权的原则。任务状态变更我用了拖拽交互但Element Plus没有内置的树形拖拽组件我用的是vuedraggable这个库。把表格按状态分组变成三列看板待办、进行中、已完成拖拽一个任务卡片到“进行中”栏就自动调状态接口。刚开始做这个功能的时候我觉得很新鲜后来领导反馈说拖拽比点按钮改状态直观多了这个功能没有白做。4. 任务分配核心逻辑与算法4.1 手动分配还是自动分配任务分配的逻辑是整个系统的灵魂。我调研过几家企业绝大部分公司实际用的还是“手动分配”——项目经理或者管理员创建任务后指定给某个人。但是“手动”不代表没有规则比如你可以让系统自动推荐一个合适的分配人管理员确认即可。所以这套系统我实现了两种分配模式手动分配创建任务时由管理员指定负责人智能推荐系统根据成员当前任务负载、任务优先级、成员技能标签推荐最合适的人选第二种模式我把它做成了“推荐候选人”而不是完全自动分配。原因是企业内部任务分配经常有很多说不清的软因素比如某人最近在休假但是没更新系统、某人跟某客户关系好所以他的任务别人接不了。完全交给算法决定有时候会出问题但算法作为辅助工具非常香。4.2 基于工作负载的推荐算法我的智能推荐算法思路很简单核心是计算每个候选成员当前的工作负载指数load 待处理任务数量 0.6 * 进行中任务数量 预估工时加权具体实现是通过数据库查询每个候选人的任务统计然后按负载升序排列推荐负载最低的那个人。为了不让分配总是集中在某几个人身上我加了一点随机性取负载最低的前三个人然后按权重随机选择一个。这样既保证大体公平又避免被同事总结出“活永远分配给张三”的规律。def recommend_assignee(project_id, exclude_user_idNone): project Project.query.get(project_id) members get_project_members(project_id) candidates [] for member in members: if exclude_user_id and member.id exclude_user_id: continue todo_count Task.query.filter_by( assignee_idmember.id, statustodo ).count() doing_count Task.query.filter_by( assignee_idmember.id, statusdoing ).count() # 查询预估工时总和 hours db.session.query( db.func.coalesce(db.func.sum(Task.estimate_hours), 0) ).filter( Task.assignee_id member.id, Task.status.in_([todo, doing]) ).scalar() load todo_count 0.6 * doing_count 0.1 * hours candidates.append({user: member, load: load}) candidates.sort(keylambda x: x[load]) # 取前3个作为候选 top candidates[:3] if not top: return None # 随机选一个降低负载分最低的人被选中的概率 weights [3, 2, 1][:len(top)] chosen random.choices(top, weightsweights)[0] return chosen[user]这个算法的复杂度不高在任务量几百条的情况下执行时间几乎可以忽略。如果以后任务量大了可以在assignee_id和status上建联合索引查询速度会更快。实际用下来算法推荐的准确率大约有七成剩下的三成还是需要人来修正但已经大大减轻了分配时的思考成本。4.3 优先级驱动与截止日期提醒任务分配不只要选对人还要排先后。系统里我规定了四档优先级默认是按“紧急且重要 重要 紧急 普通”的方式排决定优先级值但这个排序逻辑不能写死因为不同团队对优先级理解不一样。我在后端留了一个配置项运维人员可以直接在数据库的config表里修改权重。截止日期提醒是一个很能给系统加分的小功能。每天早上9点后端定时任务扫描三天内要截止且状态不是“完成”的任务给负责人发邮件提醒。Flask里我用APScheduler扩展实现定时任务from apscheduler.schedulers.background import BackgroundScheduler def check_deadlines(): today datetime.utcnow().date() target today timedelta(days3) tasks Task.query.filter( Task.due_date target, Task.status.in_([todo, doing]) ).all() for task in tasks: send_reminder_email(task) scheduler BackgroundScheduler() scheduler.add_job(check_deadlines, cron, hour9, minute0) scheduler.start()这个定时任务要小心一点如果后端是多人开发每个人都跑着自己的Flask实例就会导致重复发邮件。所以我加了一个全局开关只有拿到分布式锁的实例才执行定时任务。用Redis的SETNX命令实现分布式锁几行代码就搞定。如果你不用Redis也可以用一个数据库表记录“上次执行时间”执行前检查一下是否超时简单可靠。4.4 复杂场景扩展多级审批分配任务分配还有一个进阶场景——多级审批。有些企业的大额任务或者跨部门任务分配之后需要部门负责人审批审批通过才算正式生效。我在任务表里加了一个approval_status字段none不需要审批、pending待审批、approved已通过、rejected已驳回。任务分配给某个人后如果是需要审批的任务状态先不跳到“待处理”而是“待审批”管理员在审批面板看到请求点击通过后任务才真正生效。审批流程用Flask的蓝图实现大概多花三十行代码但给系统的正式感提升很多。管理层会觉得“这个系统符合我们公司的管理制度”而不只是一个任务列表。设计的时候我就坚持一个原则技术要服务于管理流程而不是让业务流程去适应系统的简单化逻辑。5. 环境配置、打包与部署实操5.1 开发环境的完整搭建步骤很多小白卡在环境配置这里我完整写一遍我机器的配置过程。后端部分用Python 3.10版本虚拟环境用venvcd backend python -m venv venv source venv/bin/activate # Windows是 venv\Scripts\activate pip install flask flask-sqlalchemy flask-cors pyjwt pymysql apscheduler安装完以后在config.py里配置好数据库连接import os class Config: SECRET_KEY os.environ.get(SECRET_KEY) or your-secret-key SQLALCHEMY_DATABASE_URI mysqlpymysql://username:passwordlocalhost/task_db?charsetutf8mb4 SQLALCHEMY_TRACK_MODIFICATIONS False初始化数据库时先建好MySQL数据库然后在Flask环境里执行from app import app, db with app.app_context(): db.create_all()前端部分按照前面说的vue create task-frontend创建后记得把vue.config.js里的代理配置加上然后启动开发服务器cd task-frontend npm install npm run serve打开http://localhost:8080就能看到系统页面后端接口在http://localhost:5000两者通过代理打通。5.2 前后端分离项目的打包部署开发完成后要部署到公司服务器我用的是Nginx加gunicorn方案。首先构建前端静态文件cd task-frontend npm run build构建完成后dist目录下生成一堆静态文件。把这些文件直接扔到服务器上然后Nginx配置如下server { listen 80; server_name your-domain.com; root /var/www/task-frontend/dist; index index.html; location /api/ { proxy_pass http://127.0.0.1:5000/api/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } location / { try_files $uri $uri/ /index.html; } }后端起服务用gunicorn它比Flask自带的开发服务器稳定得多支持多worker并发pip install gunicorn gunicorn -w 4 -b 127.0.0.1:5000 app:app-w 4表示开4个worker进程按服务器CPU核心数调整。需要注意的是gunicorn只支持Linux和macOSWindows服务器上可以用waitress替代pip install waitress waitress-serve --host127.0.0.1 --port5000 app:app部署的时候有个坑是Vue Router的history模式。如果前端路由用的是history模式刷新页面时Nginx会返回404因为你访问/tasks时后端实际上没有这个路径。解决办法是加一行配置try_files $uri $uri/ /index.html;意思是所有路径都往index.html上兜底。测试环境图省事可以直接用hash模式URL会变成/#/tasks不用配Nginx也不会404但正式环境还是推荐history模式加Nginx修正。5.3 MySQL存储emoji和中文的问题utf8mb4这个问题是老生常谈但必须说。如果你建表排序规则选了utf8_general_ci插入数据时遇到表情符号就会报错。企业微信的昵称、钉钉同步过来的签名里经常有emoji所以MySQL连接字符串里一定要指定charsetutf8mb4建表时也要把表默认排序规则设为utf8mb4_unicode_ci。还有一个隐藏问题是Python连接MySQL时的编码。Flask的app.config里加上JSON_AS_ASCII False这样Flask返回中文时不会被转成\uXXXX的形式。有的后端接口返回数据前端显示中文正常但浏览器F12看到的是Unicode编码其实不解决也能用但调试起来很不方便。5.4 热词“vue项目怎么打包成exe”的衍生思考搜索热词里有人问vue项目怎么打包成exe这个需求通常是想把前端和后端打成一个桌面安装包分发给内部员工。我在Electron项目里干过类似的事思路是用Electron作为壳子加载打包后的Vue静态文件后端还是跑Flask。具体做法有两种一种是把Flask部署在远程服务器上Electron只做一个浏览器壳另一种是用pyinstaller把Flask后端打包成exe文件Electron启动时通过child_process后台拉起这个exe然后前端请求http://127.0.0.1:5000。第二种方案可以做到完全离线员工只要双击安装了Electron客户端本地自动启动Python后端服务。但要注意pyinstaller打包Flask时需要把模板、静态文件都加进去而且第一次启动可能被杀毒软件拦截打包完成后必须签名。说实话能用浏览器访问就尽量用浏览器打包成exe是最后的选择。6. 常见问题与排查技巧实录6.1 跨域问题开发环境跨域问题的解决方式前面已经说了通过Vue CLI代理。但如果前端使用axios请求时配置了错误的baseURL比如直接写了http://localhost:5000代理就不会生效浏览器控制台会出现Access-Control-Allow-Origin错误。排查跨域问题我一般按以下顺序确认请求的URL开头是不是/api而不是http://localhost:5000/api确认vue.config.js的代理配置是否生效改完有没有重启npm run serve打开浏览器F12看Network面板中请求的Request URL如果还是http://localhost:5000/api/tasks说明代理配置没生效后端如果实在要开CORS安装flask-cors后一行CORS(app)即可6.2 Flask的debug模式和自动重载开发时Flask开debug模式可以自动重载代码非常方便。但有一次我改了代码保存后页面请求全部超时查了半天发现是gunicorn在生产环境启动时不小心用了app.run(debugTrue)。debug模式实际上是跑了一个极不安全的开发服务器不仅性能差还有RCE风险生产环境绝对不能开。gunicorn启动后改代码需要重启进程我习惯用systemctl管理的服务模式启动sudo systemctl restart task-backend一行搞定。6.3 Vue打包后接口地址错误npm run build之后前端静态文件里的API地址是/api开头的相对路径。如果部署的时候Nginx没有配置后端的/api代理页面能打开但所有请求都会404。排查方法是打开页面F12看Network如果/api/tasks返回的是index.html的内容而不是JSON说明Nginx把请求兜底到了前端入口文件代理配置没生效。正确的Nginx配置顺序是先写location /api/再写location /这样精确前缀匹配优先。6.4 任务状态并发更新企业内部用系统的人不会特别多但偶尔也会发生两个人同时更新同一个任务的情况。Flask默认的数据库Session不是线程安全的多worker模式下并发写可能会导致Lost connection to MySQL server错误。解决方案有两个一个是在设置任务状态时用乐观锁给任务表加一个version字段更新时校验版本号另一个是让任务状态的更新走一个单独的事务更新失败时重试一次。如果只是内部几十人用用第二种方案加一个try-except重试就够了不用过度设计。6.5 Vue组合式API和选项式API怎么选热词里有人问vue选项式和组合式区别我实际项目里的体会是写嵌套很深的组件、逻辑复用多的组件组合式API优势明显。比如任务看板页面表格筛选逻辑、分页逻辑、拖拽逻辑如果用选项式API会把data、methods、computed分散到不同区域同一个功能相关的逻辑被割裂了组合式API让你能按功能块组织代码setup() { // 任务筛选功能块 const filter reactive({ project_id: , status: , assignee_id: }) const loadTasks () { ... } // 分页功能块 const page ref(1) const total ref(0) const changePage () { ... } // 拖拽功能块 const handleDrop (task, newStatus) { ... } return { filter, loadTasks, page, total, changePage, handleDrop } }如果只写一个简单不复杂的表单页面选项式API其实更直观。我的建议是团队内统一用组合式API因为这个技能学习成本是值得的后面维护复杂功能会感谢当初的选择。6.6 节假日与工作日对截止日期的影响最后分享一个容易忽略的需求。企业内部的任务截止日期通常是按工作日算的不是自然日。比如任务要求“3个工作日内完成”周五建的任务截止日期应该是下周三而不是下周一。我在后端写了一个根据工作日计算截止日期的方法调用的是chinese_calendar这个Python库它能识别中国的法定节假日和调休安排。import chinese_calendar def calculate_due_date(start_date, workdays): current start_date while workdays 0: current timedelta(days1) if chinese_calendar.is_workday(current): workdays - 1 return current这个功能看着不起眼但在项目管理场景中非常实用因为任务排期的错误往往不是算法问题而是对日期的理解不一致。如果你服务的企业有固定的周末休息模式不需要法定节假日识别可以自己写一个“非周六周日则计数”的简化版本几行就够。实际上线之后确定截止日期还要考虑这个成员当前负载。比如李四手上已经有四个任务周五下午你给他分配一个三天工作量的任务并且要求下周二交付那是必然完不成的。系统里做一个超工作量提醒分配任务时如果发现该成员在截止日期前已有的任务预估工时加上新任务超过正常工作时间弹出警告。Flask后端只需要在创建任务接口里加一个检查函数前端收到warning字段后弹一个确认框让管理员自己决定是否硬分配。做企业任务分配系统说到底不是炫技而是要把管理规则变成系统规则同时保留人的决策权。Flask和Vue这套组合最大的价值就是灵活你可以按企业的实际流程快速调整这周改个分配规则下周加个审批流都不会伤筋动骨。即使只是一个内部工具做好任务分配逻辑真的能帮团队把协作效率提上来一大截。
返回列表