@qilitt-mickey/vue3-temp-skill 1.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/README.md +229 -0
  2. package/SKILL.md +621 -0
  3. package/bin/cli.js +579 -0
  4. package/package.json +46 -0
  5. package/references/advanced-ui.md +302 -0
  6. package/references/api-check.md +272 -0
  7. package/references/base-code-dict.md +48 -0
  8. package/references/build-optim.md +282 -0
  9. package/references/code-quality.md +235 -0
  10. package/references/crud-pages.md +316 -0
  11. package/references/data-compare.md +501 -0
  12. package/references/data-mapping.md +213 -0
  13. package/references/data-screen.md +79 -0
  14. package/references/data-writeback.md +104 -0
  15. package/references/detail-page.md +99 -0
  16. package/references/directives-advanced.md +93 -0
  17. package/references/download-export.md +68 -0
  18. package/references/feedback-loading.md +60 -0
  19. package/references/feedback-ui.md +111 -0
  20. package/references/file-management.md +132 -0
  21. package/references/flowchart-g6.md +244 -0
  22. package/references/form-advanced.md +137 -0
  23. package/references/graph-relation.md +253 -0
  24. package/references/http-api.md +188 -0
  25. package/references/layout-theme.md +540 -0
  26. package/references/mobile-h5.md +271 -0
  27. package/references/permission-auth.md +235 -0
  28. package/references/project-inventory.md +326 -0
  29. package/references/qrcode-barcode.md +92 -0
  30. package/references/rich-text.md +73 -0
  31. package/references/seamless-scroll.md +38 -0
  32. package/references/tree-table.md +111 -0
  33. package/references/ui-components.md +161 -0
  34. package/references/verify-captcha.md +96 -0
  35. package/references/vue-core.md +209 -0
  36. package/references/websocket-realtime.md +176 -0
  37. package/references/workflow-bpmn.md +206 -0
