Files
2026-08-31 11:33:59 +08:00

15 KiB

name, description, globs
name description globs
cross-plugin-web-development Madong 插件 Web 端开发规范,覆盖 Web 模板源、运行目录同步、页面路由、Web 菜单、API、认证、Element Plus 页面实现、验证与重装防回退。
backend/plugin/**/resource/menu/web.php
backend/plugin/**/resource/template/web/**/*
frontend/web/app/apps/**/pages/**/*.vue
frontend/web/app/apps/**/api/**/*.ts
frontend/web/app/apps/**/pages/routes.ts
.windsurf/skills/**/*.md

Madong 插件 Web 端开发技能

用于开发和维护插件的 Web 前台功能。重点解决 Web 页面模板源、安装复制、插件路由、Web 菜单、API 调用和卸载重装后的防回退问题。

适用场景

  • 为已有 Madong 插件增加 Web 页面。
  • 将移动端或后台已有业务迁移为 Web 页面。
  • 为插件补齐 Web API、路由和 Web 菜单。
  • 修复插件卸载重装后 Web 页面、路由或菜单回退。
  • 维护插件 Web 模板源与安装后的 Web 运行目录一致性。

总原则

  1. 先核对项目真实实现,再编写或修改 Web 插件代码。
  2. 插件 Web 的权威模板源是 backend/plugin/{plugin}/resource/template/web。
  3. 安装后的 Web 运行目录是 frontend/web/app/apps/{plugin},属于模板复制产物。
  4. 正式修改优先落在模板源,完成后再同步到 Web 运行目录。
  5. 页面、API、路由、菜单和后端接口的路径必须一致。
  6. 优先复用当前项目的 ~/utils/request、Element Plus、Pinia 和既有页面风格,不直接照搬移动端 UI。
  7. 先解决业务根因,不通过页面层伪造数据或绕过后端校验。
  8. 不删除未经确认的内容;新增页面时不要覆盖已有业务页面。

目录与复制关系

模板源

backend/plugin/{plugin}/resource/template/web/
├── api/
│   ├── data-entry.ts
│   └── exception.ts
├── pages/
│   ├── routes.ts
│   └── {module}/
│       ├── index.vue
│       ├── tasks.vue
│       └── components/
└── .gitkeep

安装后的运行目录

frontend/web/app/apps/{plugin}/
├── api/
├── pages/
│   ├── routes.ts
│   └── {module}/
└── ...

当前项目的实际对应关系:

backend/plugin/produce/resource/template/web/
frontend/web/app/apps/produce/

修改 Web 插件时,必须核对以下文件是否同步:

  • api/**/*.ts
  • pages/**/*.vue
  • pages/routes.ts
  • 页面使用的组件和辅助文件

开发前检查

开始开发前至少核对:

  1. 插件名和 Web 运行目录:frontend/web/app/apps/{plugin}。
  2. 模板源是否存在:backend/plugin/{plugin}/resource/template/web。
  3. Web 主应用是否通过 router.options.ts 加载插件路由。
  4. 后端接口的真实路径、请求方法、参数和响应结构。
  5. 登录用户信息来源和认证请求头。
  6. resource/menu/web.php 的真实菜单字段格式。
  7. 可参考的 Web 页面,例如:
    • frontend/web/app/apps/produce/pages/data-entry/index.vue
    • frontend/web/app/apps/produce/pages/data-entry/components/OrderEntryPanel.vue
  8. 移动端页面仅作为业务流程参考,不作为 Web 布局和组件规范来源。

Web 路由规范

1. 插件路由入口

当前 Web 主应用通过以下方式加载插件路由:

frontend/web/app/router.options.ts

该文件使用 import.meta.glob('~/apps/**/pages/routes.ts', { eager: true }) 加载插件路由。因此插件页面应在以下文件注册:

backend/plugin/{plugin}/resource/template/web/pages/routes.ts
frontend/web/app/apps/{plugin}/pages/routes.ts

2. 路由写法

