12 KiB
运输计划「从车辆库选择」Implementation Plan
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: 在运输计划新增/编辑弹窗车牌号旁提供「从车辆库选择」,弹窗分三方/自有 Tab,支持搜索、选择、新增、编辑,并回填计划车辆。
Architecture: 对齐 PlTransportAddressSelectDialog:计划表单旁按钮打开 PlVehicleLibrarySelectDialog(Tab + 列表);嵌套 PlVehicleFormDialog 对照 views/pl/vehicle / views/pl/self-vehicle 完整表单。选择或新增成功后写 vehicleId 并走现有 handleVehicleChange。首期不改造两管理页。
Tech Stack: Vue 3 + Element Plus + 现有 vehicleApi / usePlRemoteVehicleOptions / PlVehicleCertPartTabs
Spec: docs/superpowers/specs/2026-08-06-pl-plan-vehicle-library-select-design.md
文件结构
| 操作 | 文件 | 职责 |
|---|---|---|
| Create | web-admin/src/components/business/pl-vehicle-form-dialog/index.vue |
新增/编辑车辆完整表单(vehicleType: 'carrier' | 'self') |
| Create | web-admin/src/components/business/pl-vehicle-library-select-dialog/index.vue |
选车大弹窗:Tab + 搜索 + 列表 + 挂载表单弹窗 |
| Modify | web-admin/src/components/business/pl-plan-form-dialog/index.vue |
车牌旁按钮 + 选车回填 |
参考(只读对照,勿在首期强行抽离改造):
web-admin/src/components/business/pl-transport-address-select-dialog/index.vueweb-admin/src/components/business/pl-address-with-library/index.vueweb-admin/src/views/pl/vehicle/index.vue(三方表单弹窗)web-admin/src/views/pl/self-vehicle/index.vue(自有表单弹窗)
Task 1: PlVehicleFormDialog — 完整新增/编辑表单
Files:
-
Create:
web-admin/src/components/business/pl-vehicle-form-dialog/index.vue -
Step 1: 建立组件壳与 API
Props / 事件约定:
const props = defineProps<{
modelValue: boolean
/** carrier=三方,self=自有;锁定类型,不可切换 */
vehicleType: 'carrier' | 'self'
/** 有值则为编辑,无值则为新增 */
vehicleId?: string | null
}>()
const emit = defineEmits<{
'update:modelValue': [value: boolean]
/** 新增成功:返回详情;编辑成功:返回详情(父级刷新列表用) */
saved: [payload: { mode: 'create' | 'edit'; vehicle: VehicleDetail }]
}>()
弹窗:
-
width="1360px",class="dialog-form-layout",append-to-body,destroy-on-close -
modal-class="order-module-dialog-mask",style="backdrop-filter: none" -
标题:
- create + carrier →
新增车辆 - create + self →
新增自有车辆 - edit + carrier →
编辑车辆 - edit + self →
编辑自有车辆
- create + carrier →
-
Step 2: 移植表单 UI 与状态
从 views/pl/vehicle/index.vue / self-vehicle/index.vue 的 el-dialog 表单区对照实现:
- 基本信息
form-grid-3:车牌号、挂车号、载重、承运商、车辆状态、备注、车辆照片(AttachmentUploadField,upload-folder="pl/vehicle-photo") - 关联人员子表(添加人员 / 角色 driver|escort / 删除)
PlVehicleCertPartTabs:vehicleType === 'self':enforce-self-vehicle-required(与自有页一致)vehicleType === 'carrier'::enforce-self-vehicle-required="false"且:enforce-carrier-vehicle-required="false"(与三方页一致)
人员远程选项:
usePlRemotePersonnelOptions({
listParams: () => ({
status: '1',
personnelAffiliation: props.vehicleType === 'carrier' ? 'carrier' : 'self'
})
})
证书槽初始化:
- carrier:
ensureVehicleCertSlots(..., trailerNo, 'carrier') - self:
ensureSelfVehicleCertSlots(..., trailerNo)
校验:复用管理页 validateSubTables 逻辑(人员必选、角色、validatePersonnelTypeRoleCompat、validatePersonnelAffiliationVehicleCompat)。
- Step 3: open / submit
@open 或 watch(modelValue):
- 新增:重置表单;
vehicleStatus='available';证书空槽;加载人员选项 - 编辑:
vehicleApi.get(vehicleId)回填;编辑自有时若detail.vehicleType !== 'self'则警告并关闭;ensureSelectedCarrierOption;nextTick后再填证书并refreshRequiredSlots
提交 payload 与管理页一致(含 personnelList / certList);vehicleType 固定为 prop。
if (isEdit) {
await vehicleApi.update(formData.id, payload)
const res = await vehicleApi.get(formData.id)
const detail = pickBxData<VehicleDetail>(res)!
emit('saved', { mode: 'edit', vehicle: detail })
} else {
const res = await vehicleApi.create(payload)
let detail = pickBxData<VehicleDetail>(res)
if (!detail?.id) {
// 若 create 未回详情,不应发生;兜底可再 get —— 优先用返回体
ElMessage.error('新增成功但未返回车辆信息')
return
}
emit('saved', { mode: 'create', vehicle: detail })
}
成功后关闭弹窗;上传中拦截与管理页相同文案。
- Step 4: 手工冒烟(可选先挂临时入口)
在浏览器临时打开组件或等 Task 2 挂载后验证:三方/自有各新增一条、各编辑一条,证书与人员校验行为与管理页一致。
- Step 5: Commit(若用户要求提交时再执行)
git add web-admin/src/components/business/pl-vehicle-form-dialog/index.vue
git commit -m "$(cat <<'EOF'
feat(web-admin): add PlVehicleFormDialog for carrier/self vehicle CRUD
EOF
)"
Task 2: PlVehicleLibrarySelectDialog — Tab 选车弹窗
Files:
-
Create:
web-admin/src/components/business/pl-vehicle-library-select-dialog/index.vue -
Step 1: 弹窗外壳(对齐地址库选择)
<el-dialog
v-model="innerVisible"
title="从车辆库选择"
width="1360px"
align-center
destroy-on-close
append-to-body
class="dialog-form-layout pl-vehicle-library-select-dialog"
modal-class="order-module-dialog-mask"
:close-on-click-modal="false"
style="backdrop-filter: none"
@open="handleOpen"
>
结构:
el-tabs:carrier/self(文案:三方车辆 / 自有车辆)- 工具栏:
el-input搜索(placeholder:搜索车牌号,回车/清空检索)+type="primary"「新增车辆」 ListTable+ListPagination(dialog-list-body-only风格参考地址库弹窗)- 嵌套
<PlVehicleFormDialog v-model="formVisible" :vehicle-type="activeTab" :vehicle-id="editingId" @saved="handleFormSaved" />
- Step 2: 列表与选择
async function fetchList() {
loading.value = true
try {
const res = await vehicleApi.list({
vehicleType: activeTab.value, // 'carrier' | 'self'
q: keyword.value.trim() || undefined,
page: pagination.page,
pageSize: pagination.pageSize
})
const { list, total } = pickBxPageResult<Vehicle>(res)
rows.value = list
pagination.total = total
} finally {
loading.value = false
}
}
列:序号、车牌号(el-link → handleConfirm)、挂车号、车辆状态 Tag、载重、承运商(v-if="activeTab === 'carrier'")、关联人员摘要、操作(编辑 / 选择)。
function handleConfirm(row: Vehicle) {
emit('select', row)
innerVisible.value = false
}
function handleFormSaved(payload: { mode: 'create' | 'edit'; vehicle: VehicleDetail }) {
formVisible.value = false
editingId.value = null
if (payload.mode === 'create') {
handleConfirm(payload.vehicle)
return
}
fetchList()
}
样式::deep(.el-dialog__body) { min-height: 560px; };表格 :max-height="560"。
- Step 3: Tab / 打开重置
handleOpen:keyword 清空、page=1、activeTab 默认 carrier、关表单、fetchList。
Tab @tab-change:page=1 再 fetchList。
- Step 4: Commit(若用户要求)
git add web-admin/src/components/business/pl-vehicle-library-select-dialog/index.vue
git commit -m "$(cat <<'EOF'
feat(web-admin): add vehicle library select dialog with carrier/self tabs
EOF
)"
Task 3: 接入运输计划表单弹窗
Files:
-
Modify:
web-admin/src/components/business/pl-plan-form-dialog/index.vue -
Step 1: 车牌号旁按钮布局
将原「车牌号」单项改为与地址库相同的行布局:
<el-form-item label="车牌号">
<div class="pl-plan-vehicle-with-library">
<el-select
v-model="formData.vehicleId"
class="pl-plan-vehicle-with-library__select"
placeholder="请选择车牌号"
filterable
remote
clearable
:remote-method="searchVehicles"
:loading="vehicleSelectLoading || vehicleLoading"
@focus="onVehicleSelectFocus"
@change="handleVehicleChange"
>
<el-option
v-for="item in vehicleList"
:key="item.id"
:label="item.licensePlate"
:value="item.id"
/>
</el-select>
<el-button
class="pl-plan-vehicle-with-library__btn btn-ghost"
@click="vehicleLibraryVisible = true"
>
<el-icon><FolderOpened /></el-icon>从车辆库选择
</el-button>
</div>
</el-form-item>
<PlVehicleLibrarySelectDialog
v-model="vehicleLibraryVisible"
@select="handleVehicleLibrarySelect"
/>
样式(scoped,对照 pl-address-with-library):
.pl-plan-vehicle-with-library {
display: flex;
align-items: center;
gap: 8px;
width: 100%;
}
.pl-plan-vehicle-with-library__select {
flex: 1 1 auto;
min-width: 0;
}
.pl-plan-vehicle-with-library__btn {
flex: 0 0 auto;
white-space: nowrap;
}
- Step 2: 回填处理
const vehicleLibraryVisible = ref(false)
async function handleVehicleLibrarySelect(vehicle: Vehicle) {
if (!vehicle?.id) return
await ensureSelectedVehicleOption(vehicle.id)
formData.vehicleId = vehicle.id
await handleVehicleChange(vehicle.id)
if (vehicle.carrierId) {
await ensureSelectedCarrierOption(vehicle.carrierId)
}
}
导入:FolderOpened、PlVehicleLibrarySelectDialog、类型 Vehicle(若尚未导入)。
- Step 3: Commit(若用户要求)
git add web-admin/src/components/business/pl-plan-form-dialog/index.vue
git commit -m "$(cat <<'EOF'
feat(web-admin): wire vehicle library select into plan form dialog
EOF
)"
Task 4: 验收与图谱
- Step 1: 手工验收清单(代码已就绪,需在本地浏览器走查)
在 /#/pl/plan-module/plan 打开新增/编辑运输计划:
- 车牌旁可见「从车辆库选择」,布局与「从地址库选择」同类
- 弹窗 1360 宽、偏高;Tab 切换三方/自有列表正确过滤
- 搜索车牌有效;选择 / 链接 / 双击可回填车牌,摘要条与承运商更新
- 三方 Tab「新增车辆」:完整表单可保存,成功后自动选中并关闭选车弹窗
- 自有 Tab「新增自有车辆」:证书强制与自有管理页一致;成功后回填
- 「编辑」保存后列表刷新,计划已选车不被覆盖;选车弹窗仍打开
- 遮罩无 blur;按钮均有图标
- Step 2: 更新前端图谱
./scripts/update-graph.sh frontend
或在仓库根目录:graphify update .(仅 AST)
- Step 3: 将设计文档状态改为已实现(实现完成后)
docs/superpowers/specs/2026-08-06-pl-plan-vehicle-library-select-design.md 顶部 状态:设计中 → 已实现
Spec 覆盖自检
| Spec 要求 | Task |
|---|---|
| 车牌旁按钮「从车辆库选择」 | Task 3 |
| Tab 三方/自有 + 搜索分页 | Task 2 |
| 选择/链接/双击回填 + handleVehicleChange | Task 2 + 3 |
| 新增完整表单(对照管理页) | Task 1 + 2 |
| 编辑 + 刷新列表不自动选中 | Task 1 + 2 |
| 新增成功自动选中关弹窗 | Task 2 |
| 大尺寸 1360 / 列表偏高 | Task 1 + 2 |
| 不改两管理页 | 全任务遵守 |
| 图谱更新 | Task 4 |