com um clique
add-h5-page
Add a new mobile page to frontend.h5 using Vant 4 components
Instalar com Codex ou Claude Copie este prompt, cole no Codex, Claude ou outro assistente e deixe que ele revise a página da skill e instale para você.
Menu
Add a new mobile page to frontend.h5 using Vant 4 components
Instalar com Codex ou Claude Copie este prompt, cole no Codex, Claude ou outro assistente e deixe que ele revise a página da skill e instale para você.
Baseado na classificação ocupacional SOC
Add a new API endpoint with controller, service, and DTOs following OMS patterns
Add a new database entity with migrations for all three databases (MySQL, MSSQL, PostgreSQL)
Add a new frontend page with proper layout, table, and API integration following OMS UI standards
Audit backend code to ensure all queries properly filter by OrgId for multi-tenant security
Debug and troubleshoot issues in the OMS system with systematic approach
Create database migration files for all three databases (MySQL, MSSQL, PostgreSQL) when schema changes are needed
| name | add-h5-page |
| description | Add a new mobile page to frontend.h5 using Vant 4 components |
| disable-model-invocation | false |
| allowed-tools | Read, Write, Edit, Glob |
Add a new mobile page to the OMS H5 frontend using Vant 4 components.
When the user requests to add a new mobile page, follow these steps:
Location: frontend.h5/src/api/{module}.ts
import request from '@/utils/request'
export interface {EntityName} {
id: number
orgId: number
name: string
createdAt: string
}
export const {entityName}Api = {
getList: () => request.get<{EntityName}[]>('/api/{EntityName}'),
getById: (id: number) => request.get<{EntityName}>(`/api/{EntityName}/${id}`),
create: (data: any) => request.post('/api/{EntityName}', data),
update: (id: number, data: any) => request.put(`/api/{EntityName}/${id}`, data),
delete: (id: number) => request.delete(`/api/{EntityName}/${id}`)
}
Location: frontend.h5/src/views/{module}/{PageName}.vue
<template>
<div class="page">
<van-nav-bar
:title="title"
left-arrow
@click-left="onClickLeft"
>
<template #right>
<van-icon name="plus" @click="handleAdd" />
</template>
</van-nav-bar>
<van-pull-refresh v-model="refreshing" @refresh="onRefresh">
<van-list
v-model:loading="loading"
:finished="finished"
finished-text="没有更多了"
@load="onLoad"
>
<van-cell
v-for="item in list"
:key="item.id"
:title="item.name"
is-link
@click="handleView(item)"
>
<template #label>
<div class="item-info">
<span>{{ item.description }}</span>
</div>
</template>
</van-cell>
</van-list>
</van-pull-refresh>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { useRouter } from 'vue-router'
import { showToast } from 'vant'
import { {entityName}Api, type {EntityName} } from '@/api/{module}'
const router = useRouter()
const title = ref('{Page Title}')
const list = ref<{EntityName}[]>([])
const loading = ref(false)
const finished = ref(false)
const refreshing = ref(false)
const page = ref(1)
const pageSize = 20
const onClickLeft = () => {
router.back()
}
const onLoad = async () => {
try {
const res = await {entityName}Api.getList()
if (refreshing.value) {
list.value = []
refreshing.value = false
}
list.value.push(...(res.data || []))
loading.value = false
if (list.value.length >= (res.total || 0)) {
finished.value = true
}
} catch (error) {
showToast('加载失败')
loading.value = false
}
}
const onRefresh = () => {
finished.value = false
loading.value = true
page.value = 1
onLoad()
}
const handleAdd = () => {
router.push('/{module}/{page-name}/add')
}
const handleView = (item: {EntityName}) => {
router.push(`/{module}/{page-name}/${item.id}`)
}
</script>
<style scoped lang="less">
.page {
min-height: 100vh;
background-color: #f7f8fa;
}
.item-info {
margin-top: 4px;
font-size: 12px;
color: #969799;
}
</style>
<template>
<div class="page">
<van-nav-bar
:title="isEdit ? '编辑' : '新增'"
left-arrow
@click-left="onClickLeft"
/>
<van-form @submit="onSubmit">
<van-cell-group inset>
<van-field
v-model="formData.name"
name="name"
label="名称"
placeholder="请输入名称"
:rules="[{ required: true, message: '请输入名称' }]"
/>
<van-field
v-model="formData.description"
name="description"
label="描述"
type="textarea"
placeholder="请输入描述"
rows="3"
/>
</van-cell-group>
<div class="submit-btn">
<van-button
round
block
type="primary"
native-type="submit"
:loading="submitting"
>
提交
</van-button>
</div>
</van-form>
</div>
</template>
<script setup lang="ts">
import { ref, reactive, onMounted } from 'vue'
import { useRouter, useRoute } from 'vue-router'
import { showToast } from 'vant'
import { {entityName}Api } from '@/api/{module}'
const router = useRouter()
const route = useRoute()
const id = ref(route.params.id as string)
const isEdit = ref(!!id.value)
const submitting = ref(false)
const formData = reactive({
name: '',
description: ''
})
const onClickLeft = () => {
router.back()
}
const loadData = async () => {
if (!id.value) return
try {
const res = await {entityName}Api.getById(Number(id.value))
Object.assign(formData, res.data)
} catch (error) {
showToast('加载失败')
}
}
const onSubmit = async () => {
submitting.value = true
try {
if (isEdit.value) {
await {entityName}Api.update(Number(id.value), formData)
showToast('更新成功')
} else {
await {entityName}Api.create(formData)
showToast('创建成功')
}
router.back()
} catch (error) {
showToast('操作失败')
} finally {
submitting.value = false
}
}
onMounted(() => {
loadData()
})
</script>
<style scoped lang="less">
.page {
min-height: 100vh;
background-color: #f7f8fa;
}
.submit-btn {
margin: 16px;
}
</style>
<template>
<div class="page">
<van-nav-bar
title="详情"
left-arrow
@click-left="onClickLeft"
>
<template #right>
<van-icon name="edit" @click="handleEdit" />
</template>
</van-nav-bar>
<van-loading v-if="loading" class="loading" />
<div v-else class="content">
<van-cell-group inset>
<van-cell title="名称" :value="detail.name" />
<van-cell title="描述" :value="detail.description" />
<van-cell title="创建时间" :value="detail.createdAt" />
</van-cell-group>
<div class="actions">
<van-button
round
block
type="danger"
@click="handleDelete"
>
删除
</van-button>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { useRouter, useRoute } from 'vue-router'
import { showToast, showConfirmDialog } from 'vant'
import { {entityName}Api, type {EntityName} } from '@/api/{module}'
const router = useRouter()
const route = useRoute()
const id = ref(route.params.id as string)
const loading = ref(true)
const detail = ref<{EntityName}>({} as {EntityName})
const onClickLeft = () => {
router.back()
}
const loadData = async () => {
loading.value = true
try {
const res = await {entityName}Api.getById(Number(id.value))
detail.value = res.data
} catch (error) {
showToast('加载失败')
} finally {
loading.value = false
}
}
const handleEdit = () => {
router.push(`/{module}/{page-name}/${id.value}/edit`)
}
const handleDelete = async () => {
try {
await showConfirmDialog({
title: '确认删除',
message: '确定要删除这条记录吗?'
})
await {entityName}Api.delete(Number(id.value))
showToast('删除成功')
router.back()
} catch (error) {
if (error !== 'cancel') {
showToast('删除失败')
}
}
}
onMounted(() => {
loadData()
})
</script>
<style scoped lang="less">
.page {
min-height: 100vh;
background-color: #f7f8fa;
}
.loading {
display: flex;
justify-content: center;
align-items: center;
height: 200px;
}
.content {
padding-bottom: 20px;
}
.actions {
margin: 16px;
}
</style>
Location: frontend.h5/src/router/index.ts
{
path: '/{module}/{page-name}',
name: '{PageName}',
component: () => import('@/views/{module}/{PageName}.vue'),
meta: {
title: '{Page Title}',
requiresAuth: true
}
},
{
path: '/{module}/{page-name}/add',
name: '{PageName}Add',
component: () => import('@/views/{module}/{PageName}Form.vue'),
meta: {
title: '新增{Entity}',
requiresAuth: true
}
},
{
path: '/{module}/{page-name}/:id',
name: '{PageName}Detail',
component: () => import('@/views/{module}/{PageName}Detail.vue'),
meta: {
title: '{Entity}详情',
requiresAuth: true
}
},
{
path: '/{module}/{page-name}/:id/edit',
name: '{PageName}Edit',
component: () => import('@/views/{module}/{PageName}Form.vue'),
meta: {
title: '编辑{Entity}',
requiresAuth: true
}
}
van-nav-bar - Navigation bar with title and actionsvan-tabbar - Bottom tab barvan-sidebar - Side navigationvan-list - Infinite scroll listvan-cell - Cell item for listsvan-card - Product cardvan-tag - Tag labelvan-form - Form containervan-field - Input fieldvan-picker - Picker selectorvan-datetime-picker - Date/time pickervan-uploader - File uploadervan-switch - Toggle switchvan-checkbox - Checkboxvan-radio - Radio buttonvan-loading - Loading indicatorvan-toast - Toast message (use showToast())van-dialog - Dialog (use showDialog(), showConfirmDialog())van-action-sheet - Action sheetvan-pull-refresh - Pull to refreshvan-button - Buttonvan-icon - Iconvan-image - Image with lazy loadvan-popup - Popup layervan-search - Search barshowToast() for error messagesvan-list for long listsshowConfirmDialog() for destructive actions#f7f8fa for pagesinset prop for cell groups to add marginsround prop for buttons