export default [
  {
    path: '/{plugin}/{module}',
    component: () => import('~/apps/{plugin}/pages/{module}/index.vue'),
    meta: {
      title: '页面标题',
      layout: 'default',
    },
  },
]

要求:

  • 页面路径使用业务语义,保持与 Web 菜单 URL 一致。
  • 组件路径使用 ~/apps/{plugin}/pages/...。
  • 默认使用 layout: 'default',除非已核对项目存在其他布局。
  • 一个页面只能注册一次,避免主站路由和插件路由重复。
  • 带参数页面使用 Nuxt 约定的 [param].vue,但当前插件也可按项目路由表显式注册页面。

3. 认证与权限

Web 路由是否需要登录由当前项目权限中间件和路由元信息共同决定。涉及用户数据、异常提报、维修提交等业务时:

  • 页面必须使用真实的登录用户信息。
  • 不要在前端硬编码用户工号作为提交值。
  • 需要登录的页面应按当前项目实际权限配置 meta.permissions,先核对现有菜单和中间件实现。
  • 如果后端接口本身标记为公共接口,也不能因此在页面中跳过业务必填校验。

Web 菜单规范

1. 菜单文件

插件 Web 菜单文件:

backend/plugin/{plugin}/resource/menu/web.php

Web 菜单不是 Admin 菜单,不能直接复制 resource/menu/admin.php 的结构。当前项目 Web 菜单使用记录数组,例如:

return [
    [
        'app'        => 'web',
        'category'   => 1,
        'source'     => 'plugin',
        'code'       => null,
        'name'       => '业务页面',
        'url'        => '/{plugin}/{module}',
        'icon'       => 'ri:apps-line',
        'level'      => 1,
        'type'       => 2,
        'sort'       => 10,
        'target'     => 1,
        'is_show'    => 1,
        'enabled'    => 1,
        'created_at' => time(),
        'updated_at' => time(),
        'deleted_at' => null,
    ],
];

字段要求以当前项目和菜单导入实现为准,至少核对:app、source、name、url、type、sort、is_show、enabled。

2. 菜单与路由一致

新增 Web 页面时必须同时检查:

  • resource/menu/web.php 是否有对应菜单。
  • 菜单 url 是否等于路由 path。
  • 路由组件是否真实存在。
  • 菜单是否需要显示为一级菜单或子菜单。
  • 图标是否使用项目已有图标格式。

不要只添加路由而不添加菜单,也不要只添加菜单而不注册路由。

3. 菜单安装行为

菜单通常在插件安装时导入系统菜单。修改 resource/menu/web.php 后,当前数据库中已存在的菜单不一定会自动刷新。验证菜单时应区分:

  • 文件配置是否正确。
  • 安装导入后的系统菜单是否已更新。
  • 当前用户是否有访问该菜单的权限。

不要通过前端硬编码菜单代替插件菜单导入机制。

Web API 规范

1. API 文件位置

插件 Web API 正式放在模板源:

backend/plugin/{plugin}/resource/template/web/api/

运行目录对应:

frontend/web/app/apps/{plugin}/api/

当前项目的实际写法是业务 API 直接放在插件目录,例如:

import request from '~/utils/request'

export function getExampleList(params: Record<string, unknown> = {}) {
  return request.get('/{plugin}/{resource}', params)
}

2. 请求约定

  • 使用 ~/utils/request,不要在业务 API 中直接调用 axios 或 fetch。
  • 传入后端业务路径,例如 /produce/process,不要手动拼接 /api/。
  • GET 参数作为第二个参数传入。
  • POST 数据作为第二个参数传入。
  • 默认响应已经解包为 data。
  • 只有确实需要状态码、消息或完整响应时,才使用 returnFullResponse: true。
  • 上传使用 request.upload 和 FormData,统一由 API 层封装。
  • API 层负责类型声明、路径和参数;页面负责交互状态和展示。
  • 错误提示优先使用请求层统一处理,页面避免重复弹出相同错误。

3. API 类型

