From 83289f60253421e3704c9473fe22d8de911c0d19 Mon Sep 17 00:00:00 2001 From: zhangtao <9480807882@qq.com> Date: Sun, 1 Mar 2026 00:57:02 +0800 Subject: [PATCH 1/2] =?UTF-8?q?feat:=20=E9=87=8D=E6=9E=84=E5=89=8D?= =?UTF-8?q?=E7=AB=AF=E9=A1=B5=E9=9D=A2=E5=B8=83=E5=B1=80=E5=92=8C=E6=9D=83?= =?UTF-8?q?=E9=99=90=E6=8E=A7=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit refactor(前端): 统一调整表格高度为动态计算 fix(任务): 修正节点调试权限标识 style: 优化搜索区域样式和位置 chore: 移除不再使用的日志清理接口 docs(菜单): 更新菜单项名称和权限标识 --- backend/app/core/ap_scheduler.py | 220 +++++- .../app/plugin/module_task/job/controller.py | 33 +- .../app/plugin/module_task/node/controller.py | 2 +- backend/app/scripts/data/sys_menu.json | 58 +- frontend/src/api/module_task/job.ts | 9 +- frontend/src/styles/index.scss | 4 +- .../views/module_ai/chat_message/index.vue | 162 ++--- .../views/module_ai/chat_session/index.vue | 126 ++-- .../views/module_application/myapp/index.vue | 152 ++-- .../src/views/module_example/demo/index.vue | 178 ++--- .../views/module_generator/gencode/index.vue | 145 ++-- .../src/views/module_monitor/cache/index.vue | 10 +- .../src/views/module_monitor/online/index.vue | 85 +-- .../views/module_monitor/resource/index.vue | 79 +- .../src/views/module_system/dept/index.vue | 132 ++-- .../dict/components/DataDrawer.vue | 105 +-- .../src/views/module_system/dict/index.vue | 138 ++-- .../src/views/module_system/log/index.vue | 150 ++-- .../src/views/module_system/menu/index.vue | 132 ++-- .../src/views/module_system/notice/index.vue | 178 ++--- .../src/views/module_system/param/index.vue | 140 ++-- .../views/module_system/position/index.vue | 146 ++-- .../role/components/PermissonDrawer.vue | 2 +- .../src/views/module_system/role/index.vue | 132 ++-- .../src/views/module_system/user/index.vue | 188 ++--- frontend/src/views/module_task/job/index.vue | 678 +++++++++--------- frontend/src/views/module_task/node/index.vue | 465 ++++++++++-- 27 files changed, 2183 insertions(+), 1666 deletions(-) diff --git a/backend/app/core/ap_scheduler.py b/backend/app/core/ap_scheduler.py index 1b16191b..49fff9b9 100644 --- a/backend/app/core/ap_scheduler.py +++ b/backend/app/core/ap_scheduler.py @@ -135,16 +135,32 @@ class SchedulerUtil: if job: log.info(f"任务 {job_id} ({job.name}) 已提交执行") - # 创建执行日志 + trigger_type = cls._get_trigger_type(job_id) + + # 周期性任务(cron/interval):更新 pending 状态为 running + if trigger_type in ("cron", "interval"): + cls._update_job_log( + job_id=job_id, + status="running", + ) + else: + # 一次性任务(manual/date):创建新的 running 状态日志 + cls._create_job_log( + job_id=job_id, + job_name=job.name, + trigger_type=trigger_type, + status="running", + ) + else: + # 任务可能已经被移除(一次性任务执行完毕后自动移除) + # 尝试创建日志,job_name 留空,trigger_type 默认为 manual + log.debug(f"任务 {job_id} 提交执行,但未找到任务信息(可能已被移除),尝试创建日志") cls._create_job_log( job_id=job_id, - job_name=job.name, - trigger_type=cls._get_trigger_type(job_id), + job_name=None, + trigger_type="manual", status="running", ) - else: - # 任务可能已经被移除(一次性任务),尝试从事件中获取信息 - log.debug(f"任务 {job_id} 提交执行,但未找到任务信息(可能已被移除)") @classmethod def _handle_job_executed(cls, event: JobExecutionEvent) -> None: @@ -167,6 +183,19 @@ class SchedulerUtil: status="success", result=str(retval) if retval else None, ) + + # 为周期性任务创建新的 pending 状态日志,等待下次执行 + job = cls.get_job(job_id=job_id) + if job: + trigger_type = cls._get_trigger_type(job_id) + if trigger_type in ("cron", "interval") and job.next_run_time: + cls._create_job_log( + job_id=job_id, + job_name=job.name, + trigger_type=trigger_type, + status="pending", + ) + log.debug(f"任务 {job_id} 已创建新的 pending 状态日志,等待下次执行") @classmethod def _handle_job_error(cls, event: JobExecutionEvent) -> None: @@ -191,6 +220,19 @@ class SchedulerUtil: result="failed", error=str(exception) if exception else "未知错误", ) + + # 为周期性任务创建新的 pending 状态日志,等待下次执行 + job = cls.get_job(job_id=job_id) + if job: + trigger_type = cls._get_trigger_type(job_id) + if trigger_type in ("cron", "interval") and job.next_run_time: + cls._create_job_log( + job_id=job_id, + job_name=job.name, + trigger_type=trigger_type, + status="pending", + ) + log.debug(f"任务 {job_id} 已创建新的 pending 状态日志,等待下次执行") @classmethod def _handle_job_missed(cls, event: JobEvent) -> None: @@ -211,17 +253,45 @@ class SchedulerUtil: result="timeout", error="任务错过执行时间", ) + + # 为周期性任务创建新的 pending 状态日志,等待下次执行 + if job: + trigger_type = cls._get_trigger_type(job_id) + if trigger_type in ("cron", "interval") and job.next_run_time: + cls._create_job_log( + job_id=job_id, + job_name=job.name, + trigger_type=trigger_type, + status="pending", + ) + log.debug(f"任务 {job_id} 已创建新的 pending 状态日志,等待下次执行") @classmethod def _handle_job_removed(cls, event: JobEvent) -> None: """ 处理任务被移除事件 + + 注意:APScheduler 对于一次性任务(DateTrigger)会先触发 JOB_REMOVED, + 然后再触发 JOB_SUBMITTED 和 JOB_EXECUTED。因此: + - 对于一次性任务,不应该在 JOB_REMOVED 时创建或更新日志 + - 只有周期性任务在移除时才需要更新日志状态 """ job_id = str(event.job_id) jobstore = getattr(event, "jobstore", "unknown") log.info(f"任务 {job_id} 从 {jobstore} 存储器中移除") + # 检查是否是一次性任务(DateTrigger) + # 如果任务已经不存在,说明可能是一次性任务执行后被自动移除 + # 这种情况下不需要更新日志,因为 JOB_EXECUTED 会处理 + job = cls.get_job(job_id=job_id) + if job is None: + # 任务已经被移除,可能是一次性任务 + # 不需要在这里创建日志,JOB_SUBMITTED 和 JOB_EXECUTED 会处理 + log.debug(f"任务 {job_id} 已从调度器中移除,跳过日志更新") + return + + # 任务还存在,说明是周期性任务被手动移除 # 更新执行日志 cls._update_job_log_on_removed(job_id=job_id) @@ -236,6 +306,17 @@ class SchedulerUtil: if job: log.info(f"任务 {job_id} ({job.name}) 已添加到 {jobstore} 存储器") + + # 为周期性任务(cron/interval)创建初始的 pending 状态日志 + trigger_type = cls._get_trigger_type(job_id) + if trigger_type in ("cron", "interval"): + cls._create_job_log( + job_id=job_id, + job_name=job.name, + trigger_type=trigger_type, + status="pending", + ) + log.debug(f"任务 {job_id} 已创建初始 pending 状态日志") else: log.info(f"任务 {job_id} 已添加到 {jobstore} 存储器") @@ -337,9 +418,10 @@ class SchedulerUtil: def _handle_all_jobs_removed(cls, event: SchedulerEvent) -> None: """ 处理所有任务移除事件 + 注意:清空调度器任务不应该清空执行日志,而是将所有 pending 状态的日志更新为 cancelled """ log.info("所有任务已从调度器中移除") - cls._clear_all_job_logs() + cls._cancel_all_pending_job_logs() @classmethod def _handle_job_max_instances(cls, event: JobEvent) -> None: @@ -473,7 +555,7 @@ class SchedulerUtil: @classmethod def _clear_all_job_logs(cls) -> None: """ - 清空所有任务日志 + 清空所有任务日志(仅用于手动清空,不建议使用) """ try: from sqlalchemy.orm import Session @@ -487,6 +569,26 @@ class SchedulerUtil: except Exception as e: log.error(f"清空任务日志失败: {e!s}", exc_info=True) + @classmethod + def _cancel_all_pending_job_logs(cls) -> None: + """ + 将所有 pending 状态的执行日志更新为 cancelled + 用于清空调度器任务时,不删除日志而是更新状态 + """ + try: + from sqlalchemy.orm import Session + + from app.plugin.module_task.job.model import JobModel + + with Session(engine) as session: + session.query(JobModel).filter(JobModel.status == "pending").update( + {"status": "cancelled"} + ) + session.commit() + log.info("所有待执行任务日志已标记为已取消") + except Exception as e: + log.error(f"取消待执行任务日志失败: {e!s}", exc_info=True) + @classmethod def _get_trigger_type(cls, job_id: str) -> str: """ @@ -645,6 +747,9 @@ class SchedulerUtil: job = cls.get_job(job_id=job_id) next_run_time = str(job.next_run_time) if job and job.next_run_time else None job_state = cls._get_job_state(job) if job else None + # 如果没有传入 job_name,尝试从 job 获取 + if not job_name and job: + job_name = job.name with Session(engine) as session: job_log = JobModel( @@ -662,7 +767,8 @@ class SchedulerUtil: @classmethod def _update_job_log(cls, job_id: str, status: str, result: str | None = None, error: str | None = None) -> None: """ - 更新执行日志(更新该 job_id 最新的 pending 或 running 状态日志) + 更新执行日志(更新该 job_id 最新的 pending 状态日志) + 用于周期性任务在提交执行时将 pending 更新为 running """ from sqlalchemy.orm import Session @@ -675,7 +781,7 @@ class SchedulerUtil: with Session(engine) as session: job_log = ( session.query(JobModel) - .filter(JobModel.job_id == job_id, JobModel.status.in_(['pending', 'running'])) + .filter(JobModel.job_id == job_id, JobModel.status == "pending") .order_by(JobModel.created_time.desc()) .first() ) @@ -691,12 +797,12 @@ class SchedulerUtil: job_log.error = error session.commit() else: - log.warning(f"未找到任务 {job_id} 的待执行或运行中日志记录") + log.warning(f"未找到任务 {job_id} 的待执行日志记录") @classmethod def _update_latest_job_log(cls, job_id: str, status: str, result: str | None = None, error: str | None = None) -> None: """ - 更新最新的执行日志(更新该 job_id 最新的一条 running 状态日志) + 更新最新的执行日志(更新该 job_id 最新的一条日志) 用于每次执行完成后更新状态 """ from sqlalchemy.orm import Session @@ -708,6 +814,7 @@ class SchedulerUtil: job_state = cls._get_job_state(job) if job else None with Session(engine) as session: + # 首先尝试更新 running 状态的日志 job_log = ( session.query(JobModel) .filter(JobModel.job_id == job_id, JobModel.status == "running") @@ -725,17 +832,70 @@ class SchedulerUtil: if error: job_log.error = error session.commit() - else: - log.warning(f"未找到任务 {job_id} 的运行中日志记录") + return + + # 没有找到 running 状态的日志,尝试更新 cancelled 状态的日志 + # 这种情况发生在 EVENT_JOB_REMOVED 先于 EVENT_JOB_SUBMITTED 触发时 + job_log = ( + session.query(JobModel) + .filter(JobModel.job_id == job_id, JobModel.status == "cancelled") + .order_by(JobModel.created_time.desc()) + .first() + ) + if job_log: + job_log.status = status + if next_run_time: + job_log.next_run_time = next_run_time + if job_state: + job_log.job_state = job_state + if result: + job_log.result = result + if error: + job_log.error = error + session.commit() + return + + # 创建新的日志记录 + log.debug(f"未找到任务 {job_id} 的日志记录,创建新日志") + trigger_type = cls._get_trigger_type(job_id) if job else "manual" + new_log = JobModel( + job_id=job_id, + job_name=job.name if job else None, + trigger_type=trigger_type, + status=status, + next_run_time=next_run_time, + job_state=job_state, + result=result, + error=error, + ) + session.add(new_log) + session.commit() @classmethod def _update_job_log_on_removed(cls, job_id: str) -> None: """ - 任务被移除时,更新最新的 pending 状态日志为 cancelled + 任务被移除时,更新最新的 pending 或 running 状态日志为 cancelled + + 事件触发顺序分析: + 1. 一次性任务(manual/date): + - EVENT_JOB_SUBMITTED -> 创建日志(status=running) + - EVENT_JOB_EXECUTED/ERROR -> 更新日志(status=success/failed) + - EVENT_JOB_REMOVED -> 日志已更新,不会被标记为 cancelled + + 2. 周期性任务(cron/interval): + - EVENT_JOB_SUBMITTED -> 创建日志(status=running) + - EVENT_JOB_EXECUTED/ERROR -> 更新日志(status=success/failed) + - 下次执行 -> EVENT_JOB_SUBMITTED -> 创建新日志(status=running) + - EVENT_JOB_REMOVED -> 将 pending/running 标记为 cancelled + + 3. 特殊情况: + - 一次性任务在执行前被删除:running -> cancelled + - 周期性任务在 pending 状态被删除:pending -> cancelled + 注意: - - 只有当任务还在 pending 状态时才更新为 cancelled - - 一次性任务(trigger_type 为 date 或 manual)执行后会自动触发 REMOVED 事件,此时不应该标记为 cancelled - - REMOVED 事件可能在 SUBMITTED 之前触发,此时状态还是 pending + - 只有当任务还在 pending 或 running 状态时才更新为 cancelled + - 如果任务已经执行完成(success/failed/timeout),则不需要更新 + - 如果任务已经标记为 cancelled,则不需要更新 """ from sqlalchemy.orm import Session @@ -744,15 +904,17 @@ class SchedulerUtil: with Session(engine) as session: job_log = ( session.query(JobModel) - .filter(JobModel.job_id == job_id, JobModel.status == "pending") + .filter( + JobModel.job_id == job_id, + JobModel.status.in_(['pending', 'running']) + ) .order_by(JobModel.created_time.desc()) .first() ) if job_log: - if job_log.trigger_type in ("date", "manual"): - return job_log.status = "cancelled" session.commit() + log.info(f"任务 {job_id} 的执行日志已标记为已取消") @classmethod def get_job_status(cls, job_id: str | int) -> str: @@ -763,8 +925,12 @@ class SchedulerUtil: if not job: return "未知" - if job_id in scheduler._jobstores[job._jobstore_alias]._paused_jobs: - return "暂停中" + try: + jobstore = scheduler._jobstores.get(job._jobstore_alias) + if jobstore and hasattr(jobstore, '_paused_jobs') and job_id in jobstore._paused_jobs: + return "暂停中" + except Exception: + pass if scheduler.state == 0: return "已停止" @@ -780,13 +946,7 @@ class SchedulerUtil: from datetime import timedelta trigger = DateTrigger(run_date=datetime.now() + timedelta(seconds=0.1)) job = cls._add_job_with_trigger(job_info, trigger) - # 手动创建执行日志,确保调试时也能生成记录 - cls._create_job_log( - job_id=str(job_info.id), - job_name=job_info.name, - trigger_type="manual", - status="running", - ) + # 注意:不需要手动创建执行日志,EVENT_JOB_SUBMITTED 事件会自动创建 return job @classmethod diff --git a/backend/app/plugin/module_task/job/controller.py b/backend/app/plugin/module_task/job/controller.py index ecc34c1f..fbe7d610 100644 --- a/backend/app/plugin/module_task/job/controller.py +++ b/backend/app/plugin/module_task/job/controller.py @@ -62,7 +62,7 @@ async def get_scheduler_jobs_controller() -> JSONResponse: summary="启动调度器", description="启动调度器", response_model=ResponseSchema[None], - dependencies=[Depends(AuthPermission(["module_task:job:update"]))], + dependencies=[Depends(AuthPermission(["module_task:job:scheduler"]))], ) async def start_scheduler_controller() -> JSONResponse: """ @@ -78,7 +78,7 @@ async def start_scheduler_controller() -> JSONResponse: summary="暂停调度器", description="暂停调度器", response_model=ResponseSchema[None], - dependencies=[Depends(AuthPermission(["module_task:job:update"]))], + dependencies=[Depends(AuthPermission(["module_task:job:scheduler"]))], ) async def pause_scheduler_controller() -> JSONResponse: """ @@ -94,7 +94,7 @@ async def pause_scheduler_controller() -> JSONResponse: summary="恢复调度器", description="恢复调度器", response_model=ResponseSchema[None], - dependencies=[Depends(AuthPermission(["module_task:job:update"]))], + dependencies=[Depends(AuthPermission(["module_task:job:scheduler"]))], ) async def resume_scheduler_controller() -> JSONResponse: """ @@ -110,7 +110,7 @@ async def resume_scheduler_controller() -> JSONResponse: summary="关闭调度器", description="关闭调度器", response_model=ResponseSchema[None], - dependencies=[Depends(AuthPermission(["module_task:job:update"]))], + dependencies=[Depends(AuthPermission(["module_task:job:scheduler"]))], ) async def shutdown_scheduler_controller() -> JSONResponse: """ @@ -126,7 +126,7 @@ async def shutdown_scheduler_controller() -> JSONResponse: summary="清空所有任务", description="清空调度器中的所有任务", response_model=ResponseSchema[None], - dependencies=[Depends(AuthPermission(["module_task:job:delete"]))], + dependencies=[Depends(AuthPermission(["module_task:job:task"]))], ) async def clear_jobs_controller() -> JSONResponse: """ @@ -182,7 +182,7 @@ async def sync_jobs_controller() -> JSONResponse: summary="暂停任务", description="暂停调度器中的任务", response_model=ResponseSchema[None], - dependencies=[Depends(AuthPermission(["module_task:job:update"]))], + dependencies=[Depends(AuthPermission(["module_task:job:task"]))], ) async def pause_job_controller( job_id: Annotated[str, Path(description="调度器任务ID")], @@ -203,7 +203,7 @@ async def pause_job_controller( summary="恢复任务", description="恢复调度器中的任务", response_model=ResponseSchema[None], - dependencies=[Depends(AuthPermission(["module_task:job:update"]))], + dependencies=[Depends(AuthPermission(["module_task:job:task"]))], ) async def resume_job_controller( job_id: Annotated[str, Path(description="调度器任务ID")], @@ -224,7 +224,7 @@ async def resume_job_controller( summary="立即执行任务", description="立即执行调度器中的任务", response_model=ResponseSchema[None], - dependencies=[Depends(AuthPermission(["module_task:job:update"]))], + dependencies=[Depends(AuthPermission(["module_task:job:task"]))], ) async def run_job_controller( job_id: Annotated[str, Path(description="调度器任务ID")], @@ -343,20 +343,3 @@ async def delete_job_log_controller( await JobService.delete_job_log_service(auth=auth, ids=ids) log.info(f"删除执行日志成功: {ids}") return SuccessResponse(msg="删除执行日志成功") - - -@JobRouter.delete( - "/log/clear", - summary="清空执行日志", - description="清空所有执行日志", - response_model=ResponseSchema[None], -) -async def clear_job_log_controller( - auth: Annotated[AuthSchema, Depends(AuthPermission(["module_task:job:delete"]))], -) -> JSONResponse: - """ - 清空所有执行日志 - """ - await JobService.clear_job_log_service(auth=auth) - log.info("清空执行日志成功") - return SuccessResponse(msg="清空执行日志成功") diff --git a/backend/app/plugin/module_task/node/controller.py b/backend/app/plugin/module_task/node/controller.py index bcc43d44..8d525a38 100644 --- a/backend/app/plugin/module_task/node/controller.py +++ b/backend/app/plugin/module_task/node/controller.py @@ -214,7 +214,7 @@ async def clear_obj_controller( async def execute_job_controller( id: Annotated[int, Path(description="节点ID")], data: NodeExecuteSchema, - auth: Annotated[AuthSchema, Depends(AuthPermission(["module_task:node:update"]))], + auth: Annotated[AuthSchema, Depends(AuthPermission(["module_task:node:execute"]))], ) -> JSONResponse: """ 调试节点 diff --git a/backend/app/scripts/data/sys_menu.json b/backend/app/scripts/data/sys_menu.json index 9e85de80..e6e628e6 100644 --- a/backend/app/scripts/data/sys_menu.json +++ b/backend/app/scripts/data/sys_menu.json @@ -2632,11 +2632,11 @@ "description": "查询调度器" }, { - "name": "操作调度器", + "name": "控制调度器", "type": 3, "icon": null, "order": 2, - "permission": "module_task:job:update", + "permission": "module_task:job:scheduler", "route_name": null, "route_path": null, "component_path": null, @@ -2644,17 +2644,36 @@ "keep_alive": true, "hidden": false, "always_show": false, - "title": "操作调度器", + "title": "控制调度器", "params": null, "affix": false, "redirect": null, - "description": "操作调度器" + "description": "控制调度器" + }, + { + "name": "操作任务", + "type": 3, + "icon": null, + "order": 3, + "permission": "module_task:job:task", + "route_name": null, + "route_path": null, + "component_path": null, + "status": "0", + "keep_alive": true, + "hidden": false, + "always_show": false, + "title": "操作任务", + "params": null, + "affix": false, + "redirect": null, + "description": "操作任务" }, { "name": "删除执行日志", "type": 3, "icon": null, - "order": 3, + "order": 4, "permission": "module_task:job:delete", "route_name": null, "route_path": null, @@ -2673,7 +2692,7 @@ "name": "详情执行日志", "type": 3, "icon": null, - "order": 4, + "order": 5, "permission": "module_task:job:detail", "route_name": null, "route_path": null, @@ -2729,10 +2748,29 @@ "description": "创建节点" }, { - "name": "修改节点", + "name": "调试节点", "type": 3, "icon": null, "order": 2, + "permission": "module_task:node:execute", + "route_name": null, + "route_path": null, + "component_path": null, + "status": "0", + "keep_alive": true, + "hidden": false, + "always_show": false, + "title": "调试节点", + "params": null, + "affix": false, + "redirect": null, + "description": "调试节点" + }, + { + "name": "修改节点", + "type": 3, + "icon": null, + "order": 3, "permission": "module_task:node:update", "route_name": null, "route_path": null, @@ -2751,7 +2789,7 @@ "name": "删除节点", "type": 3, "icon": null, - "order": 3, + "order": 4, "permission": "module_task:node:delete", "route_name": null, "route_path": null, @@ -2770,7 +2808,7 @@ "name": "详情节点", "type": 3, "icon": null, - "order": 4, + "order": 5, "permission": "module_task:node:detail", "route_name": null, "route_path": null, @@ -2789,7 +2827,7 @@ "name": "查询节点", "type": 3, "icon": null, - "order": 5, + "order": 6, "permission": "module_task:node:query", "route_name": null, "route_path": null, diff --git a/frontend/src/api/module_task/job.ts b/frontend/src/api/module_task/job.ts index be922392..d784b6b3 100644 --- a/frontend/src/api/module_task/job.ts +++ b/frontend/src/api/module_task/job.ts @@ -116,13 +116,6 @@ const JobAPI = { data: ids, }); }, - - clearJobLog() { - return request({ - url: `${API_PATH}/log/clear`, - method: "delete", - }); - }, }; export default JobAPI; @@ -153,6 +146,8 @@ export interface JobLogTable extends BaseType { job_name?: string; trigger_type?: string; status: string; + next_run_time?: string; + job_state?: string; result?: string; error?: string; created_time?: string; diff --git a/frontend/src/styles/index.scss b/frontend/src/styles/index.scss index 4f2b3518..23a36325 100644 --- a/frontend/src/styles/index.scss +++ b/frontend/src/styles/index.scss @@ -82,7 +82,7 @@ html.sidebar-color-blue .layout-mix .layout__sidebar--left .el-menu { // 全局搜索区域样式 .search-container { padding: 18px 16px 0; - margin-bottom: 16px; + margin-top: 16px; background-color: var(--el-bg-color-overlay); border: 1px solid var(--el-border-color-light); border-radius: 4px; @@ -115,7 +115,7 @@ html.sidebar-color-blue .layout-mix .layout__sidebar--left .el-menu { // 表格内容区域 &__content { - // margin: 8px 0; // 表格内容区域添加内边距 + margin: 0px 0; // 表格内容区域添加内边距 } // 分页区域 diff --git a/frontend/src/views/module_ai/chat_message/index.vue b/frontend/src/views/module_ai/chat_message/index.vue index 105359a6..11990dd4 100644 --- a/frontend/src/views/module_ai/chat_message/index.vue +++ b/frontend/src/views/module_ai/chat_message/index.vue @@ -1,79 +1,6 @@