jianghu-ui 1.0.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/README.md +376 -0
- package/dist/jianghu-ui.css +2318 -0
- package/dist/jianghu-ui.js +2 -0
- package/dist/jianghu-ui.js.LICENSE.txt +1 -0
- package/package.json +56 -0
- package/src/Design.stories.mdx +195 -0
- package/src/Introduction.stories.mdx +148 -0
- package/src/components/JhAddressSelect/JhAddressSelect.md +250 -0
- package/src/components/JhAddressSelect/JhAddressSelect.stories.js +282 -0
- package/src/components/JhAddressSelect/JhAddressSelect.vue +261 -0
- package/src/components/JhCard/JhCard.md +246 -0
- package/src/components/JhCard/JhCard.stories.js +688 -0
- package/src/components/JhCard/JhCard.vue +604 -0
- package/src/components/JhCheckCard/JhCheckCard.md +245 -0
- package/src/components/JhCheckCard/JhCheckCard.stories.js +750 -0
- package/src/components/JhCheckCard/JhCheckCard.vue +476 -0
- package/src/components/JhConfirmDialog/JhConfirmDialog.md +70 -0
- package/src/components/JhConfirmDialog/JhConfirmDialog.stories.js +550 -0
- package/src/components/JhConfirmDialog/JhConfirmDialog.vue +181 -0
- package/src/components/JhDateRangePicker/JhDateRangePicker.md +56 -0
- package/src/components/JhDateRangePicker/JhDateRangePicker.stories.js +320 -0
- package/src/components/JhDateRangePicker/JhDateRangePicker.vue +307 -0
- package/src/components/JhDescriptions/JhDescriptions.md +724 -0
- package/src/components/JhDescriptions/JhDescriptions.stories.js +858 -0
- package/src/components/JhDescriptions/JhDescriptions.vue +933 -0
- package/src/components/JhDraggable/JhDraggable.md +66 -0
- package/src/components/JhDraggable/JhDraggable.stories.js +161 -0
- package/src/components/JhDraggable/JhDraggable.vue +254 -0
- package/src/components/JhDrawer/JhDrawer.md +68 -0
- package/src/components/JhDrawer/JhDrawer.stories.js +478 -0
- package/src/components/JhDrawer/JhDrawer.vue +281 -0
- package/src/components/JhDrawerForm/JhDrawerForm.md +69 -0
- package/src/components/JhDrawerForm/JhDrawerForm.stories.js +492 -0
- package/src/components/JhDrawerForm/JhDrawerForm.vue +297 -0
- package/src/components/JhEditableTable/JhEditableTable.md +507 -0
- package/src/components/JhEditableTable/JhEditableTable.stories.js +615 -0
- package/src/components/JhEditableTable/JhEditableTable.vue +685 -0
- package/src/components/JhFileInput/JhFileInput.md +56 -0
- package/src/components/JhFileInput/JhFileInput.stories.js +103 -0
- package/src/components/JhFileInput/JhFileInput.vue +253 -0
- package/src/components/JhForm/JhForm.md +676 -0
- package/src/components/JhForm/JhForm.stories.js +1375 -0
- package/src/components/JhForm/JhForm.vue +657 -0
- package/src/components/JhFormField/JhFormField.stories.js +217 -0
- package/src/components/JhFormField/JhFormField.vue +439 -0
- package/src/components/JhFormFields/JhFormFields.md +647 -0
- package/src/components/JhFormFields/JhFormFields.stories.js +922 -0
- package/src/components/JhFormFields/JhFormFields.vue +998 -0
- package/src/components/JhFormList/JhFormList.md +303 -0
- package/src/components/JhFormList/JhFormList.stories.js +661 -0
- package/src/components/JhFormList/JhFormList.vue +1127 -0
- package/src/components/JhJsonEditor/JhJsonEditor.md +54 -0
- package/src/components/JhJsonEditor/JhJsonEditor.stories.js +157 -0
- package/src/components/JhJsonEditor/JhJsonEditor.vue +178 -0
- package/src/components/JhLayout/JhLayout.md +580 -0
- package/src/components/JhLayout/JhLayout.stories.js +414 -0
- package/src/components/JhLayout/JhLayout.vue +387 -0
- package/src/components/JhList/JhList.md +441 -0
- package/src/components/JhList/JhList.stories.js +524 -0
- package/src/components/JhList/JhList.vue +571 -0
- package/src/components/JhMarkdownEditor/JhMarkdownEditor.md +56 -0
- package/src/components/JhMarkdownEditor/JhMarkdownEditor.stories.js +191 -0
- package/src/components/JhMarkdownEditor/JhMarkdownEditor.vue +188 -0
- package/src/components/JhMask/JhMask.md +62 -0
- package/src/components/JhMask/JhMask.stories.js +270 -0
- package/src/components/JhMask/JhMask.vue +123 -0
- package/src/components/JhMenu/JhMenu.md +85 -0
- package/src/components/JhMenu/JhMenu.stories.js +384 -0
- package/src/components/JhMenu/JhMenu.vue +545 -0
- package/src/components/JhModal/JhModal.md +68 -0
- package/src/components/JhModal/JhModal.stories.js +562 -0
- package/src/components/JhModal/JhModal.vue +235 -0
- package/src/components/JhModalForm/JhModalForm.md +69 -0
- package/src/components/JhModalForm/JhModalForm.stories.js +592 -0
- package/src/components/JhModalForm/JhModalForm.vue +298 -0
- package/src/components/JhPageContainer/JhPageContainer.md +409 -0
- package/src/components/JhPageContainer/JhPageContainer.stories.js +209 -0
- package/src/components/JhPageContainer/JhPageContainer.vue +72 -0
- package/src/components/JhQueryFilter/JhQueryFilter.md +77 -0
- package/src/components/JhQueryFilter/JhQueryFilter.stories.js +684 -0
- package/src/components/JhQueryFilter/JhQueryFilter.vue +429 -0
- package/src/components/JhScene/JhScene.md +64 -0
- package/src/components/JhScene/JhScene.stories.js +317 -0
- package/src/components/JhScene/JhScene.vue +376 -0
- package/src/components/JhStatisticCard/JhStatisticCard.md +363 -0
- package/src/components/JhStatisticCard/JhStatisticCard.stories.js +847 -0
- package/src/components/JhStatisticCard/JhStatisticCard.vue +459 -0
- package/src/components/JhStepsForm/JhStepsForm.md +666 -0
- package/src/components/JhStepsForm/JhStepsForm.stories.js +1224 -0
- package/src/components/JhStepsForm/JhStepsForm.vue +749 -0
- package/src/components/JhTable/JhTable.md +730 -0
- package/src/components/JhTable/JhTable.stories.js +1444 -0
- package/src/components/JhTable/JhTable.vue +2298 -0
- package/src/components/JhTableAttachment/JhTableAttachment.md +70 -0
- package/src/components/JhTableAttachment/JhTableAttachment.stories.js +198 -0
- package/src/components/JhTableAttachment/JhTableAttachment.vue +264 -0
- package/src/components/JhToast/JhToast.md +67 -0
- package/src/components/JhToast/JhToast.stories.js +386 -0
- package/src/components/JhToast/JhToast.vue +239 -0
- package/src/components/JhTreeSelect/JhTreeSelect.md +82 -0
- package/src/components/JhTreeSelect/JhTreeSelect.stories.js +391 -0
- package/src/components/JhTreeSelect/JhTreeSelect.vue +727 -0
- package/src/components/JhWaterMark/JhWaterMark.md +190 -0
- package/src/components/JhWaterMark/JhWaterMark.stories.js +675 -0
- package/src/components/JhWaterMark/JhWaterMark.vue +351 -0
- package/src/components/README.md +52 -0
- package/src/index.js +135 -0
- package/src/style/globalCSSJHV4.css +348 -0
- package/src/style/globalCSSVuetifyV4.css +637 -0
- package/src/style/storybook.css +4 -0
- package/src/tailwind.css +3 -0
- package/src/utils/vuetify.js +31 -0
|
@@ -0,0 +1,386 @@
|
|
|
1
|
+
import JhToast from './JhToast.vue';
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
title: '基础组件/JhToast - 消息提示',
|
|
5
|
+
component: JhToast,
|
|
6
|
+
tags: ['autodocs'],
|
|
7
|
+
argTypes: {
|
|
8
|
+
value: {
|
|
9
|
+
control: 'boolean',
|
|
10
|
+
description: '是否显示',
|
|
11
|
+
table: {
|
|
12
|
+
defaultValue: { summary: 'false' },
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
message: {
|
|
16
|
+
control: 'text',
|
|
17
|
+
description: '消息内容',
|
|
18
|
+
table: {
|
|
19
|
+
defaultValue: { summary: '' },
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
type: {
|
|
23
|
+
control: 'select',
|
|
24
|
+
options: ['success', 'error', 'warning', 'info', 'loading'],
|
|
25
|
+
description: '消息类型',
|
|
26
|
+
table: {
|
|
27
|
+
defaultValue: { summary: 'success' },
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
position: {
|
|
31
|
+
control: 'select',
|
|
32
|
+
options: ['top', 'bottom', 'top-left', 'top-right', 'bottom-left', 'bottom-right'],
|
|
33
|
+
description: '显示位置',
|
|
34
|
+
table: {
|
|
35
|
+
defaultValue: { summary: 'top' },
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
timeout: {
|
|
39
|
+
control: 'number',
|
|
40
|
+
description: '自动关闭时间(毫秒), -1表示不自动关闭',
|
|
41
|
+
table: {
|
|
42
|
+
defaultValue: { summary: '3000' },
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
showCloseButton: {
|
|
46
|
+
control: 'boolean',
|
|
47
|
+
description: '是否显示关闭按钮',
|
|
48
|
+
table: {
|
|
49
|
+
defaultValue: { summary: 'true' },
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
// 基础示例
|
|
56
|
+
export const 基础示例 = {
|
|
57
|
+
render: () => ({
|
|
58
|
+
components: { JhToast },
|
|
59
|
+
data() {
|
|
60
|
+
return {
|
|
61
|
+
show: false,
|
|
62
|
+
};
|
|
63
|
+
},
|
|
64
|
+
template: `
|
|
65
|
+
<div>
|
|
66
|
+
<v-btn @click="show = true">显示提示</v-btn>
|
|
67
|
+
<jh-toast
|
|
68
|
+
v-model="show"
|
|
69
|
+
message="这是一条成功消息"
|
|
70
|
+
type="success"
|
|
71
|
+
/>
|
|
72
|
+
</div>
|
|
73
|
+
`,
|
|
74
|
+
}),
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
// 不同类型
|
|
78
|
+
export const 不同类型 = {
|
|
79
|
+
render: () => ({
|
|
80
|
+
components: { JhToast },
|
|
81
|
+
data() {
|
|
82
|
+
return {
|
|
83
|
+
showSuccess: false,
|
|
84
|
+
showError: false,
|
|
85
|
+
showWarning: false,
|
|
86
|
+
showInfo: false,
|
|
87
|
+
showLoading: false,
|
|
88
|
+
};
|
|
89
|
+
},
|
|
90
|
+
template: `
|
|
91
|
+
<div>
|
|
92
|
+
<v-btn class="mr-2 mb-2" color="success" @click="showSuccess = true">Success</v-btn>
|
|
93
|
+
<v-btn class="mr-2 mb-2" color="error" @click="showError = true">Error</v-btn>
|
|
94
|
+
<v-btn class="mr-2 mb-2" color="warning" @click="showWarning = true">Warning</v-btn>
|
|
95
|
+
<v-btn class="mr-2 mb-2" color="info" @click="showInfo = true">Info</v-btn>
|
|
96
|
+
<v-btn class="mb-2" color="primary" @click="showLoading = true">Loading</v-btn>
|
|
97
|
+
|
|
98
|
+
<jh-toast
|
|
99
|
+
v-model="showSuccess"
|
|
100
|
+
message="操作成功完成!"
|
|
101
|
+
type="success"
|
|
102
|
+
/>
|
|
103
|
+
<jh-toast
|
|
104
|
+
v-model="showError"
|
|
105
|
+
message="操作失败,请重试"
|
|
106
|
+
type="error"
|
|
107
|
+
/>
|
|
108
|
+
<jh-toast
|
|
109
|
+
v-model="showWarning"
|
|
110
|
+
message="请注意此操作的风险"
|
|
111
|
+
type="warning"
|
|
112
|
+
/>
|
|
113
|
+
<jh-toast
|
|
114
|
+
v-model="showInfo"
|
|
115
|
+
message="这是一条提示信息"
|
|
116
|
+
type="info"
|
|
117
|
+
/>
|
|
118
|
+
<jh-toast
|
|
119
|
+
v-model="showLoading"
|
|
120
|
+
message="正在处理中..."
|
|
121
|
+
type="loading"
|
|
122
|
+
/>
|
|
123
|
+
</div>
|
|
124
|
+
`,
|
|
125
|
+
}),
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
// 不同位置
|
|
129
|
+
export const 不同位置 = {
|
|
130
|
+
render: () => ({
|
|
131
|
+
components: { JhToast },
|
|
132
|
+
data() {
|
|
133
|
+
return {
|
|
134
|
+
showTop: false,
|
|
135
|
+
showBottom: false,
|
|
136
|
+
showTopLeft: false,
|
|
137
|
+
showTopRight: false,
|
|
138
|
+
showBottomLeft: false,
|
|
139
|
+
showBottomRight: false,
|
|
140
|
+
};
|
|
141
|
+
},
|
|
142
|
+
template: `
|
|
143
|
+
<div>
|
|
144
|
+
<div class="mb-2">
|
|
145
|
+
<v-btn class="mr-2" @click="showTopLeft = true">Top Left</v-btn>
|
|
146
|
+
<v-btn class="mr-2" @click="showTop = true">Top</v-btn>
|
|
147
|
+
<v-btn @click="showTopRight = true">Top Right</v-btn>
|
|
148
|
+
</div>
|
|
149
|
+
<div>
|
|
150
|
+
<v-btn class="mr-2" @click="showBottomLeft = true">Bottom Left</v-btn>
|
|
151
|
+
<v-btn class="mr-2" @click="showBottom = true">Bottom</v-btn>
|
|
152
|
+
<v-btn @click="showBottomRight = true">Bottom Right</v-btn>
|
|
153
|
+
</div>
|
|
154
|
+
|
|
155
|
+
<jh-toast v-model="showTop" message="顶部居中" position="top" />
|
|
156
|
+
<jh-toast v-model="showBottom" message="底部居中" position="bottom" />
|
|
157
|
+
<jh-toast v-model="showTopLeft" message="顶部左侧" position="top-left" />
|
|
158
|
+
<jh-toast v-model="showTopRight" message="顶部右侧" position="top-right" />
|
|
159
|
+
<jh-toast v-model="showBottomLeft" message="底部左侧" position="bottom-left" />
|
|
160
|
+
<jh-toast v-model="showBottomRight" message="底部右侧" position="bottom-right" />
|
|
161
|
+
</div>
|
|
162
|
+
`,
|
|
163
|
+
}),
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
// 带操作按钮
|
|
167
|
+
export const 带操作按钮 = {
|
|
168
|
+
render: () => ({
|
|
169
|
+
components: { JhToast },
|
|
170
|
+
data() {
|
|
171
|
+
return {
|
|
172
|
+
show: false,
|
|
173
|
+
actionCount: 0,
|
|
174
|
+
};
|
|
175
|
+
},
|
|
176
|
+
template: `
|
|
177
|
+
<div>
|
|
178
|
+
<v-btn @click="show = true">显示带操作的提示</v-btn>
|
|
179
|
+
<p class="mt-2">操作按钮点击次数: {{ actionCount }}</p>
|
|
180
|
+
<jh-toast
|
|
181
|
+
v-model="show"
|
|
182
|
+
message="文件已删除"
|
|
183
|
+
type="warning"
|
|
184
|
+
action-text="撤销"
|
|
185
|
+
@action="actionCount++; show = false"
|
|
186
|
+
/>
|
|
187
|
+
</div>
|
|
188
|
+
`,
|
|
189
|
+
}),
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
// 自定义图标
|
|
193
|
+
export const 自定义图标 = {
|
|
194
|
+
render: () => ({
|
|
195
|
+
components: { JhToast },
|
|
196
|
+
data() {
|
|
197
|
+
return {
|
|
198
|
+
show1: false,
|
|
199
|
+
show2: false,
|
|
200
|
+
show3: false,
|
|
201
|
+
};
|
|
202
|
+
},
|
|
203
|
+
template: `
|
|
204
|
+
<div>
|
|
205
|
+
<v-btn class="mr-2" @click="show1 = true">自定义图标</v-btn>
|
|
206
|
+
<v-btn class="mr-2" @click="show2 = true">自定义颜色</v-btn>
|
|
207
|
+
<v-btn @click="show3 = true">自定义图标+颜色</v-btn>
|
|
208
|
+
|
|
209
|
+
<jh-toast
|
|
210
|
+
v-model="show1"
|
|
211
|
+
message="有新消息"
|
|
212
|
+
icon="mdi-email"
|
|
213
|
+
/>
|
|
214
|
+
<jh-toast
|
|
215
|
+
v-model="show2"
|
|
216
|
+
message="自定义紫色"
|
|
217
|
+
color="purple"
|
|
218
|
+
/>
|
|
219
|
+
<jh-toast
|
|
220
|
+
v-model="show3"
|
|
221
|
+
message="点赞成功"
|
|
222
|
+
icon="mdi-heart"
|
|
223
|
+
color="pink"
|
|
224
|
+
/>
|
|
225
|
+
</div>
|
|
226
|
+
`,
|
|
227
|
+
}),
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
// 长文本消息
|
|
231
|
+
export const 长文本消息 = {
|
|
232
|
+
render: () => ({
|
|
233
|
+
components: { JhToast },
|
|
234
|
+
data() {
|
|
235
|
+
return {
|
|
236
|
+
show1: false,
|
|
237
|
+
show2: false,
|
|
238
|
+
};
|
|
239
|
+
},
|
|
240
|
+
template: `
|
|
241
|
+
<div>
|
|
242
|
+
<v-btn class="mr-2" @click="show1 = true">中等长度消息</v-btn>
|
|
243
|
+
<v-btn @click="show2 = true">超长消息(会被截断)</v-btn>
|
|
244
|
+
|
|
245
|
+
<jh-toast
|
|
246
|
+
v-model="show1"
|
|
247
|
+
message="这是一条中等长度的消息,用于测试消息内容的显示效果"
|
|
248
|
+
type="info"
|
|
249
|
+
/>
|
|
250
|
+
<jh-toast
|
|
251
|
+
v-model="show2"
|
|
252
|
+
message="这是一条非常非常长的消息内容,主要用于测试当消息内容超过最大长度限制时的显示效果,多余的内容会被自动截断并添加省略号。这样可以保证界面的整洁性,避免消息过长影响用户体验。"
|
|
253
|
+
type="warning"
|
|
254
|
+
:max-length="50"
|
|
255
|
+
/>
|
|
256
|
+
</div>
|
|
257
|
+
`,
|
|
258
|
+
}),
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
// 不自动关闭
|
|
262
|
+
export const 不自动关闭 = {
|
|
263
|
+
render: () => ({
|
|
264
|
+
components: { JhToast },
|
|
265
|
+
data() {
|
|
266
|
+
return {
|
|
267
|
+
show: false,
|
|
268
|
+
};
|
|
269
|
+
},
|
|
270
|
+
template: `
|
|
271
|
+
<div>
|
|
272
|
+
<v-btn @click="show = true">显示不自动关闭的提示</v-btn>
|
|
273
|
+
<p class="mt-2 text-caption">需要手动点击关闭按钮</p>
|
|
274
|
+
<jh-toast
|
|
275
|
+
v-model="show"
|
|
276
|
+
message="重要提示:此消息不会自动关闭"
|
|
277
|
+
type="warning"
|
|
278
|
+
:timeout="-1"
|
|
279
|
+
/>
|
|
280
|
+
</div>
|
|
281
|
+
`,
|
|
282
|
+
}),
|
|
283
|
+
};
|
|
284
|
+
|
|
285
|
+
// 垂直布局
|
|
286
|
+
export const 垂直布局 = {
|
|
287
|
+
render: () => ({
|
|
288
|
+
components: { JhToast },
|
|
289
|
+
data() {
|
|
290
|
+
return {
|
|
291
|
+
show: false,
|
|
292
|
+
};
|
|
293
|
+
},
|
|
294
|
+
template: `
|
|
295
|
+
<div>
|
|
296
|
+
<v-btn @click="show = true">显示垂直布局</v-btn>
|
|
297
|
+
<jh-toast
|
|
298
|
+
v-model="show"
|
|
299
|
+
message="这是一条使用垂直布局的消息,按钮会显示在下方"
|
|
300
|
+
type="info"
|
|
301
|
+
vertical
|
|
302
|
+
action-text="查看详情"
|
|
303
|
+
/>
|
|
304
|
+
</div>
|
|
305
|
+
`,
|
|
306
|
+
}),
|
|
307
|
+
};
|
|
308
|
+
|
|
309
|
+
// 无关闭按钮
|
|
310
|
+
export const 无关闭按钮 = {
|
|
311
|
+
render: () => ({
|
|
312
|
+
components: { JhToast },
|
|
313
|
+
data() {
|
|
314
|
+
return {
|
|
315
|
+
show: false,
|
|
316
|
+
};
|
|
317
|
+
},
|
|
318
|
+
template: `
|
|
319
|
+
<div>
|
|
320
|
+
<v-btn @click="show = true">显示无关闭按钮的提示</v-btn>
|
|
321
|
+
<p class="mt-2 text-caption">3秒后自动关闭</p>
|
|
322
|
+
<jh-toast
|
|
323
|
+
v-model="show"
|
|
324
|
+
message="此消息没有关闭按钮,会自动关闭"
|
|
325
|
+
type="success"
|
|
326
|
+
:show-close-button="false"
|
|
327
|
+
/>
|
|
328
|
+
</div>
|
|
329
|
+
`,
|
|
330
|
+
}),
|
|
331
|
+
};
|
|
332
|
+
|
|
333
|
+
// 实际应用
|
|
334
|
+
export const 实际应用 = {
|
|
335
|
+
render: () => ({
|
|
336
|
+
components: { JhToast },
|
|
337
|
+
data() {
|
|
338
|
+
return {
|
|
339
|
+
showToast: false,
|
|
340
|
+
toastMessage: '',
|
|
341
|
+
toastType: 'success',
|
|
342
|
+
isProcessing: false,
|
|
343
|
+
};
|
|
344
|
+
},
|
|
345
|
+
methods: {
|
|
346
|
+
async simulateSave() {
|
|
347
|
+
this.isProcessing = true;
|
|
348
|
+
this.toastMessage = '正在保存...';
|
|
349
|
+
this.toastType = 'loading';
|
|
350
|
+
this.showToast = true;
|
|
351
|
+
|
|
352
|
+
// 模拟异步操作
|
|
353
|
+
setTimeout(() => {
|
|
354
|
+
const success = Math.random() > 0.3;
|
|
355
|
+
this.toastMessage = success ? '保存成功!' : '保存失败,请重试';
|
|
356
|
+
this.toastType = success ? 'success' : 'error';
|
|
357
|
+
this.isProcessing = false;
|
|
358
|
+
}, 2000);
|
|
359
|
+
},
|
|
360
|
+
async simulateDelete() {
|
|
361
|
+
const result = await this.confirmDelete();
|
|
362
|
+
if (result) {
|
|
363
|
+
this.toastMessage = '删除成功';
|
|
364
|
+
this.toastType = 'warning';
|
|
365
|
+
this.showToast = true;
|
|
366
|
+
}
|
|
367
|
+
},
|
|
368
|
+
async confirmDelete() {
|
|
369
|
+
// 这里应该使用 confirm dialog,这里简化处理
|
|
370
|
+
return window.confirm('确定要删除吗?');
|
|
371
|
+
},
|
|
372
|
+
},
|
|
373
|
+
template: `
|
|
374
|
+
<div>
|
|
375
|
+
<v-btn class="mr-2" @click="simulateSave" :loading="isProcessing">保存数据</v-btn>
|
|
376
|
+
<v-btn @click="simulateDelete">删除数据</v-btn>
|
|
377
|
+
|
|
378
|
+
<jh-toast
|
|
379
|
+
v-model="showToast"
|
|
380
|
+
:message="toastMessage"
|
|
381
|
+
:type="toastType"
|
|
382
|
+
/>
|
|
383
|
+
</div>
|
|
384
|
+
`,
|
|
385
|
+
}),
|
|
386
|
+
};
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<v-snackbar
|
|
3
|
+
v-model="isVisible"
|
|
4
|
+
:color="currentColor"
|
|
5
|
+
:timeout="currentTimeout"
|
|
6
|
+
:top="position.includes('top')"
|
|
7
|
+
:bottom="position.includes('bottom')"
|
|
8
|
+
:left="position.includes('left')"
|
|
9
|
+
:right="position.includes('right')"
|
|
10
|
+
:vertical="vertical"
|
|
11
|
+
:elevation="elevation"
|
|
12
|
+
:class="['jh-toast', `jh-toast--${type}`]"
|
|
13
|
+
@input="handleInput"
|
|
14
|
+
>
|
|
15
|
+
<div class="jh-toast__content">
|
|
16
|
+
<v-progress-circular
|
|
17
|
+
v-if="type === 'loading'"
|
|
18
|
+
:size="18"
|
|
19
|
+
:width="3"
|
|
20
|
+
color="white"
|
|
21
|
+
indeterminate
|
|
22
|
+
class="jh-toast__icon"
|
|
23
|
+
></v-progress-circular>
|
|
24
|
+
<v-icon
|
|
25
|
+
v-else-if="currentIcon"
|
|
26
|
+
:color="iconColor"
|
|
27
|
+
class="jh-toast__icon"
|
|
28
|
+
>
|
|
29
|
+
{{ currentIcon }}
|
|
30
|
+
</v-icon>
|
|
31
|
+
<span class="jh-toast__message">{{ currentMessage }}</span>
|
|
32
|
+
</div>
|
|
33
|
+
|
|
34
|
+
<template v-slot:action="{ attrs }">
|
|
35
|
+
<v-btn
|
|
36
|
+
v-if="showCloseButton"
|
|
37
|
+
:color="closeButtonColor"
|
|
38
|
+
text
|
|
39
|
+
small
|
|
40
|
+
v-bind="attrs"
|
|
41
|
+
@click="close"
|
|
42
|
+
>
|
|
43
|
+
{{ closeButtonText }}
|
|
44
|
+
</v-btn>
|
|
45
|
+
<v-btn
|
|
46
|
+
v-if="actionText"
|
|
47
|
+
:color="actionButtonColor"
|
|
48
|
+
text
|
|
49
|
+
small
|
|
50
|
+
v-bind="attrs"
|
|
51
|
+
@click="handleAction"
|
|
52
|
+
>
|
|
53
|
+
{{ actionText }}
|
|
54
|
+
</v-btn>
|
|
55
|
+
</template>
|
|
56
|
+
</v-snackbar>
|
|
57
|
+
</template>
|
|
58
|
+
|
|
59
|
+
<script>
|
|
60
|
+
export default {
|
|
61
|
+
name: 'JhToast',
|
|
62
|
+
props: {
|
|
63
|
+
// 是否显示
|
|
64
|
+
value: {
|
|
65
|
+
type: Boolean,
|
|
66
|
+
default: false,
|
|
67
|
+
},
|
|
68
|
+
// 消息内容
|
|
69
|
+
message: {
|
|
70
|
+
type: String,
|
|
71
|
+
default: '',
|
|
72
|
+
},
|
|
73
|
+
// 类型: success, error, warning, info, loading
|
|
74
|
+
type: {
|
|
75
|
+
type: String,
|
|
76
|
+
default: 'success',
|
|
77
|
+
validator: (v) => ['success', 'error', 'warning', 'info', 'loading'].includes(v),
|
|
78
|
+
},
|
|
79
|
+
// 位置: top, bottom, top-left, top-right, bottom-left, bottom-right
|
|
80
|
+
position: {
|
|
81
|
+
type: String,
|
|
82
|
+
default: 'top',
|
|
83
|
+
validator: (v) => ['top', 'bottom', 'top-left', 'top-right', 'bottom-left', 'bottom-right'].includes(v),
|
|
84
|
+
},
|
|
85
|
+
// 自动关闭时间(毫秒), -1 表示不自动关闭
|
|
86
|
+
timeout: {
|
|
87
|
+
type: Number,
|
|
88
|
+
default: 3000,
|
|
89
|
+
},
|
|
90
|
+
// 自定义颜色
|
|
91
|
+
color: {
|
|
92
|
+
type: String,
|
|
93
|
+
default: '',
|
|
94
|
+
},
|
|
95
|
+
// 自定义图标
|
|
96
|
+
icon: {
|
|
97
|
+
type: String,
|
|
98
|
+
default: '',
|
|
99
|
+
},
|
|
100
|
+
// 图标颜色
|
|
101
|
+
iconColor: {
|
|
102
|
+
type: String,
|
|
103
|
+
default: 'white',
|
|
104
|
+
},
|
|
105
|
+
// 是否显示关闭按钮
|
|
106
|
+
showCloseButton: {
|
|
107
|
+
type: Boolean,
|
|
108
|
+
default: true,
|
|
109
|
+
},
|
|
110
|
+
// 关闭按钮文本
|
|
111
|
+
closeButtonText: {
|
|
112
|
+
type: String,
|
|
113
|
+
default: '关闭',
|
|
114
|
+
},
|
|
115
|
+
// 关闭按钮颜色
|
|
116
|
+
closeButtonColor: {
|
|
117
|
+
type: String,
|
|
118
|
+
default: 'white',
|
|
119
|
+
},
|
|
120
|
+
// 操作按钮文本
|
|
121
|
+
actionText: {
|
|
122
|
+
type: String,
|
|
123
|
+
default: '',
|
|
124
|
+
},
|
|
125
|
+
// 操作按钮颜色
|
|
126
|
+
actionButtonColor: {
|
|
127
|
+
type: String,
|
|
128
|
+
default: 'white',
|
|
129
|
+
},
|
|
130
|
+
// 是否垂直布局
|
|
131
|
+
vertical: {
|
|
132
|
+
type: Boolean,
|
|
133
|
+
default: false,
|
|
134
|
+
},
|
|
135
|
+
// 阴影高度
|
|
136
|
+
elevation: {
|
|
137
|
+
type: [Number, String],
|
|
138
|
+
default: 24,
|
|
139
|
+
},
|
|
140
|
+
// 消息最大长度
|
|
141
|
+
maxLength: {
|
|
142
|
+
type: Number,
|
|
143
|
+
default: 100,
|
|
144
|
+
},
|
|
145
|
+
},
|
|
146
|
+
data() {
|
|
147
|
+
return {
|
|
148
|
+
isVisible: false,
|
|
149
|
+
};
|
|
150
|
+
},
|
|
151
|
+
computed: {
|
|
152
|
+
currentMessage() {
|
|
153
|
+
let msg = this.message || '';
|
|
154
|
+
if (msg.length > this.maxLength) {
|
|
155
|
+
msg = msg.substring(0, this.maxLength) + '...';
|
|
156
|
+
}
|
|
157
|
+
return msg;
|
|
158
|
+
},
|
|
159
|
+
currentColor() {
|
|
160
|
+
if (this.color) return this.color;
|
|
161
|
+
|
|
162
|
+
const colorMap = {
|
|
163
|
+
success: 'success',
|
|
164
|
+
error: 'error',
|
|
165
|
+
warning: 'warning',
|
|
166
|
+
info: 'info',
|
|
167
|
+
loading: 'primary',
|
|
168
|
+
};
|
|
169
|
+
return colorMap[this.type] || 'success';
|
|
170
|
+
},
|
|
171
|
+
currentIcon() {
|
|
172
|
+
if (this.icon) return this.icon;
|
|
173
|
+
if (this.type === 'loading') return '';
|
|
174
|
+
|
|
175
|
+
const iconMap = {
|
|
176
|
+
success: 'mdi-check-circle',
|
|
177
|
+
error: 'mdi-alert-circle',
|
|
178
|
+
warning: 'mdi-alert',
|
|
179
|
+
info: 'mdi-information',
|
|
180
|
+
};
|
|
181
|
+
return iconMap[this.type] || 'mdi-check-circle';
|
|
182
|
+
},
|
|
183
|
+
currentTimeout() {
|
|
184
|
+
return this.type === 'loading' ? -1 : this.timeout;
|
|
185
|
+
},
|
|
186
|
+
},
|
|
187
|
+
watch: {
|
|
188
|
+
value(newVal) {
|
|
189
|
+
this.isVisible = newVal;
|
|
190
|
+
},
|
|
191
|
+
isVisible(newVal) {
|
|
192
|
+
if (!newVal) {
|
|
193
|
+
this.$emit('input', false);
|
|
194
|
+
}
|
|
195
|
+
},
|
|
196
|
+
},
|
|
197
|
+
mounted() {
|
|
198
|
+
this.isVisible = this.value;
|
|
199
|
+
},
|
|
200
|
+
methods: {
|
|
201
|
+
handleInput(value) {
|
|
202
|
+
this.$emit('input', value);
|
|
203
|
+
if (!value) {
|
|
204
|
+
this.$emit('close');
|
|
205
|
+
}
|
|
206
|
+
},
|
|
207
|
+
close() {
|
|
208
|
+
this.isVisible = false;
|
|
209
|
+
},
|
|
210
|
+
handleAction() {
|
|
211
|
+
this.$emit('action');
|
|
212
|
+
},
|
|
213
|
+
},
|
|
214
|
+
};
|
|
215
|
+
</script>
|
|
216
|
+
|
|
217
|
+
<style scoped>
|
|
218
|
+
.jh-toast__content {
|
|
219
|
+
display: flex;
|
|
220
|
+
align-items: center;
|
|
221
|
+
flex: 1;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
.jh-toast__icon {
|
|
225
|
+
margin-right: 12px;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
.jh-toast__message {
|
|
229
|
+
flex: 1;
|
|
230
|
+
word-break: break-word;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/* 响应式样式 */
|
|
234
|
+
@media screen and (max-width: 600px) {
|
|
235
|
+
.jh-toast__message {
|
|
236
|
+
max-width: calc(100vw - 200px);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
</style>
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# JhTreeSelect - 树形选择器
|
|
2
|
+
|
|
3
|
+
JhTreeSelect 提供多选/单选树对话框,集成搜索、全选、移动端适配和已选列表,适用于组织、权限、分类等层级数据的选择。
|
|
4
|
+
|
|
5
|
+
## 功能特性
|
|
6
|
+
|
|
7
|
+
- 🌳 **多模式**:支持 `multiple`/`single` 两种模式,可选择节点或仅选择叶子
|
|
8
|
+
- 🔍 **即时搜索**:关键字过滤树节点,并支持“全选当前搜索结果”
|
|
9
|
+
- 📱 **响应式**:小屏自动切换全屏弹窗,已选区域在移动端折叠展示
|
|
10
|
+
- 🧺 **批量操作**:内置全选、清除、批量删除已选项
|
|
11
|
+
- 🔁 **受控对话框**:`visible` + `v-model` 统一控制,确认再回传
|
|
12
|
+
|
|
13
|
+
## 基础用法
|
|
14
|
+
|
|
15
|
+
```vue
|
|
16
|
+
<template>
|
|
17
|
+
<jh-tree-select
|
|
18
|
+
v-model="selectedNodes"
|
|
19
|
+
:visible.sync="dialogVisible"
|
|
20
|
+
:data="treeData"
|
|
21
|
+
mode="multiple"
|
|
22
|
+
:allow-select-node="true"
|
|
23
|
+
title="选择部门"
|
|
24
|
+
@confirm="handleConfirm"
|
|
25
|
+
/>
|
|
26
|
+
</template>
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## API
|
|
30
|
+
|
|
31
|
+
### Props
|
|
32
|
+
|
|
33
|
+
| 参数 | 说明 | 类型 | 默认值 |
|
|
34
|
+
| --- | --- | --- | --- |
|
|
35
|
+
| value | `v-model`,选中节点数组 | Array | [] |
|
|
36
|
+
| visible | 弹窗是否显示 | boolean | false |
|
|
37
|
+
| mode | 选择模式 `multiple/single` | string | `multiple` |
|
|
38
|
+
| title | 对话框标题 | string | `选择节点` |
|
|
39
|
+
| placeholder | 搜索框占位符 | string | `搜索节点` |
|
|
40
|
+
| maxWidth | 弹窗最大宽度 | string | `1000px` |
|
|
41
|
+
| data | 树形数据源 | Array | [] |
|
|
42
|
+
| nodeKey | 节点唯一 key 字段名 | string | `id` |
|
|
43
|
+
| nodeLabel | 节点显示字段名 | string | `label` |
|
|
44
|
+
| nodeChildren | 子节点字段名 | string | `children` |
|
|
45
|
+
| allowSelectNode | 是否允许点击节点本身选择 | boolean | false |
|
|
46
|
+
| showSearch | 是否展示搜索框 | boolean | true |
|
|
47
|
+
| showSelectAll | 是否展示全选按钮(仅 multiple) | boolean | true |
|
|
48
|
+
| loading | 数据加载状态 | boolean | false |
|
|
49
|
+
|
|
50
|
+
### Events
|
|
51
|
+
|
|
52
|
+
| 事件名 | 说明 | 回调参数 |
|
|
53
|
+
| --- | --- | --- |
|
|
54
|
+
| input | `v-model` 更新,返回选中节点数组 | (nodes: Array) |
|
|
55
|
+
| update:visible | 同步弹窗开关 | (visible: boolean) |
|
|
56
|
+
| confirm | 点击“确定”后触发 | (nodes: Array) |
|
|
57
|
+
| cancel | 点击“取消”或关闭图标后触发 | - |
|
|
58
|
+
|
|
59
|
+
### Slots
|
|
60
|
+
|
|
61
|
+
组件暂未开放插槽。
|
|
62
|
+
|
|
63
|
+
## 数据结构示例
|
|
64
|
+
|
|
65
|
+
```js
|
|
66
|
+
const treeData = [
|
|
67
|
+
{
|
|
68
|
+
id: 'dept-1',
|
|
69
|
+
label: '总部',
|
|
70
|
+
children: [
|
|
71
|
+
{ id: 'dept-1-1', label: '技术部' },
|
|
72
|
+
{ id: 'dept-1-2', label: '运营部' }
|
|
73
|
+
]
|
|
74
|
+
}
|
|
75
|
+
];
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## 使用建议
|
|
79
|
+
|
|
80
|
+
- 如果只允许选择叶子节点,可将 `allowSelectNode` 设为 false,并在 `toggleNode` 中自定义策略
|
|
81
|
+
- 需要默认展开其他层级时,可修改组件数据或 fork 组件以暴露更多控制属性
|
|
82
|
+
- 回传的数据为节点对象,包含原始字段,可直接提交给后端或提取 ID
|