应根据后端真实返回结构定义接口,例如:

export interface ExampleItem {
  id: string | number
  name?: string
  status?: number
}

export function getExampleDetail(id: string | number) {
  return request.get<ExampleItem>(`/example/${id}`)
}

列表接口要确认返回的是数组还是 { items, total },不能凭页面名称猜测。

4. API 与页面解耦

页面中不要重复实现:

  • 基础 URL 拼接。
  • Authorization 认证头。
  • 响应解包。
  • 上传认证。
  • 通用错误弹窗。

如果多个页面共享业务请求,应提取到插件 Web API 文件,而不是复制请求代码。

页面开发规范

1. 技术栈

当前项目 Web 端使用:

  • Vue 3 <script setup lang="ts">。
  • Nuxt 4。
  • Element Plus。
  • Pinia。
  • 当前项目已有的 SCSS / CSS 变量和布局。

2. 页面结构

建议结构:

<template>
  <div class="produce-work-page">
    <div class="page-shell">
      <section class="page-hero">...</section>
      <div class="content-grid">
        <main class="main-column">...</main>
        <aside class="side-column">...</aside>
      </div>
    </div>
  </div>
</template>

要求:

  • 页面应适配 layouts/default.vue 的滚动区域。
  • 页面主体避免依赖移动端 view、text、u-* 组件。
  • Web 页面使用 div、button、Element Plus 组件和原生表单语义。
  • 页面需要考虑 1440px、1024px、768px 等宽度下的响应式布局。
  • 重要操作按钮必须有 loading、禁用或重复提交保护。
  • 空数据、加载中、提交失败和成功状态要有明确反馈。
  • 不要为了追求视觉效果引入项目没有使用的新 UI 框架。

3. 业务迁移

从移动端迁移时:

  • 保留业务流程、校验规则、接口参数和状态含义。
  • 将 u-popup 改为 el-dialog、el-drawer、el-select 或页面内面板。
  • 将 u-input 改为 el-input。
  • 将 u-button 或自绘点击区域改为 el-button 或符合语义的按钮。
  • 将移动端图片选择改为 el-upload,上传由 API 层完成。
  • 将 uni.showToast 改为 ElMessage 或 ElNotification。
  • 将移动端本地用户信息读取改为 useMemberStore(),但要以当前项目真实 Store 结构为准。
  • 不要把移动端的 uni、rpx、原生扫码插件直接带入 Web 页面。

4. 图片上传

推荐流程:

  1. 使用 el-upload 设置 :auto-upload="false"。
  2. 读取 UploadUserFile.raw。
  3. 调用插件 API 的上传方法。
  4. 将返回的 URL 放入业务提交参数。
  5. 限制文件类型、数量和必要的大小。
  6. 上传失败时停止提交并保留用户选择,方便重试。

模板源优先与同步流程

标准流程:

  1. 核对插件后端接口和业务返回结构。
  2. 在 backend/plugin/{plugin}/resource/template/web 中创建或修改 Web API。
  3. 在模板源中创建或修改页面及页面组件。
  4. 在模板源 pages/routes.ts 注册路由。
  5. 在 resource/menu/web.php 增加或更新 Web 菜单。
  6. 核对模板源文件之间的导入路径和路由路径。
  7. 将模板源同步到 frontend/web/app/apps/{plugin}。
  8. 核对模板源与运行目录内容一致。
  9. 启动 Web 端,验证菜单、路由、页面和接口。
  10. 如菜单由安装流程导入,重新安装或执行项目规定的菜单刷新流程后再验证菜单。

禁止只修改以下运行目录而不更新模板源:

frontend/web/app/apps/{plugin}

验证清单

文件与同步

  • 模板源 Web API 存在。
  • 模板源页面存在。
  • 模板源 pages/routes.ts 已注册。
  • resource/menu/web.php 已配置对应菜单。
  • 运行目录已同步。
  • 模板源与运行目录文件内容一致。

