组件治理
1. 治理目标
src/components 当前已经形成较完整的公共组件资产,但也出现了以下典型治理问题:
- 同类能力并存,AI 和人工都不容易第一时间选到“标准件”。
- 部分旧组件已进入兼容/废弃态,但仍与新组件共存,容易继续被误用。
- 重点组件缺少 README、示例或迁移说明,导致“看得到目录、用不对组件”。
本治理文档的目标是:
- 识别重复实现、废弃组件和高优先级公共资产。
- 划分“核心 UI 组件”和“特殊场景专用组件”。
- 明确后续文档补齐与规则拦截方向。
2. 盘点结论
2.1 核心 UI 组件
以下组件应视为优先复用的“平台标准件”:
| 分类 | 标准组件 | 说明 |
|---|---|---|
| 搜索 | BaseQuery | 复杂列表页搜索区标准实现,支持高级条件、分组、业务组件注册 |
| 表格 | CmcTable | 标准表格场景首选 |
| 卡片表格 | CmcCardTable | 高密度、多行信息、卡片式列表场景首选 |
| 数据网格 | CmcDataGrid | 超复杂交互表格、虚拟滚动、列配置、导出等重场景 |
| 弹窗 | CmcDialog | 标准弹窗首选 |
| 选择器 | CmcSelect | 统一选择器底座,逐步承接多种业务选择能力 |
| 图标 | CmcIcon | 新标准图标组件 |
| 标签 | CmcTag、CmcFlexibleTag | 标准状态标签与自适应标签 |
| 分页 | Pagination | 统一分页组件 |
2.2 特殊场景专用组件
以下组件不是“到处可用”的通用控件,而是有明确业务或交互边界:
| 分类 | 组件 | 适用场景 |
|---|---|---|
| 船期/港口检索 | VesselVoyageKeywordSelect、PortSelect、TradeLaneSelect | 船名航次、港口、航线类业务检索 |
| 业务查询组合 | ShipScheduleSearch、schedule-search/* | 船期搜索专用场景 |
| 文件/解析 | ExcelParse、CmcFileUpload、CmcGroupedUpload、UploadWithPreview | 导入导出、附件上传、文件预览 |
| 风险/确认 | CmcConfirm、OperationLogDialog、BookingOptionsDialog | 确认、日志、业务决策类弹窗 |
| 特殊业务字段 | DangerousGoodsCasGroup、AgreementNoDialog、ContainerBaseDataSelect | 特定业务域输入与选择 |
| 性能/骨架 | SkeletonLoader、SimpleLoader、ProgressiveLoader、GlobalLoading、CmcLoading | 加载阶段、全局遮罩、骨架屏等不同层次场景 |
2.3 已明确废弃 / 兼容态组件
以下组件已经在代码中明确出现 @deprecated 或废弃警告,应禁止新页面继续使用:
| 组件 | 当前状态 | 建议替代 |
|---|---|---|
CmcContentDialog | 已废弃 | CmcDialog |
SvgIcon | 兼容层,建议停用 | CmcIcon |
BaseDataSelect | 已废弃 | CmcSelect |
Pagination.condition 传参 | 已废弃用法 | 独立的 pageNo/pageSize/total |
2.4 疑似重复实现 / 需要进一步归并的组件族
以下不是“立刻删除”,而是需要纳入后续治理的重点分组:
| 组件族 | 现状 | 治理建议 |
|---|---|---|
| 选择器族 | CountrySelect、CurrencySelect、DictSelect、PortSelect、TradeLaneSelect 与 CmcSelect 并存 | 逐步形成“CmcSelect 底座 + 业务壳组件”的双层结构 |
| 表格族 | CmcTable、CmcCardTable、CmcDataGrid 能力边界容易混淆 | 补文档,明确场景边界,禁止误选 |
| 加载族 | CmcLoading、GlobalLoading、SkeletonLoader、SimpleLoader、ProgressiveLoader | 明确“全局遮罩 / 区域加载 / 骨架屏 / 渐进加载”分层 |
| 容器尺寸选择族 | CntrSizeTypeSelect、ContainerSelectSizeType、ContainerBaseDataSelect 命名与职责有重叠感 | 统一命名与使用建议,避免新增第 4 套实现 |
| 搜索增强族 | BaseQuery、QueryExpand、AllConditionChange | 确立 BaseQuery 为主入口,其他作为局部增强能力 |
3. 重点问题说明
3.1 为什么会“重复造轮子”
根因不是单一文档缺失,而是以下问题叠加:
- 规则层只强调“优先复用”,但缺少“场景强映射”。
- 重点组件没有形成 README + 示例 + 测试 + 迁移说明的闭环。
- 兼容组件仍然可用,导致新代码继续沿用旧实现。
- 组件命名存在历史包袱,AI 和人工都可能凭直觉误选。
3.2 BaseQuery 的治理结论
BaseQuery 已经是成熟的复杂搜索组件,不应再让复杂列表页自行拼装原生 el-form。
建议形成硬规则:
- 当页面顶部存在“查询 + 重置 + 展开/收起 + 多字段联动”时,优先使用
BaseQuery。 - 当只是 1 到 2 个极简单字段时,可保留原生
el-form。 - 在
.mdc规则中增加“复杂搜索区 -> BaseQuery”的强映射。
4. 组件分层建议
4.1 推荐分层
| 层级 | 说明 | 代表组件 |
|---|---|---|
| 基础 UI 层 | 通用视觉控件 | CmcIcon、CmcTag、CmcDivider、CmcButtonGroup |
| 容器交互层 | 表格、弹窗、分页、查询等结构性组件 | BaseQuery、CmcTable、CmcCardTable、CmcDialog、Pagination |
| 业务选择器层 | 与港口、航次、字典、基础资料绑定的组件 | PortSelect、TradeLaneSelect、VesselVoyageKeywordSelect、CmcSelect |
| 特殊业务层 | 针对某一业务域或复杂流程的组件 | DangerousGoodsCasGroup、AgreementNoDialog、BookingOptionsDialog |
| 兼容迁移层 | 仅为历史代码保留,不得新增使用 | CmcContentDialog、SvgIcon、BaseDataSelect |
4.2 新增组件准入原则
新增组件前必须回答 4 个问题:
src/components中是否已有 80% 可复用实现?- 是通用 UI 还是业务专用?
- 是否只是旧组件换名字再实现一次?
- 是否需要 README、单测、迁移说明一起补齐?
只要有一个问题答不清,就不应直接新增组件。
5. 文档补齐优先级
5.1 已补或已有 README
CmcDialogCmcSelectTextEllipsisExcelParseOptimizedImage
5.2 当前最优先补齐
BaseQueryCmcTableCmcCardTable
原因:
- 它们是复杂 B 端页面的主骨架。
- AI 是否能稳定复用,直接决定“会不会再重复造轮子”。
- 它们的场景边界如果不清晰,最容易被误用。
5.3 第二梯队建议
CmcDataGridPaginationVesselVoyageKeywordSelectPortSelect
6. 废弃组件影响面详细清单
盘点时间: 2026-04-27 | 数据来源: 全仓 grep 扫描
src/views/
6.1 CmcContentDialog → CmcDialog(🟠 中高风险)
涉及生产页面(7 个):
| 文件 | 所属域 |
|---|---|
export/booking/index.vue | 订舱管理 |
export/booking/components/SoListItem.vue | 订舱-S/O明细 |
export/manifest_manage/index.vue | 舱单管理 |
export/lading/management/LadingInfo.vue | 提单-提单详情 |
export/lading/components/UserName/src/UserNameSelect.vue | 提单-用户名选择 |
payment_settlement/pay-manage/index.vue | 结算-支付管理 |
payment_settlement/pay-manage/components/StatementDetails.vue | 结算-账单明细 |
迁移复杂度:中
Props 差异要点:
- CmcContentDialog 默认
:show-close="false":close-on-click-modal="false"draggable— 迁移到 CmcDialog 需显式设置 bodyHeight属性已废弃,改用 CSS- footer slot 结构一致
迁移模板:
<!-- ❌ 旧 -->
<CmcContentDialog v-model="visible" title="标题" :width="750">
<template #footer>...</template>
</CmcContentDialog>
<!-- ✅ 新 -->
<CmcDialog v-model="visible" title="标题" :width="750"
:show-close="false" :close-on-click-modal="false"
:close-on-press-escape="false" draggable destroy-on-close
>
<template #footer>...</template>
</CmcDialog>
已验证测试覆盖:CmcDialog 已有单测 (__tests__/CmcDialog.test.ts)。
6.2 SvgIcon → CmcIcon(🟡 低风险)
涉及生产页面(10 个):
| 文件 | 所属域 |
|---|---|
export/booking/ModifySchedulePort/components/SearchForm.vue | 订舱-改港 |
export/booking/ModifySchedule/SearchForm.vue | 订舱-改船期 |
export/booking/components/SearchSchedule/search-form/PointToPointSearchForm.vue | 订舱-船期搜索 |
export/manifest_manage/components/AddBoxDialog.vue | 舱单-加箱 |
export/lading/components/container/Edit.vue | 提单-箱信息编辑 |
export/container-manage/components/form-dialog/VgmInfoSection.vue | 箱管-VGM |
search_service/ship-schedules/components/PortTimeDetailsCard.vue | 船期查询 |
search_service/qick-search/surcharge-rate/index.vue | 快查-附加费 |
search_service/statistics/CostStatistics.vue | 统计-费用 |
search_service/statistics/CargoStatistics.vue | 统计-货量 |
迁移复杂度:极低
SvgIcon已是兼容代理层,内部转发到CmcIcon- 运行时行为完全一致
- 只需替换
import路径:~/components/SvgIcon→~/components/CmcIcon - 组件用法不变:
<SvgIcon name="xxx" />→<CmcIcon name="xxx" />
迁移模板:
<!-- ❌ 旧 -->
import SvgIcon from '~/components/SvgIcon'
<SvgIcon name="booking" :size="16" />
<!-- ✅ 新 -->
import CmcIcon from '~/components/CmcIcon'
<CmcIcon name="booking" :size="16" />
6.3 BaseDataSelect → CmcSelect(🔴 高风险 — 暂不执行迁移)
涉及生产页面(14 个):
| 文件 | 所属域 |
|---|---|
export/booking/components/BookingApprove.vue | 订舱-审批 |
export/booking/components/BookingQuery.vue | 订舱-查询 |
export/booking/components/SelectTemplate.vue | 订舱-模板 |
export/booking/components/SearchSchedule/search-form/PointToPointSearchForm.vue | 订舱-船期搜索 |
export/booking/components/SearchSchedule/search-form/VesselVoyageSearchForm.vue | 订舱-船期搜索 |
export/lading/components/SIBasicInfo.vue | 提单-SI基本信息 |
export/lading/components/cargo/index.vue | 提单-货物信息 |
export/lading/components/container/Edit.vue | 提单-箱信息编辑 |
export/lading/split/SplitBasicInfo.vue | 提单-分单 |
export/manifest_manage/components/OrderInfoHeader.vue | 舱单-订单头 |
export/manifest_manage/components/GoodInfoSingle.vue | 舱单-单票货 |
export/manifest_manage/components/GoodInfoMultiple.vue | 舱单-多票货 |
export/manifest_manage/components/AddBoxDialog.vue | 舱单-加箱 |
search_service/qick-search/surcharge-rate/index.vue | 快查-附加费 |
迁移复杂度:高(暂不执行)
Props 差异完整映射表:
| BaseDataSelect Props | CmcSelect Props |
|---|---|
category | category(一致) |
only-show | readonly |
label-shallow | variant="underline" |
clearable | clearable(一致) |
filterable | filterable(一致) |
set-default | ❌ 不支持,需手动处理 |
bind-label | ❌ 不支持,使用 @change 替代 |
emit-item | ❌ 不支持,使用 @change 获取完整 option |
save-to-selection | ❌ 不支持,需手动调用 selectionStore |
风险点:
setDefault/bindLabel/emitItem/saveToSelection等 Props 在 CmcSelect 中无对应- BaseDataSelect 通过
useDocBaseCodescomposable 加载数据 +selectionStore联动 - CmcSelect 的 baseData 数据源行为需逐页验证
⚠️ 处置策略:暂不迁移。仅通过规则拦截禁止新页面使用。待 CmcSelect 的 selectionStore 联动能力补齐后再启动迁移。
7. 生产环境分阶段治理计划
核心原则:先堵增量,再治存量;每次变更可独立回滚。
阶段 0:规则拦截(立即执行,零生产风险)
目标:阻止任何新的废弃组件进入代码库。
操作:
- 修改
.trae/rules/vue-component.mdc,加入以下硬规则:
# 组件使用强制映射(违反则阻断)
- 新弹窗 → 必须使用 CmcDialog(禁止 CmcContentDialog)
- 新图标 → 必须使用 CmcIcon(禁止 SvgIcon)
- 新基础数据选择 → 必须使用 CmcSelect data-source="baseData"(禁止 BaseDataSelect)
- 复杂搜索区 → 必须使用 BaseQuery(禁止手写 el-form 拼装)
- 标准表格 → 必须使用 CmcTable
- 卡片列表 → 必须使用 CmcCardTable
- Pagination.condition 传参 → 已废弃,必须使用独立的 pageNo/pageSize/total
- 在
src/components/BaseDataSelect/index.ts中添加@deprecatedJSDoc 注释的构建时警告(如果构建工具支持)。
回滚方式:恢复 .mdc 文件即可。
阶段 1:文档补齐(本周完成,零生产风险)
已完成 README:
CmcDialog✅CmcSelect✅CmcTable✅CmcCardTable✅BaseQuery✅TextEllipsis✅ExcelParse✅OptimizedImage✅
待补齐:
| 优先级 | 组件 | 需包含内容 |
|---|---|---|
| P0 | Pagination | 新旧 API 对比、废弃用法警告 |
| P1 | CmcDataGrid | 场景边界(与 CmcTable/CmcCardTable 的区别) |
| P1 | VesselVoyageKeywordSelect | Props、事件、远程搜索行为 |
| P1 | PortSelect | 与 CmcSelect preset="port" 的关系 |
| P2 | CmcContentDialog | MIGRATION.md(含迁移模板和常见坑) |
| P2 | BaseDataSelect | MIGRATION.md(含 Props 对照表和暂不迁移声明) |
| P2 | SvgIcon | MIGRATION.md(仅 import 替换说明) |
阶段 2:SvgIcon 批量迁移(第 1 批,低风险)
范围:~10 个页面,仅 import 路径替换。
步骤:
- 创建 feature 分支
refactor/migrate-svgicon-to-cmcicon - 逐文件替换:
import SvgIcon from '~/components/SvgIcon'→import CmcIcon from '~/components/CmcIcon' <SvgIcon→<CmcIcon(标签名替换)- 本地
npm run type-check && npm run lint:check - 视觉回归:在 dev 环境逐个检查使用了图标的页面
- 合并 → 部署 → 生产验证
回滚方式:每个 commit 为一个文件,可单独 revert。
验收标准:所有图标渲染与原效果一致,无控制台警告。
阶段 3:CmcContentDialog 逐页迁移(第 2 批,中风险)
范围:~7 个页面。
步骤(每个页面独立完成):
- 创建 page 级 feature 分支(如
refactor/migrate-dialog-booking) - 替换 CmcContentDialog → CmcDialog,按迁移模板设置 props
- 本地打开该页面,测试:弹窗打开/关闭、确认/取消按钮、footer 自定义内容、拖拽、点击遮罩不关闭
npm run type-check- 合并 → 部署 → 生产验证
- 验证通过后再迁移下一个页面
迁移检查清单(每页必验):
- 弹窗正常打开和关闭
- 确认/取消按钮行为正确
- footer 自定义内容正常渲染
- 点击遮罩层不关闭弹窗(保持旧行为)
- 弹窗可拖拽
- 关闭时内容销毁(destroy-on-close)
- 无 console 报错或警告
回滚方式:每个页面独立分支,出问题只回滚该页面。
阶段 4:BaseDataSelect 迁移(第 3 批,高风险 — 暂缓)
前置条件(任一不满足则不启动):
- CmcSelect 补齐
saveToSelection/setDefault/bindLabel/emitItem等价能力 - 或通过 composable 封装解决 selectionStore 联动问题
- 至少 2 个页面在 staging 环境完成完整回归测试
暂不启动的原因:
- 涉及 14 个生产页面,覆盖订舱/提单/舱单/快查四大核心域
- API 差异大,迁移不当会导致基础数据加载失败
selectionStore联动逻辑需要额外封装- 收益/风险比不足(旧组件功能正常,暂无 Bug)
阶段 5:废弃源码清理(全部迁移完成后)
触发条件:阶段 2、3、4 全部完成并通过生产验证。
操作:
- 删除
src/components/CmcContentDialog/、src/components/BaseDataSelect/、src/components/SvgIcon/源码目录 - 各目录保留
index.ts,改为仅重导出新组件(保持向后兼容) - 在
package.json中记录 breaking change(如果后续有大版本发布)
8. 回滚策略总览
| 阶段 | 风险 | 回滚粒度 | 回滚方式 |
|---|---|---|---|
| 阶段 0(规则拦截) | 无 | 文件级 | 恢复 .mdc 文件 |
| 阶段 1(文档补齐) | 无 | - | 无需回滚 |
| 阶段 2(SvgIcon 迁移) | 低 | commit 级 | git revert 单 commit |
| 阶段 3(CmcContentDialog 迁移) | 中 | 页面级 | 每个页面独立分支,出问题只 revert 该分支 |
| 阶段 4(BaseDataSelect 迁移) | 高 | 页面级 | 暂不执行 |
| 阶段 5(源码清理) | 低 | - | 仅在全部迁移完成且生产稳定后执行 |
9. 持续监控指标
| 指标 | 目标值 | 监控方式 |
|---|---|---|
| 新页面使用废弃组件 | 0 | CI 中 grep 检查 |
| 已迁移页面回归 Bug | 0 | 每个迁移页面必须手动验证 |
| 组件 README 覆盖率 | ≥ 80% | 人工定期盘点 |
| SvgIcon 存活文件数 | 趋于 0 | grep 命令 |
10. 当前结论
当前 src/components 不是“没有公共组件”,而是“公共组件很多,但标准入口和迁移边界还不够强”。
治理重点不是继续无序新增,而是:
- 立标准件
- 标废弃件
- 补重点文档
- 用规则把复杂场景强行映射到标准件
只有这样,AI 和人工才能在复杂页面中真正做到高复用,而不是继续重复实现。
执行优先级总结
| 优先级 | 动作 | 风险 | 状态 |
|---|---|---|---|
| 🔴 P0 立即 | 修改 .trae/rules/vue-component.mdc 加入强制映射 + 禁止废弃组件 | 零 | ⬜ 待执行 |
| 🟡 P1 本周 | 补齐 Pagination/CmcDataGrid 等 README + 3 个 MIGRATION.md | 零 | ⬜ 待执行 |
| 🟢 P2 本周 | SvgIcon → CmcIcon 批量 import 替换(~10 页) | 低 | ⬜ 待执行 |
| 🟠 P3 本月 | CmcContentDialog → CmcDialog 逐页迁移(~7 页) | 中 | ⬜ 待执行 |
| 🔴 P4 暂缓 | BaseDataSelect → CmcSelect 迁移(~14 页) | 高 | ⛔ 暂缓 |
一句话总结:先通过规则把"增量"管住,再用最安全的方式逐步消化"存量"。每步都可独立回滚,不影响生产稳定性。