@vela-studio/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 (68) hide show
  1. package/README.md +152 -0
  2. package/dist/index.d.ts +696 -0
  3. package/dist/index.js +10 -0
  4. package/dist/index.js.map +1 -0
  5. package/dist/index.mjs +11786 -0
  6. package/dist/index.mjs.map +1 -0
  7. package/dist/index.umd.js +10 -0
  8. package/dist/index.umd.js.map +1 -0
  9. package/dist/style.css +1 -0
  10. package/index.ts +150 -0
  11. package/package.json +73 -0
  12. package/src/components/advanced/scripting/Scripting.vue +189 -0
  13. package/src/components/advanced/state/State.vue +231 -0
  14. package/src/components/advanced/trigger/Trigger.vue +256 -0
  15. package/src/components/basic/button/Button.vue +120 -0
  16. package/src/components/basic/container/Container.vue +22 -0
  17. package/src/components/chart/barChart/barChart.vue +176 -0
  18. package/src/components/chart/doughnutChart/doughnutChart.vue +128 -0
  19. package/src/components/chart/funnelChart/funnelChart.vue +128 -0
  20. package/src/components/chart/gaugeChart/gaugeChart.vue +144 -0
  21. package/src/components/chart/lineChart/lineChart.vue +188 -0
  22. package/src/components/chart/pieChart/pieChart.vue +114 -0
  23. package/src/components/chart/radarChart/radarChart.vue +115 -0
  24. package/src/components/chart/sankeyChart/sankeyChart.vue +144 -0
  25. package/src/components/chart/scatterChart/scatterChart.vue +162 -0
  26. package/src/components/chart/stackedBarChart/stackedBarChart.vue +184 -0
  27. package/src/components/content/html/Html.vue +104 -0
  28. package/src/components/content/iframe/Iframe.vue +111 -0
  29. package/src/components/content/markdown/Markdown.vue +174 -0
  30. package/src/components/controls/breadcrumb/Breadcrumb.vue +79 -0
  31. package/src/components/controls/buttonGroup/ButtonGroup.vue +93 -0
  32. package/src/components/controls/checkboxGroup/CheckboxGroup.vue +147 -0
  33. package/src/components/controls/dateRange/DateRange.vue +174 -0
  34. package/src/components/controls/multiSelect/MultiSelect.vue +155 -0
  35. package/src/components/controls/navButton/NavButton.vue +97 -0
  36. package/src/components/controls/pagination/Pagination.vue +94 -0
  37. package/src/components/controls/searchBox/SearchBox.vue +170 -0
  38. package/src/components/controls/select/Select.vue +134 -0
  39. package/src/components/controls/slider/Slider.vue +167 -0
  40. package/src/components/controls/switch/Switch.vue +107 -0
  41. package/src/components/data/cardGrid/CardGrid.vue +318 -0
  42. package/src/components/data/list/List.vue +282 -0
  43. package/src/components/data/pivot/Pivot.vue +270 -0
  44. package/src/components/data/table/Table.vue +150 -0
  45. package/src/components/data/timeline/Timeline.vue +315 -0
  46. package/src/components/group/Group.vue +75 -0
  47. package/src/components/kpi/box/Box.vue +98 -0
  48. package/src/components/kpi/countUp/CountUp.vue +193 -0
  49. package/src/components/kpi/progress/Progress.vue +159 -0
  50. package/src/components/kpi/stat/Stat.vue +205 -0
  51. package/src/components/kpi/text/Text.vue +74 -0
  52. package/src/components/layout/badge/Badge.vue +105 -0
  53. package/src/components/layout/col/Col.vue +114 -0
  54. package/src/components/layout/flex/Flex.vue +105 -0
  55. package/src/components/layout/grid/Grid.vue +89 -0
  56. package/src/components/layout/modal/Modal.vue +118 -0
  57. package/src/components/layout/panel/Panel.vue +162 -0
  58. package/src/components/layout/row/Row.vue +99 -0
  59. package/src/components/layout/tabs/Tabs.vue +117 -0
  60. package/src/components/media/image/Image.vue +132 -0
  61. package/src/components/media/video/Video.vue +115 -0
  62. package/src/components/v2/basic/BaseButton.vue +179 -0
  63. package/src/components/v2/kpi/KpiCard.vue +215 -0
  64. package/src/components/v2/layout/GridBox.vue +55 -0
  65. package/src/hooks/useDataSource.ts +123 -0
  66. package/src/types/gis.ts +251 -0
  67. package/src/utils/chartUtils.ts +349 -0
  68. package/src/utils/dataUtils.ts +403 -0