页面与交互

  • 页面使用 Web 组件,不包含移动端 uni、u-*、rpx。
  • 加载、空数据、错误和提交状态完整。
  • 提交按钮有重复提交保护。
  • 关键字段有前端校验,后端校验仍然保留。
  • 用户信息来自真实 Store 或当前项目认证机制。
  • 页面在窄屏下仍可用。

API

  • API 使用 ~/utils/request。
  • 请求路径未重复拼接 /api/。
  • 请求方法、参数名称和响应结构与后端一致。
  • 文件上传通过 API 层完成。
  • 错误提示没有重复弹出。

路由与菜单

  • 菜单 URL 与路由 path 一致。
  • 路由 component 路径真实存在。
  • 菜单排序、图标、显示状态符合项目实际格式。
  • 菜单导入后的系统数据已经刷新或重新安装验证。

构建与运行

  • Web 端定向 ESLint 检查通过,或已区分本次新增问题与项目历史问题。
  • Web 端类型检查通过,或已记录项目历史类型错误。
  • 页面可以正常打开。
  • API 请求返回预期数据。
  • 提交、上传、跳转和返回流程正常。

防回退规则

卸载重装插件会重新从模板源复制 Web 文件。以下情况会导致回退:

  • 只修改了 frontend/web/app/apps/{plugin}。
  • 模板源缺少新增页面或 API。
  • 模板源 pages/routes.ts 没有同步。
  • resource/menu/web.php 没有同步。
  • 运行目录临时修复后没有回写模板源。

最终交付必须同时满足:

  • 模板源版本正确。
  • Web 运行目录版本正确。
  • 路由和菜单版本正确。
  • 重新安装插件后页面不会回退。

常见问题

1. 页面 404

排查顺序:

  1. 页面文件是否位于 frontend/web/app/apps/{plugin}/pages。
  2. 插件 pages/routes.ts 是否存在。
  3. router.options.ts 是否仍使用插件路由 glob。
  4. 路由 path 是否与访问地址一致。
  5. import 路径大小写是否正确。
  6. 模板源和运行目录是否都包含该页面。

2. 菜单不显示

排查:

  • resource/menu/web.php 是否新增记录。
  • app 是否为 web。
  • enabled、is_show 是否启用。
  • 菜单是否已重新导入系统表。
  • 当前登录用户是否具备对应菜单访问权限。

3. 页面打开但请求 404

常见原因:

  • API 路径多拼了 /api/。
  • 请求路径与后端控制器路由不一致。
  • 后端插件未加载或运行进程未重载。
  • API 文件只同步到了运行目录,重装后被旧模板覆盖。

4. 模板源文件出现模块找不到

模板源不一定是主 Web 项目的 TypeScript 编译入口。若编辑器提示 vue、element-plus、~/stores/member 或 ~/apps/{plugin}/api 找不到,先确认运行目录中的同一文件可以解析,再区分编辑器静态提示和真实构建错误。

5. 页面使用了公共接口但无法提交

公共接口不等于无需业务校验。应继续检查:

  • 当前用户工号是否存在。
  • 必填字段是否完整。
  • 业务状态是否允许操作。
  • 后端是否要求先校验订单或维修任务。

6. Windows 下模板修改未生效

如果修改模板源、菜单、安装逻辑后运行结果仍是旧版本:

  • 确认 Webman 进程是否已重新加载插件代码。
  • 确认插件安装流程是否重新复制模板。
  • 确认菜单数据是否重新导入。
  • 不要只根据浏览器缓存判断模板是否更新。

输出契约

完成一个插件 Web 功能时,至少交付:

  • Web API 模板源。
  • Web 页面模板源。
  • 插件 Web 路由模板源。
  • resource/menu/web.php 菜单配置。
  • 同步后的 frontend/web/app/apps/{plugin} 运行目录。
  • 页面、接口、菜单和路由一致性验证。
  • 构建或定向检查结果。
  • 如发现既有 Web skill 与项目真实实现不一致,应同步更新对应 skill,并说明更新内容。