For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (
- [ ]) syntax for tracking.
Goal: 在停车管理下新增"进出场操作"页面,支持手动输入车牌/RFID 完成车辆进场和出场操作
Architecture: 前端新增 Vue 3 组件 entryExit.vue,Tab 切换进场/出场模式,复用已有 vehicle.js API。后端仅需在 seed.go 新增菜单记录和权限映射。
Tech Stack: Vue 3 + Element Plus + Pinia, Go 1.25 + GIN
预估文件变更: 2 files (1 new + 1 modified)
| 文件 | 操作 | 职责 |
|---|---|---|
frontend/src/view/parking/entryExit.vue |
新建 | 进出场操作页面:输入查询、车辆详情、进场/出场操作 |
internal/initialize/seed.go |
修改 | 新增菜单记录(entryExit, ID=31) + authority_menu 映射 |
Files:
Modify: internal/initialize/seed.go
[ ] Step 1: 新增菜单记录
在 pricing 菜单之后插入(第 122 行之后):
{MenuLevel: 0, ParentId: 20, Path: "entryExit", Name: "entryExit", Hidden: false, Component: "view/parking/entryExit.vue", Sort: 5, Meta: dao.Meta{Title: "进出场操作", Icon: "switch"}},
在第 173 行之后新增:
// 进出场操作 (31) — admin + operator
{MenuId: "31", AuthorityId: "618"}, {MenuId: "31", AuthorityId: "888"}, {MenuId: "31", AuthorityId: "9527"},
cd lc_garage && go build -o build/bin/smart-parking.exe .
git add internal/initialize/seed.go
git commit -m "feat: 新增进出场操作菜单和权限——MenuId=31, ParentId=20"
Files:
Create: frontend/src/view/parking/entryExit.vue
[ ] Step 1: 完整文件内容(template + script + style)
<template>
<div class="entry-exit-page">
<!-- Tab 切换进场/出场 -->
<el-tabs v-model="activeTab" @tab-change="handleTabChange">
<el-tab-pane label="车辆进场" name="entry" />
<el-tab-pane label="车辆出场" name="exit" />
</el-tabs>
<!-- 输入区:车牌号 + RFID -->
<el-card class="query-card">
<el-form :model="queryForm" inline>
<el-form-item label="车牌号">
<el-input
v-model="queryForm.plate_number"
placeholder="请输入车牌号"
clearable
style="width: 200px"
@keyup.enter="queryVehicle"
/>
</el-form-item>
<el-form-item label="RFID标签">
<el-input
v-model="queryForm.rfid_tag"
placeholder="请输入RFID标签"
clearable
style="width: 240px"
@keyup.enter="queryVehicle"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="queryVehicle">查询</el-button>
<el-button icon="Refresh" @click="resetForm">重置</el-button>
</el-form-item>
</el-form>
</el-card>
<!-- 车辆详情卡片(查询后显示) -->
<el-card v-if="vehicleLoaded" class="detail-card">
<template #header>
<span class="card-title">{{ activeTab === 'entry' ? '车辆信息' : '出场信息' }}</span>
</template>
<el-descriptions :column="3" border>
<el-descriptions-item label="车牌号">
{{ vehicleInfo.plate_number || '-' }}
</el-descriptions-item>
<el-descriptions-item label="RFID标签">
{{ vehicleInfo.rfid_tag || '-' }}
</el-descriptions-item>
<el-descriptions-item label="车辆类型">
<el-tag v-if="vehicleInfo.VehicleType" :type="vehicleInfo.VehicleType.ID === 1 ? 'warning' : 'success'">
{{ vehicleInfo.VehicleType.name }}
</el-tag>
<el-tag v-else type="warning">临时车</el-tag>
</el-descriptions-item>
<el-descriptions-item v-if="vehicleInfo.Owner" label="车主">
{{ vehicleInfo.Owner.owner_name }}
</el-descriptions-item>
<el-descriptions-item v-if="vehicleInfo.Owner && vehicleInfo.Owner.is_vip" label="VIP">
<el-tag type="danger">是</el-tag>
</el-descriptions-item>
</el-descriptions>
<!-- 进场特有操作区 -->
<div v-if="activeTab === 'entry'" class="action-area">
<el-divider />
<el-form :model="entryForm" inline>
<el-form-item label="选择停车场" required>
<el-select
v-model="entryForm.parking_lot_id"
placeholder="请选择停车场"
style="width: 260px"
>
<el-option
v-for="lot in parkingLotList"
:key="lot.ID"
:label="lot.lot_name"
:value="lot.ID"
/>
</el-select>
</el-form-item>
<el-form-item>
<el-button
type="success"
icon="Check"
:loading="submitting"
@click="confirmEntry"
>
确认进场
</el-button>
</el-form-item>
</el-form>
</div>
<!-- 出场特有操作区 -->
<div v-if="activeTab === 'exit'" class="action-area">
<el-divider />
<p class="exit-tip">确认后系统将自动计算停车费用</p>
<el-button
type="danger"
icon="Check"
:loading="submitting"
@click="confirmExit"
>
确认出场并收费
</el-button>
</div>
</el-card>
<!-- 查询结果:未找到车辆 -->
<el-result
v-if="notFound"
icon="warning"
title="未找到车辆信息"
:sub-title="activeTab === 'entry' ? '将按临时车处理,请选择停车场后确认进场' : '该车辆未登记,请核实车牌号'"
>
<template v-if="activeTab === 'entry'" #extra>
<el-form :model="entryForm" inline>
<el-form-item label="选择停车场" required>
<el-select
v-model="entryForm.parking_lot_id"
placeholder="请选择停车场"
style="width: 260px"
>
<el-option
v-for="lot in parkingLotList"
:key="lot.ID"
:label="lot.lot_name"
:value="lot.ID"
/>
</el-select>
</el-form-item>
<el-form-item>
<el-button
type="success"
:loading="submitting"
@click="confirmEntry"
>
确认进场(临时车)
</el-button>
</el-form-item>
</el-form>
</template>
</el-result>
</div>
</template>
<script setup>
import { ref, reactive, onMounted } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { obtainVehicleLicense, vehicleEntry, vehicleAppear } from '@/api/vehicle'
import { obtainLotList } from '@/api/pullOver'
// ---- 状态 ----
const activeTab = ref('entry')
const queryForm = reactive({ plate_number: '', rfid_tag: '' })
const vehicleInfo = ref(null)
const vehicleLoaded = ref(false)
const notFound = ref(false)
const submitting = ref(false)
const entryForm = reactive({ parking_lot_id: null })
const parkingLotList = ref([])
// ---- 初始化:加载停车场列表 ----
onMounted(async () => {
try {
const res = await obtainLotList({ page: 1, pageSize: 100 })
parkingLotList.value = res.data?.list || []
} catch (e) {
console.error('加载停车场列表失败', e)
}
})
// ---- 查询车辆 ----
async function queryVehicle() {
const plate = queryForm.plate_number.trim()
const rfid = queryForm.rfid_tag.trim()
if (!plate && !rfid) {
ElMessage.warning('请至少输入车牌号或RFID标签')
return
}
vehicleInfo.value = null
vehicleLoaded.value = false
notFound.value = false
try {
const res = await obtainVehicleLicense(plate || rfid)
if (res.code === 0 && res.data) {
vehicleInfo.value = res.data
vehicleLoaded.value = true
} else {
notFound.value = true
}
} catch (e) {
notFound.value = true
}
}
// ---- 确认进场 ----
async function confirmEntry() {
if (!entryForm.parking_lot_id) {
ElMessage.warning('请选择停车场')
return
}
if (submitting.value) return
submitting.value = true
try {
const res = await vehicleEntry({
plate_number: queryForm.plate_number.trim(),
rfid_tag: queryForm.rfid_tag.trim(),
parking_lot_id: entryForm.parking_lot_id
})
if (res.code === 0) {
const plate = res.data?.data?.plate_number || queryForm.plate_number.trim()
ElMessage.success(`入场成功!${plate} 已进入停车场`)
resetForm()
} else {
ElMessage.error(res.msg || '入场失败')
}
} catch (e) {
ElMessage.error(e.message || '入场失败')
} finally {
submitting.value = false
}
}
// ---- 确认出场 ----
async function confirmExit() {
const plate = vehicleInfo.value?.plate_number || queryForm.plate_number.trim()
await ElMessageBox.confirm(
`确认车辆 ${plate} 出场?`,
'确认出场',
{ confirmButtonText: '确认出场', cancelButtonText: '取消', type: 'warning' }
)
if (submitting.value) return
submitting.value = true
try {
const res = await vehicleAppear({
plate_number: queryForm.plate_number.trim(),
rfid_tag: queryForm.rfid_tag.trim()
})
if (res.code === 0) {
const data = res.data?.data || res.data || {}
const fee = data.fee || 0
const stayTime = data.stay_time || 0
const entryTime = data.entry_time ? new Date(data.entry_time * 1000).toLocaleString('zh-CN') : '-'
ElMessage.success(
`出场成功!进场: ${entryTime} | 停留: ${formatDuration(stayTime)} | 费用: ¥${fee.toFixed(2)}`
)
resetForm()
} else {
ElMessage.error(res.msg || '出场失败')
}
} catch (e) {
ElMessage.error(e.message || '出场失败')
} finally {
submitting.value = false
}
}
// ---- 工具 ----
function handleTabChange() {
resetForm()
}
function resetForm() {
queryForm.plate_number = ''
queryForm.rfid_tag = ''
vehicleInfo.value = null
vehicleLoaded.value = false
notFound.value = false
entryForm.parking_lot_id = null
}
function formatDuration(minutes) {
if (!minutes && minutes !== 0) return '-'
const h = Math.floor(minutes / 60)
const m = minutes % 60
return h > 0 ? `${h}小时${m}分钟` : `${m}分钟`
}
</script>
<style scoped>
.entry-exit-page {
padding: 0;
}
.query-card {
margin-bottom: 16px;
}
.detail-card {
margin-bottom: 16px;
}
.card-title {
font-weight: bold;
font-size: 16px;
}
.action-area {
margin-top: 8px;
}
.exit-tip {
color: #909399;
margin-bottom: 12px;
}
</style>
git add frontend/src/view/parking/entryExit.vue
git commit -m "feat: 新增手动进出场操作页面——支持车牌/RFID查询进出场"
cd lc_garage && wails dev
| 场景 | 预期结果 |
|---|---|
| 空输入点查询 | "请至少输入车牌号或RFID标签" |
| 未选停车场点确认进场 | "请选择停车场" |
| 已出场车辆再次出场 | "车辆未入场或已出场" |
| 不在场车辆点出场 | "车辆未入场或已出场" |
git add -A && git commit -m "verify: 手动进出场页面端到端验证通过"