c-admin-kit 1.0.0

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 (56) hide show
  1. package/README.md +232 -0
  2. package/dist/c-admin-kit.css +1 -0
  3. package/dist/c-admin-kit.js +2482 -0
  4. package/dist/c-admin-kit.umd.cjs +10 -0
  5. package/dist/components/AsyncButton/index.vue.d.ts +48 -0
  6. package/dist/components/CollapsibleContainer/index.vue.d.ts +73 -0
  7. package/dist/components/CustomDrawer/index.vue.d.ts +149 -0
  8. package/dist/components/ImagePreview/index.vue.d.ts +34 -0
  9. package/dist/components/SearchBox/index.vue.d.ts +122 -0
  10. package/dist/components/SelectWithAll/index.vue.d.ts +169 -0
  11. package/dist/components/SelectWithPage/index.vue.d.ts +78 -0
  12. package/dist/components/SimpleTable/index.vue.d.ts +337 -0
  13. package/dist/components/SimpleTable/useTableColumnConfig.d.ts +5 -0
  14. package/dist/components/diff/index.vue.d.ts +33 -0
  15. package/dist/components/index.d.ts +12 -0
  16. package/dist/composables/index.d.ts +6 -0
  17. package/dist/composables/useConfirmAction.d.ts +5 -0
  18. package/dist/composables/useConfirmSubmit.d.ts +5 -0
  19. package/dist/composables/useDialog.d.ts +5 -0
  20. package/dist/composables/useDownload.d.ts +5 -0
  21. package/dist/composables/useForm.d.ts +5 -0
  22. package/dist/composables/useListPage.d.ts +5 -0
  23. package/dist/index.d.ts +9 -0
  24. package/dist/types.d.ts +282 -0
  25. package/dist/utils/index.d.ts +4 -0
  26. package/dist/utils/scroll-to.d.ts +7 -0
  27. package/dist/utils/searchFieldFactory.d.ts +34 -0
  28. package/dist/utils/treeManager.d.ts +64 -0
  29. package/dist/utils/validate.d.ts +30 -0
  30. package/package.json +86 -0
  31. package/src/components/AsyncButton/index.vue +58 -0
  32. package/src/components/CollapsibleContainer/index.vue +240 -0
  33. package/src/components/CustomDrawer/index.vue +167 -0
  34. package/src/components/ImagePreview/index.vue +88 -0
  35. package/src/components/SearchBox/index.vue +582 -0
  36. package/src/components/SelectWithAll/index.vue +281 -0
  37. package/src/components/SelectWithPage/index.vue +204 -0
  38. package/src/components/SimpleTable/index.vue +781 -0
  39. package/src/components/SimpleTable/useTableColumnConfig.ts +139 -0
  40. package/src/components/diff/index.vue +265 -0
  41. package/src/components/index.ts +35 -0
  42. package/src/composables/index.ts +6 -0
  43. package/src/composables/useConfirmAction.ts +62 -0
  44. package/src/composables/useConfirmSubmit.ts +68 -0
  45. package/src/composables/useDialog.ts +48 -0
  46. package/src/composables/useDownload.ts +83 -0
  47. package/src/composables/useForm.ts +114 -0
  48. package/src/composables/useListPage.ts +243 -0
  49. package/src/env.d.ts +7 -0
  50. package/src/index.ts +44 -0
  51. package/src/types.ts +344 -0
  52. package/src/utils/index.ts +4 -0
  53. package/src/utils/scroll-to.ts +60 -0
  54. package/src/utils/searchFieldFactory.ts +302 -0
  55. package/src/utils/treeManager.ts +218 -0
  56. package/src/utils/validate.ts +64 -0
