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.
Files changed (112) hide show
  1. package/README.md +376 -0
  2. package/dist/jianghu-ui.css +2318 -0
  3. package/dist/jianghu-ui.js +2 -0
  4. package/dist/jianghu-ui.js.LICENSE.txt +1 -0
  5. package/package.json +56 -0
  6. package/src/Design.stories.mdx +195 -0
  7. package/src/Introduction.stories.mdx +148 -0
  8. package/src/components/JhAddressSelect/JhAddressSelect.md +250 -0
  9. package/src/components/JhAddressSelect/JhAddressSelect.stories.js +282 -0
  10. package/src/components/JhAddressSelect/JhAddressSelect.vue +261 -0
  11. package/src/components/JhCard/JhCard.md +246 -0
  12. package/src/components/JhCard/JhCard.stories.js +688 -0
  13. package/src/components/JhCard/JhCard.vue +604 -0
  14. package/src/components/JhCheckCard/JhCheckCard.md +245 -0
  15. package/src/components/JhCheckCard/JhCheckCard.stories.js +750 -0
  16. package/src/components/JhCheckCard/JhCheckCard.vue +476 -0
  17. package/src/components/JhConfirmDialog/JhConfirmDialog.md +70 -0
  18. package/src/components/JhConfirmDialog/JhConfirmDialog.stories.js +550 -0
  19. package/src/components/JhConfirmDialog/JhConfirmDialog.vue +181 -0
  20. package/src/components/JhDateRangePicker/JhDateRangePicker.md +56 -0
  21. package/src/components/JhDateRangePicker/JhDateRangePicker.stories.js +320 -0
  22. package/src/components/JhDateRangePicker/JhDateRangePicker.vue +307 -0
  23. package/src/components/JhDescriptions/JhDescriptions.md +724 -0
  24. package/src/components/JhDescriptions/JhDescriptions.stories.js +858 -0
  25. package/src/components/JhDescriptions/JhDescriptions.vue +933 -0
  26. package/src/components/JhDraggable/JhDraggable.md +66 -0
  27. package/src/components/JhDraggable/JhDraggable.stories.js +161 -0
  28. package/src/components/JhDraggable/JhDraggable.vue +254 -0
  29. package/src/components/JhDrawer/JhDrawer.md +68 -0
  30. package/src/components/JhDrawer/JhDrawer.stories.js +478 -0
  31. package/src/components/JhDrawer/JhDrawer.vue +281 -0
  32. package/src/components/JhDrawerForm/JhDrawerForm.md +69 -0
  33. package/src/components/JhDrawerForm/JhDrawerForm.stories.js +492 -0
  34. package/src/components/JhDrawerForm/JhDrawerForm.vue +297 -0
  35. package/src/components/JhEditableTable/JhEditableTable.md +507 -0
  36. package/src/components/JhEditableTable/JhEditableTable.stories.js +615 -0
  37. package/src/components/JhEditableTable/JhEditableTable.vue +685 -0
  38. package/src/components/JhFileInput/JhFileInput.md +56 -0
  39. package/src/components/JhFileInput/JhFileInput.stories.js +103 -0
  40. package/src/components/JhFileInput/JhFileInput.vue +253 -0
  41. package/src/components/JhForm/JhForm.md +676 -0
  42. package/src/components/JhForm/JhForm.stories.js +1375 -0
  43. package/src/components/JhForm/JhForm.vue +657 -0
  44. package/src/components/JhFormField/JhFormField.stories.js +217 -0
  45. package/src/components/JhFormField/JhFormField.vue +439 -0
  46. package/src/components/JhFormFields/JhFormFields.md +647 -0
  47. package/src/components/JhFormFields/JhFormFields.stories.js +922 -0
  48. package/src/components/JhFormFields/JhFormFields.vue +998 -0
  49. package/src/components/JhFormList/JhFormList.md +303 -0
  50. package/src/components/JhFormList/JhFormList.stories.js +661 -0
  51. package/src/components/JhFormList/JhFormList.vue +1127 -0
  52. package/src/components/JhJsonEditor/JhJsonEditor.md +54 -0
  53. package/src/components/JhJsonEditor/JhJsonEditor.stories.js +157 -0
  54. package/src/components/JhJsonEditor/JhJsonEditor.vue +178 -0
  55. package/src/components/JhLayout/JhLayout.md +580 -0
  56. package/src/components/JhLayout/JhLayout.stories.js +414 -0
  57. package/src/components/JhLayout/JhLayout.vue +387 -0
  58. package/src/components/JhList/JhList.md +441 -0
  59. package/src/components/JhList/JhList.stories.js +524 -0
  60. package/src/components/JhList/JhList.vue +571 -0
  61. package/src/components/JhMarkdownEditor/JhMarkdownEditor.md +56 -0
  62. package/src/components/JhMarkdownEditor/JhMarkdownEditor.stories.js +191 -0
  63. package/src/components/JhMarkdownEditor/JhMarkdownEditor.vue +188 -0
  64. package/src/components/JhMask/JhMask.md +62 -0
  65. package/src/components/JhMask/JhMask.stories.js +270 -0
  66. package/src/components/JhMask/JhMask.vue +123 -0
  67. package/src/components/JhMenu/JhMenu.md +85 -0
  68. package/src/components/JhMenu/JhMenu.stories.js +384 -0
  69. package/src/components/JhMenu/JhMenu.vue +545 -0
  70. package/src/components/JhModal/JhModal.md +68 -0
  71. package/src/components/JhModal/JhModal.stories.js +562 -0
  72. package/src/components/JhModal/JhModal.vue +235 -0
  73. package/src/components/JhModalForm/JhModalForm.md +69 -0
  74. package/src/components/JhModalForm/JhModalForm.stories.js +592 -0
  75. package/src/components/JhModalForm/JhModalForm.vue +298 -0
  76. package/src/components/JhPageContainer/JhPageContainer.md +409 -0
  77. package/src/components/JhPageContainer/JhPageContainer.stories.js +209 -0
  78. package/src/components/JhPageContainer/JhPageContainer.vue +72 -0
  79. package/src/components/JhQueryFilter/JhQueryFilter.md +77 -0
  80. package/src/components/JhQueryFilter/JhQueryFilter.stories.js +684 -0
  81. package/src/components/JhQueryFilter/JhQueryFilter.vue +429 -0
  82. package/src/components/JhScene/JhScene.md +64 -0
  83. package/src/components/JhScene/JhScene.stories.js +317 -0
  84. package/src/components/JhScene/JhScene.vue +376 -0
  85. package/src/components/JhStatisticCard/JhStatisticCard.md +363 -0
  86. package/src/components/JhStatisticCard/JhStatisticCard.stories.js +847 -0
  87. package/src/components/JhStatisticCard/JhStatisticCard.vue +459 -0
  88. package/src/components/JhStepsForm/JhStepsForm.md +666 -0
  89. package/src/components/JhStepsForm/JhStepsForm.stories.js +1224 -0
  90. package/src/components/JhStepsForm/JhStepsForm.vue +749 -0
  91. package/src/components/JhTable/JhTable.md +730 -0
  92. package/src/components/JhTable/JhTable.stories.js +1444 -0
  93. package/src/components/JhTable/JhTable.vue +2298 -0
  94. package/src/components/JhTableAttachment/JhTableAttachment.md +70 -0
  95. package/src/components/JhTableAttachment/JhTableAttachment.stories.js +198 -0
  96. package/src/components/JhTableAttachment/JhTableAttachment.vue +264 -0
  97. package/src/components/JhToast/JhToast.md +67 -0
  98. package/src/components/JhToast/JhToast.stories.js +386 -0
  99. package/src/components/JhToast/JhToast.vue +239 -0
  100. package/src/components/JhTreeSelect/JhTreeSelect.md +82 -0
  101. package/src/components/JhTreeSelect/JhTreeSelect.stories.js +391 -0
  102. package/src/components/JhTreeSelect/JhTreeSelect.vue +727 -0
  103. package/src/components/JhWaterMark/JhWaterMark.md +190 -0
  104. package/src/components/JhWaterMark/JhWaterMark.stories.js +675 -0
  105. package/src/components/JhWaterMark/JhWaterMark.vue +351 -0
  106. package/src/components/README.md +52 -0
  107. package/src/index.js +135 -0
  108. package/src/style/globalCSSJHV4.css +348 -0
  109. package/src/style/globalCSSVuetifyV4.css +637 -0
  110. package/src/style/storybook.css +4 -0
  111. package/src/tailwind.css +3 -0
  112. package/src/utils/vuetify.js +31 -0
