Front-end development preparation

The front end uses Vue 3, TypeScrIPt, Vite and Element Plus.

Fixed operating environment:

node -v
pnpm -v

Recommended version:

Node.js 22.22.3
pnpm 10.34.3

Install dependencies:

cd frontend
pnpm install

Start the frontend:

pnpm run dev

Visit address:

http://localhost:5173

It can also be started via Gradle:

gradle frontend

Remote environment configuration

When developing locally, you can modify the backend access address through .env.local.

code .env.local

Example:

VITE_VUE_APP_BASE_DOMAIN=http://localhost:8080

If you need to connect to a remote environment:

VITE_VUE_APP_BASE_DOMAIN=http://101.132.73.185:8080

Front-end project structure

The front-end adopts a hierarchical structure of "basic application + removable business modules".

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/

Hierarchical responsibilities

  • src/app is the basic platform layer, which contains application startup, layout, basic routing, global store, common components, common tools, platform management and shared capabilities.
  • src/app/management contains the platform’s built-in management features, such as tenants, users, roles, authorizations, and SSO.
  • src/app/routes contains basic routing groups such as platform, administrator, and workspace management.
  • src/app/shared holds APIs, components, hooks and tools shared by multiple business modules.
  • src/modules is the business module layer. Each business module has its own api, views, routes.ts, index.ts, and components, config.ts and share-routes.ts can be provided on demand.
  • src/services and src/views are deprecated, do not recreate them; application-level pages are placed in src/app/views, and business pages are placed in src/modules/*/views.

Module standard structure

When adding a new business module, it is recommended to use the following structure:

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

The workspace routing is uniformly written in:

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

The public sharing page route is written at:

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

Basic routing will automatically discover module routing through src/app/router/module-routes.ts, and there is no need to manually import specific business modules in the basic application.

Import rules

Base layer rules:

  • src/app cannot statically import @/modules.
  • src/app/components must remain generic and cannot rely on specific business modules.
  • When the base layer requires business module routing, src/modules/*/routes.ts and src/modules/*/share-routes.ts are automatically discovered by src/app/router/module-routes.ts.

Business module rules:

  • Within the module, relative paths or the public surface of this module are preferred.
  • Cross-modules can only import each other's 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'

Cross-module deep import of each other's internal pages or private paths is prohibited:

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

Stable resource queries reused by multiple modules are placed in src/app/shared/api/resources.ts:

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

Business modules should not directly import interfaces under src/app/management. When platform data such as users, authorizations, tenants, etc. are needed, first expose the facade in src/app/shared/api:

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

Application-level capabilities are uniformly imported from @/app:

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

Naming convention

Files and directories are named using underscores.

component-demo/
component-demo.vue

Vue component references use the underscore lowercase mode uniformly:

<component-demo></component-demo>

Component Development Specifications

  • Pop-up windows and drawers preferentially use existing components under src/app/components.
  • Components that are highly reusable and have nothing to do with business are placed in src/app/components.
  • Business-related components are placed in the components directory of the module to which they belong.
  • src/app/components cannot rely on specific business modules.
  • The script tags of Vue files uniformly use <script lang="ts" setup>.
  • The style tags of Vue files use SCSS uniformly.
  • The style class name is named according to the module name, and it is best to use a root node to wrap it.
  • New components developed in src/app/lib are named in the form of zqy-component name.
  • Complex logic requires retaining necessary comments.

List page specifications

The management class list page preferentially uses src/app/components/block-table/index.vue. When adding or modifying a list, you need to keep the list structure, header, operation bar, and batch operation experience consistent.

basic layout

  • Use Breadcrumb consistently at the top of the page.
  • The root node of the list page uses zqy-seach-table, and appends the independent class name of the current page, such as user-center-page, tenant-list-page.
  • The top toolbar uses zqy-table-top, with the main operation button on the left and the search input box on the right.
  • The search input box width follows the global .zqy-seach .el-input, and the placeholder copy uses "Please enter xxx and press Enter to search".
  • The outer layer of the table uses zqy-table, and the table component uses BlockTable.
  • Loading and network exceptions use LoadingPage to wrap the table area.

Table columns

  • Table configurations are maintained uniformly in *.config.ts at the same level as the page. Do not scatter column configurations in the template.
  • The operation column in colConfigs is fixed on the right side, configuration fixed: 'right'.
  • The first column is fixed to the left by BlockTable by default; if the page enables multi-selection, the serial number column will automatically switch to the selection column.
  • Line breaks are not allowed in the header. Long table headers are carried by column width or minimum width. Use horizontal scrolling when necessary and do not rely on line wrapping for display.
  • Column titles must use clear business names to avoid long descriptions; if the title is long, sufficient minWidth should be given.
  • Turn on showOverflowTooltIP:true for long text content.
  • The status column uses el-tag uniformly, and the commonly used status colors are:
    • ENABLE: type="success", displays "enabled".
    • DISABLE: type="danger", displays "disabled".
    • APPLYING or pending status: type="warning".

row action bar

  • The row action bar of the management list is unified into the "Main Action + More" mode.
  • Only keep one common main operation, such as Edit; the rest of the operations are placed in the More drop-down.
  • If there is no suitable main action, the action bar only displays a centered More.
  • Use span to operate column text buttons, do not use ordinary el-button to squeeze the height of the table.
  • Row operation containers need to be horizontally centered and spaced uniformly:
<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>

The action bar style needs to be isolated by page class name to avoid contaminating other pages:

.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;
    }
}

More operating instructions

For more operations, use Element Plus el-dropdown, and the trigger method is click:

<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>

The drop-down styles are unified and compact:

.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');
    }
}

More copywriting rules for action:

  • Copywriting must be short and clear, and give priority to using object-object structures, such as "enable", "disable", "delete", "change password" and "copy link".
  • For administrator-related operations, use "set as administrator" and "cancel administrator" uniformly. Do not write lengthy text such as "set as platform administrator", "set as tenant administrator", "cancel tenant administrator", etc.
  • Dangerous operations such as deletion and removal are placed at the end of the drop-down list.
  • Asynchronous operations need to display the loading state and disable repeated clicks while loading.
  • Don't put bulk operations in the More drop-down that are not related to the current row.

Multiple selection and batch operation specifications

Management lists that require batch processing must support multiple selections. Multiple selections uniformly use the checkbox capability of BlockTable.

Table configuration:

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

Page template:

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

Bulk toolbar rules:

  • After at least one row is selected, the batch operation toolbar is displayed overlaying above zqy-table-top.
  • The batch toolbar uses an absolute positioning mask to cover the original toolbar with a white background to prevent the top button and search box from appearing at the same time.
  • Batch operation buttons are arranged in business order. The commonly used order is: Enable, Disable, business batch operation, Delete/Remove, Cancel selection.
  • Deselect is placed last and does not use the theme color to accentuate.
  • Batch operations must filter out inapplicable data and give a clear prompt, such as "Please select disabled users."
  • Use batchLoading uniformly during batch interface execution to disable deselection and repeated submission.
  • After the batch is successful, refresh the list and clear selectedRows.

State management example:

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

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

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

Batch mask style example:

.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 file organization order

In order to ensure a unified Vue code style, it is recommended that the contents of <script setup> be organized in the following order:

<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>

Schema check

After the structure change run at least:

pnpm check:architecture
pnpm build

Run a complete check when module boundaries and route discovery are involved:

pnpm check:architecture:full
pnpm build

View the cross-module public surface report:

pnpm check:architecture:report

The current architecture check blocks the following issues:

  • src/app static dependency src/modules
  • src/modules directly depends on src/app/management
  • Directly quote VIP source code
  • Cross-module deep import of each other's views or private path
  • Module is missing required index.ts or routes.ts
  • Basic application cannot be built after deleting src/modules

Commonly used debugging addresses

Swagger:

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

H2 database:

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