@@ -0,0 +1,240 @@
1
+ <template>
2
+ <div class="collapsible-container">
3
+ <!-- 左侧面板 -->
4
+ <div class="left-panel" :class="{ collapsed: isCollapsed, resizing: isResizing }">
5
+ <div class="toggle-btn" @click="toggleCollapse">
6
+ <el-icon>
7
+ <Expand v-if="isCollapsed" />
8
+ <Fold v-else />
9
+ </el-icon>
10
+ </div>
11
+ <div v-show="!isCollapsed" class="left-content">
12
+ <slot name="left"></slot>
13
+ </div>
14
+ <!-- 拖拽分隔线 -->
15
+ <div
16
+ v-if="!isCollapsed"
17
+ class="resize-handle"
18
+ @mousedown="startResize"
19
+ ></div>
20
+ </div>
21
+ <!-- 右侧面板 -->
22
+ <div class="right-panel">
23
+ <div class="right-content">
24
+ <slot name="right"></slot>
25
+ </div>
26
+ </div>
27
+ </div>
28
+ </template>
29
+
30
+ <script setup lang="ts">
31
+ import { ref, computed } from 'vue'
32
+ import { Expand, Fold } from '@element-plus/icons-vue'
33
+
34
+ defineOptions({ name: 'CCollapsibleContainer' })
35
+
36
+ const props = defineProps({
37
+ defaultCollapsed: {
38
+ type: Boolean,
39
+ default: false
40
+ },
41
+ defaultWidth: {
42
+ type: Number,
43
+ default: 240
44
+ },
45
+ minWidth: {
46
+ type: Number,
47
+ default: 200
48
+ },
49
+ maxWidth: {
50
+ type: Number,
51
+ default: 600
52
+ },
53
+ height: {
54
+ type: String,
55
+ default: 'calc(100vh - 84px)'
56
+ }
57
+ })
58
+
59
+ const emit = defineEmits<{
60
+ (e: 'collapse-change', collapsed: boolean): void
61
+ (e: 'width-change', width: number): void
62
+ }>()
63
+
64
+ const isCollapsed = ref(props.defaultCollapsed)
65
+ const leftPanelWidth = ref(props.defaultWidth)
66
+ const isResizing = ref(false)
67
+
68
+ const gridTemplateColumns = computed(() => {
69
+ return isCollapsed.value ? '48px 1fr' : `${leftPanelWidth.value}px 1fr`
70
+ })
71
+
72
+ const actualLeftPanelWidth = computed(() => {
73
+ return isCollapsed.value ? '48px' : `${leftPanelWidth.value}px`
74
+ })
75
+
76
+ const toggleCollapse = (): void => {
77
+ isCollapsed.value = !isCollapsed.value
78
+ emit('collapse-change', isCollapsed.value)
79
+ }
80
+
81
+ const startResize = (e: MouseEvent): void => {
82
+ e.preventDefault()
83
+ isResizing.value = true
84
+
85
+ const startX = e.clientX
86
+ const startWidth = leftPanelWidth.value
87
+
88
+ let rafId: number | null = null
89
+ let pendingWidth: number | null = null
90
+
91
+ const updateWidth = (): void => {
92
+ if (pendingWidth !== null) {
93
+ leftPanelWidth.value = pendingWidth
94
+ emit('width-change', pendingWidth)
95
+ pendingWidth = null
96
+ }
97
+ rafId = null
98
+ }
99
+
100
+ const handleMouseMove = (e: MouseEvent): void => {
101
+ const delta = e.clientX - startX
102
+ const newWidth = startWidth + delta
103
+ const clampedWidth = Math.max(props.minWidth, Math.min(props.maxWidth, newWidth))
104
+ pendingWidth = clampedWidth
105
+
106
+ if (rafId === null) {
107
+ rafId = requestAnimationFrame(updateWidth)
108
+ }
109
+ }
110
+
111
+ const handleMouseUp = (): void => {
112
+ isResizing.value = false
113
+ if (rafId !== null) {
114
+ cancelAnimationFrame(rafId)
115
+ }
116
+
117
+ if (pendingWidth !== null) {
118
+ leftPanelWidth.value = pendingWidth
119
+ emit('width-change', pendingWidth)
120
+ }
121
+
122
+ document.removeEventListener('mousemove', handleMouseMove)
123
+ document.removeEventListener('mouseup', handleMouseUp)
124
+ document.body.style.cursor = ''
125
+ document.body.style.userSelect = ''
126
+ }
127
+
128
+ document.body.style.cursor = 'col-resize'
129
+ document.body.style.userSelect = 'none'
130
+ document.addEventListener('mousemove', handleMouseMove)
131
+ document.addEventListener('mouseup', handleMouseUp)
132
+ }
133
+
134
+ defineExpose({
135
+ toggleCollapse,
136
+ isCollapsed,
137
+ leftPanelWidth
138
+ })
139
+ </script>
140
+
141
+ <style scoped>
142
+ .collapsible-container {
143
+ display: grid;
144
+ grid-template-columns: v-bind(gridTemplateColumns);
145
+ height: v-bind(height);
146
+ width: 100%;
147
+ background: #f5f7fa;
148
+ overflow: hidden;
149
+ }
150
+
151
+ .left-panel {
152
+ background: #fff;
153
+ border-right: 1px solid #e4e7ed;
154
+ display: flex;
155
+ flex-direction: column;
156
+ overflow: hidden;
157
+ max-width: v-bind(actualLeftPanelWidth);
158
+ min-width: v-bind(actualLeftPanelWidth);
159
+ position: relative;
160
+ will-change: max-width, min-width;
161
+ }
162
+
163
+ .left-panel:not(.resizing) {
164
+ transition: all 0.3s ease-in-out;
165
+ }
166
+
167
+ .left-content {
168
+ height: 100%;
169
+ padding: 16px;
170
+ overflow-y: auto;
171
+ min-width: 0;
172
+ transition: opacity 0.2s ease-in-out;
173
+ opacity: 1;
174
+ }
175
+
176
+ .left-panel.collapsed .left-content {
177
+ opacity: 0;
178
+ }
179
+
180
+ .right-panel {
181
+ background: #fff;
182
+ transition: all 0.3s ease-in-out;
183
+ max-width: 100%;
184
+ overflow: hidden;
185
+ }
186
+
187
+ .toggle-btn {
188
+ width: 48px;
189
+ height: 48px;
190
+ display: flex;
191
+ align-items: center;
192
+ justify-content: center;
193
+ cursor: pointer;
194
+ flex-shrink: 0;
195
+ transition: transform 0.3s ease-in-out;
196
+ }
197
+
198
+ .toggle-btn:hover {
199
+ background-color: #f5f7fa;
200
+ }
201
+
202
+ .right-content {
203
+ height: 100%;
204
+ padding: 16px;
205
+ overflow-y: auto;
206
+ max-width: 100%;
207
+ box-sizing: border-box;
208
+ }
209
+
210
+ .resize-handle {
211
+ position: absolute;
212
+ right: 0;
213
+ top: 0;
214
+ width: 4px;
215
+ height: 100%;
216
+ cursor: col-resize;
217
+ background: transparent;
218
+ transition: background 0.2s ease;
219
+ z-index: 10;
220
+ will-change: background;
221
+ padding: 0 4px;
222
+ margin: 0 -4px;
223
+ }
224
+
225
+ .resize-handle:hover {
226
+ background: #409eff;
227
+ }
228
+
229
+ .resize-handle:active {
230
+ background: #337ecc;
231
+ }
232
+
233
+ .collapsible-container:has(.resize-handle:active) {
234
+ transition: none;
235
+ }
236
+
237
+ .left-panel:has(.resize-handle:active) {
238
+ transition: none;
239
+ }
240
+ </style>
@@ -0,0 +1,167 @@
1
+ <template>
2
+ <el-drawer
3
+ v-model="visible"
4
+ :title="title"
5
+ :size="size"
6
+ :direction="direction"
7
+ :before-close="handleBeforeClose"
8
+ :close-on-click-modal="closeOnClickModal"
9
+ :close-on-press-escape="closeOnPressEscape"
10
+ :destroy-on-close="destroyOnClose"
11
+ @closed="handleClosed"
12
+ :class="{ 'drawer-with-footer': showFooter }"
13
+ >
14
+ <div
15
+ class="drawer-content"
16
+ :class="{ 'has-footer': showFooter }"
17
+ >
18
+ <slot></slot>
19
+ </div>
20
+ <template
21
+ #footer
22
+ v-if="showFooter"
23
+ >
24
+ <div class="drawer-footer">
25
+ <el-button
26
+ @click="handleCancel"
27
+ size="default"
28
+ >
29
+ 取消
30
+ </el-button>
31
+ <el-button
32
+ type="primary"
33
+ :loading="confirmLoading"
34
+ @click="handleConfirm"
35
+ size="default"
36
+ >
37
+ 确定
38
+ </el-button>
39
+ </div>
40
+ </template>
41
+ </el-drawer>
42
+ </template>
43
+
44
+ <script setup lang="ts">
45
+ import { computed, type PropType } from 'vue'
46
+
47
+ defineOptions({ name: 'CCustomDrawer' })
48
+
49
+ const props = defineProps({
50
+ modelValue: {
51
+ type: Boolean,
52
+ default: false,
53
+ },
54
+ title: {
55
+ type: String,
56
+ default: '',
57
+ },
58
+ size: {
59
+ type: [String, Number],
60
+ default: '30%',
61
+ },
62
+ direction: {
63
+ type: String as PropType<'rtl' | 'ltr' | 'ttb' | 'btt'>,
64
+ default: 'rtl',
65
+ validator: (val: string) => ['rtl', 'ltr', 'ttb', 'btt'].includes(val),
66
+ },
67
+ showFooter: {
68
+ type: Boolean,
69
+ default: false,
70
+ },
71
+ closeOnClickModal: {
72
+ type: Boolean,
73
+ default: true,
74
+ },
75
+ closeOnPressEscape: {
76
+ type: Boolean,
77
+ default: true,
78
+ },
79
+ destroyOnClose: {
80
+ type: Boolean,
81
+ default: false,
82
+ },
83
+ confirmLoading: {
84
+ type: Boolean,
85
+ default: false,
86
+ },
87
+ beforeClose: {
88
+ type: Function as PropType<((done: () => void) => void) | null>,
89
+ default: null,
90
+ },
91
+ onClose: {
92
+ type: Function as PropType<(() => void) | null>,
93
+ default: null,
94
+ },
95
+ onConfirm: {
96
+ type: Function as PropType<(() => void) | null>,
97
+ default: null,
98
+ },
99
+ onCancel: {
100
+ type: Function as PropType<(() => void) | null>,
101
+ default: null,
102
+ },
103
+ })
104
+
105
+ const emit = defineEmits<{
106
+ (e: 'update:modelValue', value: boolean): void
107
+ (e: 'close'): void
108
+ (e: 'confirm'): void
109
+ (e: 'cancel'): void
110
+ }>()
111
+
112
+ const visible = computed({
113
+ get: () => props.modelValue,
114
+ set: (val: boolean) => emit('update:modelValue', val),
115
+ })
116
+
117
+ const handleBeforeClose = (done: () => void): void => {
118
+ if (props.beforeClose) {
119
+ props.beforeClose(done)
120
+ } else {
121
+ done()
122
+ }
123
+ }
124
+
125
+ const handleClosed = (): void => {
126
+ if (props.onClose) {
127
+ props.onClose()
128
+ }
129
+ emit('close')
130
+ }
131
+
132
+ const handleConfirm = (): void => {
133
+ if (props.onConfirm) {
134
+ props.onConfirm()
135
+ }
136
+ emit('confirm')
137
+ }
138
+
139
+ const handleCancel = (): void => {
140
+ visible.value = false
141
+ if (props.onCancel) {
142
+ props.onCancel()
143
+ }
144
+ emit('cancel')
145
+ }
146
+
147
+ defineExpose({
148
+ open: () => {
149
+ visible.value = true
150
+ },
151
+ close: () => {
152
+ visible.value = false
153
+ },
154
+ })
155
+ </script>
156
+
157
+ <style scoped>
158
+ .drawer-content {
159
+ height: 100%;
160
+ }
161
+
162
+ .drawer-footer {
163
+ display: flex;
164
+ justify-content: flex-end;
165
+ gap: 12px;
166
+ }
167
+ </style>
@@ -0,0 +1,88 @@
1
+ <template>
2
+ <el-image
3
+ :src="realSrc"
4
+ fit="cover"
5
+ :style="`width:${realWidth};height:${realHeight};`"
6
+ :preview-src-list="realSrcList"
7
+ preview-teleported
8
+ v-bind="$attrs"
9
+ >
10
+ <template #error>
11
+ <div class="image-slot">
12
+ <el-icon><PictureFilled /></el-icon>
13
+ </div>
14
+ </template>
15
+ </el-image>
16
+ </template>
17
+
18
+ <script setup lang="ts">
19
+ import { computed, type PropType } from 'vue'
20
+ import { PictureFilled } from '@element-plus/icons-vue'
21
+
22
+ defineOptions({ name: 'CImagePreview' })
23
+
24
+ const props = defineProps({
25
+ src: {
26
+ type: [String, Array] as PropType<string | string[]>,
27
+ default: ""
28
+ },
29
+ width: {
30
+ type: [Number, String],
31
+ default: "60px"
32
+ },
33
+ height: {
34
+ type: [Number, String],
35
+ default: "60px"
36
+ }
37
+ })
38
+
39
+ const realSrcList = computed<string[]>(() => {
40
+ if (!props.src) return []
41
+ if (Array.isArray(props.src)) {
42
+ return props.src.map((s) => String(s).trim()).filter(Boolean)
43
+ }
44
+ return String(props.src)
45
+ .split(",")
46
+ .map((s) => s.trim())
47
+ .filter(Boolean)
48
+ })
49
+
50
+ const realSrc = computed(() => {
51
+ return realSrcList.value[0] || ""
52
+ })
53
+
54
+ const realWidth = computed(() =>
55
+ typeof props.width === "string" ? props.width : `${props.width}px`
56
+ )
57
+
58
+ const realHeight = computed(() =>
59
+ typeof props.height === "string" ? props.height : `${props.height}px`
60
+ )
61
+ </script>
62
+
63
+ <style scoped>
64
+ .el-image {
65
+ border-radius: 5px;
66
+ background-color: #ebeef5;
67
+ box-shadow: 0 0 5px 1px #ccc;
68
+ }
69
+
70
+ :deep(.el-image__inner) {
71
+ transition: all 0.3s;
72
+ cursor: pointer;
73
+ }
74
+
75
+ :deep(.el-image__inner:hover) {
76
+ transform: scale(1.08);
77
+ }
78
+
79
+ :deep(.image-slot) {
80
+ display: flex;
81
+ justify-content: center;
82
+ align-items: center;
83
+ width: 100%;
84
+ height: 100%;
85
+ color: #909399;
86
+ font-size: 24px;
87
+ }
88
+ </style>