@@ -0,0 +1,70 @@
1
+ # JhTableAttachment - 附件分组面板
2
+
3
+ JhTableAttachment 专注于以卡片方式呈现附件列表,内置分组、搜索、预览、下载、删除等能力,可作为表格附件详情的补充。
4
+
5
+ ## 功能特性
6
+
7
+ - 🗂️ **分组展示**:按 `groupBy` 字段将附件划分多个卡片区域
8
+ - 🔍 **内置搜索**:顶部输入框可实时过滤文件名/类型
9
+ - 👀 **预览能力**:图片类附件支持放大预览,其他类型显示文件图标
10
+ - 📥 **下载/删除事件**:提供 download/delete 事件,方便接入后台接口
11
+ - ➕ **新增入口**:可展示“新增附件”按钮,自定义上传流程
12
+
13
+ ## 基础用法
14
+
15
+ ```vue
16
+ <template>
17
+ <jh-table-attachment
18
+ :attachments="attachmentList"
19
+ group-by="category"
20
+ :show-create-button="canEdit"
21
+ @create-click="openUploader"
22
+ @delete="removeAttachment"
23
+ @download="downloadAttachment"
24
+ />
25
+ </template>
26
+ ```
27
+
28
+ ## API
29
+
30
+ ### Props
31
+
32
+ | 参数 | 说明 | 类型 | 默认值 |
33
+ | --- | --- | --- | --- |
34
+ | attachments | 附件数组 | Array | [] |
35
+ | showCreateButton | 是否显示“新增附件”按钮 | boolean | true |
36
+ | readonly | 是否只读(隐藏删除按钮) | boolean | false |
37
+ | groupBy | 分组字段名 | string | `fileType` |
38
+ | previewPrefix | 预览地址前缀,拼接 `downloadPath` | string | `/upload/` |
39
+ | loading | 是否展示 loading 遮罩 | boolean | false |
40
+
41
+ 附件对象推荐结构:
42
+
43
+ ```js
44
+ {
45
+ id: 1,
46
+ filename: '合同.pdf',
47
+ downloadPath: '2023/contract.pdf',
48
+ fileType: '合同',
49
+ fileSubtype: 'pdf'
50
+ }
51
+ ```
52
+
53
+ ### Events
54
+
55
+ | 事件名 | 说明 | 回调参数 |
56
+ | --- | --- | --- |
57
+ | create-click | 点击新增附件按钮 | - |
58
+ | preview | 点击预览按钮 | (item) |
59
+ | download | 点击下载按钮 | (item) |
60
+ | delete | 点击删除按钮 | (item) |
61
+
62
+ ### Slots
63
+
64
+ 组件暂未开放插槽,如需扩展可 fork 组件进行定制。
65
+
66
+ ## 使用建议
67
+
68
+ - 需要与上传接口联动时,可在 `create-click` 中打开上传抽屉,完成后刷新 `attachments`
69
+ - 若后端已返回文件完整 URL,可把 `previewPrefix` 设为空字符串
70
+ - 通过 `readonly` 控制权限,避免无权限用户看到删除按钮
@@ -0,0 +1,198 @@
1
+ import JhTableAttachment from './JhTableAttachment.vue';
2
+
3
+ export default {
4
+ title: '基础组件/JhTableAttachment - 附件表格',
5
+ component: JhTableAttachment,
6
+ tags: ['autodocs'],
7
+ argTypes: {
8
+ attachments: {
9
+ control: 'object',
10
+ description: '附件列表数据',
11
+ },
12
+ showCreateButton: {
13
+ control: 'boolean',
14
+ description: '是否显示新增按钮',
15
+ },
16
+ readonly: {
17
+ control: 'boolean',
18
+ description: '是否只读模式',
19
+ },
20
+ groupBy: {
21
+ control: 'text',
22
+ description: '分组字段名',
23
+ },
24
+ previewPrefix: {
25
+ control: 'text',
26
+ description: '预览路径前缀',
27
+ },
28
+ loading: {
29
+ control: 'boolean',
30
+ description: '是否加载中',
31
+ },
32
+ },
33
+ };
34
+
35
+ const sampleAttachments = [
36
+ {
37
+ id: 1,
38
+ filename: '合同文档.pdf',
39
+ downloadPath: 'documents/contract.pdf',
40
+ fileType: '合同文件',
41
+ fileSubtype: 'pdf'
42
+ },
43
+ {
44
+ id: 2,
45
+ filename: '产品图片1.jpg',
46
+ downloadPath: 'images/product1.jpg',
47
+ fileType: '产品图片',
48
+ fileSubtype: 'jpg'
49
+ },
50
+ {
51
+ id: 3,
52
+ filename: '产品图片2.png',
53
+ downloadPath: 'images/product2.png',
54
+ fileType: '产品图片',
55
+ fileSubtype: 'png'
56
+ },
57
+ {
58
+ id: 4,
59
+ filename: '技术文档.docx',
60
+ downloadPath: 'documents/tech.docx',
61
+ fileType: '技术资料',
62
+ fileSubtype: 'docx'
63
+ },
64
+ {
65
+ id: 5,
66
+ filename: '财务报表.xlsx',
67
+ downloadPath: 'documents/financial.xlsx',
68
+ fileType: '财务文件',
69
+ fileSubtype: 'xlsx'
70
+ },
71
+ {
72
+ id: 6,
73
+ filename: '宣传海报.jpg',
74
+ downloadPath: 'images/poster.jpg',
75
+ fileType: '产品图片',
76
+ fileSubtype: 'jpg'
77
+ },
78
+ ];
79
+
80
+ export const 基础示例 = {
81
+ args: {
82
+ attachments: sampleAttachments,
83
+ showCreateButton: true,
84
+ readonly: false,
85
+ groupBy: 'fileType',
86
+ previewPrefix: '/upload/',
87
+ loading: false,
88
+ },
89
+ };
90
+
91
+ export const 空数据 = {
92
+ args: {
93
+ attachments: [],
94
+ showCreateButton: true,
95
+ readonly: false,
96
+ groupBy: 'fileType',
97
+ previewPrefix: '/upload/',
98
+ loading: false,
99
+ },
100
+ };
101
+
102
+ export const 加载状态 = {
103
+ args: {
104
+ attachments: [],
105
+ showCreateButton: true,
106
+ readonly: false,
107
+ groupBy: 'fileType',
108
+ previewPrefix: '/upload/',
109
+ loading: true,
110
+ },
111
+ };
112
+
113
+ export const 只读模式 = {
114
+ args: {
115
+ attachments: sampleAttachments,
116
+ showCreateButton: false,
117
+ readonly: true,
118
+ groupBy: 'fileType',
119
+ previewPrefix: '/upload/',
120
+ loading: false,
121
+ },
122
+ };
123
+
124
+ export const 仅图片 = {
125
+ args: {
126
+ attachments: [
127
+ {
128
+ id: 1,
129
+ filename: '风景照片1.jpg',
130
+ downloadPath: 'photos/landscape1.jpg',
131
+ fileType: '风景照片',
132
+ fileSubtype: 'jpg'
133
+ },
134
+ {
135
+ id: 2,
136
+ filename: '风景照片2.jpg',
137
+ downloadPath: 'photos/landscape2.jpg',
138
+ fileType: '风景照片',
139
+ fileSubtype: 'jpg'
140
+ },
141
+ {
142
+ id: 3,
143
+ filename: '人物照片1.png',
144
+ downloadPath: 'photos/portrait1.png',
145
+ fileType: '人物照片',
146
+ fileSubtype: 'png'
147
+ },
148
+ {
149
+ id: 4,
150
+ filename: '人物照片2.png',
151
+ downloadPath: 'photos/portrait2.png',
152
+ fileType: '人物照片',
153
+ fileSubtype: 'png'
154
+ },
155
+ ],
156
+ showCreateButton: true,
157
+ readonly: false,
158
+ groupBy: 'fileType',
159
+ previewPrefix: '/upload/',
160
+ loading: false,
161
+ },
162
+ };
163
+
164
+ export const CustomGroupBy = {
165
+ args: {
166
+ attachments: [
167
+ {
168
+ id: 1,
169
+ filename: 'report-q1.pdf',
170
+ downloadPath: 'reports/q1.pdf',
171
+ fileType: '报告',
172
+ fileSubtype: 'pdf',
173
+ year: '2024'
174
+ },
175
+ {
176
+ id: 2,
177
+ filename: 'report-q2.pdf',
178
+ downloadPath: 'reports/q2.pdf',
179
+ fileType: '报告',
180
+ fileSubtype: 'pdf',
181
+ year: '2024'
182
+ },
183
+ {
184
+ id: 3,
185
+ filename: 'report-q1.pdf',
186
+ downloadPath: 'reports/2025-q1.pdf',
187
+ fileType: '报告',
188
+ fileSubtype: 'pdf',
189
+ year: '2025'
190
+ },
191
+ ],
192
+ showCreateButton: true,
193
+ readonly: false,
194
+ groupBy: 'year',
195
+ previewPrefix: '/upload/',
196
+ loading: false,
197
+ },
198
+ };
@@ -0,0 +1,264 @@
1
+ <template>
2
+ <v-app>
3
+ <v-container fluid>
4
+ <v-card class="px-4">
5
+ <!-- 工具栏 -->
6
+ <v-row class="ma-0 pt-0 d-flex">
7
+ <v-btn
8
+ v-if="showCreateButton"
9
+ color="success"
10
+ small
11
+ dark
12
+ class="elevation-0 mr-2"
13
+ @click="handleCreateClick"
14
+ >
15
+ 新增附件
16
+ </v-btn>
17
+
18
+ <v-spacer></v-spacer>
19
+
20
+ <v-col cols="12" sm="6" md="3" xl="3" class="pa-0 pt-2 pt-sm-0">
21
+ <v-text-field
22
+ v-model="searchInput"
23
+ color="success"
24
+ prefix="表格过滤:"
25
+ class="jh-v-input"
26
+ dense
27
+ filled
28
+ single-line
29
+ hide-details
30
+ ></v-text-field>
31
+ </v-col>
32
+ </v-row>
33
+
34
+ <!-- 附件分组展示 -->
35
+ <v-card
36
+ v-for="(itemList, groupName) in groupedAttachments"
37
+ :key="groupName"
38
+ outlined
39
+ class="my-2"
40
+ >
41
+ <v-card-title class="pl-4" style="font-size: 20px; font-weight: 500;">
42
+ {{ groupName }}
43
+ </v-card-title>
44
+ <v-card-text style="display: flex; flex-flow: wrap;">
45
+ <v-card
46
+ v-for="item in itemList"
47
+ :key="item.id"
48
+ width="200px"
49
+ class="mr-2 mb-2"
50
+ outlined
51
+ >
52
+ <v-img
53
+ :src="getPreviewUrl(item)"
54
+ class="white--text align-end"
55
+ gradient="to bottom, rgba(0,0,0,.1), rgba(0,0,0,.5)"
56
+ height="120px"
57
+ >
58
+ <v-card-title class="text-subtitle-2">{{ item.filename }}</v-card-title>
59
+ </v-img>
60
+
61
+ <v-card-actions>
62
+ <v-chip small>{{ item.fileSubtype || getFileType(item.filename) }}</v-chip>
63
+ <v-spacer></v-spacer>
64
+
65
+ <!-- 预览按钮 -->
66
+ <v-btn
67
+ v-if="isImage(item.downloadPath || item.filename)"
68
+ icon
69
+ small
70
+ @click="handlePreview(item)"
71
+ title="预览"
72
+ >
73
+ <v-icon>mdi-eye</v-icon>
74
+ </v-btn>
75
+
76
+ <!-- 下载按钮 -->
77
+ <v-btn
78
+ v-if="item.downloadPath"
79
+ icon
80
+ small
81
+ @click="handleDownload(item)"
82
+ title="下载"
83
+ >
84
+ <v-icon>mdi-download</v-icon>
85
+ </v-btn>
86
+
87
+ <!-- 删除按钮 -->
88
+ <v-btn
89
+ v-if="!readonly"
90
+ icon
91
+ small
92
+ @click="handleDelete(item)"
93
+ title="删除"
94
+ >
95
+ <v-icon>mdi-trash-can-outline</v-icon>
96
+ </v-btn>
97
+ </v-card-actions>
98
+ </v-card>
99
+ </v-card-text>
100
+ </v-card>
101
+
102
+ <!-- 加载状态 -->
103
+ <v-overlay absolute :value="loading" color="#fff">
104
+ <v-progress-circular
105
+ color="success"
106
+ indeterminate
107
+ size="32"
108
+ ></v-progress-circular>
109
+ </v-overlay>
110
+
111
+ <!-- 空状态 -->
112
+ <div v-if="!loading && filteredAttachments.length === 0" class="mt-5 text-center">
113
+ 暂无附件
114
+ </div>
115
+ </v-card>
116
+
117
+ <!-- 预览对话框 -->
118
+ <v-dialog v-model="previewDialog" max-width="90vw">
119
+ <v-card>
120
+ <v-card-title>
121
+ {{ previewItem ? previewItem.filename : '' }}
122
+ <v-spacer></v-spacer>
123
+ <v-btn icon @click="previewDialog = false">
124
+ <v-icon>mdi-close</v-icon>
125
+ </v-btn>
126
+ </v-card-title>
127
+ <v-card-text class="pa-0">
128
+ <v-img
129
+ v-if="previewItem"
130
+ :src="getPreviewUrl(previewItem)"
131
+ contain
132
+ max-height="70vh"
133
+ ></v-img>
134
+ </v-card-text>
135
+ </v-card>
136
+ </v-dialog>
137
+ </v-container>
138
+ </v-app>
139
+ </template>
140
+
141
+ <script>
142
+ export default {
143
+ name: 'JhTableAttachment',
144
+ props: {
145
+ // 附件列表
146
+ attachments: {
147
+ type: Array,
148
+ default: () => []
149
+ /**
150
+ * [{
151
+ * id: 1,
152
+ * filename: '文件名.jpg',
153
+ * downloadPath: '/upload/xxx.jpg',
154
+ * fileType: '分类名称',
155
+ * fileSubtype: 'jpg'
156
+ * }]
157
+ */
158
+ },
159
+ // 是否显示新增按钮
160
+ showCreateButton: {
161
+ type: Boolean,
162
+ default: true
163
+ },
164
+ // 是否只读
165
+ readonly: {
166
+ type: Boolean,
167
+ default: false
168
+ },
169
+ // 分组字段名
170
+ groupBy: {
171
+ type: String,
172
+ default: 'fileType'
173
+ },
174
+ // 预览路径前缀
175
+ previewPrefix: {
176
+ type: String,
177
+ default: '/upload/'
178
+ },
179
+ // 是否加载中
180
+ loading: {
181
+ type: Boolean,
182
+ default: false
183
+ }
184
+ },
185
+ data() {
186
+ return {
187
+ searchInput: '',
188
+ previewDialog: false,
189
+ previewItem: null
190
+ };
191
+ },
192
+ computed: {
193
+ filteredAttachments() {
194
+ if (!this.searchInput) {
195
+ return this.attachments;
196
+ }
197
+ return this.attachments.filter(item =>
198
+ item.filename.toLowerCase().includes(this.searchInput.toLowerCase()) ||
199
+ (item.fileType && item.fileType.toLowerCase().includes(this.searchInput.toLowerCase()))
200
+ );
201
+ },
202
+ groupedAttachments() {
203
+ const grouped = {};
204
+ this.filteredAttachments.forEach(item => {
205
+ const groupName = item[this.groupBy] || '未分类';
206
+ if (!grouped[groupName]) {
207
+ grouped[groupName] = [];
208
+ }
209
+ grouped[groupName].push(item);
210
+ });
211
+ return grouped;
212
+ }
213
+ },
214
+ methods: {
215
+ getPreviewUrl(item) {
216
+ if (item.downloadPath) {
217
+ return `${this.previewPrefix}${item.downloadPath}`;
218
+ }
219
+ return '/placeholder-image.jpg';
220
+ },
221
+ isImage(filename) {
222
+ if (!filename) return false;
223
+ return /\.(gif|jpg|jpeg|png|webp|svg|bmp)$/i.test(filename);
224
+ },
225
+ getFileType(filename) {
226
+ if (!filename) return '';
227
+ const ext = filename.split('.').pop();
228
+ return ext ? ext.toLowerCase() : '';
229
+ },
230
+ handleCreateClick() {
231
+ this.$emit('create-click');
232
+ },
233
+ handlePreview(item) {
234
+ this.previewItem = item;
235
+ this.previewDialog = true;
236
+ this.$emit('preview', item);
237
+ },
238
+ handleDownload(item) {
239
+ this.$emit('download', item);
240
+ // 默认下载逻辑
241
+ if (item.downloadPath) {
242
+ const link = document.createElement('a');
243
+ link.href = this.getPreviewUrl(item);
244
+ link.download = item.filename;
245
+ link.click();
246
+ }
247
+ },
248
+ handleDelete(item) {
249
+ this.$emit('delete', item);
250
+ }
251
+ }
252
+ };
253
+ </script>
254
+
255
+ <style scoped>
256
+ :deep(.jh-v-input) {
257
+ border: 1px solid #e5e7eb;
258
+ }
259
+
260
+ :deep(.jh-v-input .v-input__slot) {
261
+ margin: 0 !important;
262
+ background-color: #fff !important;
263
+ }
264
+ </style>
@@ -0,0 +1,67 @@
1
+ # JhToast - 全局提示组件
2
+
3
+ JhToast 基于 `v-snackbar` 封装,提供成功/错误/警告/信息/加载等多种提示效果,可用于全局消息通知。
4
+
5
+ ## 功能特性
6
+
7
+ - ⚡ **多状态**:内置 success、error、warning、info、loading 五种风格
8
+ - 📍 **自由定位**:通过 `position` 控制四角/上下等位置
9
+ - 🧭 **按钮扩展**:可同时显示关闭按钮和自定义操作按钮
10
+ - ⏱️ **超时控制**:不同类型可以设置不同的超时时间,loading 默认不自动关闭
11
+ - ✨ **图标自定义**:可传入 icon/iconColor,也可完全隐藏
12
+
13
+ ## 基础用法
14
+
15
+ ```vue
16
+ <template>
17
+ <jh-toast
18
+ v-model="toast.visible"
19
+ :message="toast.message"
20
+ :type="toast.type"
21
+ position="bottom-right"
22
+ action-text="撤销"
23
+ @action="undo"
24
+ />
25
+ </template>
26
+ ```
27
+
28
+ ## API
29
+
30
+ ### Props
31
+
32
+ | 参数 | 说明 | 类型 | 默认值 |
33
+ | --- | --- | --- | --- |
34
+ | value | `v-model`,控制显隐 | boolean | false |
35
+ | message | 提示文案 | string | `''` |
36
+ | type | 提示类型 `success/error/warning/info/loading` | string | `success` |
37
+ | position | 显示位置 | string | `top` |
38
+ | timeout | 自动关闭时间(ms),-1 表示常驻 | number | 3000 |
39
+ | color | 自定义背景色 | string | `''` |
40
+ | icon | 自定义图标 | string | `''` |
41
+ | iconColor | 图标颜色 | string | `white` |
42
+ | showCloseButton | 是否显示关闭按钮 | boolean | true |
43
+ | closeButtonText | 关闭按钮文案 | string | `关闭` |
44
+ | closeButtonColor | 关闭按钮颜色 | string | `white` |
45
+ | actionText | 操作按钮文本 | string | `''` |
46
+ | actionButtonColor | 操作按钮颜色 | string | `white` |
47
+ | vertical | 是否垂直布局 | boolean | false |
48
+ | elevation | 阴影高度 | number \| string | 24 |
49
+ | maxLength | 文案裁剪长度 | number | 100 |
50
+
51
+ ### Events
52
+
53
+ | 事件名 | 说明 | 回调参数 |
54
+ | --- | --- | --- |
55
+ | input | `v-model` 更新 | (visible: boolean) |
56
+ | close | Toast 消失时触发 | - |
57
+ | action | 点击操作按钮时触发 | - |
58
+
59
+ ### Slots
60
+
61
+ 组件不提供插槽。
62
+
63
+ ## 使用建议
64
+
65
+ - 业务中可封装 store 或 composable,集中管理 `message/type/visible`
66
+ - 加载状态时 `timeout` 会自动变为 `-1`,需手动关闭
67
+ - 若需多实例并存,可在父组件中渲染多个 JhToast 或改用队列