@@ -0,0 +1,271 @@
1
+ ---
2
+ skill: mobile-h5
3
+ description: 规范移动端 H5 页面开发、Vant 组件使用、微信 JS-SDK 集成和移动端路由配置。开发移动端页面或微信相关功能时调用。
4
+ scope: project
5
+ tags: [vue3, mobile, h5, vant, wechat, miniprogram, responsive, touch]
6
+ ---
7
+
8
+ # 移动端 H5 与微信开发规范
9
+
10
+ ## 移动端页面结构
11
+
12
+ ### 页面目录
13
+
14
+ 移动端页面放在 `src/views/mobile/` 下,与 PC 端页面隔离:
15
+
16
+ ```
17
+ src/views/mobile/
18
+ ├── index/
19
+ │ └── index.vue # 移动端首页
20
+ ├── customer/
21
+ │ └── index.vue # 客户列表
22
+ ├── order/
23
+ └── index.vue # 订单列表
24
+ └── profile/
25
+ └── index.vue # 个人中心
26
+ ```
27
+
28
+ ### 移动端路由
29
+
30
+ 移动端路由独立配置在 `src/router/mobile.ts`:
31
+
32
+ ```typescript
33
+ export default {
34
+ path: "/m",
35
+ component: MobileLayout,
36
+ children: [
37
+ {
38
+ path: "/m/index",
39
+ name: "MobileIndex",
40
+ component: () => import("@/views/mobile/index/index.vue"),
41
+ meta: { title: "首页" },
42
+ },
43
+ {
44
+ path: "/m/customer",
45
+ name: "MobileCustomer",
46
+ component: () => import("@/views/mobile/customer/index.vue"),
47
+ meta: { title: "客户" },
48
+ },
49
+ ],
50
+ } satisfies RouteConfigsTable;
51
+ ```
52
+
53
+ ### 移动端布局
54
+
55
+ ```vue
56
+ <!-- src/layout/mobile.vue -->
57
+ <template>
58
+ <div class="mobile-layout">
59
+ <router-view />
60
+ <van-tabbar v-model="active" route>
61
+ <van-tabbar-item to="/m/index" icon="home-o">首页</van-tabbar-item>
62
+ <van-tabbar-item to="/m/customer" icon="friends-o">客户</van-tabbar-item>
63
+ <van-tabbar-item to="/m/profile" icon="user-o">我的</van-tabbar-item>
64
+ </van-tabbar>
65
+ </div>
66
+ </template>
67
+ ```
68
+
69
+ ## Vant 组件使用规范
70
+
71
+ ### 列表页(触底加载)
72
+
73
+ ```vue
74
+ <script setup lang="ts">
75
+ import { ref } from "vue";
76
+ import { getCustomerListApi } from "@/api/customer";
77
+
78
+ defineOptions({ name: "MobileCustomer" });
79
+
80
+ const list = ref<CustomerItem[]>([]);
81
+ const loading = ref(false);
82
+ const finished = ref(false);
83
+ const page = ref(1);
84
+ const pageSize = 10;
85
+
86
+ async function onLoad() {
87
+ loading.value = true;
88
+ try {
89
+ const { data } = await getCustomerListApi({ page: page.value, pageSize });
90
+ if (data.list.length < pageSize) {
91
+ finished.value = true;
92
+ }
93
+ list.value.push(...data.list);
94
+ page.value++;
95
+ } finally {
96
+ loading.value = false;
97
+ }
98
+ }
99
+
100
+ function onRefresh() {
101
+ list.value = [];
102
+ page.value = 1;
103
+ finished.value = false;
104
+ onLoad();
105
+ }
106
+ </script>
107
+
108
+ <template>
109
+ <div class="re-mobile-customer">
110
+ <van-pull-refresh v-model="loading" @refresh="onRefresh">
111
+ <van-list
112
+ v-model:loading="loading"
113
+ :finished="finished"
114
+ finished-text="没有更多了"
115
+ @load="onLoad"
116
+ >
117
+ <div
118
+ v-for="item in list"
119
+ :key="item.id"
120
+ class="card-base mb-3 p-3"
121
+ @click="goDetail(item.id)"
122
+ >
123
+ <div class="flex-between mb-2">
124
+ <span class="font-bold">{{ item.name }}</span>
125
+ <van-tag :type="item.status ? 'success' : 'danger'">
126
+ {{ item.status ? "活跃" : "沉默" }}
127
+ </van-tag>
128
+ </div>
129
+ <div class="text-sm text-gray-500">{{ item.phone }}</div>
130
+ </div>
131
+ </van-list>
132
+ </van-pull-refresh>
133
+ </div>
134
+ </template>
135
+ ```
136
+
137
+ ### 表单页
138
+
139
+ ```vue
140
+ <template>
141
+ <div class="re-mobile-form">
142
+ <van-form
143
+ ref="formRef"
144
+ :model="formData"
145
+ :rules="formRules"
146
+ label-width="5em"
147
+ >
148
+ <van-field
149
+ v-model="formData.name"
150
+ label="姓名"
151
+ placeholder="请输入姓名"
152
+ :rules="[{ required: true, message: '请输入姓名' }]"
153
+ />
154
+ <van-field
155
+ v-model="formData.phone"
156
+ label="手机号"
157
+ type="tel"
158
+ placeholder="请输入手机号"
159
+ :rules="[{ required: true, message: '请输入手机号' }]"
160
+ />
161
+ <van-field
162
+ v-model="formData.remark"
163
+ label="备注"
164
+ type="textarea"
165
+ rows="3"
166
+ placeholder="请输入备注"
167
+ />
168
+ </van-form>
169
+
170
+ <div class="p-4">
171
+ <van-button type="primary" block :loading="loading" @click="handleSubmit">
172
+ 保存
173
+ </van-button>
174
+ </div>
175
+ </div>
176
+ </template>
177
+ ```
178
+
179
+ ### 选择器
180
+
181
+ ```vue
182
+ <!-- 移动端下拉选择:使用 van-popup + van-picker -->
183
+ <van-field
184
+ v-model="selectedLabel"
185
+ is-link
186
+ readonly
187
+ label="类型"
188
+ placeholder="请选择"
189
+ @click="showPicker = true"
190
+ />
191
+ <van-popup v-model:show="showPicker" position="bottom">
192
+ <van-picker
193
+ :columns="options"
194
+ @confirm="onPickerConfirm"
195
+ @cancel="showPicker = false"
196
+ />
197
+ </van-popup>
198
+ ```
199
+
200
+ ### Vant 组件约定
201
+
202
+ 1. 移动端列表使用 `van-list` + 触底加载,不使用分页组件。
203
+ 2. 下拉刷新使用 `van-pull-refresh` 包裹列表。
204
+ 3. 选择器使用 `van-popup` + `van-picker` 弹出,不使用内联选择。
205
+ 4. 表单使用 `van-form` + `van-field`,校验规则与 PC 端保持一致。
206
+ 5. 弹窗确认使用 `showConfirmDialog`,提示使用 `showToast`。
207
+ 6. 导航使用底部 `van-tabbar`,不使用侧边栏。
208
+
209
+ ## 微信 JS-SDK 集成
210
+
211
+ ### useWechat Hook
212
+
213
+ ```typescript
214
+ import { useWechat } from "@/hooks/modules/useWechat";
215
+
216
+ const {
217
+ initWechat, // 初始化微信配置
218
+ shareToWechat, // 设置分享
219
+ chooseImage, // 选择图片
220
+ uploadImage, // 上传图片
221
+ getLocation, // 获取位置
222
+ scanQRCode, // 扫码
223
+ wx, // wx 对象
224
+ isWechatBrowser, // 是否微信浏览器
225
+ } = useWechat();
226
+
227
+ onMounted(async () => {
228
+ if (isWechatBrowser) {
229
+ await initWechat({
230
+ jsApiList: ["chooseImage", "uploadImage", "getLocation", "scanQRCode"],
231
+ });
232
+ }
233
+ });
234
+ ```
235
+
236
+ ### 微信分享配置
237
+
238
+ ```typescript
239
+ async function setupShare() {
240
+ await shareToWechat({
241
+ title: "页面标题",
242
+ desc: "页面描述",
243
+ link: window.location.href,
244
+ imgUrl: "https://example.com/share.png",
245
+ });
246
+ }
247
+ ```
248
+
249
+ ### 微信约定
250
+
251
+ 1. 微信相关逻辑统一封装在 `useWechat` Hook 中。
252
+ 2. 先判断 `isWechatBrowser` 再调用微信 API。
253
+ 3. 微信 JS-SDK 配置通过后端接口获取签名。
254
+ 4. 分享配置在每个页面 `onMounted` 中设置。
255
+
256
+ ## 移动端通用约定
257
+
258
+ 1. 移动端页面放在 `src/views/mobile/` 下,路由前缀 `/m`。
259
+ 2. 使用 `useApp().isMobile` 判断设备类型,不要用 `window.innerWidth` 手动判断。
260
+ 3. 样式使用 `rem` 或 UnoCSS 原子类,不要使用固定 `px` 宽度。
261
+ 4. 触摸交互使用 Vant 内置手势,不要自己实现 touch 事件。
262
+ 5. 图片使用 `van-image` 组件,支持懒加载和占位图。
263
+ 6. 移动端不显示侧边栏菜单,使用底部 TabBar 导航。
264
+
265
+ ## 常见反例
266
+
267
+ - 移动端使用 `el-table` 展示数据(应使用 `van-list` + 卡片)。
268
+ - 移动端使用 `el-select` 做下拉选择(应使用 `van-popup` + `van-picker`)。
269
+ - 不使用 `van-list` 触底加载而是一次性加载全部数据。
270
+ - 在微信浏览器中直接调用微信 API 而不先 `wx.config`。
271
+ - 移动端页面放在 `src/views/` 根目录与 PC 端混在一起。
@@ -0,0 +1,235 @@
1
+ ---
2
+ skill: permission-auth
3
+ description: 规范页面权限、按钮权限、自定义指令和 Hooks 的使用。在开发涉及权限控制、自定义指令或编写组合式函数时调用。
4
+ scope: project
5
+ tags: [vue3, permission, auth, directive, hooks, v-auth, role, access-control]
6
+ ---
7
+
8
+ # 权限控制与自定义指令
9
+
10
+ ## 权限体系概览
11
+
12
+ 项目权限分为三个层级:
13
+
14
+ | 层级 | 控制方式 | 说明 |
15
+ |------|---------|------|
16
+ | 页面级 | 路由 `meta.roles` | 控制菜单可见性和路由访问 |
17
+ | 按钮级 | `v-auth` 指令 / `ReAuth` 组件 | 控制页面内按钮的显示/隐藏 |
18
+ | 接口级 | 后端校验 | 前端只做展示控制,后端做安全校验 |
19
+
20
+ ## 页面级权限
21
+
22
+ ### 路由 meta.roles 配置
23
+
24
+ ```typescript
25
+ // src/router/modules/system.ts
26
+ export default {
27
+ path: "/system",
28
+ meta: { title: "系统管理" },
29
+ children: [
30
+ {
31
+ path: "/system/user",
32
+ name: "SystemUser",
33
+ component: () => import("@/views/system/user/index.vue"),
34
+ meta: {
35
+ title: "用户管理",
36
+ roles: ["admin", "editor"], // 只有这些角色可以访问
37
+ },
38
+ },
39
+ {
40
+ path: "/system/config",
41
+ name: "SystemConfig",
42
+ component: () => import("@/views/system/config/index.vue"),
43
+ meta: {
44
+ title: "系统配置",
45
+ roles: ["admin"], // 仅管理员可访问
46
+ },
47
+ },
48
+ ],
49
+ } satisfies RouteConfigsTable;
50
+ ```
51
+
52
+ ### 路由守卫校验
53
+
54
+ 路由守卫在 `src/router/utils.ts` 中统一处理:
55
+
56
+ 1. 白名单路由(登录页、404 等)直接放行。
57
+ 2. 有 Token 但没有菜单数据时,调用接口获取菜单并 `addRoute` 动态注册。
58
+ 3. 每次路由跳转检查 `meta.roles`,无权限则跳转 403 页面。
59
+
60
+ ## 按钮级权限
61
+
62
+ ### v-auth 指令
63
+
64
+ ```vue
65
+ <template>
66
+ <!-- 只有 admin 角色能看到这个按钮 -->
67
+ <el-button v-auth="['admin']" type="danger" @click="handleDelete">
68
+ 删除
69
+ </el-button>
70
+
71
+ <!-- 多个角色任一匹配即可 -->
72
+ <el-button v-auth="['admin', 'editor']" @click="handleEdit">
73
+ 编辑
74
+ </el-button>
75
+ </template>
76
+ ```
77
+
78
+ ### ReAuth 组件
79
+
80
+ 当需要控制一整块区域时,使用 `ReAuth` 组件包裹:
81
+
82
+ ```vue
83
+ <template>
84
+ <ReAuth :value="['admin']">
85
+ <div class="admin-only-section">
86
+ <!-- 仅管理员可见的内容 -->
87
+ </div>
88
+ </ReAuth>
89
+ </template>
90
+ ```
91
+
92
+ ### 权限判断 Hook
93
+
94
+ 在 JS 逻辑中判断权限:
95
+
96
+ ```typescript
97
+ import { useAuth } from "@/hooks/useAuth";
98
+
99
+ const { hasAuth } = useAuth();
100
+
101
+ function handleDelete() {
102
+ if (!hasAuth(["admin"])) {
103
+ ElMessage.warning("无权限操作");
104
+ return;
105
+ }
106
+ // 执行删除
107
+ }
108
+ ```
109
+
110
+ ### 权限约定
111
+
112
+ 1. 按钮权限使用 `v-auth` 指令,不要手动 `v-if` 判断角色。
113
+ 2. 区域权限使用 `ReAuth` 组件包裹。
114
+ 3. JS 逻辑中的权限判断使用 `hasAuth()` 方法。
115
+ 4. 前端权限只做 UI 展示控制,后端接口必须同步校验权限。
116
+ 5. 权限数据来源:登录后获取用户角色列表,存储在 `usePermissionStore` 中。
117
+
118
+ ## 自定义指令
119
+
120
+ ### 指令文件结构
121
+
122
+ ```
123
+ src/directives/
124
+ ├── index.ts # 统一注册入口
125
+ ├── auth/
126
+ │ └── index.ts # v-auth 权限指令
127
+ ├── copy/
128
+ │ └── index.ts # v-copy 复制指令
129
+ ├── compare/
130
+ │ └── index.ts # v-compare 数据变更对比(见 data-compare 模块)
131
+ └── autoFont/
132
+ └── index.ts # v-auto-font 字体自适应指令
133
+ ```
134
+
135
+ ### v-auth 实现原理
136
+
137
+ ```typescript
138
+ // src/directives/auth/index.ts
139
+ import { usePermissionStoreHook } from "@/store/modules/permission";
140
+
141
+ export const auth = {
142
+ mounted(el: HTMLElement, binding: DirectiveBinding<string[]>) {
143
+ const { value } = binding;
144
+ const userRoles = usePermissionStoreHook().roles;
145
+
146
+ if (value && value.length && !value.some(role => userRoles.includes(role))) {
147
+ el.parentNode?.removeChild(el);
148
+ }
149
+ },
150
+ };
151
+ ```
152
+
153
+ ### v-copy / v-auto-font / v-compare
154
+
155
+ - 复制与字体:见 **directives-advanced**
156
+ - 数据变更对比:见 **data-compare**(不是数值条件显示指令)
157
+
158
+ ### 指令约定
159
+
160
+ 1. 自定义指令放在 `src/directives/` 下,每个指令一个目录。
161
+ 2. 指令在 `src/directives/index.ts` 中统一注册到 Vue 实例。
162
+ 3. 指令命名使用短横线格式:`v-auth`、`v-copy`、`v-auto-font`。
163
+ 4. 指令的逻辑尽量简洁,复杂逻辑抽取到工具函数中。
164
+
165
+ ## 组合式函数 (Hooks)
166
+
167
+ ### Hooks 文件结构
168
+
169
+ ```
170
+ src/hooks/
171
+ ├── useApp.ts # 应用级 Hook(isMobile、theme 等)
172
+ ├── useTableSearch.ts # 查询列表页 Hook
173
+ ├── useAuth.ts # 权限判断 Hook
174
+ ├── useDetail.ts # 详情页 Hook
175
+ └── modules/
176
+ └── useWechat.ts # 微信相关 Hook
177
+ ```
178
+
179
+ ### useApp
180
+
181
+ ```typescript
182
+ import { useApp } from "@/hooks/useApp";
183
+
184
+ const {
185
+ isMobile, // 是否移动端
186
+ theme, // 当前主题(light/dark)
187
+ locale, // 当前语言
188
+ toggleDark, // 切换暗色模式
189
+ } = useApp();
190
+ ```
191
+
192
+ ### useTableSearch
193
+
194
+ ```typescript
195
+ import { useTableSearch } from "@/hooks/useTableSearch";
196
+
197
+ const {
198
+ content, // 表格数据
199
+ pagination, // 分页信息
200
+ loading, // 加载状态
201
+ handleSearch, // 查询
202
+ handleReset, // 重置
203
+ handlePageChange, // 翻页
204
+ handleSizeChange, // 切换每页条数
205
+ } = useTableSearch(formQuery, apiFn, options);
206
+ ```
207
+
208
+ ### useDetail
209
+
210
+ ```typescript
211
+ import { useDetail } from "@/hooks/useDetail";
212
+
213
+ const {
214
+ detail, // 详情数据
215
+ loading, // 加载状态
216
+ loadDetail, // 加载详情
217
+ isEdit, // 是否编辑模式
218
+ } = useDetail(detailApi, { idKey: "id" });
219
+ ```
220
+
221
+ ### Hooks 约定
222
+
223
+ 1. Hook 文件名使用 `use[Name].ts` 命名。
224
+ 2. Hook 必须返回类型明确的对象或方法。
225
+ 3. Hook 内部处理 loading、error 状态,组件只需消费结果。
226
+ 4. 不要在 Hook 中直接操作 DOM。
227
+ 5. 通用 Hook 放在 `src/hooks/`,模块专属 Hook 放在 `src/hooks/modules/`。
228
+
229
+ ## 常见反例
230
+
231
+ - 使用 `v-if="roles.includes('admin')"` 手动判断权限而不使用 `v-auth`。
232
+ - 在组件中直接调用权限 store 而不通过 `useAuth` Hook。
233
+ - 自定义指令没有统一注册,散落在各组件中。
234
+ - Hook 返回 `any` 类型或不声明返回类型。
235
+ - 在 Hook 中操作 DOM(应使用 ref + 生命周期)。