@agile-team/wl-skills-kit 2.3.0 → 2.3.1
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/CHANGELOG.md +33 -63
- package/README.md +15 -148
- package/bin/wl-skills.js +2 -100
- package/files/.github/guides/README.md +13 -13
- package/files/.github/guides/architecture.md +555 -576
- package/files/.github/guides/usage.md +176 -176
- package/files/.github/reports/README.md +65 -65
- package/files/.github/reports/SYS_DICT_INFO.md +50 -50
- package/files/.github/reports/SYS_MENU_INFO.md +247 -247
- package/files/.github/reports/SYS_PERMISSION_INFO.md +20 -20
- package/files/.github/reports//347/273/204/344/273/266/346/217/220/345/217/226/345/273/272/350/256/256.md +33 -33
- package/files/.github/reports//350/247/204/350/214/203/345/256/241/346/237/245/346/212/245/345/221/212.md +44 -44
- package/files/.github/skills/_compat/README.md +108 -108
- package/files/.github/skills/_compat/headers/agents.txt +8 -8
- package/files/.github/skills/_compat/headers/claude-code.txt +7 -7
- package/files/.github/skills/_compat/headers/cline.txt +7 -7
- package/files/.github/skills/_compat/headers/cursor-mdc.txt +16 -16
- package/files/.github/skills/_compat/headers/cursor-rules.txt +7 -7
- package/files/.github/skills/_compat/headers/github-copilot.txt +1 -1
- package/files/.github/skills/_compat/headers/kiro.txt +10 -10
- package/files/.github/skills/_compat/headers/trae.txt +11 -11
- package/files/.github/skills/_compat/headers/windsurf.txt +7 -7
- package/files/.github/skills/_registry.md +81 -81
- package/files/.github/skills/core/api-contract/SKILL.md +344 -344
- package/files/.github/skills/core/api-contract/USAGE.md +110 -110
- package/files/.github/skills/core/convention-audit/SKILL.md +189 -189
- package/files/.github/skills/core/convention-audit/USAGE.md +99 -99
- package/files/.github/skills/core/page-codegen/SKILL.md +973 -973
- package/files/.github/skills/core/page-codegen/USAGE.md +102 -102
- package/files/.github/skills/core/page-codegen/templates/_index.md +46 -46
- package/files/.github/skills/core/page-codegen/templates/domains/_CONTRIBUTING.md +107 -107
- package/files/.github/skills/core/page-codegen/templates/domains/produce/TPL-OPERATION-STATION.md +442 -442
- package/files/.github/skills/core/page-codegen/templates/domains/sale/README.md +26 -26
- package/files/.github/skills/core/page-codegen/templates/universal/TPL-CHANGE-HISTORY.md +276 -276
- package/files/.github/skills/core/page-codegen/templates/universal/TPL-DETAIL-TABS.md +1145 -1145
- package/files/.github/skills/core/page-codegen/templates/universal/TPL-DRIVEN.md +124 -124
- package/files/.github/skills/core/page-codegen/templates/universal/TPL-FORM-ROUTE.md +436 -436
- package/files/.github/skills/core/page-codegen/templates/universal/TPL-LIST.md +191 -191
- package/files/.github/skills/core/page-codegen/templates/universal/TPL-MASTER-DETAIL.md +148 -148
- package/files/.github/skills/core/page-codegen/templates/universal/TPL-RECORD-FORM.md +376 -376
- package/files/.github/skills/core/page-codegen/templates/universal/TPL-TREE-LIST.md +186 -186
- package/files/.github/skills/core/prototype-scan/SKILL.md +498 -498
- package/files/.github/skills/core/prototype-scan/USAGE.md +95 -95
- package/files/.github/skills/core/template-extract/SKILL.md +139 -139
- package/files/.github/skills/core/template-extract/USAGE.md +93 -93
- package/files/.github/skills/domain/README.md +51 -51
- package/files/.github/skills/sync/menu-sync/SKILL.md +263 -263
- package/files/.github/skills/sync/menu-sync/USAGE.md +104 -104
- package/files/.github/skills/sync/menu-sync/env/env.local.json +7 -7
- package/files/.github/skills/sync/menu-sync/env/guide.md +99 -99
- package/files/.github/skills/sync/permission-sync/SKILL.draft.md +91 -91
- package/files/.github/standards/01-toolchain.md +57 -57
- package/files/.github/standards/02-code-structure.md +111 -111
- package/files/.github/standards/03-comments.md +53 -53
- package/files/.github/standards/04-coding-basics.md +33 -33
- package/files/.github/standards/05-logging.md +38 -38
- package/files/.github/standards/06-security.md +44 -44
- package/files/.github/standards/07-config.md +52 -52
- package/files/.github/standards/08-git.md +60 -60
- package/files/.github/standards/09-typescript.md +71 -71
- package/files/.github/standards/10-pinia.md +57 -57
- package/files/.github/standards/11-form-validation.md +81 -81
- package/files/.github/standards/12-base-table.md +153 -153
- package/files/.github/standards/13-platform-components.md +123 -123
- package/files/.github/standards/index.md +89 -89
- package/files/demo/produce/aiflow/mmwr-customer-apply-add/api.md +1 -1
- package/files/demo/produce/aiflow/mmwr-customer-apply-change-history/data.ts +196 -196
- package/files/demo/produce/aiflow/mmwr-customer-apply-change-history/index.scss +150 -150
- package/files/demo/produce/aiflow/mmwr-customer-apply-change-history/index.vue +79 -79
- package/files/docs/jh-date-range.md +257 -257
- package/files/docs/jh-date.md +222 -222
- package/files/docs/jh-dept-picker.md +190 -190
- package/files/docs/jh-drag-row.md +590 -590
- package/files/docs/jh-file-upload.md +216 -216
- package/files/docs/jh-picker.md +218 -218
- package/files/docs/jh-select.md +148 -148
- package/files/docs/jh-text.md +248 -248
- package/files/docs/jh-user-picker.md +197 -197
- package/files/docs/request.md +24 -9
- package/files/src/components/global/C_RightToolbar/data.ts +228 -0
- package/files/src/components/global/C_RightToolbar/index.scss +44 -0
- package/files/src/components/global/C_RightToolbar/index.vue +34 -336
- package/files/src/components/global/C_Splitter/index.scss +61 -0
- package/files/src/components/global/C_Splitter/index.vue +2 -64
- package/files/src/components/global/C_SvgIcon/index.scss +15 -0
- package/files/src/components/global/C_SvgIcon/index.vue +20 -50
- package/files/src/components/global/C_TagStatus/index.scss +20 -0
- package/files/src/components/global/C_TagStatus/index.vue +1 -22
- package/files/src/components/global/C_Tree/data.ts +61 -0
- package/files/src/components/global/C_Tree/index.vue +12 -53
- package/files/src/components/local/c_listModal/index.scss +4 -0
- package/files/src/components/local/c_listModal/index.vue +1 -1
- package/package.json +5 -9
|
@@ -1,197 +1,197 @@
|
|
|
1
|
-
# jh-user-picker - 用户选择组件
|
|
2
|
-
|
|
3
|
-
> 平台统一的用户挑选组件,用于选择单个或多个用户,内置用户数据加载与回显逻辑
|
|
4
|
-
|
|
5
|
-
## 📦 组件位置
|
|
6
|
-
|
|
7
|
-
```ts
|
|
8
|
-
import "@jhlc/common-core";
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
组件已全局注册,可直接在模板中使用 `<jh-user-picker />`。
|
|
12
|
-
|
|
13
|
-
---
|
|
14
|
-
|
|
15
|
-
## 基本用法
|
|
16
|
-
|
|
17
|
-
### 1️⃣ 单选用户(最常用)
|
|
18
|
-
|
|
19
|
-
```vue
|
|
20
|
-
<template>
|
|
21
|
-
<jh-user-picker v-model="form.userId" placeholder="请选择负责人" />
|
|
22
|
-
</template>
|
|
23
|
-
|
|
24
|
-
<script setup lang="ts">
|
|
25
|
-
import { ref } from "vue";
|
|
26
|
-
|
|
27
|
-
const form = ref({
|
|
28
|
-
userId: ""
|
|
29
|
-
});
|
|
30
|
-
</script>
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
---
|
|
34
|
-
|
|
35
|
-
### 2️⃣ 多选用户
|
|
36
|
-
|
|
37
|
-
```vue
|
|
38
|
-
<jh-user-picker v-model="form.userIds" multiple placeholder="请选择相关人员" />
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
---
|
|
42
|
-
|
|
43
|
-
## Props 属性
|
|
44
|
-
|
|
45
|
-
| 参数 | 说明 | 类型 | 默认值 |
|
|
46
|
-
| -------------------- | ---------------------- | --------------------- | ---------------- |
|
|
47
|
-
| modelValue / v-model | 绑定值 | `string \| string[]` | - |
|
|
48
|
-
| multiple | 是否多选 | `boolean` | `false` |
|
|
49
|
-
| placeholder | 占位提示 | `string` | `"请选择用户"` |
|
|
50
|
-
| disabled | 是否禁用 | `boolean` | `false` |
|
|
51
|
-
| clearable | 是否可清空 | `boolean` | `true` |
|
|
52
|
-
| dataType | 返回数据类型(多选时) | `"array" \| "string"` | `"array"` |
|
|
53
|
-
| dialogTitle | 弹窗标题 | `string` | `"选择用户"` |
|
|
54
|
-
| dialogWidth | 弹窗宽度 | `string` | `"800px"` |
|
|
55
|
-
| searchPlaceholder | 搜索框占位文本 | `string` | `"请输入用户名"` |
|
|
56
|
-
|
|
57
|
-
> **重点**: 多选时,`dataType="string"` 会返回逗号分隔的字符串,`dataType="array"` 返回数组。
|
|
58
|
-
|
|
59
|
-
---
|
|
60
|
-
|
|
61
|
-
## Events 事件
|
|
62
|
-
|
|
63
|
-
| 事件名 | 说明 | 回调参数 |
|
|
64
|
-
| ----------------- | ------------------------ | ------------------------------------- |
|
|
65
|
-
| update:modelValue | v-model 更新 | `(value: string \| string[]) => void` |
|
|
66
|
-
| confirm | 确认选择时触发 | `() => void` |
|
|
67
|
-
| clear | 清空时触发 | `() => void` |
|
|
68
|
-
| blur | 失去焦点时触发 | `() => void` |
|
|
69
|
-
| closed | 弹窗关闭时触发 | `() => void` |
|
|
70
|
-
| remove | 移除选中项时触发(多选) | `() => void` |
|
|
71
|
-
|
|
72
|
-
---
|
|
73
|
-
|
|
74
|
-
## 常见场景
|
|
75
|
-
|
|
76
|
-
### 场景 1:表单负责人选择
|
|
77
|
-
|
|
78
|
-
```vue
|
|
79
|
-
<jh-user-picker v-model="form.ownerId" placeholder="请选择负责人" />
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
---
|
|
83
|
-
|
|
84
|
-
### 场景 2:查询条件(多选)
|
|
85
|
-
|
|
86
|
-
```vue
|
|
87
|
-
<jh-user-picker v-model="query.userIds" multiple placeholder="请选择用户" />
|
|
88
|
-
```
|
|
89
|
-
|
|
90
|
-
---
|
|
91
|
-
|
|
92
|
-
### 场景 3:详情页只读展示(配合 jh-text)
|
|
93
|
-
|
|
94
|
-
```vue
|
|
95
|
-
<jh-text type="user" :value="detail.userId" />
|
|
96
|
-
```
|
|
97
|
-
|
|
98
|
-
> ⚠️ `jh-user-picker` 仅用于选择,展示请使用 `jh-text`
|
|
99
|
-
|
|
100
|
-
---
|
|
101
|
-
|
|
102
|
-
## 与手动实现对比
|
|
103
|
-
|
|
104
|
-
### 使用 jh-user-picker(推荐)
|
|
105
|
-
|
|
106
|
-
```vue
|
|
107
|
-
<jh-user-picker v-model="form.userId" />
|
|
108
|
-
```
|
|
109
|
-
|
|
110
|
-
### 手动实现(不推荐)
|
|
111
|
-
|
|
112
|
-
```vue
|
|
113
|
-
<el-select v-model="form.userId">
|
|
114
|
-
<el-option
|
|
115
|
-
v-for="user in userList"
|
|
116
|
-
:key="user.id"
|
|
117
|
-
:label="user.name"
|
|
118
|
-
:value="user.id"
|
|
119
|
-
/>
|
|
120
|
-
</el-select>
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
❌ 需要自己加载用户列表
|
|
124
|
-
❌ 需要处理回显
|
|
125
|
-
❌ 每个页面重复实现
|
|
126
|
-
|
|
127
|
-
---
|
|
128
|
-
|
|
129
|
-
## 最佳实践
|
|
130
|
-
|
|
131
|
-
### 1️⃣ 编辑 & 展示分离
|
|
132
|
-
|
|
133
|
-
| 场景 | 编辑 | 展示 |
|
|
134
|
-
| ---- | -------------- | ------- |
|
|
135
|
-
| 用户 | jh-user-picker | jh-text |
|
|
136
|
-
|
|
137
|
-
---
|
|
138
|
-
|
|
139
|
-
### 2️⃣ 表单中直接使用 v-model
|
|
140
|
-
|
|
141
|
-
```vue
|
|
142
|
-
<jh-user-picker v-model="form.userId" />
|
|
143
|
-
```
|
|
144
|
-
|
|
145
|
-
避免手动监听 `change` 事件
|
|
146
|
-
|
|
147
|
-
---
|
|
148
|
-
|
|
149
|
-
### 3️⃣ 多选返回值说明
|
|
150
|
-
|
|
151
|
-
```ts
|
|
152
|
-
// 单选
|
|
153
|
-
userId: "u001";
|
|
154
|
-
|
|
155
|
-
// 多选
|
|
156
|
-
userIds: ["u001", "u002"];
|
|
157
|
-
```
|
|
158
|
-
|
|
159
|
-
---
|
|
160
|
-
|
|
161
|
-
## 注意事项
|
|
162
|
-
|
|
163
|
-
1. **组件内部已处理用户数据加载**
|
|
164
|
-
- 不需要手动请求接口
|
|
165
|
-
- 支持自动回显
|
|
166
|
-
|
|
167
|
-
2. **仅返回用户 ID**
|
|
168
|
-
- 展示用户名称请使用 `jh-text`
|
|
169
|
-
|
|
170
|
-
3. **多选时注意字段类型**
|
|
171
|
-
- 必须使用数组接收
|
|
172
|
-
|
|
173
|
-
---
|
|
174
|
-
|
|
175
|
-
## 🎯 真实项目示例
|
|
176
|
-
|
|
177
|
-
### 示例 1:新增页面
|
|
178
|
-
|
|
179
|
-
```vue
|
|
180
|
-
<jh-user-picker v-model="form.createUserId" />
|
|
181
|
-
```
|
|
182
|
-
|
|
183
|
-
### 示例 2:查询条件
|
|
184
|
-
|
|
185
|
-
```vue
|
|
186
|
-
<jh-user-picker v-model="query.userIds" multiple />
|
|
187
|
-
```
|
|
188
|
-
|
|
189
|
-
---
|
|
190
|
-
|
|
191
|
-
## 🚀 快速开始
|
|
192
|
-
|
|
193
|
-
- **单选**:直接使用 v-model
|
|
194
|
-
- **多选**:添加 `multiple`
|
|
195
|
-
- **展示**:统一使用 `jh-text type="user"`
|
|
196
|
-
|
|
197
|
-
**推荐作为平台统一的用户选择组件使用!**
|
|
1
|
+
# jh-user-picker - 用户选择组件
|
|
2
|
+
|
|
3
|
+
> 平台统一的用户挑选组件,用于选择单个或多个用户,内置用户数据加载与回显逻辑
|
|
4
|
+
|
|
5
|
+
## 📦 组件位置
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
import "@jhlc/common-core";
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
组件已全局注册,可直接在模板中使用 `<jh-user-picker />`。
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## 基本用法
|
|
16
|
+
|
|
17
|
+
### 1️⃣ 单选用户(最常用)
|
|
18
|
+
|
|
19
|
+
```vue
|
|
20
|
+
<template>
|
|
21
|
+
<jh-user-picker v-model="form.userId" placeholder="请选择负责人" />
|
|
22
|
+
</template>
|
|
23
|
+
|
|
24
|
+
<script setup lang="ts">
|
|
25
|
+
import { ref } from "vue";
|
|
26
|
+
|
|
27
|
+
const form = ref({
|
|
28
|
+
userId: ""
|
|
29
|
+
});
|
|
30
|
+
</script>
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
### 2️⃣ 多选用户
|
|
36
|
+
|
|
37
|
+
```vue
|
|
38
|
+
<jh-user-picker v-model="form.userIds" multiple placeholder="请选择相关人员" />
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## Props 属性
|
|
44
|
+
|
|
45
|
+
| 参数 | 说明 | 类型 | 默认值 |
|
|
46
|
+
| -------------------- | ---------------------- | --------------------- | ---------------- |
|
|
47
|
+
| modelValue / v-model | 绑定值 | `string \| string[]` | - |
|
|
48
|
+
| multiple | 是否多选 | `boolean` | `false` |
|
|
49
|
+
| placeholder | 占位提示 | `string` | `"请选择用户"` |
|
|
50
|
+
| disabled | 是否禁用 | `boolean` | `false` |
|
|
51
|
+
| clearable | 是否可清空 | `boolean` | `true` |
|
|
52
|
+
| dataType | 返回数据类型(多选时) | `"array" \| "string"` | `"array"` |
|
|
53
|
+
| dialogTitle | 弹窗标题 | `string` | `"选择用户"` |
|
|
54
|
+
| dialogWidth | 弹窗宽度 | `string` | `"800px"` |
|
|
55
|
+
| searchPlaceholder | 搜索框占位文本 | `string` | `"请输入用户名"` |
|
|
56
|
+
|
|
57
|
+
> **重点**: 多选时,`dataType="string"` 会返回逗号分隔的字符串,`dataType="array"` 返回数组。
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## Events 事件
|
|
62
|
+
|
|
63
|
+
| 事件名 | 说明 | 回调参数 |
|
|
64
|
+
| ----------------- | ------------------------ | ------------------------------------- |
|
|
65
|
+
| update:modelValue | v-model 更新 | `(value: string \| string[]) => void` |
|
|
66
|
+
| confirm | 确认选择时触发 | `() => void` |
|
|
67
|
+
| clear | 清空时触发 | `() => void` |
|
|
68
|
+
| blur | 失去焦点时触发 | `() => void` |
|
|
69
|
+
| closed | 弹窗关闭时触发 | `() => void` |
|
|
70
|
+
| remove | 移除选中项时触发(多选) | `() => void` |
|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
|
|
74
|
+
## 常见场景
|
|
75
|
+
|
|
76
|
+
### 场景 1:表单负责人选择
|
|
77
|
+
|
|
78
|
+
```vue
|
|
79
|
+
<jh-user-picker v-model="form.ownerId" placeholder="请选择负责人" />
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
### 场景 2:查询条件(多选)
|
|
85
|
+
|
|
86
|
+
```vue
|
|
87
|
+
<jh-user-picker v-model="query.userIds" multiple placeholder="请选择用户" />
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
### 场景 3:详情页只读展示(配合 jh-text)
|
|
93
|
+
|
|
94
|
+
```vue
|
|
95
|
+
<jh-text type="user" :value="detail.userId" />
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
> ⚠️ `jh-user-picker` 仅用于选择,展示请使用 `jh-text`
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
## 与手动实现对比
|
|
103
|
+
|
|
104
|
+
### 使用 jh-user-picker(推荐)
|
|
105
|
+
|
|
106
|
+
```vue
|
|
107
|
+
<jh-user-picker v-model="form.userId" />
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### 手动实现(不推荐)
|
|
111
|
+
|
|
112
|
+
```vue
|
|
113
|
+
<el-select v-model="form.userId">
|
|
114
|
+
<el-option
|
|
115
|
+
v-for="user in userList"
|
|
116
|
+
:key="user.id"
|
|
117
|
+
:label="user.name"
|
|
118
|
+
:value="user.id"
|
|
119
|
+
/>
|
|
120
|
+
</el-select>
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
❌ 需要自己加载用户列表
|
|
124
|
+
❌ 需要处理回显
|
|
125
|
+
❌ 每个页面重复实现
|
|
126
|
+
|
|
127
|
+
---
|
|
128
|
+
|
|
129
|
+
## 最佳实践
|
|
130
|
+
|
|
131
|
+
### 1️⃣ 编辑 & 展示分离
|
|
132
|
+
|
|
133
|
+
| 场景 | 编辑 | 展示 |
|
|
134
|
+
| ---- | -------------- | ------- |
|
|
135
|
+
| 用户 | jh-user-picker | jh-text |
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
### 2️⃣ 表单中直接使用 v-model
|
|
140
|
+
|
|
141
|
+
```vue
|
|
142
|
+
<jh-user-picker v-model="form.userId" />
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
避免手动监听 `change` 事件
|
|
146
|
+
|
|
147
|
+
---
|
|
148
|
+
|
|
149
|
+
### 3️⃣ 多选返回值说明
|
|
150
|
+
|
|
151
|
+
```ts
|
|
152
|
+
// 单选
|
|
153
|
+
userId: "u001";
|
|
154
|
+
|
|
155
|
+
// 多选
|
|
156
|
+
userIds: ["u001", "u002"];
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
---
|
|
160
|
+
|
|
161
|
+
## 注意事项
|
|
162
|
+
|
|
163
|
+
1. **组件内部已处理用户数据加载**
|
|
164
|
+
- 不需要手动请求接口
|
|
165
|
+
- 支持自动回显
|
|
166
|
+
|
|
167
|
+
2. **仅返回用户 ID**
|
|
168
|
+
- 展示用户名称请使用 `jh-text`
|
|
169
|
+
|
|
170
|
+
3. **多选时注意字段类型**
|
|
171
|
+
- 必须使用数组接收
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
175
|
+
## 🎯 真实项目示例
|
|
176
|
+
|
|
177
|
+
### 示例 1:新增页面
|
|
178
|
+
|
|
179
|
+
```vue
|
|
180
|
+
<jh-user-picker v-model="form.createUserId" />
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
### 示例 2:查询条件
|
|
184
|
+
|
|
185
|
+
```vue
|
|
186
|
+
<jh-user-picker v-model="query.userIds" multiple />
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
---
|
|
190
|
+
|
|
191
|
+
## 🚀 快速开始
|
|
192
|
+
|
|
193
|
+
- **单选**:直接使用 v-model
|
|
194
|
+
- **多选**:添加 `multiple`
|
|
195
|
+
- **展示**:统一使用 `jh-text type="user"`
|
|
196
|
+
|
|
197
|
+
**推荐作为平台统一的用户选择组件使用!**
|
package/files/docs/request.md
CHANGED
|
@@ -78,23 +78,32 @@ const result = await request({
|
|
|
78
78
|
|
|
79
79
|
## 响应数据结构
|
|
80
80
|
|
|
81
|
-
request
|
|
81
|
+
`request()` 的 response interceptor 执行 `return cloneDeep(res.data)`,直接返回 HTTP 响应体,即 `ApiResult<T>` 结构:
|
|
82
82
|
|
|
83
83
|
```typescript
|
|
84
|
-
//
|
|
84
|
+
// ApiResult<T> 类型定义(来自 @jhlc/types)
|
|
85
|
+
interface ApiResult<T = any> {
|
|
86
|
+
code: number; // 2000 = 成功,200 也被接受(mock 常用)
|
|
87
|
+
message: string; // 提示信息
|
|
88
|
+
data: T; // 实际业务数据
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// 后端返回的 HTTP 响应体(request() 直接返回这层,不再多一层 .data)
|
|
85
92
|
{
|
|
86
|
-
code:
|
|
93
|
+
code: 2000,
|
|
87
94
|
message: "操作成功",
|
|
88
95
|
data: { id: 1, name: "张三" }
|
|
89
96
|
}
|
|
90
97
|
|
|
91
|
-
// 使用 request
|
|
98
|
+
// 使用 request 后直接拿到
|
|
92
99
|
const result = await request({ url: "/api/xxx", method: "get" });
|
|
93
|
-
console.log(result.code); //
|
|
100
|
+
console.log(result.code); // 2000
|
|
94
101
|
console.log(result.message); // "操作成功"
|
|
95
|
-
console.log(result.data); // { id: 1, name: "张三" }
|
|
102
|
+
console.log(result.data); // { id: 1, name: "张三" }(业务数据)
|
|
96
103
|
```
|
|
97
104
|
|
|
105
|
+
> **与 `getAction`/`postAction` 的关系**:这些 action 函数内部调用 `request()`,返回值结构**完全相同**,类型签名为 `Promise<ApiResult<T>>`。`ApiResult<T>` 就是上面的 `{ code, message, data: T }`,两者等价。
|
|
106
|
+
|
|
98
107
|
## 常见场景
|
|
99
108
|
|
|
100
109
|
### 场景 1:列表查询
|
|
@@ -254,8 +263,8 @@ await request({ url: "/api/user/detail", method: "get", params: { id: 1 } });
|
|
|
254
263
|
1. **自动解包**:返回 `{ code, message, data }` 格式,无需 `res.data.data`
|
|
255
264
|
2. **Token 注入**:自动从 localStorage 读取 token 并添加到请求头
|
|
256
265
|
3. **错误拦截**:统一处理 401/403/500 等错误,自动弹出提示
|
|
257
|
-
4.
|
|
258
|
-
5.
|
|
266
|
+
4. **双码成功**:`code: 2000`(真实后端业务码)和 `code: 200`(mock 常用值)均视为成功
|
|
267
|
+
5. **i18n 支持**:自动在请求头注入当前语言(`Lang` header)
|
|
259
268
|
|
|
260
269
|
## 在组件中使用
|
|
261
270
|
|
|
@@ -326,8 +335,9 @@ try {
|
|
|
326
335
|
2. **params vs data**:
|
|
327
336
|
- `params`:URL 查询参数,适用于 GET/DELETE
|
|
328
337
|
- `data`:请求体数据,适用于 POST/PUT
|
|
329
|
-
3.
|
|
338
|
+
3. **返回值**:直接返回 `ApiResult<T>` = `{ code, message, data }`,不是 axios 的 response 对象(已自动解包一层)
|
|
330
339
|
4. **错误处理**:request 会自动处理错误并提示,通常不需要手动 catch
|
|
340
|
+
5. **mock 的 code 值**:response interceptor 同时接受 `code: 200` 和 `code: 2000` 作为成功。项目 mock 文件通常写 `code: 200`(HTTP 标准),真实后端使用业务码 `code: 2000`,两者均正常工作
|
|
331
341
|
|
|
332
342
|
## 实际案例
|
|
333
343
|
|
|
@@ -424,6 +434,11 @@ export function createFormModal(props, mode, emit) {
|
|
|
424
434
|
## 📌 AbstractPageQueryHook 基类内置方法
|
|
425
435
|
|
|
426
436
|
> 在继承 `AbstractPageQueryHook` 的页面中,可以直接使用以下内置方法,**无需单独创建 API 层文件**。
|
|
437
|
+
> 这些方法(`getAction`/`postAction`/`putAction`/`deleteAction`)也可以单独导入使用:
|
|
438
|
+
> ```typescript
|
|
439
|
+
> import { getAction, postAction, putAction, deleteAction } from "@jhlc/common-core/src/api/action";
|
|
440
|
+
> ```
|
|
441
|
+
> 返回值均为 `Promise<ApiResult<T>>`,即 `{ code: number, message: string, data: T }`,与直接调用 `request()` 完全相同。
|
|
427
442
|
|
|
428
443
|
### 可用方法一览
|
|
429
444
|
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
import { ref, computed, watch, nextTick } from "vue";
|
|
2
|
+
import { postAction, getAction } from "@jhlc/common-core/src/api/action";
|
|
3
|
+
import Sortable from "sortablejs";
|
|
4
|
+
import { deepClone } from "@/util";
|
|
5
|
+
import { useI18n } from "vue-i18n";
|
|
6
|
+
|
|
7
|
+
// ===== 类型定义 =====
|
|
8
|
+
export interface RightToolbarProps {
|
|
9
|
+
proVisible?: boolean;
|
|
10
|
+
showSearch?: boolean;
|
|
11
|
+
columns?: any[];
|
|
12
|
+
tableId?: string;
|
|
13
|
+
initialColumns?: any[];
|
|
14
|
+
id?: string;
|
|
15
|
+
propsValue?: any[];
|
|
16
|
+
disabled?: boolean;
|
|
17
|
+
showRefreshTool?: boolean;
|
|
18
|
+
showSearchTool?: boolean;
|
|
19
|
+
showColumnTool?: boolean;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// ===== 组件逻辑 =====
|
|
23
|
+
export function createRightToolbar(
|
|
24
|
+
props: RightToolbarProps,
|
|
25
|
+
emits: (event: string, ...args: any[]) => void,
|
|
26
|
+
) {
|
|
27
|
+
const { t } = useI18n();
|
|
28
|
+
|
|
29
|
+
const transferRef = ref<any>(null);
|
|
30
|
+
const data = ref<any[]>([]);
|
|
31
|
+
const title = ref(t("设置显示字段"));
|
|
32
|
+
const open = ref(false);
|
|
33
|
+
const showFields = ref("");
|
|
34
|
+
const hideFields = ref("");
|
|
35
|
+
const defaultColumns = ref<any[]>([]);
|
|
36
|
+
const columnId = ref("");
|
|
37
|
+
const emitTemp = ref<any[]>([]);
|
|
38
|
+
const submitLoading = ref(false);
|
|
39
|
+
|
|
40
|
+
const modelValue = ref<any[]>([]);
|
|
41
|
+
watch(
|
|
42
|
+
() => props.propsValue,
|
|
43
|
+
(newVal) => {
|
|
44
|
+
modelValue.value = newVal ?? [];
|
|
45
|
+
},
|
|
46
|
+
{ immediate: true },
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
const columnsProp = computed(() => props.columns);
|
|
50
|
+
const showSearchProp = computed(() => props.showSearch);
|
|
51
|
+
|
|
52
|
+
const temp = computed(() => data.value.filter((item) => item.visible));
|
|
53
|
+
|
|
54
|
+
// 搜索
|
|
55
|
+
function toggleSearch() {
|
|
56
|
+
emits("update:showSearch", !props.showSearch);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// 刷新
|
|
60
|
+
function refresh() {
|
|
61
|
+
emits("queryTable");
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// 关闭弹窗
|
|
65
|
+
const closeDialog = () => {
|
|
66
|
+
initColumns();
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
// 穿梭变化
|
|
70
|
+
function dataChange(val: any[]) {
|
|
71
|
+
if (val) {
|
|
72
|
+
data.value.forEach((item) => {
|
|
73
|
+
const index = val.findIndex((ele) => ele === item.key);
|
|
74
|
+
item.visible = index === -1;
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// 打开显隐列dialog
|
|
80
|
+
function showColumn() {
|
|
81
|
+
open.value = true;
|
|
82
|
+
data.value = [];
|
|
83
|
+
defaultColumns.value = [];
|
|
84
|
+
props.columns?.forEach((item) => {
|
|
85
|
+
defaultColumns.value.push(deepClone(item));
|
|
86
|
+
data.value.push(deepClone(item));
|
|
87
|
+
});
|
|
88
|
+
nextTick(() => {
|
|
89
|
+
const transfer = transferRef.value.$el;
|
|
90
|
+
const leftPanel = transfer
|
|
91
|
+
.getElementsByClassName("el-transfer-panel")[0]
|
|
92
|
+
.getElementsByClassName("el-transfer-panel__body")[0];
|
|
93
|
+
const leftEl = leftPanel.getElementsByClassName(
|
|
94
|
+
"el-transfer-panel__list",
|
|
95
|
+
)[0];
|
|
96
|
+
Sortable.create(leftEl, {
|
|
97
|
+
onEnd: (evt: any) => {
|
|
98
|
+
const { oldIndex, newIndex } = evt;
|
|
99
|
+
const visible = data.value.filter((item) => item.visible);
|
|
100
|
+
const [moved] = visible.splice(oldIndex, 1);
|
|
101
|
+
visible.splice(newIndex, 0, moved);
|
|
102
|
+
const hidden = data.value.filter((item) => !item.visible);
|
|
103
|
+
data.value = [...visible, ...hidden];
|
|
104
|
+
},
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// 恢复工具
|
|
110
|
+
function recoverTool(recoverData: any[]) {
|
|
111
|
+
const dataTemp: any[] = [];
|
|
112
|
+
const modelValueTemp: any[] = [];
|
|
113
|
+
recoverData.forEach((item) => {
|
|
114
|
+
dataTemp.push(deepClone(item));
|
|
115
|
+
if (!item.visible) modelValueTemp.push(item.key);
|
|
116
|
+
});
|
|
117
|
+
data.value = dataTemp;
|
|
118
|
+
modelValue.value = modelValueTemp;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function initColumns() {
|
|
122
|
+
recoverTool(defaultColumns.value);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function clearValue() {
|
|
126
|
+
recoverTool(props.initialColumns ?? []);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function cancelBtn() {
|
|
130
|
+
initColumns();
|
|
131
|
+
open.value = false;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function submitBtn() {
|
|
135
|
+
showFields.value = "";
|
|
136
|
+
hideFields.value = "";
|
|
137
|
+
temp.value.forEach((item) => {
|
|
138
|
+
showFields.value += `${item.key},`;
|
|
139
|
+
});
|
|
140
|
+
hideFields.value = modelValue.value.join();
|
|
141
|
+
const paramsList = {
|
|
142
|
+
id: columnId.value || props.id,
|
|
143
|
+
tableId: props.tableId,
|
|
144
|
+
showFields: showFields.value,
|
|
145
|
+
hideFields: hideFields.value,
|
|
146
|
+
};
|
|
147
|
+
submitLoading.value = true;
|
|
148
|
+
postAction("system/pageShowFields/saveOrUpdate", paramsList)
|
|
149
|
+
.then((res: any) => {
|
|
150
|
+
temp.value.forEach((item) => emitTemp.value.push(item));
|
|
151
|
+
data.value.forEach((item) => {
|
|
152
|
+
if (!item.visible) emitTemp.value.push(item);
|
|
153
|
+
});
|
|
154
|
+
columnId.value = res.data.id;
|
|
155
|
+
emits("update:columns", emitTemp.value);
|
|
156
|
+
emits("update:propsValue", modelValue.value);
|
|
157
|
+
emitTemp.value = [];
|
|
158
|
+
})
|
|
159
|
+
.catch(() => initColumns())
|
|
160
|
+
.finally(() => {
|
|
161
|
+
submitLoading.value = false;
|
|
162
|
+
open.value = false;
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function getColumns(columnsInfo: any) {
|
|
167
|
+
const _columnsInfo = deepClone(columnsInfo);
|
|
168
|
+
_columnsInfo.propsValue = [];
|
|
169
|
+
return getAction(
|
|
170
|
+
`/system/pageShowFields/getByTableIdAndUserNo?tableId=${_columnsInfo.tableId}`,
|
|
171
|
+
).then((res: any) => {
|
|
172
|
+
if (res.data) {
|
|
173
|
+
_columnsInfo.id = res.data.id;
|
|
174
|
+
let showArr: string[] | undefined;
|
|
175
|
+
if (res.data.showFields) showArr = res.data.showFields.split(",");
|
|
176
|
+
let hideArr: string[] | undefined;
|
|
177
|
+
if (res.data.hideFields) hideArr = res.data.hideFields.split(",");
|
|
178
|
+
|
|
179
|
+
let flag: any[] = [];
|
|
180
|
+
if (showArr) {
|
|
181
|
+
flag = showArr.reduce((acc: any[], key: string) => {
|
|
182
|
+
const value = _columnsInfo.columns.find((v: any) => v.key === key);
|
|
183
|
+
if (value) acc.push(value);
|
|
184
|
+
return acc;
|
|
185
|
+
}, []);
|
|
186
|
+
}
|
|
187
|
+
const result: any[] = [...new Set(flag.concat(_columnsInfo.columns))];
|
|
188
|
+
_columnsInfo.columns = result;
|
|
189
|
+
|
|
190
|
+
_columnsInfo.columns.forEach((item: any) => {
|
|
191
|
+
if (showArr) {
|
|
192
|
+
item.visible = showArr.includes(item.key) ? true : item.visible;
|
|
193
|
+
}
|
|
194
|
+
if (hideArr) {
|
|
195
|
+
item.visible = hideArr.includes(item.key) ? false : item.visible;
|
|
196
|
+
}
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
if (hideArr) {
|
|
200
|
+
hideArr.forEach((item: string) => _columnsInfo.propsValue.push(item));
|
|
201
|
+
}
|
|
202
|
+
return _columnsInfo;
|
|
203
|
+
}
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
return {
|
|
208
|
+
transferRef,
|
|
209
|
+
data,
|
|
210
|
+
title,
|
|
211
|
+
open,
|
|
212
|
+
modelValue,
|
|
213
|
+
columnsProp,
|
|
214
|
+
showSearchProp,
|
|
215
|
+
temp,
|
|
216
|
+
submitLoading,
|
|
217
|
+
toggleSearch,
|
|
218
|
+
refresh,
|
|
219
|
+
closeDialog,
|
|
220
|
+
dataChange,
|
|
221
|
+
showColumn,
|
|
222
|
+
initColumns,
|
|
223
|
+
clearValue,
|
|
224
|
+
cancelBtn,
|
|
225
|
+
submitBtn,
|
|
226
|
+
getColumns,
|
|
227
|
+
};
|
|
228
|
+
}
|