5b877451eb
旧模板的 193 个文件整体删除,换成纯 Vue 3.5 + Naive UI 2.42 的树。
- 去掉 auto-import(组件与 API 全显式 import)、v-hasPerm(改 user.has('码'),全库 8 处)、
Stylelint;n-tag 的 type 统一过 utils/tag.ts 转换(Arco 色词直接透传会静默变灰)
- 图标改为构建期抽取:scripts/extract-pc-icons.py -> icons/icon-data.ts(37 个),
内网离线部署不会白屏
- 重搭后修掉 6 类"编译 / lint / typecheck 全绿、界面上就是坏的"故障:
① NGrid 的 cols 写成像素值(不是断点名 -> 永远只 1 列,工作台因此错乱)
② n-tab-pane 用 tab 属性(只渲染空注释节点,整排 tab 一个字都不显示)
③ 漏 import 被当未知自定义元素(样式整块丢) ④ .n-card__content 类名不存在
(高度链第一环失效,表格行在 DOM 里但屏幕上没有) ⑤ 填报单状态映射错位一格
⑥ 有标题的卡片没有上内边距(naive 的 padding-top 只给"内容区是长子"的卡片)
- 卡片式版面提成全局基线:styles/index.scss 的 xt-* 令牌 + .page-card / .toolbar /
.section-title,页面只引用、不再各写一套
- 品牌:改名「深学在线」,文字标换成几何标(方 = 卷宗 / 环 = 在线,不用文字字形);
index.html 的页签图标从 404 的 /favicon.ico 改指真实存在的 /favicon.svg
93 lines
3.8 KiB
Markdown
93 lines
3.8 KiB
Markdown
---
|
||
name: code-vue
|
||
description: Vue 3 Composition API、SFC 块顺序、Naive UI 组件显式导入、props/emits 与模板约定。编写或修改 .vue 文件时加载本技能。
|
||
---
|
||
|
||
# Vue 规范(Vue 3 + Naive UI)
|
||
|
||
适用于 Composition API + `<script setup lang="ts">`。样式见 [code-css](../code-css/SKILL.md)。
|
||
|
||
## 基础
|
||
|
||
- 新组件一律 **Composition API** + `<script setup lang="ts">`,禁止 Options API。
|
||
- 块顺序:`<template>` → `<script setup>` → `<style scoped>`。
|
||
- 需要组件名时用 `defineOptions({ name: 'XxxYyy' })`。
|
||
|
||
## Script 内顺序
|
||
|
||
类型导入 → 第三方 → Vue API → 项目模块 → `defineOptions` → `defineProps` + `withDefaults` → `defineEmits` → 常量/函数 → `ref`/`reactive` → `computed` → 方法 → 生命周期/`watch`。
|
||
|
||
## ⚠️ 显式 import(本仓库没有 auto-import)
|
||
|
||
```ts
|
||
import type { DataTableColumns, FormInst } from 'naive-ui'
|
||
import { NButton, NCard, NDataTable, NForm, NFormItem, NInput, NModal, NTag, useMessage } from 'naive-ui'
|
||
import { computed, h, onMounted, reactive, ref } from 'vue'
|
||
import * as tenantApi from '@/apis/xt/tenant'
|
||
```
|
||
|
||
- 组件用 **PascalCase** 标签:`<NDataTable :columns="columns" :data="list" />`(**不是** `<a-table>`)。
|
||
- 弹窗/抽屉、表格、表单全部来自 **naive-ui**;主题与全局配置在 `utils/naive.ts` / `App.vue` 一侧。
|
||
|
||
### 漏 import 是**静默故障**(2026-10 实测踩到 4 个文件 6 处)
|
||
|
||
写小写标签(`<n-button>`)而忘了 import 时,Vue 把它当**未知自定义元素**渲染:
|
||
**不报错、不崩、`pnpm typecheck` 与 `pnpm lint` 全绿**,只是样式整块丢掉。
|
||
实测表现:`<n-button>` 变成无边框透明块、`<n-avatar>` 变成 18×29 的裸元素、`<n-form>` 的表单排版全丢 ——
|
||
用户看到的就是"这个页面样式坏了"。
|
||
|
||
> **统一写 PascalCase 标签**(`<NButton>`),漏 import 时 Vue 会给出
|
||
> `Failed to resolve component` 警告,比小写标签好排查。
|
||
> 另外 `python scripts/check-wiring.py` 的**第 ⑧ 项**会静态核对
|
||
> 「模板里用到的 `<n-*>` ↔ 本文件的 import」,改完 PC 端跑一下它。
|
||
|
||
## ⚠️ `n-tab-pane` 的标签必须用 `#tab` **插槽**
|
||
|
||
naive-ui 2.45.3 里,`tab="文字"` **属性**写法渲染不出任何文字(只留一个空注释节点),
|
||
表现是**整排 tab 一个字都不显示、整行高度只剩 12px** —— 筛选功能等于消失,且不报错。
|
||
|
||
```vue
|
||
<!-- ✅ 正确 -->
|
||
<NTabs v-model:value="node">
|
||
<NTabPane :name="1">
|
||
<template #tab>
|
||
初审(班主任)
|
||
</template>
|
||
</NTabPane>
|
||
</NTabs>
|
||
|
||
<!-- ❌ 不要:标签会静默消失 -->
|
||
<NTabPane :name="1" tab="初审(班主任)" />
|
||
```
|
||
|
||
原因:属性值走 `normalizeVNode(() => render(mergedTab ?? name))`,而这个 thunk 返回
|
||
Text VNode,被当成块 `createBlock(vnode)` 处理;插槽那条分支返回数组,所以正常。
|
||
|
||
## 权限与状态色
|
||
|
||
```ts
|
||
const user = useUserStore()
|
||
// 没有 v-hasPerm 指令,权限判断就是一次函数调用
|
||
const canManage = computed(() => user.has('org:manage'))
|
||
```
|
||
|
||
```vue
|
||
<NTag
|
||
:type="tagOf(row.status)"
|
||
size="small"
|
||
>
|
||
{{ STATUS_NAMES[row.status] }}
|
||
</NTag>
|
||
```
|
||
|
||
⚠️ **不要**把 `apis/xt/*.ts` 里的 `color`(Arco 色词 `green`/`arcoblue`/`gold`)直接传给 `n-tag` 的 `type` ——
|
||
会**静默变成默认灰色**且不报错;必须过 `@/utils/tag.ts` 的 `tagOf()`。
|
||
|
||
## 模板
|
||
|
||
- 事件名、插槽名 **kebab-case**;组件 **PascalCase**。
|
||
- 表格列用 `h()` 渲染(`NButton` / `NTag` / `AppIcon`),逻辑别堆在模板里。
|
||
- 受控表单优先 `v-model`;复杂逻辑抽到 `computed` 或方法。
|
||
- 图标统一用 `@/icons/AppIcon.vue`(缺图标是**空占位、不报错**)。
|
||
- 子组件通过 props/emits 通信,避免访问父组件实例。
|