@@ -0,0 +1,167 @@
1
+ <template>
2
+ <div :style="containerStyle">
3
+ <el-slider
4
+ v-model="internalValue"
5
+ :min="min"
6
+ :max="max"
7
+ :step="step"
8
+ :disabled="disabled"
9
+ :show-stops="showStops"
10
+ :show-tooltip="showTooltip"
11
+ :show-input="showInput"
12
+ :show-input-controls="showInputControls"
13
+ :format-tooltip="formatTooltipFn"
14
+ :range="range"
15
+ :vertical="vertical"
16
+ :height="height"
17
+ :marks="marks"
18
+ @change="handleChange"
19
+ @input="handleInput"
20
+ />
21
+ <div v-if="showValue" :style="valueStyle" class="slider-value">
22
+ {{ displayValue }}
23
+ </div>
24
+ </div>
25
+ </template>
26
+
27
+ <script setup lang="ts">
28
+ import { computed, ref, watch } from 'vue'
29
+ import type { CSSProperties } from 'vue'
30
+ import { ElSlider } from 'element-plus'
31
+
32
+ const props = withDefaults(
33
+ defineProps<{
34
+ modelValue?: number | number[]
35
+ min?: number
36
+ max?: number
37
+ step?: number
38
+ disabled?: boolean
39
+ showStops?: boolean
40
+ showTooltip?: boolean
41
+ showInput?: boolean
42
+ showInputControls?: boolean
43
+ range?: boolean
44
+ vertical?: boolean
45
+ height?: string
46
+ marks?: Record<number, string | { style?: CSSProperties; label: string }>
47
+ showValue?: boolean
48
+ valueFormat?: string
49
+ padding?: number
50
+ backgroundColor?: string
51
+ activeColor?: string
52
+ inactiveColor?: string
53
+ buttonSize?: number
54
+ valueFontSize?: number
55
+ valueColor?: string
56
+ valueAlign?: 'left' | 'center' | 'right'
57
+ }>(),
58
+ {
59
+ modelValue: 0,
60
+ min: 0,
61
+ max: 100,
62
+ step: 1,
63
+ disabled: false,
64
+ showStops: false,
65
+ showTooltip: true,
66
+ showInput: false,
67
+ showInputControls: true,
68
+ range: false,
69
+ vertical: false,
70
+ height: '200px',
71
+ marks: undefined,
72
+ showValue: true,
73
+ valueFormat: '{value}',
74
+ padding: 16,
75
+ backgroundColor: 'transparent',
76
+ activeColor: '#409eff',
77
+ inactiveColor: '#e4e7ed',
78
+ buttonSize: 20,
79
+ valueFontSize: 14,
80
+ valueColor: '#606266',
81
+ valueAlign: 'center',
82
+ },
83
+ )
84
+
85
+ const emit = defineEmits<{
86
+ 'update:modelValue': [value: number | number[]]
87
+ change: [value: number | number[]]
88
+ input: [value: number | number[]]
89
+ }>()
90
+
91
+ // 内部值
92
+ const internalValue = ref<number | number[]>(
93
+ props.range
94
+ ? Array.isArray(props.modelValue)
95
+ ? [...props.modelValue]
96
+ : [props.min, props.max]
97
+ : Array.isArray(props.modelValue)
98
+ ? props.modelValue[0]
99
+ : props.modelValue,
100
+ )
101
+
102
+ // 监听外部值变化
103
+ watch(
104
+ () => props.modelValue,
105
+ (newVal) => {
106
+ if (props.range) {
107
+ internalValue.value = Array.isArray(newVal) ? [...newVal] : [props.min, props.max]
108
+ } else {
109
+ internalValue.value = Array.isArray(newVal) ? newVal[0] : newVal
110
+ }
111
+ },
112
+ )
113
+
114
+ // 样式
115
+ const containerStyle = computed<CSSProperties>(
116
+ () =>
117
+ ({
118
+ padding: `${props.padding}px`,
119
+ backgroundColor: props.backgroundColor,
120
+ '--el-slider-main-bg-color': props.activeColor,
121
+ '--el-slider-runway-bg-color': props.inactiveColor,
122
+ '--el-slider-button-size': `${props.buttonSize}px`,
123
+ }) as CSSProperties,
124
+ )
125
+
126
+ const valueStyle = computed<CSSProperties>(() => ({
127
+ marginTop: '8px',
128
+ fontSize: `${props.valueFontSize}px`,
129
+ color: props.valueColor,
130
+ textAlign: props.valueAlign,
131
+ }))
132
+
133
+ // 格式化提示
134
+ const formatTooltipFn = (val: number) => {
135
+ return props.valueFormat.replace('{value}', String(val))
136
+ }
137
+
138
+ // 显示值
139
+ const displayValue = computed(() => {
140
+ if (Array.isArray(internalValue.value)) {
141
+ return `${internalValue.value[0]} - ${internalValue.value[1]}`
142
+ }
143
+ return formatTooltipFn(internalValue.value)
144
+ })
145
+
146
+ // 事件处理
147
+ const handleChange = (value: number | number[]) => {
148
+ emit('update:modelValue', value)
149
+ emit('change', value)
150
+ }
151
+
152
+ const handleInput = (value: number | number[]) => {
153
+ emit('input', value)
154
+ }
155
+ </script>
156
+
157
+ <style scoped>
158
+ .slider-value {
159
+ margin-top: 8px;
160
+ }
161
+
162
+ :deep(.el-slider) {
163
+ --el-slider-main-bg-color: v-bind('containerStyle["--el-slider-main-bg-color"]');
164
+ --el-slider-runway-bg-color: v-bind('containerStyle["--el-slider-runway-bg-color"]');
165
+ --el-slider-button-size: v-bind('containerStyle["--el-slider-button-size"]');
166
+ }
167
+ </style>
@@ -0,0 +1,107 @@
1
+ <template>
2
+ <div :style="containerStyle">
3
+ <el-switch
4
+ v-model="internalValue"
5
+ :size="size"
6
+ :disabled="disabled"
7
+ :loading="loading"
8
+ :active-text="activeText"
9
+ :inactive-text="inactiveText"
10
+ :active-value="activeValue"
11
+ :inactive-value="inactiveValue"
12
+ :inline-prompt="inlinePrompt"
13
+ :active-icon="activeIcon"
14
+ :inactive-icon="inactiveIcon"
15
+ :width="width"
16
+ @change="handleChange"
17
+ />
18
+ </div>
19
+ </template>
20
+
21
+ <script setup lang="ts">
22
+ import { computed, ref, watch } from 'vue'
23
+ import type { CSSProperties, Component } from 'vue'
24
+ import { ElSwitch } from 'element-plus'
25
+
26
+ const props = withDefaults(
27
+ defineProps<{
28
+ modelValue?: boolean | string | number
29
+ size?: 'large' | 'default' | 'small'
30
+ disabled?: boolean
31
+ loading?: boolean
32
+ activeText?: string
33
+ inactiveText?: string
34
+ activeValue?: boolean | string | number
35
+ inactiveValue?: boolean | string | number
36
+ inlinePrompt?: boolean
37
+ activeIcon?: string | Component
38
+ inactiveIcon?: string | Component
39
+ padding?: number
40
+ backgroundColor?: string
41
+ activeColor?: string
42
+ inactiveColor?: string
43
+ borderColor?: string
44
+ width?: number | string
45
+ }>(),
46
+ {
47
+ modelValue: false,
48
+ size: 'default',
49
+ disabled: false,
50
+ loading: false,
51
+ activeText: '',
52
+ inactiveText: '',
53
+ activeValue: true,
54
+ inactiveValue: false,
55
+ inlinePrompt: false,
56
+ activeIcon: undefined,
57
+ inactiveIcon: undefined,
58
+ padding: 16,
59
+ backgroundColor: 'transparent',
60
+ activeColor: '#409eff',
61
+ inactiveColor: '#dcdfe6',
62
+ borderColor: '#dcdfe6',
63
+ },
64
+ )
65
+
66
+ const emit = defineEmits<{
67
+ 'update:modelValue': [value: boolean | string | number]
68
+ change: [value: boolean | string | number]
69
+ }>()
70
+
71
+ // 内部值
72
+ const internalValue = ref<boolean | string | number>(props.modelValue)
73
+
74
+ // 监听外部值变化
75
+ watch(
76
+ () => props.modelValue,
77
+ (newVal) => {
78
+ internalValue.value = newVal
79
+ },
80
+ )
81
+
82
+ // 样式
83
+ const containerStyle = computed<CSSProperties>(
84
+ () =>
85
+ ({
86
+ padding: `${props.padding}px`,
87
+ backgroundColor: props.backgroundColor,
88
+ '--el-switch-on-color': props.activeColor,
89
+ '--el-switch-off-color': props.inactiveColor,
90
+ '--el-switch-border-color': props.borderColor,
91
+ }) as CSSProperties,
92
+ )
93
+
94
+ // 事件处理
95
+ const handleChange = (value: boolean | string | number) => {
96
+ emit('update:modelValue', value)
97
+ emit('change', value)
98
+ }
99
+ </script>
100
+
101
+ <style scoped>
102
+ :deep(.el-switch) {
103
+ --el-switch-on-color: v-bind('containerStyle["--el-switch-on-color"]');
104
+ --el-switch-off-color: v-bind('containerStyle["--el-switch-off-color"]');
105
+ --el-switch-border-color: v-bind('containerStyle["--el-switch-border-color"]');
106
+ }
107
+ </style>
@@ -0,0 +1,318 @@
1
+ <script setup lang="ts">
2
+ import { computed } from 'vue'
3
+ import type { CSSProperties } from 'vue'
4
+ import { ElScrollbar, ElEmpty, ElCard, ElTag } from 'element-plus'
5
+
6
+ export interface CardItem {
7
+ title?: string
8
+ description?: string
9
+ footer?: string
10
+ tags?: string[] | string
11
+ image?: string
12
+ [key: string]: unknown
13
+ }
14
+
15
+ const props = withDefaults(
16
+ defineProps<{
17
+ // 数据
18
+ data?: CardItem[]
19
+ // 字段映射
20
+ titleField?: string
21
+ descriptionField?: string
22
+ footerField?: string
23
+ tagsField?: string
24
+ imageField?: string
25
+ // 配置
26
+ showImage?: boolean
27
+ showTitle?: boolean
28
+ showDescription?: boolean
29
+ showTags?: boolean
30
+ showFooter?: boolean
31
+ cardShadow?: 'always' | 'hover' | 'never'
32
+ tagSize?: 'large' | 'default' | 'small'
33
+ emptyText?: string
34
+ scrollHeight?: string
35
+ columns?: number
36
+ gap?: number
37
+ // 样式
38
+ opacity?: number
39
+ visible?: boolean
40
+ backgroundColor?: string
41
+ borderRadius?: number
42
+ padding?: number
43
+ cardBorderRadius?: number
44
+ cardPadding?: number
45
+ imageHeight?: number
46
+ imageBorderRadius?: number
47
+ titleFontSize?: number
48
+ titleColor?: string
49
+ titleFontWeight?: string
50
+ descriptionFontSize?: number
51
+ descriptionColor?: string
52
+ footerFontSize?: number
53
+ footerColor?: string
54
+ footerBorderColor?: string
55
+ }>(),
56
+ {
57
+ data: () => [],
58
+ titleField: 'title',
59
+ descriptionField: 'description',
60
+ footerField: 'footer',
61
+ tagsField: 'tags',
62
+ imageField: 'image',
63
+ showImage: false,
64
+ showTitle: true,
65
+ showDescription: true,
66
+ showTags: false,
67
+ showFooter: false,
68
+ cardShadow: 'hover',
69
+ tagSize: 'small',
70
+ emptyText: '暂无数据',
71
+ scrollHeight: '100%',
72
+ columns: 3,
73
+ gap: 16,
74
+ opacity: 100,
75
+ visible: true,
76
+ backgroundColor: '#f5f7fa',
77
+ borderRadius: 4,
78
+ padding: 16,
79
+ cardBorderRadius: 4,
80
+ cardPadding: 14,
81
+ imageHeight: 150,
82
+ imageBorderRadius: 4,
83
+ titleFontSize: 16,
84
+ titleColor: '#303133',
85
+ titleFontWeight: '600',
86
+ descriptionFontSize: 13,
87
+ descriptionColor: '#606266',
88
+ footerFontSize: 12,
89
+ footerColor: '#909399',
90
+ footerBorderColor: '#ebeef5',
91
+ },
92
+ )
93
+
94
+ const emit = defineEmits<{
95
+ (e: 'card-click', item: CardItem, index: number): void
96
+ }>()
97
+
98
+ // 默认数据
99
+ const defaultData: CardItem[] = [
100
+ {
101
+ title: '卡片 1',
102
+ description: '这是卡片的描述信息',
103
+ footer: '2024-01-01',
104
+ tags: ['标签1', '标签2'],
105
+ },
106
+ { title: '卡片 2', description: '这是卡片的描述信息', footer: '2024-01-02', tags: ['标签3'] },
107
+ {
108
+ title: '卡片 3',
109
+ description: '这是卡片的描述信息',
110
+ footer: '2024-01-03',
111
+ tags: ['标签4', '标签5'],
112
+ },
113
+ ]
114
+
115
+ const cardData = computed(() => (props.data.length > 0 ? props.data : defaultData))
116
+
117
+ function getTitle(item: CardItem): string {
118
+ return String(item[props.titleField] ?? '')
119
+ }
120
+
121
+ function getDescription(item: CardItem): string {
122
+ return String(item[props.descriptionField] ?? '')
123
+ }
124
+
125
+ function getFooter(item: CardItem): string {
126
+ return String(item[props.footerField] ?? '')
127
+ }
128
+
129
+ function getTags(item: CardItem): string[] {
130
+ const tags = item[props.tagsField]
131
+ if (Array.isArray(tags)) return tags.map(String)
132
+ if (typeof tags === 'string') return tags.split(',').map((s) => s.trim())
133
+ return []
134
+ }
135
+
136
+ function getImage(item: CardItem): string {
137
+ return String(item[props.imageField] ?? '')
138
+ }
139
+
140
+ function getTagType(index: number): 'primary' | 'success' | 'info' | 'warning' | 'danger' {
141
+ const types: ('primary' | 'success' | 'info' | 'warning' | 'danger')[] = [
142
+ 'primary',
143
+ 'success',
144
+ 'info',
145
+ 'warning',
146
+ 'danger',
147
+ ]
148
+ return types[index % types.length]
149
+ }
150
+
151
+ // 样式
152
+ const containerStyle = computed<CSSProperties>(() => ({
153
+ opacity: props.opacity / 100,
154
+ display: props.visible === false ? 'none' : 'block',
155
+ width: '100%',
156
+ height: '100%',
157
+ backgroundColor: props.backgroundColor,
158
+ borderRadius: `${props.borderRadius}px`,
159
+ padding: `${props.padding}px`,
160
+ overflow: 'hidden',
161
+ boxSizing: 'border-box',
162
+ }))
163
+
164
+ const gridStyle = computed<CSSProperties>(() => ({
165
+ display: 'grid',
166
+ gridTemplateColumns: `repeat(${props.columns}, 1fr)`,
167
+ gap: `${props.gap}px`,
168
+ }))
169
+
170
+ const cardStyle = computed<CSSProperties>(() => ({
171
+ cursor: 'pointer',
172
+ transition: 'all 0.3s',
173
+ borderRadius: `${props.cardBorderRadius}px`,
174
+ }))
175
+
176
+ const cardBodyStyle = computed(() => ({
177
+ padding: `${props.cardPadding}px`,
178
+ }))
179
+
180
+ const imageStyle = computed<CSSProperties>(() => ({
181
+ width: '100%',
182
+ height: `${props.imageHeight}px`,
183
+ marginBottom: '12px',
184
+ borderRadius: `${props.imageBorderRadius}px`,
185
+ overflow: 'hidden',
186
+ }))
187
+
188
+ const titleStyle = computed<CSSProperties>(() => ({
189
+ fontSize: `${props.titleFontSize}px`,
190
+ color: props.titleColor,
191
+ fontWeight: props.titleFontWeight,
192
+ marginBottom: '8px',
193
+ overflow: 'hidden',
194
+ textOverflow: 'ellipsis',
195
+ whiteSpace: 'nowrap',
196
+ }))
197
+
198
+ const descriptionStyle = computed<CSSProperties>(() => ({
199
+ fontSize: `${props.descriptionFontSize}px`,
200
+ color: props.descriptionColor,
201
+ lineHeight: 1.6,
202
+ marginBottom: '8px',
203
+ }))
204
+
205
+ const footerStyle = computed<CSSProperties>(() => ({
206
+ fontSize: `${props.footerFontSize}px`,
207
+ color: props.footerColor,
208
+ marginTop: '8px',
209
+ paddingTop: '8px',
210
+ borderTop: `1px solid ${props.footerBorderColor}`,
211
+ }))
212
+
213
+ const tagStyle = computed<CSSProperties>(() => ({
214
+ marginRight: '6px',
215
+ marginTop: '6px',
216
+ }))
217
+
218
+ function handleCardClick(item: CardItem, index: number) {
219
+ emit('card-click', item, index)
220
+ }
221
+ </script>
222
+
223
+ <template>
224
+ <div class="card-grid-container" :style="containerStyle">
225
+ <el-scrollbar :height="scrollHeight">
226
+ <div v-if="cardData.length === 0" class="empty-state">
227
+ <el-empty :description="emptyText" :image-size="80" />
228
+ </div>
229
+ <div v-else class="grid-wrapper" :style="gridStyle">
230
+ <el-card
231
+ v-for="(item, index) in cardData"
232
+ :key="index"
233
+ :shadow="cardShadow"
234
+ :body-style="cardBodyStyle"
235
+ class="grid-card"
236
+ :style="cardStyle"
237
+ @click="handleCardClick(item, index)"
238
+ >
239
+ <!-- 卡片图片 -->
240
+ <div v-if="showImage && getImage(item)" class="card-image" :style="imageStyle">
241
+ <img :src="getImage(item)" :alt="getTitle(item)" />
242
+ </div>
243
+
244
+ <!-- 卡片内容 -->
245
+ <div class="card-content">
246
+ <!-- 标题 -->
247
+ <div v-if="showTitle" class="card-title" :style="titleStyle">
248
+ {{ getTitle(item) }}
249
+ </div>
250
+
251
+ <!-- 描述 -->
252
+ <div v-if="showDescription" class="card-description" :style="descriptionStyle">
253
+ {{ getDescription(item) }}
254
+ </div>
255
+
256
+ <!-- 标签 -->
257
+ <div v-if="showTags && getTags(item).length > 0" class="card-tags">
258
+ <el-tag
259
+ v-for="(tag, tagIndex) in getTags(item)"
260
+ :key="tagIndex"
261
+ :type="getTagType(tagIndex)"
262
+ :size="tagSize"
263
+ :style="tagStyle"
264
+ >
265
+ {{ tag }}
266
+ </el-tag>
267
+ </div>
268
+
269
+ <!-- 底部信息 -->
270
+ <div v-if="showFooter" class="card-footer" :style="footerStyle">
271
+ {{ getFooter(item) }}
272
+ </div>
273
+ </div>
274
+ </el-card>
275
+ </div>
276
+ </el-scrollbar>
277
+ </div>
278
+ </template>
279
+
280
+ <style scoped>
281
+ .card-grid-container {
282
+ box-sizing: border-box;
283
+ }
284
+
285
+ .empty-state {
286
+ display: flex;
287
+ align-items: center;
288
+ justify-content: center;
289
+ min-height: 200px;
290
+ }
291
+
292
+ .grid-wrapper {
293
+ width: 100%;
294
+ }
295
+
296
+ .grid-card:hover {
297
+ transform: translateY(-4px);
298
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
299
+ }
300
+
301
+ .card-image img {
302
+ width: 100%;
303
+ height: 100%;
304
+ object-fit: cover;
305
+ }
306
+
307
+ .card-content {
308
+ display: flex;
309
+ flex-direction: column;
310
+ }
311
+
312
+ .card-tags {
313
+ display: flex;
314
+ flex-wrap: wrap;
315
+ margin-top: 4px;
316
+ margin-right: -6px;
317
+ }
318
+ </style>