前端开发准备

前端使用 Vue 3、TypeScrIPt、Vite 和 Element Plus。

固定运行环境:

node -v
pnpm -v

推荐版本:

Node.js 22.22.3
pnpm 10.34.3

安装依赖:

cd frontend
pnpm install

启动前端:

pnpm run dev

访问地址:

http://localhost:5173

也可以通过 Gradle 启动:

gradle frontend

远程环境配置

本地开发时可通过 .env.local 修改后端访问地址。

code .env.local

示例:

VITE_VUE_APP_BASE_DOMAIN=http://localhost:8080

如果需要连接远程环境:

VITE_VUE_APP_BASE_DOMAIN=http://101.132.73.185:8080

前端项目结构

前端采用“基础应用 + 可移除业务模块”的分层结构。

src/
  app/                         # 基础应用层,不能静态依赖 modules
    api/                       # 登录、认证等基础接口
    assets/                    # 全局样式、图片、字体等静态资源
    components/                # 全局通用组件,只能依赖 app/shared/utils 等基础能力
    hooks/                     # 全局通用 hooks
    layout/                    # 页面布局、顶部栏、菜单、面包屑
    lib/                       # 通用前端库或可复用引擎
    management/                # 平台内置管理功能:租户、用户、角色、授权、SSO 等
    plugins/                   # HTTP 请求插件等基础插件
    router/                    # 基础路由、路由守卫、模块路由自动发现
    routes/                    # 平台、管理员、工作区管理等基础路由分组
    shared/                    # 跨业务模块共享的稳定能力
      api/                     # 共享接口 facade,例如资源查询、用户、授权等
      components/              # 跨业务模块复用的通用组件
      hooks/                   # 跨业务模块复用 hooks
      utils/                   # 跨业务模块复用工具
    store/                     # 全局状态
    utils/                     # 基础工具、http、事件总线、格式化等
    views/                     # 登录、首页、系统错误页等基础页面
  modules/                     # 业务模块层,可移除;删除后基础 app 仍需可编译
    access-rule/
    computer-group/
    custom-api/
    custom-form/
    custom-func/
    data-planning/
    datasource/
    driver-management/
    file-center/
    global-variables/
    home-overview/
    lib-package/
    message-center/
    metadata-page/
    realtime-computing/
    report/
    schedule/
    spark-container/
    workflow/

