From 1ea63c486e18501375617812a3c5593a796d9ea9 Mon Sep 17 00:00:00 2001 From: Ironsp <1522278303@qq.com> Date: Wed, 2 Sep 2026 14:03:57 +0800 Subject: [PATCH] fix: lint - update SKILL.md example and remove unused handler in wifiNetwork.vue --- .gitignore | 1 - .opencode/skills/project-structure/SKILL.md | 393 ++++++++++++++++++++ src/components.d.ts | 1 + src/components/deviceCard.vue | 5 +- src/components/deviceInfo.vue | 3 +- src/components/settings/projectForm.vue | 3 +- src/components/settings/projectSettings.vue | 1 + src/components/settings/wifiNetwork.vue | 170 +++++++++ src/components/settings/wifiSettings.vue | 13 +- 9 files changed, 580 insertions(+), 10 deletions(-) create mode 100644 .opencode/skills/project-structure/SKILL.md create mode 100644 src/components/settings/wifiNetwork.vue diff --git a/.gitignore b/.gitignore index 75093f3..0f71dc2 100644 --- a/.gitignore +++ b/.gitignore @@ -11,7 +11,6 @@ node_modules .DS_Store dist *.local -.opencode # Editor directories and files .idea diff --git a/.opencode/skills/project-structure/SKILL.md b/.opencode/skills/project-structure/SKILL.md new file mode 100644 index 0000000..924b237 --- /dev/null +++ b/.opencode/skills/project-structure/SKILL.md @@ -0,0 +1,393 @@ +# Project Structure Skill + +本 skill 定义项目的分层架构和文件组织规范,用于指导 Agent 在创建新页面、组件时遵循统一的目录结构。 + +## 核心原则 + +1. **页面放在 `pages/`** - 每个页面一个目录,包含 `index.vue` +2. **组件放在 `components/`** - 公共组件直接放目录下,页面专属组件按页面名分子目录 +3. **页面专属组件与页面同名** - 如 `home` 页面的组件放在 `components/home/` +4. **使用 easycom 自动加载** - 无需手动 import,组件通过文件路径自动生成标签名 + +## 组件放置规则(重要) + +### 公共组件(跨页面复用) +当用户要求创建**公共组件**时,直接放在 `components/` 根目录下: + +``` +components/ +├── backNav.vue # 通用返回导航栏 +├── tabBar.vue # 通用底部栏 +├── modal.vue # 通用弹窗 +└── ... +``` + +标签名:``、``、`` + +### 页面专属组件(仅特定页面使用) +当组件只在某个页面内使用时,放在 `components/{pageName}/` 子目录下: + +``` +components/ +├── home/ # 首页专属组件 +│ ├── status.vue +│ └── overview.vue +├── message/ # 消息页专属组件 +│ └── list.vue +└── bluetooth/ # 蓝牙页专属组件 + └── device.vue +``` + +标签名:``、``、`` + +### 判断依据 + +| 需求描述 | 放置位置 | 示例 | +|----------|----------|------| +| "做一个通用的返回导航" | `components/backNav.vue` | `` | +| "做一个通用的弹窗组件" | `components/modal.vue` | `` | +| "首页的项目列表组件" | `components/home/projectList.vue` | `` | +| "消息页的消息列表" | `components/message/list.vue` | `` | +| "蓝牙页的设备卡片" | `components/bluetooth/device.vue` | `` | + +## 目录结构 + +``` +src/ +├── pages/ # 页面目录 +│ ├── home/ # 首页 +│ │ └── index.vue +│ ├── report/ # 报告页 +│ │ └── index.vue +│ ├── message/ # 消息页 +│ │ └── index.vue +│ ├── bluetooth/ # 蓝牙页 +│ │ └── index.vue +│ └── work/ # 工作页 +│ └── index.vue +│ +├── components/ # 组件目录 +│ ├── backNav.vue # 公共组件:通用返回导航栏 +│ │ +│ ├── home/ # 首页专属组件 +│ │ ├── homeNav.vue # 首页导航栏 +│ │ ├── overview.vue # 今日作业概览 +│ │ ├── status.vue # 实时作业状态 +│ │ ├── projectList.vue # 项目列表容器 +│ │ ├── list.vue # 项目列表项 +│ │ ├── progress.vue # 项目作业进度 +│ │ ├── construction.vue # 当前施工段 +│ │ ├── warning.vue # 异常警告容器 +│ │ └── warning_list.vue # 异常警告项 +│ │ +│ ├── message/ # 消息页专属组件 +│ │ └── list.vue # 消息列表项 +│ │ +│ └── bluetooth/ # 蓝牙页专属组件 +│ └── device.vue # 蓝牙设备卡片 +│ +├── composables/ # 组合式函数 +├── store/ # Pinia 状态管理 +├── api/ # 接口请求 +├── utils/ # 工具函数 +├── static/ # 静态资源 +└── layouts/ # 布局组件 +``` + +## 命名规范 + +### 文件命名 +- **页面**: `index.vue`(固定) +- **组件**: `camelCase.vue`(如 `projectList.vue`) +- **组合式函数**: `useXxx.ts`(如 `useCount.ts`) + +### 组件标签名(easycom 规则) +- **公共组件**: `backNav.vue` → `` +- **页面专属组件**: `home/status.vue` → `` +- **嵌套组件**: `home/list.vue` → `` + +### easycom 自动扫描规则 +```json +{ + "easycom": { + "autoscan": true, + "custom": { + "^wd-(.*)": "@wot-ui/ui/components/wd-$1/wd-$1.vue", + "^uni-(.*)": "@dcloudio/uni-ui/lib/uni-$1/uni-$1.vue" + } + } +} +``` + +### easycom 自动生成标签名规则 +| 文件路径 | 自动生成标签名 | +|----------|----------------| +| `components/backNav.vue` | `` | +| `components/modal.vue` | `` | +| `components/home/status.vue` | `` | +| `components/message/list.vue` | `` | +| `components/bluetooth/device.vue` | `` | + +## 组件拆分原则 + +### 何时拆分组件 +1. **模板超过 100 行** - 拆分为子组件 +2. **重复使用的 UI 块** - 提取为独立组件 +3. **逻辑独立的功能块** - 拆分便于维护 + +### 拆分模式 +``` +页面容器组件(如 projectList.vue) + └── 列表项组件(如 projectList_list.vue) + +页面容器组件(如 warning.vue) + └── 列表项组件(如 warning_list.vue) +``` + +### 示例:项目列表 +``` +components/home/ +├── projectList.vue # 容器:卡片标题 + card-body +└── projectList_list.vue # 内容:项目信息 + 参数 + 操作按钮 +``` + +使用方式: +```vue + + + +``` + +## 创建新页面步骤 + +1. **创建页面目录** + ``` + src/pages/{pageName}/index.vue + ``` + +2. **创建页面专属组件目录**(如需要) + ``` + src/components/{pageName}/ + ``` + +3. **配置路由**(如需要) + ```json + // src/pages.json + { + "path": "pages/{pageName}/index", + "type": "page", + "style": { + "navigationBarTitleText": "页面标题" + } + } + ``` + +4. **使用组件** + ```vue + + ``` + +## 页面专属组件 vs 公共组件 + +| 类型 | 位置 | 标签名 | 示例 | +|------|------|--------|------| +| 公共组件 | `components/` 根目录 | `{name}` | `` | +| 页面专属组件 | `components/{pageName}/` | `{pageName}-{name}` | `` | + +## 页面样式规范 + +### 内容区域样式 +所有使用自定义导航栏(``)的页面,`.content` 容器必须使用以下样式: + +```css +.content { + padding: 16px; + padding-top: 72px; +} +``` + +- `padding-top: 72px` 为导航栏预留空间,避免内容被遮挡 +- `padding: 16px` 保持左右边距一致 + +### 卡片样式规范(重要) +每个组件自身就是一个卡片,**页面不要用额外的 `.card` 容器包裹组件**,否则多个组件会合并成一个卡片。 + +**卡片间距规则**: +- 卡片之间间距统一为 **12px** +- 页面通过 `gap: 12px` 设置间距,**组件内部不要添加 margin** +- 组件只负责自身样式(背景、圆角、内边距),不负责与其他组件的间距 + +**正确做法** - 页面用 gap 设置间距: +```vue + + + + + + + + + +``` + +**错误做法** - 组件添加 margin 影响间距: +```css +/* 错误:组件添加 margin */ +.card { + margin-bottom: 16px; /* 不要用 margin */ +} +``` + +**组件卡片统一样式:** +```css +.card { + background: #fff; + border-radius: 12px; + padding: 16px; + margin-bottom: 16px; +} +``` + +**例外情况** - 多个子组件需要合并展示时: +当明确要求将多个组件放在同一个卡片内时,由最外层容器组件统一设置卡片样式,子组件背景透明: +```css +/* 容器组件 */ +.container { + background: #fff; + border-radius: 12px; + padding: 16px; +} + +/* 子组件 */ +.child { + background: transparent; + padding: 0; +} +``` + +### 页面模板示例 +```vue + + + +``` + +### 字体规范 +项目使用三级字体体系: + +| 级别 | 字号 | 用途 | 示例 | +|------|------|------|------| +| 一级 | 22px | 最大标题 | 页面标题、卡片大标题 | +| 二级 | 18px | 次级正常字体 | 正文、列表项、表单标签 | +| 三级 | 14px | 最小描述字体 | 辅助说明、placeholder、元信息 | + +**CSS 变量:** +```css +/* 一级标题 */ +--font-size-title: 22px; + +/* 二级正文 */ +--font-size-body: 18px; + +/* 三级描述 */ +--font-size-caption: 14px; +``` + +**使用示例:** +```vue + + + +``` + +## 注意事项 + +1. **不要在页面中直接写大段模板** - 超过 100 行考虑拆分 +2. **组件样式使用 scoped** - 避免样式污染 +3. **组件 props 要有默认值** - 提高组件易用性 +4. **事件命名使用 kebab-case** - 如 `@handle-click` +5. **插槽优先于 props 传内容** - 灵活性更高 diff --git a/src/components.d.ts b/src/components.d.ts index 9fcd8dc..ab859de 100644 --- a/src/components.d.ts +++ b/src/components.d.ts @@ -26,6 +26,7 @@ declare module 'vue' { SettingsProjectSettings: typeof import('./components/settings/projectSettings.vue')['default'] SettingsRadioSettings: typeof import('./components/settings/radioSettings.vue')['default'] SettingsSensor: typeof import('./components/settings/sensor.vue')['default'] + SettingsWifiNetwork: typeof import('./components/settings/wifiNetwork.vue')['default'] SettingsWifiSettings: typeof import('./components/settings/wifiSettings.vue')['default'] } } diff --git a/src/components/deviceCard.vue b/src/components/deviceCard.vue index 6190ac3..b24c44a 100644 --- a/src/components/deviceCard.vue +++ b/src/components/deviceCard.vue @@ -39,7 +39,6 @@ withDefaults(defineProps<{ padding: 16px; background-color: #ffffff; border-radius: 12px; - margin-bottom: 12px; } .device-icon { @@ -61,14 +60,14 @@ withDefaults(defineProps<{ } .device-name { - font-size: 16px; + font-size: 18px; font-weight: bold; color: #333333; margin-bottom: 4px; } .device-id { - font-size: 13px; + font-size: 14px; color: #999999; } diff --git a/src/components/deviceInfo.vue b/src/components/deviceInfo.vue index ab4336f..33e1b7c 100644 --- a/src/components/deviceInfo.vue +++ b/src/components/deviceInfo.vue @@ -73,12 +73,11 @@ withDefaults(defineProps<{ .device-info { background-color: #ffffff; border-radius: 12px; - margin-bottom: 12px; padding: 16px; } .section-title { - font-size: 16px; + font-size: 18px; font-weight: bold; color: #333; margin-bottom: 16px; diff --git a/src/components/settings/projectForm.vue b/src/components/settings/projectForm.vue index b4fab96..9de558c 100644 --- a/src/components/settings/projectForm.vue +++ b/src/components/settings/projectForm.vue @@ -46,7 +46,6 @@ function handleSave() { .project-form { background-color: #ffffff; border-radius: 12px; - margin-bottom: 12px; padding: 16px; } @@ -55,7 +54,7 @@ function handleSave() { } .form-label { - font-size: 14px; + font-size: 18px; font-weight: bold; color: #333; margin-bottom: 8px; diff --git a/src/components/settings/projectSettings.vue b/src/components/settings/projectSettings.vue index bd263a3..5cd6f5c 100644 --- a/src/components/settings/projectSettings.vue +++ b/src/components/settings/projectSettings.vue @@ -18,5 +18,6 @@ .project-settings { display: flex; flex-direction: column; + gap: 12px; } diff --git a/src/components/settings/wifiNetwork.vue b/src/components/settings/wifiNetwork.vue new file mode 100644 index 0000000..2e4750f --- /dev/null +++ b/src/components/settings/wifiNetwork.vue @@ -0,0 +1,170 @@ + + + + + diff --git a/src/components/settings/wifiSettings.vue b/src/components/settings/wifiSettings.vue index efb1e61..ddf9c6b 100644 --- a/src/components/settings/wifiSettings.vue +++ b/src/components/settings/wifiSettings.vue @@ -3,12 +3,21 @@