@qilitt-mickey/vue3-temp-skill 1.1.2 → 1.1.3
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.
- package/SKILL.md +8 -2
- package/package.json +1 -1
- package/references/code-quality.md +13 -0
- package/references/core-kernel.md +157 -76
- package/references/crud-pages.md +37 -10
- package/references/form-advanced.md +1 -1
- package/references/icons.md +1 -1
- package/references/project-inventory.md +1 -1
package/SKILL.md
CHANGED
|
@@ -39,7 +39,8 @@ tags: [vue3, typescript, element-plus, vant, pinia, vite, unocss, crud, componen
|
|
|
39
39
|
- 选型表已指定三方库(如图谱、流程图、富文本)→ **用指定库,勿擅自换成同类其他库**;确需新库须「待确认」
|
|
40
40
|
3. **按示例编写**:按本包 `references/` 中的**完整示例**与约定写法编写(命名、结构、调用方式以示例为准)。
|
|
41
41
|
4. **无则上报**:清单与 Skill 均无对应能力时,标注「待确认」新依赖,未经用户同意不得引入。
|
|
42
|
-
5.
|
|
42
|
+
5. **真相源优先级**:`仓库已有可复用封装(Re* / Hook / utils)` > `本 Skill references 完整示例` > 通用 Vue 习惯。
|
|
43
|
+
**禁止**把仓库里的 views 演示页当作规范来源(业务项目可能没有演示页);列表布局、弹窗写法等以 Skill 内黄金样板为准。
|
|
43
44
|
命名以代码为准:列表方法 **`onSearch` / `reset`**;响应类型全局 **`Result<T>`**(`types/global.d.ts`);HTTP 为 **`import { http } from '@/utils/http'`**。
|
|
44
45
|
6. **依赖 / 插件安装(addon 必做)**:命中专项能力时,先读业务项目 `package.json`。
|
|
45
46
|
- 已装齐 → 直接写代码
|
|
@@ -173,7 +174,7 @@ export function getXxxListApi(data: XxxQuery) {
|
|
|
173
174
|
### 列表页
|
|
174
175
|
|
|
175
176
|
```typescript
|
|
176
|
-
const { content, pagination, loading, onSearch, reset, formRef, tableData, ... } =
|
|
177
|
+
const { content, pagination, loading, onSearch, reset, formRef, tableData, tableHeight, ... } =
|
|
177
178
|
useTableSearch(formQuery, getXxxListApi, `${import.meta.env.VITE_API_BASE_URL}/console/xxx/list`);
|
|
178
179
|
|
|
179
180
|
onSearch(); // 查询
|
|
@@ -182,6 +183,10 @@ reset(); // 重置(配合 formRef.resetFields)
|
|
|
182
183
|
|
|
183
184
|
- 方法名必须是 **`onSearch` / `reset`**,不是 handleSearch/handleReset。
|
|
184
185
|
- 配合 `ReGrid` / `ReGridItem` / `ReTableBar` / `RePagination` / `ReTableOperate`。
|
|
186
|
+
- **布局必须按 `core-kernel`「列表黄金样板」完整输出**(技能自包含,不依赖仓库 views 演示页):
|
|
187
|
+
- `el-card ref="content"` + 固定 `height`(含 `headerHeight`),禁止只用 `minHeight` / `ref` 挂 `div`
|
|
188
|
+
- 查询区 `bg-bg_color p-5` + `ReGrid` `:gap="[0, 20]"` + 表单项 `w-[100%] !mr-0`;与 `ReTableBar` 展示区分开
|
|
189
|
+
- 表格绑 `tableHeight`;分页放在 `ReTableBar` 内,避免无数据时贴查询区
|
|
185
190
|
|
|
186
191
|
### 路由
|
|
187
192
|
|
|
@@ -247,6 +252,7 @@ reset(); // 重置(配合 formRef.resetFields)
|
|
|
247
252
|
**3. API** — `@/utils/http`;方法跟后端(允许 GET/POST);封装在 `src/api/`;`VITE_API_BASE_URL`;无虚构 `/api` / 无 `crypto: true`
|
|
248
253
|
**4. 安全** — 无危险 v-html;无硬编码密钥
|
|
249
254
|
**5. 业务** — `onSearch`/`reset`;提交 loading;权限;未二次 Layout
|
|
255
|
+
**5b. 列表布局** — `el-card`+`content`+固定 height;查询/展示分区;`tableHeight`;分页在 ReTableBar 内
|
|
250
256
|
**6. 风格** — 无多余 console;`$t('中文')`;`--vts-*` / UnoCSS;图标走 `svg-icon` / `icons` 约定
|
|
251
257
|
|
|
252
258
|
完整项见 `references/code-quality.md`。
|
package/package.json
CHANGED
|
@@ -22,6 +22,7 @@ tags: [code-review, quality, checklist, lint, typescript, vue3, security, perfor
|
|
|
22
22
|
- [ ] HTTP 为 `import { http } from '@/utils/http'` + 全局 `Result<T>`
|
|
23
23
|
- [ ] 业务路由未二次挂 Layout
|
|
24
24
|
- [ ] 使用对应 `references/<模块>.md` 示例与约定写法
|
|
25
|
+
- [ ] **查询列表布局**:`el-card ref="content"` + 固定 `height`(含 `headerHeight`);查询区 `bg-bg_color p-5` + `ReGrid` `gap`/`cols`;表格绑 `tableHeight`;分页在 `ReTableBar` 内
|
|
25
26
|
|
|
26
27
|
|
|
27
28
|
## 一、TypeScript 类型检查
|
|
@@ -226,6 +227,18 @@ const fullName = computed(() => `${user.firstName} ${user.lastName}`);
|
|
|
226
227
|
- [ ] 路由跳转使用了正确的参数传递方式(`query` 而非 `params`)。
|
|
227
228
|
- [ ] 分页切换正确触发了数据加载。
|
|
228
229
|
|
|
230
|
+
### 查询列表页布局(命中 crud-pages / 列表需求时必查)
|
|
231
|
+
|
|
232
|
+
- [ ] `ref="content"` 挂在 **`el-card`**(组件),不是原生 `div`。
|
|
233
|
+
- [ ] 卡片样式为固定 **`height: calc(100vh - 2 * var(--vts-margin) - ${headerHeight}px)`**,不是 `minHeight`。
|
|
234
|
+
- [ ] 查询 `el-form`:`inline` + `class="bg-bg_color p-5"`;`ReGrid` 有 `:gap="[0, 20]"` 与响应式 `cols`。
|
|
235
|
+
- [ ] 表单项有 `class="w-[100%] !mr-0"`;操作按钮用 `<ReGridItem suffix>`。
|
|
236
|
+
- [ ] 折叠事件为 `@should-show-collapse`(不是 `@show-collapse`)。
|
|
237
|
+
- [ ] 展示区用 `ReTableBar class="bg-bg_color"`,与查询区视觉分离。
|
|
238
|
+
- [ ] 表格 `:style="{ height: \`${tableHeight}px\` }"`;`paginationRef` 在表格下方且同属 `ReTableBar` `#default`。
|
|
239
|
+
- [ ] `RePagination` 使用 `:pagination` + `@handle-current-change` / `@handle-size-change`。
|
|
240
|
+
- [ ] `ReTableOperate` 使用 `:list`(不是 `:buttons`)。
|
|
241
|
+
|
|
229
242
|
## 八、代码风格检查
|
|
230
243
|
|
|
231
244
|
### 必须通过
|
|
@@ -28,6 +28,7 @@ tags: [vue3, kernel, conventions, layout, router, http, crud, inventory]
|
|
|
28
28
|
| 8 | i18n:`$t('中文key')`,key 即中文文案 |
|
|
29
29
|
| 9 | 禁止用 `v-html` 渲染不可信内容 |
|
|
30
30
|
| 10 | 新三方依赖必须用户确认;已有封装须复用,选型表指定库勿擅自替换 |
|
|
31
|
+
| 11 | **查询列表页布局硬约束**(见下方黄金样板):`el-card` + `ref="content"` + **固定 `height`**(非 `minHeight`);查询区与表格区分区;表格用 `tableHeight`;分页放在 `ReTableBar` 内 |
|
|
31
32
|
| 11 | addon 缺失时按 `project-inventory` 第九节**精确版本**安装;禁止 `@latest` / 无版本号;Vite 插件与 `build/plugins/*` 成套核对 |
|
|
32
33
|
|
|
33
34
|
---
|
|
@@ -116,8 +117,23 @@ ReAuth、ReDialog、ReGrid、ReGridItem、RePagination、ReTableBar、ReTableOpe
|
|
|
116
117
|
|
|
117
118
|
### 列表黄金样板
|
|
118
119
|
|
|
120
|
+
> **布局真相源即本样板**(技能自包含,不依赖业务仓库是否存在演示页)。生成查询列表页必须完整按下方结构输出,禁止简化成「表单 + 裸表格 + 裸分页」——否则会出现字段挤在一起、查询与展示区分不清、无数据时分页贴查询区、页面高度不饱满。
|
|
121
|
+
|
|
122
|
+
#### 布局硬约束(违反即不合格)
|
|
123
|
+
|
|
124
|
+
| # | 必须 | 说明 |
|
|
125
|
+
|---|------|------|
|
|
126
|
+
| L1 | `el-card` + `ref="content"` | `useTableSearch` 通过 `content.$el` 算高度;`ref` 必须挂在 **组件**(`el-card`)上,禁止挂在原生 `div` |
|
|
127
|
+
| L2 | 卡片用固定 **`height`** | `height: calc(100vh - 2 * var(--vts-margin) - ${headerHeight}px)`;**禁止**只写 `minHeight`(否则 `tableHeight` 算不准,表格无法撑满) |
|
|
128
|
+
| L3 | 查询区与展示区分开 | 查询:`el-form` + `class="bg-bg_color p-5"`;展示:`ReTableBar class="bg-bg_color"`;两区视觉分离 |
|
|
129
|
+
| L4 | `ReGrid` 间距与栅格 | `:gap="[0, 20]"` + `:cols="{ xs: 1, sm: 2, md: 2, lg: 3, xl: 4 }"`;表单项 `class="w-[100%] !mr-0"`;事件为 `@should-show-collapse` |
|
|
130
|
+
| L5 | 操作按钮用 `suffix` | `<ReGridItem suffix>`,勿用随意 `index=99` 冒充后缀列 |
|
|
131
|
+
| L6 | 表格动态高度 | `:style="{ height: \`${tableHeight}px\` }"`;分页 **必须在** `ReTableBar` 的 `#default` 内、表格下方,保证空数据时仍占满剩余高度 |
|
|
132
|
+
| L7 | `RePagination` 真实 API | `:pagination="pagination"` + `@handle-current-change` / `@handle-size-change`;**禁止**虚构的 `v-model:current-page` |
|
|
133
|
+
|
|
119
134
|
```vue
|
|
120
135
|
<script setup lang="ts">
|
|
136
|
+
import type { OperateButton } from "@/components/ReTableOperate";
|
|
121
137
|
import type { Customer, CustomerQuery } from "@/api/customer";
|
|
122
138
|
import { getCustomerList } from "@/api/customer";
|
|
123
139
|
import { ReGrid } from "@/components/ReGrid";
|
|
@@ -159,6 +175,15 @@ const {
|
|
|
159
175
|
|
|
160
176
|
const tableList = computed(() => tableData.value.tableList as Customer[]);
|
|
161
177
|
|
|
178
|
+
const operateButtons: OperateButton[] = [
|
|
179
|
+
{
|
|
180
|
+
label: $t("删除"),
|
|
181
|
+
type: "danger",
|
|
182
|
+
icon: "i-ep:delete",
|
|
183
|
+
onClick: (row) => handleDelete(row as Customer),
|
|
184
|
+
},
|
|
185
|
+
];
|
|
186
|
+
|
|
162
187
|
onMounted(() => {
|
|
163
188
|
onSearch();
|
|
164
189
|
});
|
|
@@ -167,91 +192,147 @@ function handleDelete(row: Customer) {
|
|
|
167
192
|
message($t("删除成功"), { type: "success" });
|
|
168
193
|
onSearch();
|
|
169
194
|
}
|
|
195
|
+
|
|
196
|
+
function handleCurrentChange(cur: number) {
|
|
197
|
+
pagination.currentPage = cur;
|
|
198
|
+
onSearch();
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function handleSizeChange(size: number) {
|
|
202
|
+
pagination.pageSize = size;
|
|
203
|
+
onSearch();
|
|
204
|
+
}
|
|
170
205
|
</script>
|
|
171
206
|
|
|
172
207
|
<template>
|
|
173
|
-
<div
|
|
174
|
-
ref=
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
<el-form-item :label="$t('关键词')" prop="keyword">
|
|
182
|
-
<el-input v-model="formQuery.keyword" clearable :placeholder="$t('请输入')" />
|
|
183
|
-
</el-form-item>
|
|
184
|
-
</ReGridItem>
|
|
185
|
-
<ReGridItem :index="1">
|
|
186
|
-
<el-form-item :label="$t('状态')" prop="status">
|
|
187
|
-
<el-select v-model="formQuery.status" clearable :placeholder="$t('请选择')">
|
|
188
|
-
<el-option :label="$t('启用')" :value="1" />
|
|
189
|
-
<el-option :label="$t('禁用')" :value="0" />
|
|
190
|
-
</el-select>
|
|
191
|
-
</el-form-item>
|
|
192
|
-
</ReGridItem>
|
|
193
|
-
<ReGridItem :index="99" :collapsed="false">
|
|
194
|
-
<el-form-item>
|
|
195
|
-
<el-button type="primary" :loading="loading" @click="onSearch">{{ $t("查询") }}</el-button>
|
|
196
|
-
<el-button @click="reset()">{{ $t("重置") }}</el-button>
|
|
197
|
-
<el-button
|
|
198
|
-
v-if="showCollapse"
|
|
199
|
-
text
|
|
200
|
-
type="primary"
|
|
201
|
-
@click="collapsedToggle"
|
|
202
|
-
>
|
|
203
|
-
{{ collapsed ? $t("展开") : $t("收起") }}
|
|
204
|
-
<component :is="collapsed ? downBold : upBold" class="ml-1" />
|
|
205
|
-
</el-button>
|
|
206
|
-
</el-form-item>
|
|
207
|
-
</ReGridItem>
|
|
208
|
-
</ReGrid>
|
|
209
|
-
</el-form>
|
|
210
|
-
|
|
211
|
-
<ReTableBar
|
|
212
|
-
:columns="[]"
|
|
213
|
-
@refresh="onSearch"
|
|
214
|
-
>
|
|
215
|
-
<template #buttons>
|
|
216
|
-
<el-button type="primary" v-auth="['admin']">{{ $t("新增") }}</el-button>
|
|
217
|
-
</template>
|
|
218
|
-
</ReTableBar>
|
|
219
|
-
|
|
220
|
-
<el-table
|
|
221
|
-
ref="multipleTableRef"
|
|
222
|
-
v-loading="loading"
|
|
223
|
-
:element-loading-svg="svgLoading"
|
|
224
|
-
:data="tableList"
|
|
225
|
-
:height="tableHeight"
|
|
226
|
-
border
|
|
227
|
-
row-key="id"
|
|
208
|
+
<div>
|
|
209
|
+
<!-- L1/L2:ref=content 必须在 el-card;用 height 非 minHeight -->
|
|
210
|
+
<el-card
|
|
211
|
+
ref="content"
|
|
212
|
+
shadow="never"
|
|
213
|
+
:style="{
|
|
214
|
+
height: `calc(100vh - 2 * var(--vts-margin) - ${headerHeight}px)`,
|
|
215
|
+
}"
|
|
228
216
|
>
|
|
229
|
-
|
|
230
|
-
<
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
217
|
+
<!-- L3:查询区独立背景与内边距 -->
|
|
218
|
+
<div>
|
|
219
|
+
<el-form
|
|
220
|
+
ref="formRef"
|
|
221
|
+
:inline="true"
|
|
222
|
+
:model="formQuery"
|
|
223
|
+
class="bg-bg_color p-5"
|
|
224
|
+
>
|
|
225
|
+
<!-- L4:gap + cols;事件名 should-show-collapse -->
|
|
226
|
+
<ReGrid
|
|
227
|
+
ref="gridRef"
|
|
228
|
+
:collapsed="collapsed"
|
|
229
|
+
:gap="[0, 20]"
|
|
230
|
+
:cols="{ xs: 1, sm: 2, md: 2, lg: 3, xl: 4 }"
|
|
231
|
+
style="max-height: 300px; overflow-y: auto;"
|
|
232
|
+
@should-show-collapse="(val) => (showCollapse = val)"
|
|
233
|
+
>
|
|
234
|
+
<ReGridItem index="0">
|
|
235
|
+
<el-form-item :label="$t('关键词')" prop="keyword" class="w-[100%] !mr-0">
|
|
236
|
+
<el-input
|
|
237
|
+
v-model="formQuery.keyword"
|
|
238
|
+
clearable
|
|
239
|
+
:placeholder="$t('请输入')"
|
|
240
|
+
/>
|
|
241
|
+
</el-form-item>
|
|
242
|
+
</ReGridItem>
|
|
243
|
+
<ReGridItem index="1">
|
|
244
|
+
<el-form-item :label="$t('状态')" prop="status" class="w-[100%] !mr-0">
|
|
245
|
+
<el-select v-model="formQuery.status" clearable :placeholder="$t('请选择')">
|
|
246
|
+
<el-option :label="$t('启用')" :value="1" />
|
|
247
|
+
<el-option :label="$t('禁用')" :value="0" />
|
|
248
|
+
</el-select>
|
|
249
|
+
</el-form-item>
|
|
250
|
+
</ReGridItem>
|
|
251
|
+
<!-- L5:操作列用 suffix -->
|
|
252
|
+
<ReGridItem suffix>
|
|
253
|
+
<div class="flex flex-end">
|
|
254
|
+
<el-button
|
|
255
|
+
type="primary"
|
|
256
|
+
size="small"
|
|
257
|
+
:loading="loading"
|
|
258
|
+
@click="pagination.currentPage = 1; pagination.pageSize = 10; onSearch()"
|
|
259
|
+
>
|
|
260
|
+
{{ $t("查询") }}
|
|
261
|
+
</el-button>
|
|
262
|
+
<el-button size="small" @click="reset">
|
|
263
|
+
{{ $t("重置") }}
|
|
264
|
+
</el-button>
|
|
265
|
+
<el-button
|
|
266
|
+
v-if="showCollapse"
|
|
267
|
+
type="primary"
|
|
268
|
+
link
|
|
269
|
+
class="!font-size-12"
|
|
270
|
+
@click="collapsedToggle"
|
|
271
|
+
>
|
|
272
|
+
{{ collapsed ? $t("展开") : $t("收起") }}
|
|
273
|
+
<el-icon class="el-icon--right">
|
|
274
|
+
<component :is="collapsed ? downBold : upBold" />
|
|
275
|
+
</el-icon>
|
|
276
|
+
</el-button>
|
|
277
|
+
</div>
|
|
278
|
+
</ReGridItem>
|
|
279
|
+
</ReGrid>
|
|
280
|
+
</el-form>
|
|
281
|
+
</div>
|
|
282
|
+
|
|
283
|
+
<!-- L3/L6:展示区在 ReTableBar 内;表格+分页同槽,保证空数据也撑满 -->
|
|
284
|
+
<ReTableBar class="bg-bg_color" :columns="[]" @refresh="onSearch">
|
|
285
|
+
<template #title>
|
|
286
|
+
<span class="truncate font-size-16 font-bold">{{ $t("展示列表") }}</span>
|
|
287
|
+
</template>
|
|
288
|
+
<template #buttons>
|
|
289
|
+
<el-button type="primary" size="small" v-auth="['admin']">
|
|
290
|
+
{{ $t("新增") }}
|
|
291
|
+
</el-button>
|
|
292
|
+
</template>
|
|
293
|
+
<template #default="{ size }">
|
|
294
|
+
<div class="vl-parent">
|
|
295
|
+
<el-table
|
|
296
|
+
ref="multipleTableRef"
|
|
297
|
+
v-loading.lock="loading"
|
|
298
|
+
:element-loading-text="$t('加载中')"
|
|
299
|
+
:element-loading-spinner="svgLoading"
|
|
300
|
+
element-loading-svg-view-box="0, 0, 20, 20"
|
|
301
|
+
:style="{ height: `${tableHeight}px` }"
|
|
302
|
+
:size="size"
|
|
303
|
+
:data="tableList"
|
|
304
|
+
:header-cell-style="{
|
|
305
|
+
background: 'var(--el-fill-color-light)',
|
|
306
|
+
color: 'var(--el-text-color-primary)',
|
|
307
|
+
}"
|
|
308
|
+
row-key="id"
|
|
309
|
+
>
|
|
310
|
+
<el-table-column prop="code" :label="$t('编号')" min-width="120" />
|
|
311
|
+
<el-table-column prop="name" :label="$t('名称')" min-width="140" />
|
|
312
|
+
<el-table-column :label="$t('操作')" width="160" fixed="right">
|
|
313
|
+
<template #default="{ row, $index }">
|
|
314
|
+
<ReTableOperate :list="operateButtons" :row="row" :index="$index" />
|
|
315
|
+
</template>
|
|
316
|
+
</el-table-column>
|
|
317
|
+
</el-table>
|
|
318
|
+
|
|
319
|
+
<!-- L7:真实分页 API;ref=paginationRef 参与 tableHeight 计算 -->
|
|
320
|
+
<RePagination
|
|
321
|
+
ref="paginationRef"
|
|
322
|
+
:size="size"
|
|
323
|
+
:pagination="pagination"
|
|
324
|
+
@handle-current-change="handleCurrentChange"
|
|
325
|
+
@handle-size-change="handleSizeChange"
|
|
326
|
+
/>
|
|
327
|
+
</div>
|
|
239
328
|
</template>
|
|
240
|
-
</
|
|
241
|
-
</el-
|
|
242
|
-
|
|
243
|
-
<RePagination
|
|
244
|
-
ref="paginationRef"
|
|
245
|
-
v-model:current-page="pagination.currentPage"
|
|
246
|
-
v-model:page-size="pagination.pageSize"
|
|
247
|
-
:total="pagination.total"
|
|
248
|
-
@change="onSearch"
|
|
249
|
-
/>
|
|
329
|
+
</ReTableBar>
|
|
330
|
+
</el-card>
|
|
250
331
|
</div>
|
|
251
332
|
</template>
|
|
252
333
|
```
|
|
253
334
|
|
|
254
|
-
`useTableSearch(formQuery, searchFn, url)`:`searchFn` 签名为 `(query, url) => Promise<Result<T>>`,分页字段为 `pageNo` / `pageSize`,列表数据取 `data.content`,总数 `data.totalCount
|
|
335
|
+
`useTableSearch(formQuery, searchFn, url)`:`searchFn` 签名为 `(query, url) => Promise<Result<T>>`,分页字段为 `pageNo` / `pageSize`,列表数据取 `data.content`,总数 `data.totalCount`。返回值中的 `content` / `tableHeight` / `paginationRef` / `formRef` 必须按样板绑定,否则动态高度失效。
|
|
255
336
|
|
|
256
337
|
---
|
|
257
338
|
|
package/references/crud-pages.md
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
---
|
|
2
2
|
skill: crud-pages
|
|
3
|
-
description: 查询列表页、表单页、标准 CRUD 流程。含 useTableSearch(onSearch/reset)、ReGrid
|
|
3
|
+
description: 查询列表页、表单页、标准 CRUD 流程。含 useTableSearch(onSearch/reset)、ReGrid 查询区、表格工具栏与动态高度布局。务必按照本文件与 core-kernel 完整示例编写。
|
|
4
4
|
scope: project
|
|
5
|
-
tags: [vue3, crud, list, form, detail, table, useTableSearch, page]
|
|
5
|
+
tags: [vue3, crud, list, form, detail, table, useTableSearch, page, layout]
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# CRUD 页面开发规范
|
|
9
9
|
|
|
10
10
|
> 以下示例即规范真相。
|
|
11
11
|
> 须同时遵守 `core-kernel` / `http-api`。详情跳转见 `detail-page`,回显见 `data-writeback`。
|
|
12
|
+
> **查询列表页完整模板以 `core-kernel.md`「列表黄金样板」为准**(技能内完整示例,勿到仓库里找 views 演示页对照)。
|
|
12
13
|
|
|
13
14
|
## 查询列表页
|
|
14
15
|
|
|
@@ -17,23 +18,40 @@ tags: [vue3, crud, list, form, detail, table, useTableSearch, page]
|
|
|
17
18
|
```typescript
|
|
18
19
|
useTableSearch(formQuery, searchFn, url)
|
|
19
20
|
// searchFn: (query: CombinedQueryData, url: string) => Promise<Result<T>>
|
|
20
|
-
// 返回含:content, tableData, pagination, loading, formRef, onSearch, reset,
|
|
21
|
+
// 返回含:content, tableData, pagination, loading, formRef, onSearch, reset,
|
|
22
|
+
// tableHeight, paginationRef, gridRef, showCollapse, collapsed,
|
|
23
|
+
// collapsedToggle, upBold, downBold, multipleTableRef, ...
|
|
21
24
|
```
|
|
22
25
|
|
|
23
26
|
- 查询:`onSearch()`
|
|
24
27
|
- 重置:`reset()`(内部 `formRef.resetFields()`)
|
|
25
28
|
- 列表:`tableData.value.tableList`(或经 computed 断言)
|
|
26
29
|
- 分页:请求带 `pageNo` / `pageSize`;响应 `data.content` / `data.totalCount`
|
|
30
|
+
- **高度**:`tableHeight` 由 ResizeObserver 根据 `content` / `formRef` / `paginationRef` 计算;三者必须按黄金样板绑定
|
|
31
|
+
|
|
32
|
+
### 布局硬约束(生成列表页必须全部满足)
|
|
33
|
+
|
|
34
|
+
| 区域 | 正确写法 | 禁止 |
|
|
35
|
+
|------|---------|------|
|
|
36
|
+
| 外层容器 | `<el-card ref="content" shadow="never" :style="{ height: \`calc(100vh - 2 * var(--vts-margin) - ${headerHeight}px)\` }">` | `ref` 挂原生 `div`;只用 `minHeight`;漏 `headerHeight` |
|
|
37
|
+
| 查询区 | `el-form` `:inline="true"` + `class="bg-bg_color p-5"` | 无内边距、无背景、字段横向挤成一团 |
|
|
38
|
+
| 栅格 | `ReGrid` `:gap="[0, 20]"` + `:cols="{ xs: 1, sm: 2, md: 2, lg: 3, xl: 4 }"`;`@should-show-collapse` | 无 `gap`;事件写成 `@show-collapse` |
|
|
39
|
+
| 表单项 | `el-form-item` 加 `class="w-[100%] !mr-0"`;`ReGridItem` 用 `index="0"` 字符串 | 表单项无宽度、彼此贴死 |
|
|
40
|
+
| 查询按钮 | `<ReGridItem suffix>` + `flex flex-end` | 用 `index="99"` 冒充后缀 |
|
|
41
|
+
| 展示区 | `ReTableBar class="bg-bg_color"`,与查询区分离 | 查询与表格糊成一块、无标题栏 |
|
|
42
|
+
| 表格高度 | `:style="{ height: \`${tableHeight}px\` }"` | 不绑 `tableHeight`(空数据时分页贴查询区) |
|
|
43
|
+
| 分页 | 放在 `ReTableBar` `#default` 内、表格下方;`RePagination` + `:pagination` + `@handle-current-change` / `@handle-size-change` | 分页在卡片外;虚构 `v-model:current-page` |
|
|
27
44
|
|
|
28
45
|
### 完整列表示例
|
|
29
46
|
|
|
30
47
|
见 `core-kernel.md`「列表黄金样板」。本模块补充约定:
|
|
31
48
|
|
|
32
49
|
1. 查询区用 **`ReGrid` + `ReGridItem`**,折叠与 `collapsed` / `showCollapse` / `collapsedToggle` 配套。
|
|
33
|
-
2. 工具栏用 **`ReTableBar`**,行操作用 **`ReTableOperate
|
|
34
|
-
3. 查询 / 重置按钮文案 `$t('查询')` / `$t('重置')
|
|
50
|
+
2. 工具栏用 **`ReTableBar`**,行操作用 **`ReTableOperate`**(`:list` 非 `:buttons`),分页用 **`RePagination`**。
|
|
51
|
+
3. 查询 / 重置按钮文案 `$t('查询')` / `$t('重置')`(或模板一致的「搜索」),点击绑定 **`onSearch` / `reset`**。
|
|
35
52
|
4. `defineOptions({ name })` 与路由 `name` 一致(keep-alive / 标签)。
|
|
36
53
|
5. 需要权限的按钮加 `v-auth` / `ReAuth`。
|
|
54
|
+
6. 从 `useApp()` 取 **`headerHeight`**、`svgLoading`(及按需 `filterValue` / `baseCodeGet`)。
|
|
37
55
|
|
|
38
56
|
### API 配套
|
|
39
57
|
|
|
@@ -45,11 +63,12 @@ export function getCustomerList(data: CustomerQuery, url: string) {
|
|
|
45
63
|
```
|
|
46
64
|
|
|
47
65
|
```typescript
|
|
48
|
-
const { onSearch, reset, tableData, loading, pagination, ... } =
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
66
|
+
const { onSearch, reset, tableData, loading, pagination, content, tableHeight, ... } =
|
|
67
|
+
useTableSearch(
|
|
68
|
+
formQuery,
|
|
69
|
+
getCustomerList,
|
|
70
|
+
`${import.meta.env.VITE_API_BASE_URL}/console/customer/list`,
|
|
71
|
+
);
|
|
53
72
|
onMounted(() => onSearch());
|
|
54
73
|
```
|
|
55
74
|
|
|
@@ -140,3 +159,11 @@ async function onSubmit() {
|
|
|
140
159
|
- 提交无 loading
|
|
141
160
|
- 业务路由二次 Layout
|
|
142
161
|
- ReSelectQuery 写成不存在的 `:api` / `label-key` API(真实 props 见 form-advanced)
|
|
162
|
+
- **`ref="content"` 挂在 `div` 上**(`content.$el` 为空 → `tableHeight` 恒为 0)
|
|
163
|
+
- 卡片只用 **`minHeight`**,不用固定 **`height`**(页面不饱满、表格高度算错)
|
|
164
|
+
- 查询表单无 `p-5` / 无 `gap` / 表单项无 `w-[100%] !mr-0`(字段挤在一起)
|
|
165
|
+
- 查询区与 `ReTableBar` 无分区(条件与展示糊成一块)
|
|
166
|
+
- 表格不绑 `tableHeight`,分页放在卡片外(无数据时分页直接贴查询条件)
|
|
167
|
+
- `RePagination` 写成 `v-model:current-page`(组件真实 API 为 `:pagination` + handle 事件)
|
|
168
|
+
- `ReTableOperate` 写成 `:buttons`(真实 prop 为 `:list`)
|
|
169
|
+
- `ReGrid` 监听 `@show-collapse`(真实事件为 `@should-show-collapse`)
|
|
@@ -203,7 +203,7 @@ function parseAddress(text: string) {
|
|
|
203
203
|
## ReDialog 通用弹窗(必用)
|
|
204
204
|
|
|
205
205
|
> 落点:`src/components/ReDialog`。基于 `el-dialog` 封装:居中、可拖拽、`destroy-on-close`、默认禁止点遮罩关闭、可选全屏切换、内容区 `el-scrollbar`。
|
|
206
|
-
>
|
|
206
|
+
> 写法以**本模块下方黄金样板**为准(技能自包含,勿依赖仓库 views 演示页)。
|
|
207
207
|
|
|
208
208
|
### Props / 插槽
|
|
209
209
|
|
package/references/icons.md
CHANGED
|
@@ -221,7 +221,7 @@
|
|
|
221
221
|
| **useApp** | 全局应用状态(布局、字典、登出等) | `baseCodeGet(codeType)`、`filterValue()`、`headerHeight`、`svgLoading` |
|
|
222
222
|
| **useDetail** | 详情页跳转与参数传递 | `toDetail({ id, mark, text })`、`initToDetail()`、`getParameter()` |
|
|
223
223
|
| **useFormCheck** | 多表单校验聚合 | 追踪各字段校验状态,emit `form-validate` 事件 |
|
|
224
|
-
| **useTableSearch** | 列表页搜索/分页/Loading | `content`、`tableData`、`pagination`、`loading`、`onSearch()`、`reset()`、`formRef` |
|
|
224
|
+
| **useTableSearch** | 列表页搜索/分页/Loading/动态表高 | `content`(须绑 `el-card`)、`tableHeight`、`paginationRef`、`tableData`、`pagination`、`loading`、`onSearch()`、`reset()`、`formRef`、`gridRef`、`collapsed` / `showCollapse` / `collapsedToggle` |
|
|
225
225
|
| **useTags** | 多标签页管理 | 右键菜单(刷新/关闭/关闭其他/全屏) |
|
|
226
226
|
| **useTheme** | 主题切换(暗色/亮色) | 主题色生成、布局预设、缓存重置 |
|
|
227
227
|
| **useWebSocket** | WebSocket 通信 | `subscribe()`、`send()`、`sendRaw()`、`isConnected` |
|