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/appis the basic platform layer, which contains application startup, layout, basic routing, global store, common components, common tools, platform management and shared capabilities.src/app/managementcontains the platform’s built-in management features, such as tenants, users, roles, authorizations, and SSO.src/app/routescontains basic routing groups such as platform, administrator, and workspace management.src/app/sharedholds APIs, components, hooks and tools shared by multiple business modules.src/modulesis the business module layer. Each business module has its ownapi,views,routes.ts,index.ts, andcomponents,config.tsandshare-routes.tscan be provided on demand.src/servicesandsrc/viewsare deprecated, do not recreate them; application-level pages are placed insrc/app/views, and business pages are placed insrc/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/appcannot statically import@/modules.src/app/componentsmust remain generic and cannot rely on specific business modules.- When the base layer requires business module routing,
src/modules/*/routes.tsandsrc/modules/*/share-routes.tsare automatically discovered bysrc/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
componentsdirectory of the module to which they belong. src/app/componentscannot 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/libare named in the form ofzqy-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
Breadcrumbconsistently 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 asuser-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 usesBlockTable. - Loading and network exceptions use
LoadingPageto wrap the table area.
Table columns
- Table configurations are maintained uniformly in
*.config.tsat the same level as the page. Do not scatter column configurations in the template. - The operation column in
colConfigsis fixed on the right side, configurationfixed: 'right'. - The first column is fixed to the left by
BlockTableby 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
minWidthshould be given. - Turn on
showOverflowTooltIP:truefor long text content. - The status column uses
el-taguniformly, and the commonly used status colors are:ENABLE:type="success", displays "enabled".DISABLE:type="danger", displays "disabled".APPLYINGor 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 theMoredrop-down. - If there is no suitable main action, the action bar only displays a centered
More. - Use
spanto operate column text buttons, do not use ordinaryel-buttonto 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. Deselectis 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
batchLoadinguniformly 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/appstatic dependencysrc/modulessrc/modulesdirectly depends onsrc/app/management- Directly quote VIP source code
- Cross-module deep import of each other's
viewsor private path - Module is missing required
index.tsorroutes.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
