rrj-astra-ui 1.0.2

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 (38) hide show
  1. package/README.en.md +36 -0
  2. package/README.md +37 -0
  3. package/components/AuiBadge.vue +50 -0
  4. package/components/AuiBlockBox.vue +85 -0
  5. package/components/AuiButton.vue +210 -0
  6. package/components/AuiCustomerForm.vue +304 -0
  7. package/components/AuiDivider.vue +66 -0
  8. package/components/AuiFold.vue +40 -0
  9. package/components/AuiFoldItem.vue +173 -0
  10. package/components/AuiForm.vue +76 -0
  11. package/components/AuiFormItem.vue +88 -0
  12. package/components/AuiGrid.vue +26 -0
  13. package/components/AuiGridItem.vue +20 -0
  14. package/components/AuiIcon.vue +145 -0
  15. package/components/AuiImage.vue +152 -0
  16. package/components/AuiInput.vue +176 -0
  17. package/components/AuiLamp.vue +254 -0
  18. package/components/AuiLineProgress.vue +169 -0
  19. package/components/AuiList.vue +18 -0
  20. package/components/AuiListItem.vue +142 -0
  21. package/components/AuiMultiSelect.vue +303 -0
  22. package/components/AuiNoticeBar.vue +62 -0
  23. package/components/AuiNumberBox.vue +282 -0
  24. package/components/AuiPicker.vue +619 -0
  25. package/components/AuiPopup.vue +57 -0
  26. package/components/AuiSelectGroup.vue +312 -0
  27. package/components/AuiTab.vue +173 -0
  28. package/components/AuiTabItem.vue +43 -0
  29. package/components/AuiTable.vue +357 -0
  30. package/components/AuiTag.vue +112 -0
  31. package/components/AuiText.vue +81 -0
  32. package/components/AuiTextarea.vue +203 -0
  33. package/components/AuiToast.vue +96 -0
  34. package/components/AuiUpdate.vue +271 -0
  35. package/components/AuiUpload.vue +524 -0
  36. package/index.js +93 -0
  37. package/package.json +36 -0
  38. package/style.scss +30 -0