分层职责

  • src/app 是基础平台层,放应用启动、布局、基础路由、全局 store、通用组件、通用工具、平台管理和 shared 能力。
  • src/app/management 放平台内置管理功能,例如租户、用户、角色、授权和 SSO。
  • src/app/routes 放平台、管理员、工作区管理等基础路由分组。
  • src/app/shared 放多个业务模块共享的 API、组件、hooks 和工具。
  • src/modules 是业务模块层,每个业务模块拥有自己的 api、views、routes.ts、index.ts,可按需提供 components、config.ts、share-routes.ts。
  • src/services 和 src/views 已废弃,不要重新创建;应用级页面放在 src/app/views,业务页面放在 src/modules/*/views。

模块标准结构

新增业务模块时,推荐使用以下结构:

src/modules/<module-name>/
  api/                         # 模块自己的业务接口
  views/                       # 模块页面
  components/                  # 模块公开或内部组件
  config.ts                    # 可被其他模块复用的稳定配置,可选
  routes.ts                    # 工作区路由,必需
  share-routes.ts              # 分享页/公开页路由,可选
  index.ts                     # 模块 public export,必需

工作区路由统一写在:

src/modules/<module-name>/routes.ts

公开分享页路由写在:

src/modules/<module-name>/share-routes.ts

基础路由会通过 src/app/router/module-routes.ts 自动发现模块路由,不需要在基础应用中手动 import 具体业务模块。

Import 规则

基础层规则:

  • src/app 不能静态 import @/modules。
  • src/app/components 必须保持通用,不能依赖具体业务模块。
  • 基础层需要业务模块路由时,由 src/app/router/module-routes.ts 自动发现 src/modules/*/routes.ts 和 src/modules/*/share-routes.ts。

业务模块规则:

  • 模块内部优先使用相对路径或本模块 public surface。
  • 跨模块只能导入对方 public surface。
import { GetWorkflowList } from '@/modules/workflow/api'
import { TypeList } from '@/modules/workflow/config'
import { ZqyFlow } from '@/modules/workflow/components'
import { workflowRoutes } from '@/modules/workflow'

禁止跨模块 deep import 对方内部页面或私有路径:

// 禁止
import WorkItem from '@/modules/workflow/views/work-item/index.vue'

多个模块复用的稳定资源查询放到 src/app/shared/api/resources.ts:

import { GetDatasourceList, GetComputerGroupList } from '@/app/shared/api/resources'

业务模块不要直接 import src/app/management 下的接口。需要用户、授权、租户等平台数据时,先在 src/app/shared/api 暴露 facade:

import { GetUserList } from '@/app/shared/api/user'
import { CheckLicenseStatus } from '@/app/shared/api/license'

应用级能力统一从 @/app 下导入:

import { LoginUserInfo } from '@/app/api'

命名规范

文件和目录统一使用中划线命名。

component-demo/
component-demo.vue

Vue 组件引用统一使用中划线小写模式:

<component-demo></component-demo>

组件开发规范

  • 弹窗和抽屉优先使用 src/app/components 下已有组件。
  • 复用性高、与业务无关的组件放到 src/app/components。
  • 业务相关组件放在所属模块的 components 目录。
  • src/app/components 不能依赖具体业务模块。
  • Vue 文件的 script 标签统一使用 <script lang="ts" setup>。
  • Vue 文件的 style 标签统一使用 SCSS。
  • 样式类名根据模块名命名,尽量使用一个根节点包裹。
  • src/app/lib 中开发的新组件统一使用 zqy-组件名 形式命名。
  • 复杂逻辑需要保留必要注释。

列表页面规范

管理类列表页面优先使用 src/app/components/block-table/index.vue。新增或改造列表时,需要保持列表结构、表头、操作栏和批量操作体验一致。

基础布局

  • 页面顶部统一使用 Breadcrumb。
  • 列表页面根节点使用 zqy-seach-table,并追加当前页面独立类名,例如 user-center-page、tenant-list-page。
  • 顶部工具栏使用 zqy-table-top,左侧放主操作按钮,右侧放搜索输入框。
  • 搜索输入框宽度沿用全局 .zqy-seach .el-input,占位文案使用“请输入 xxx 回车进行搜索”。
  • 表格外层使用 zqy-table,表格组件使用 BlockTable。
  • 加载和网络异常统一使用 LoadingPage 包裹表格区域。

表格列

  • 表格配置统一维护在页面同级 *.config.ts 中,不要把列配置散落在模板里。
  • colConfigs 中操作列固定在右侧,配置 fixed:'right'。
  • 第一列默认由 BlockTable 固定在左侧;如页面开启多选,序号列会自动切换为选择列。
  • 表头不允许换行。长表头通过列宽或最小宽度承载,必要时使用横向滚动,不要依赖换行展示。
  • 列标题必须使用清晰业务名称,避免过长描述;如标题较长,应给足 minWidth。
  • 长文本内容开启 showOverflowTooltIP:true。
  • 状态列统一使用 el-tag,常用状态颜色:
    • ENABLE:type="success",显示“启用”。
    • DISABLE:type="danger",显示“禁用”。
    • APPLYING 或待处理状态:type="warning"。

行操作栏

  • 管理列表的行操作栏统一为“主要操作 + 更多”模式。
  • 常用主操作只保留一个,例如 编辑;其余操作放入 更多 下拉。
  • 如果没有合适的主操作,操作栏只显示居中的 更多。
  • 操作列文字按钮使用 span,不要使用普通 el-button 挤占表格高度。
  • 行操作容器需要水平居中,间距统一:
<template #options="scopeSlot">
    <div class="btn-group page-action-group">
        <span class="page-action-button" @click="editData(scopeSlot.row)">编辑</span>
        <el-dropdown trigger="click" popper-class="page-action-dropdown">
            <span class="click-show-more page-action-button">更多</span>
            <template #dropdown>
                <el-dropdown-menu>
                    <el-dropdown-item @click="changeStatus(scopeSlot.row)">
                        {{ scopeSlot.row.status === 'ENABLE' ? '禁用' : '启用' }}
                    </el-dropdown-item>
                    <el-dropdown-item @click="deleteData(scopeSlot.row)">删除</el-dropdown-item>
                </el-dropdown-menu>
            </template>
        </el-dropdown>
    </div>
</template>

操作栏样式需要按页面类名隔离,避免污染其他页面:

.page-action-group {
    justify-content: center;
    gap: 16px;

    .page-action-button {
        display: inline-flex;
        align-items: center;
        line-height: 1;
        font-size: getCssVar('font-size', 'extra-small');
        color: getCssVar('color', 'primary');
        cursor: pointer;
        white-space: nowrap;
    }
}

更多操作规范

更多操作统一使用 Element Plus el-dropdown,触发方式为点击:

<el-dropdown trigger="click" popper-class="page-action-dropdown">
    <span class="click-show-more page-action-button">更多</span>
    <template #dropdown>
        <el-dropdown-menu>
            <el-dropdown-item>启用</el-dropdown-item>
            <el-dropdown-item>禁用</el-dropdown-item>
            <el-dropdown-item>删除</el-dropdown-item>
        </el-dropdown-menu>
    </template>
</el-dropdown>

下拉样式统一保持紧凑:

.page-action-dropdown {
    .el-dropdown-menu {
        padding: 4px 0;
        min-width: 76px;
    }

    .el-dropdown-menu__item {
        height: 26px;
        line-height: 26px;
        font-family: Avenir, Helvetica, Arial, sans-serif;
        font-size: getCssVar('font-size', 'extra-small');
    }
}

更多操作的文案规则:

  • 文案必须短、明确,优先使用动宾结构,例如“启用”“禁用”“删除”“修改密码”“复制链接”。
  • 管理员相关操作统一使用“设为管理员”“取消管理员”,不要写成“设为平台管理员”“设为租户管理员”“取消租户管理员”等冗长文案。
  • 删除、移除等危险操作放在下拉末尾。
  • 异步操作需要显示 loading 状态,并在 loading 时禁用重复点击。
  • 不要在更多下拉里放与当前行无关的批量操作。

多选和批量操作规范

需要批量处理的管理列表必须支持多选。多选统一使用 BlockTable 的 checkbox 能力。

表格配置:

export const TableConfig: TableConfig = {
    tableData: [],
    colConfigs,
    checkbox: true,
    pagination: {
        currentPage: 1,
        pageSize: 10,
        total: 0
    },
    seqType: 'seq',
    loading: false
}

页面模板:

<BlockTable
    :table-config="tableConfig"
    @size-change="handleSizeChange"
    @current-change="handleCurrentChange"
    @checkbox-change="handleSelectionChange"
/>

批量工具栏规则:

  • 选中至少一行后,在 zqy-table-top 上方覆盖显示批量操作工具栏。
  • 批量工具栏使用绝对定位遮罩覆盖原工具栏,背景为白色,避免顶部按钮和搜索框同时出现。
  • 批量操作按钮按业务顺序排列,常用顺序为:启用、禁用、业务批量操作、删除/移除、取消选择。
  • 取消选择 放在最后,不使用主题色强调。
  • 批量操作必须过滤不适用的数据,并给出明确提示,例如“请选择禁用状态的用户”。
  • 批量接口执行期间统一使用 batchLoading,禁用取消选择和重复提交。
  • 批量成功后刷新列表,并清空 selectedRows。

状态管理示例:

const selectedRows = ref<any[]>([])
const batchLoading = ref(false)

function handleSelectionChange(records: any[]) {
    selectedRows.value = records || []
}

function cancelSelection() {
    selectedRows.value = []
    tableConfig.tableData = [...tableConfig.tableData]
}

批量遮罩样式示例:

.page-batch-mask {
    position: absolute;
    z-index: 2;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: flex-start;
    padding: 0 20px;
    box-sizing: border-box;
    background-color: #fff;
}

.page-batch-actions {
    display: flex;
    align-items: center;
    gap: 12px;
}

.page-batch-action {
    min-width: 66px;
    height: 32px;
    line-height: 30px;
    border-color: getCssVar('color', 'primary');
    color: getCssVar('color', 'primary');
    background-color: #fff;

    &:hover,
    &:focus {
        border-color: getCssVar('color', 'primary');
        color: #fff;
        background-color: getCssVar('color', 'primary');
    }
}

Vue 文件组织顺序

为了保证 Vue 代码风格统一,<script setup> 内容推荐按以下顺序组织:

<script lang="ts" setup>
// 顶部统一引入模块插件
import { computed, onMounted, onUnmounted, reactive, ref, watch } from 'vue'
import { useRoute } from 'vue-router'

// 第一部分:接口、路由、store 等声明
interface DemoInterface {
}

const route = useRoute()

// 第二部分:参数声明,ref 在上,reactive 在下
const keyword = ref('')
const object = reactive({})

// 第三部分:props 参数
const props = defineProps<{
  modelConfig: ModalConfig
}>()

// 第四部分:计算属性和侦听器
const computedParam = computed(() => {
  return ''
})

watch(
  () => props.modelConfig.visible,
  (newVal) => {
    visible.value = newVal
  }
)

// 第五部分:函数和事件,统一使用 function 声明
function fun() {}

// 第六部分:生命周期
onMounted(() => {
})

onUnmounted(() => {
})
</script>

架构检查

结构变更后至少运行:

pnpm check:architecture
pnpm build

涉及模块边界、路由发现时运行完整检查:

pnpm check:architecture:full
pnpm build

查看跨模块 public surface 报告:

pnpm check:architecture:report

当前架构检查会阻断以下问题:

  • src/app 静态依赖 src/modules
  • src/modules 直接依赖 src/app/management
  • 直接引用 VIP 源码
  • 跨模块 deep import 对方 views 或私有路径
  • 模块缺少必需的 index.ts 或 routes.ts
  • 删除 src/modules 后基础应用无法构建

常用调试地址

Swagger:

http://101.132.73.185:8080/swagger-ui/index.html
username: admin
password: admin123

H2 数据库:

http://101.132.73.185:8080/h2-console
jdbc: jdbc:h2:/data/zhiqingyun/GH-2092/h2/data;AUTO_SERVER=TRUE
username: root
password: root123