@@ -0,0 +1,312 @@
1
+ <template>
2
+ <view :class="[`aui-select-group`,`aui-select-group-theme-${props.theme}`]">
3
+ <view
4
+ class="aui-select-group__item"
5
+ :class="{
6
+ 'aui-select-group__item_active': isSelected(item),
7
+ 'has-badge': shouldShowBadge(item)
8
+ }"
9
+ v-for="item in internalDataSource"
10
+ :key="item.id || item[props.valueKey] || item"
11
+ @click="toggleSelection(item)"
12
+ @mouseenter="hoveredItem = item"
13
+ @mouseleave="hoveredItem = null"
14
+ >
15
+ <span>{{ item[props.labelKey] || item }}</span>
16
+ <span
17
+ v-if="shouldShowBadge(item)"
18
+ class="aui-badge"
19
+ :class="{
20
+ 'aui-badge-hover': hoveredItem === item && props.showBadgeOnHover
21
+ }"
22
+ :style="{ backgroundColor: badgeBgColor, color: badgeColor }"
23
+ >
24
+ {{ getBadgeCount(item) || '' }}
25
+ </span>
26
+ </view>
27
+
28
+ <!-- 添加功能区域 -->
29
+ <view v-if="props.showAddButton" class="add-section">
30
+ <view v-if="!isAdding" class="aui-select-group__add" @click="startAdding">
31
+ + 添加
32
+ </view>
33
+
34
+ <view v-else class="add-input-container">
35
+ <aui-input
36
+ v-model="newItemLabel"
37
+ placeholder="输入新选项"
38
+ @keyup.enter="addNewItem"
39
+ ></aui-input>
40
+ <view class="add-buttons">
41
+ <aui-button type="primary" @click="addNewItem">确认</aui-button>
42
+ <aui-button type="text" @click="cancelAdding">取消</aui-button>
43
+ </view>
44
+ <view v-if="errorMessage" class="error-message">{{ errorMessage }}</view>
45
+ </view>
46
+ </view>
47
+ </view>
48
+ </template>
49
+
50
+ <script setup>
51
+ import { ref, defineProps, defineEmits, computed, watchEffect } from 'vue';
52
+
53
+ const _name = "AuiSelectGroup";
54
+ defineOptions({ name: _name });
55
+
56
+ const props = defineProps({
57
+ dataSource: {
58
+ type: Array,
59
+ default: () => []
60
+ },
61
+ modelValue: {
62
+ type: Array,
63
+ default: () => []
64
+ },
65
+ badgeCount: {
66
+ type: Object,
67
+ default: () => ({})
68
+ },
69
+ showBadgeOnHover: {
70
+ type: Boolean,
71
+ default: false
72
+ },
73
+ badgeColor: {
74
+ type: String,
75
+ default: '#ffffff'
76
+ },
77
+ badgeBgColor: {
78
+ type: String,
79
+ default: '#ff4d4f'
80
+ },
81
+ showBadge: {
82
+ type: Boolean,
83
+ default: false
84
+ },
85
+ showAddButton: {
86
+ type: Boolean,
87
+ default: false
88
+ },
89
+ valueKey: {
90
+ type: String,
91
+ default: 'value'
92
+ },
93
+ labelKey: {
94
+ type: String,
95
+ default: 'label'
96
+ },
97
+ theme:{
98
+ type:String,
99
+ default:'none',
100
+ validator(value){
101
+ return ['none','trans'].includes(value)
102
+ }
103
+ }
104
+ });
105
+
106
+ const emits = defineEmits(['update:modelValue', 'new-item']);
107
+ const hoveredItem = ref(null);
108
+ const isAdding = ref(false);
109
+ const newItemLabel = ref('');
110
+ const errorMessage = ref('');
111
+
112
+ // 内部数据源,用于存储添加的新项
113
+ const internalDataSource = ref([...props.dataSource]);
114
+
115
+ // 监听外部数据源变化,同步到内部
116
+ watchEffect(() => {
117
+ internalDataSource.value = [...props.dataSource];
118
+ });
119
+
120
+ // 计算是否显示徽章
121
+ const shouldShowBadge = (item) => {
122
+ const key = item[props.valueKey] !== undefined ? item[props.valueKey] : item;
123
+ return props.showBadge && (props.badgeCount[key] || 0) > 0;
124
+ };
125
+
126
+ // 检查是否选中
127
+ const isSelected = (item) => {
128
+ const value = item[props.valueKey] !== undefined ? item[props.valueKey] : item;
129
+ return props.modelValue.includes(value);
130
+ };
131
+
132
+ // 获取徽章数值
133
+ const getBadgeCount = (item) => {
134
+ const key = item[props.valueKey] !== undefined ? item[props.valueKey] : item;
135
+ return props.badgeCount[key] > 0 ? props.badgeCount[key] : null;
136
+ };
137
+
138
+ // 切换选中状态
139
+ const toggleSelection = (item) => {
140
+ const value = item[props.valueKey] !== undefined ? item[props.valueKey] : item;
141
+ emits('update:modelValue', isSelected(item)
142
+ ? props.modelValue.filter(v => v !== value)
143
+ : [...props.modelValue, value]
144
+ );
145
+ };
146
+
147
+ // 开始添加新项
148
+ const startAdding = () => {
149
+ isAdding.value = true;
150
+ errorMessage.value = '';
151
+ };
152
+
153
+ // 取消添加
154
+ const cancelAdding = () => {
155
+ isAdding.value = false;
156
+ newItemLabel.value = '';
157
+ errorMessage.value = '';
158
+ };
159
+
160
+ // 添加新项
161
+ const addNewItem = () => {
162
+ const label = newItemLabel.value.trim();
163
+ if (!label) {
164
+ errorMessage.value = '请输入内容';
165
+ return;
166
+ }
167
+
168
+ // 检查是否已存在相同标签或值的项
169
+ const valueKey = props.valueKey;
170
+ const labelKey = props.labelKey;
171
+
172
+ const exists = internalDataSource.value.some(item => {
173
+ const itemValue = item[valueKey] !== undefined ? item[valueKey] : item;
174
+ const itemLabel = item[labelKey] !== undefined ? item[labelKey] : item;
175
+
176
+ return itemValue === label || itemLabel === label;
177
+ });
178
+
179
+ if (exists) {
180
+ errorMessage.value = '该选项已存在';
181
+ return;
182
+ }
183
+
184
+ // 创建新项
185
+ let newItem;
186
+ if (internalDataSource.value.length > 0 && typeof internalDataSource.value[0] === 'object') {
187
+ // 对象数组格式
188
+ newItem = {
189
+ [valueKey]: label,
190
+ [labelKey]: label,
191
+ id: `new_${Date.now()}` // 为新项生成唯一ID
192
+ };
193
+ } else {
194
+ // 简单数组格式
195
+ newItem = label;
196
+ }
197
+
198
+ // 添加到内部数据源(使用更明确的响应式更新)
199
+ internalDataSource.value = [...internalDataSource.value, newItem];
200
+
201
+ // 触发更新事件(通知父组件)
202
+ emits('update:modelValue', [...props.modelValue, newItem[valueKey] || newItem]);
203
+
204
+ // 可选:触发新项添加事件
205
+ emits('new-item', newItem);
206
+
207
+ // 重置状态
208
+ isAdding.value = false;
209
+ newItemLabel.value = '';
210
+ errorMessage.value = '';
211
+ };
212
+ </script>
213
+ <style scoped lang="scss">
214
+ @import '../style.scss';
215
+
216
+ .aui-select-group {
217
+ display: flex;
218
+ flex-wrap: wrap;
219
+ gap: 10px;
220
+ }
221
+
222
+ .aui-select-group__item {
223
+ position: relative;
224
+ padding: 5px 5px;
225
+ border: 1px solid #F5F5F5;
226
+ background-color: #F5F5F5;
227
+ color: #666666;
228
+ border-radius: 3px;
229
+ cursor: pointer;
230
+ min-width: 60px;
231
+ text-align: center;
232
+ transition: all 0.2s ease;
233
+ }
234
+
235
+ .aui-select-group__item_active {
236
+ background-color: $aui-primary-color;
237
+ color: #FFFFFF;
238
+ border-color: $aui-primary-color;
239
+ }
240
+
241
+ .aui-badge {
242
+ position: absolute;
243
+ top: -8px;
244
+ right: -8px;
245
+ min-width: 18px;
246
+ height: 18px;
247
+ line-height: 18px;
248
+ padding: 0 4px;
249
+ border-radius: 9px;
250
+ font-size: 12px;
251
+ text-align: center;
252
+ transform: scale(0.9);
253
+ opacity: 0.9;
254
+ transition: all 0.2s ease;
255
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
256
+ visibility: hidden;
257
+ }
258
+
259
+ .has-badge .aui-badge {
260
+ visibility: visible;
261
+ }
262
+
263
+ .aui-badge-hover {
264
+ transform: scale(1);
265
+ opacity: 1;
266
+ }
267
+
268
+ .aui-select-group__add {
269
+ padding: 10px 15px;
270
+ border: 1px solid $aui-primary-color;
271
+ color: $aui-primary-color;
272
+ border-radius: 3px;
273
+ cursor: pointer;
274
+ transition: all 0.2s ease;
275
+ display: flex;
276
+ align-items: center;
277
+ justify-content: center;
278
+ }
279
+ .aui-select-group__add:hover {
280
+ background-color: rgba($aui-primary-color, 0.05);
281
+ }
282
+
283
+ .add-section {
284
+ width: 100%;
285
+ }
286
+
287
+ .add-input-container {
288
+ display: flex;
289
+ flex-direction: column;
290
+ gap: 10px;
291
+ width: 100%;
292
+ }
293
+
294
+ .add-buttons {
295
+ display: flex;
296
+ gap: 10px;
297
+ }
298
+
299
+ .error-message {
300
+ color: #ff4d4f;
301
+ font-size: 12px;
302
+ margin-top: 5px;
303
+ }
304
+ .aui-select-group-theme-trans .aui-select-group__item{
305
+ background-color: none;
306
+ border-radius: 5px;
307
+ border:1px solid #E5E7EB;
308
+ color:#111827;
309
+ font-size: 14px;
310
+ }
311
+ .aui-select-group .aui-select-group__item_active{border-color: #2196F3;background-color: #EFF6FF;color:#2196F3}
312
+ </style>
@@ -0,0 +1,173 @@
1
+ <template>
2
+ <div class="aui-tab">
3
+ <div class="aui-tab-header" :class="{ 'scrollable': scrollable, 'equal-width': equalWidth }">
4
+ <div class="aui-tab-scroll-container">
5
+ <div
6
+ class="aui-tab-item"
7
+ v-for="(item, index) in tabs"
8
+ :key="item.key || index"
9
+ :class="{ active: currentIndex === index, disabled: item.disabled }"
10
+ @click="!item.disabled && handleTabClick(index)"
11
+ >
12
+ <span>{{ item.title }}</span>
13
+ <div class="tab-indicator" v-if="currentIndex === index"></div>
14
+ </div>
15
+ </div>
16
+ </div>
17
+ <div class="aui-tab-content">
18
+ <slot></slot>
19
+ </div>
20
+ </div>
21
+ </template>
22
+
23
+ <script setup>
24
+ import { ref, provide, computed, onMounted, watch } from 'vue'
25
+
26
+ const _name="AuiTab";
27
+ defineOptions({name:_name})
28
+ const props = defineProps({
29
+ modelValue: {
30
+ type: [String, Number],
31
+ default: ''
32
+ },
33
+ type: {
34
+ type: String,
35
+ default: 'line',
36
+ validator: value => ['line', 'card', 'capsule'].includes(value)
37
+ },
38
+ scrollable: {
39
+ type: Boolean,
40
+ default: false
41
+ },
42
+ equalWidth: {
43
+ type: Boolean,
44
+ default: false
45
+ }
46
+ })
47
+
48
+ const emits = defineEmits(['update:modelValue'])
49
+
50
+ const tabs = ref([])
51
+ const currentIndex = ref(0)
52
+
53
+ // 注册tab项
54
+ const registerTab = (tab) => {
55
+ tabs.value.push(tab)
56
+
57
+ // 如果是第一个tab或者与v-model匹配,则设为当前激活项
58
+ if (tabs.value.length === 1 || tab.name === props.modelValue) {
59
+ setActiveTab(tab.name)
60
+ }
61
+ }
62
+
63
+ // 设置激活的tab
64
+ const setActiveTab = (name) => {
65
+ const index = tabs.value.findIndex(tab => tab.name === name)
66
+ if (index !== -1) {
67
+ currentIndex.value = index
68
+ emits('update:modelValue', name)
69
+ }
70
+ }
71
+
72
+ // 处理tab点击
73
+ const handleTabClick = (index) => {
74
+ if (index !== currentIndex.value) {
75
+ currentIndex.value = index
76
+ emits('update:modelValue', tabs.value[index].name)
77
+ }
78
+ }
79
+
80
+ // 提供给子组件使用
81
+ provide('auiTab', {
82
+ tabs,
83
+ currentIndex,
84
+ registerTab,
85
+ setActiveTab
86
+ })
87
+
88
+ // 监听v-model变化
89
+ watch(() => props.modelValue, (newValue) => {
90
+ const index = tabs.value.findIndex(tab => tab.name === newValue)
91
+ if (index !== -1) {
92
+ currentIndex.value = index
93
+ }
94
+ })
95
+
96
+ // 初始化
97
+ onMounted(() => {
98
+ // 如果初始没有匹配的v-model值,则激活第一个tab
99
+ if (tabs.value.length > 0 && !tabs.value.some(tab => tab.name === props.modelValue)) {
100
+ setActiveTab(tabs.value[0].name)
101
+ }
102
+ })
103
+ </script>
104
+
105
+ <style scoped>
106
+ .aui-tab {
107
+ display: flex;
108
+ flex-direction: column;
109
+ height: 100%;
110
+ }
111
+
112
+ .aui-tab-header {
113
+ display: flex;
114
+ border-bottom: 1px solid #e4e7ed;
115
+ overflow: hidden;
116
+ position: relative;
117
+ }
118
+
119
+ .scrollable .aui-tab-scroll-container {
120
+ white-space: nowrap;
121
+ overflow-x: auto;
122
+ -webkit-overflow-scrolling: touch;
123
+ scrollbar-width: none;
124
+ }
125
+
126
+ .scrollable .aui-tab-scroll-container::-webkit-scrollbar {
127
+ display: none;
128
+ }
129
+
130
+ .equal-width .aui-tab-item {
131
+ flex: 1;
132
+ min-width: 0;
133
+ text-align: center;
134
+ }
135
+
136
+ .aui-tab-item {
137
+ padding: 0 20px;
138
+ height: 40px;
139
+ line-height: 40px;
140
+ font-size: 14px;
141
+ color: #606266;
142
+ cursor: pointer;
143
+ position: relative;
144
+ transition: color 0.3s;
145
+ display: inline-block;
146
+ }
147
+
148
+ .aui-tab-item.active {
149
+ color: #409eff;
150
+ font-weight: 500;
151
+ }
152
+
153
+ .aui-tab-item.disabled {
154
+ color: #c0c4cc;
155
+ cursor: not-allowed;
156
+ }
157
+
158
+ .tab-indicator {
159
+ position: absolute;
160
+ bottom: 0;
161
+ left: 0;
162
+ width: 100%;
163
+ height: 2px;
164
+ background-color: #409eff;
165
+ transition: all 0.3s;
166
+ }
167
+
168
+ .aui-tab-content {
169
+ flex: 1;
170
+ overflow: auto;
171
+ height: 100%; /* 确保内容区域有足够高度 */
172
+ }
173
+ </style>
@@ -0,0 +1,43 @@
1
+ <template>
2
+ <div class="aui-tab-pane" v-show="isActive">
3
+ <slot></slot>
4
+ </div>
5
+ </template>
6
+
7
+ <script setup>
8
+ import { inject, computed } from 'vue'
9
+
10
+ const _name="AuiTabItem";
11
+ defineOptions({name:_name})
12
+ const props = defineProps({
13
+ name: { type: [String, Number], required: true },
14
+ title: { type: String, required: true },
15
+ disabled: { type: Boolean, default: false }
16
+ })
17
+
18
+ const tabContext = inject('auiTab')
19
+
20
+ // 明确解包 ref 的值
21
+ const isActive = computed(() => {
22
+ const tabsArray = tabContext.tabs.value // 获取数组
23
+ const currentIndex = tabContext.currentIndex.value // 获取当前索引
24
+ return currentIndex === tabsArray.findIndex(tab => tab.name === props.name)
25
+ })
26
+
27
+ // 注册标签页到父组件
28
+ tabContext.registerTab({
29
+ name: props.name,
30
+ title: props.title,
31
+ disabled: props.disabled
32
+ })
33
+ </script>
34
+
35
+ <style scoped>
36
+ .aui-tab-pane {
37
+ display: block;
38
+ }
39
+
40
+ .aui-tab-pane.active {
41
+ display: block;
42
+ }
43
+ </style>