v-uni-app-ui 1.0.0 → 1.0.4

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 (86) hide show
  1. package/README.md +147 -0
  2. package/components/config/css/basic.scss +19 -0
  3. package/components/config/interface/basic-type.js +16 -0
  4. package/components/config/interface/components-interface.ts +0 -0
  5. package/components/config/interface/monitor/components/input-monitor.js +0 -0
  6. package/components/config/interface/monitor/property-monitor.ts +136 -0
  7. package/components/config/interface/props/basic-props.ts +88 -0
  8. package/components/config/interface/props/components/button-props.ts +85 -0
  9. package/components/config/interface/props/components/input-props.ts +69 -0
  10. package/components/config/interface/props/props-tools.ts +64 -0
  11. package/components/config/style/basic.js +346 -0
  12. package/components/config/style/component-registry.js +142 -0
  13. package/components/config/style/components/button-style.js +160 -0
  14. package/components/config/style/components/input-style.js +98 -0
  15. package/components/config/style/components-style.js +622 -0
  16. package/components/config/style/property-mapper.js +377 -0
  17. package/components/config/style/pseudo-processor.js +213 -0
  18. package/components/config.js +123 -0
  19. package/components/icon/iconfont.css +87 -0
  20. package/components/icon/iconfont.js +1 -0
  21. package/components/icon/iconfont.json +135 -0
  22. package/components/icon/iconfont.ttf +0 -0
  23. package/components/icon/iconfont.woff +0 -0
  24. package/components/icon/iconfont.woff2 +0 -0
  25. package/components/layout/v-card/v-card.vue +108 -0
  26. package/components/layout/v-grid/v-grid.vue +162 -0
  27. package/components/layout/v-icon-grid/v-icon-grid.vue +195 -0
  28. package/components/layout/v-infinite-scroll/v-infinite-scroll.vue +172 -0
  29. package/components/layout/v-list/v-list.vue +43 -0
  30. package/components/layout/v-row/v-row.vue +142 -0
  31. package/components/layout/v-waterfall/v-waterfall.vue +79 -0
  32. package/components/model/compound/v-checkbox-group/v-checkbox-group.vue +96 -0
  33. package/components/model/compound/v-console/v-console.js +20 -0
  34. package/components/model/compound/v-console/v-console.vue +299 -0
  35. package/components/model/compound/v-date-time/v-date-time.vue +261 -0
  36. package/components/model/compound/v-dialog/v-dialog.vue +178 -0
  37. package/components/model/compound/v-drum-select-picker/v-drum-select-picker.vue +83 -0
  38. package/components/model/compound/v-form/v-form.vue +226 -0
  39. package/components/model/compound/v-form-item/v-form-item.vue +255 -0
  40. package/components/model/compound/v-image/v-image.vue +357 -0
  41. package/components/model/compound/v-input-desensitize/v-input-desensitize.vue +101 -0
  42. package/components/model/compound/v-page/v-page.vue +11 -0
  43. package/components/model/compound/v-pages/v-pages.vue +141 -0
  44. package/components/model/compound/v-picker-list/v-picker-list.vue +109 -0
  45. package/components/model/compound/v-popup/v-popup.vue +151 -0
  46. package/components/model/compound/v-radio-group/v-radio-group.vue +86 -0
  47. package/components/model/compound/v-select-picker/v-select-picker.vue +202 -0
  48. package/components/model/compound/v-series-picker-list/v-series-picker-list.vue +221 -0
  49. package/components/model/compound/v-series-select-picker/v-series-select-picker.vue +203 -0
  50. package/components/model/compound/v-switch/v-switch.vue +136 -0
  51. package/components/model/compound/v-tabs-page/v-tabs-page.vue +138 -0
  52. package/components/model/native/v-badge/v-badge.vue +143 -0
  53. package/components/model/native/v-button/v-button.vue +81 -0
  54. package/components/model/native/v-carousel/v-carousel.vue +138 -0
  55. package/components/model/native/v-checkbox/v-checkbox.vue +215 -0
  56. package/components/model/native/v-collapse/v-collapse.vue +190 -0
  57. package/components/model/native/v-header-navigation-bar/v-header-navigation-bar.vue +92 -0
  58. package/components/model/native/v-input/v-input.vue +163 -0
  59. package/components/model/native/v-input-code/v-input-code.vue +146 -0
  60. package/components/model/native/v-loading/v-loading.vue +206 -0
  61. package/components/model/native/v-menu/v-menu.vue +222 -0
  62. package/components/model/native/v-menu-slide/v-menu-slide.vue +364 -0
  63. package/components/model/native/v-min-loading/v-min-loading.vue +80 -0
  64. package/components/model/native/v-null/v-null.vue +97 -0
  65. package/components/model/native/v-overlay/v-overlay.vue +96 -0
  66. package/components/model/native/v-pull-up-refresh/v-pull-up-refresh.vue +157 -0
  67. package/components/model/native/v-radio/v-radio.vue +138 -0
  68. package/components/model/native/v-scroll-list/v-scroll-list.vue +169 -0
  69. package/components/model/native/v-steps/v-steps.vue +253 -0
  70. package/components/model/native/v-table/v-table.vue +203 -0
  71. package/components/model/native/v-tabs/v-tabs.vue +235 -0
  72. package/components/model/native/v-tag/v-tag.vue +206 -0
  73. package/components/model/native/v-text/v-text.vue +187 -0
  74. package/components/model/native/v-textarea/v-textarea.vue +178 -0
  75. package/components/model/native/v-title/v-title.vue +91 -0
  76. package/components/model/native/v-toast/info.png +0 -0
  77. package/components/model/native/v-toast/success.png +0 -0
  78. package/components/model/native/v-toast/v-toast.vue +198 -0
  79. package/components/model/native/v-toast/warn.png +0 -0
  80. package/components/model/native/v-upload-file-button/v-upload-file-button.vue +296 -0
  81. package/components/model/native/v-video/v-video.vue +175 -0
  82. package/components/model/native/v-window/v-window.vue +158 -0
  83. package/components/utils/event-modifiers.ts +139 -0
  84. package/components/utils/validator.ts +451 -0
  85. package/index.js +372 -0
  86. package/package.json +25 -93
@@ -0,0 +1,157 @@
1
+ <template>
2
+ <view class="v-pull-up-refresh" :style="{ height: containerHeight }">
3
+ <view class="pull-up-refresh-header" v-if="pullingDown || refreshing">
4
+ <slot name="pull-up-refresh" :pullingDown="pullingDown" :refreshing="refreshing">
5
+ <view class="pull-up-refresh-header-icon">
6
+ <view v-if="!refreshing" class="arrow-down"></view>
7
+ <v-min-loading v-model:value="refreshing" type="info"></v-min-loading>
8
+ </view>
9
+ <text>{{ pullingDown ? '释放刷新' : refreshing ? '正在刷新...' : '下拉刷新' }}</text>
10
+ </slot>
11
+ </view>
12
+ <view class="pull-up-refresh-content" :style="{ transform: `translateY(${translateY}px)` }">
13
+ <slot></slot>
14
+ </view>
15
+ </view>
16
+ </template>
17
+
18
+ <script lang="ts" setup>
19
+ import { ref, onMounted, onUnmounted, nextTick, inject } from 'vue';
20
+
21
+ /**
22
+ * v-pull-up-refresh 上拉刷新
23
+ *
24
+ */
25
+ const props = defineProps({
26
+ refreshHeight: {
27
+ type: Number,
28
+ default: 60
29
+ }
30
+ });
31
+
32
+ const emits = defineEmits(['refresh']);
33
+
34
+ const config = inject<any>('config');
35
+ const containerHeight = ref('100%');
36
+ const translateY = ref(0);
37
+ const pullingDown = ref(false);
38
+ const refreshing = ref(false);
39
+
40
+ let startY = 0;
41
+ let moveY = 0;
42
+ let moveDistance = 0;
43
+
44
+ const handleTouchStart = (event: TouchEvent) => {
45
+ startY = event.touches[0].pageY;
46
+ };
47
+
48
+ const handleTouchMove = (event: TouchEvent) => {
49
+ if (refreshing.value) return;
50
+
51
+ moveY = event.touches[0].pageY;
52
+ moveDistance = moveY - startY;
53
+
54
+ if (moveDistance > 0) {
55
+ pullingDown.value = true;
56
+ translateY.value = moveDistance;
57
+ }
58
+ };
59
+
60
+ const handleTouchEnd = () => {
61
+ if (refreshing.value) return;
62
+
63
+ if (pullingDown.value && moveDistance > props.refreshHeight) {
64
+ startRefresh();
65
+ } else {
66
+ translateY.value = 0;
67
+ pullingDown.value = false;
68
+ }
69
+ };
70
+
71
+ const startRefresh = () => {
72
+ refreshing.value = true;
73
+ pullingDown.value = false;
74
+ translateY.value = props.refreshHeight;
75
+
76
+ emits('refresh', () => {
77
+ endRefresh();
78
+ });
79
+ };
80
+
81
+ const endRefresh = () => {
82
+ refreshing.value = false;
83
+ translateY.value = 0;
84
+ nextTick(() => {
85
+ translateY.value = 0;
86
+ });
87
+ };
88
+
89
+ onMounted(() => {
90
+ const container = document.querySelector('.v-pull-up-refresh');
91
+ if (container) {
92
+ container.addEventListener('touchstart', handleTouchStart);
93
+ container.addEventListener('touchmove', handleTouchMove);
94
+ container.addEventListener('touchend', handleTouchEnd);
95
+ }
96
+ });
97
+
98
+ onUnmounted(() => {
99
+ const container = document.querySelector('.v-pull-up-refresh');
100
+ if (container) {
101
+ container.removeEventListener('touchstart', handleTouchStart);
102
+ container.removeEventListener('touchmove', handleTouchMove);
103
+ container.removeEventListener('touchend', handleTouchEnd);
104
+ }
105
+ });
106
+ </script>
107
+
108
+ <style lang="scss" scoped>
109
+ .v-pull-up-refresh {
110
+ position: relative;
111
+ overflow: hidden;
112
+ }
113
+
114
+ .pull-up-refresh-content {
115
+ transition: transform 0.3s ease;
116
+ }
117
+
118
+ .pull-up-refresh-header {
119
+ display: flex;
120
+ align-items: center;
121
+ justify-content: center;
122
+ height: 120rpx;
123
+ background-color: v-bind("config.backgroundColor.reversal");
124
+ color: v-bind("config.fontColor.mainText");
125
+ font-size: 14px;
126
+ }
127
+
128
+ .pull-up-refresh-header-icon {
129
+ margin-right: 16rpx;
130
+ }
131
+
132
+ .arrow-down {
133
+ width: 0;
134
+ height: 0;
135
+ border: 5rpx solid transparent;
136
+ border-bottom-color: v-bind("config.fontColor.mainText");
137
+ transition: transform 0.3s ease;
138
+ }
139
+
140
+ .spinner {
141
+ width: 20px;
142
+ height: 20px;
143
+ border: 2px solid v-bind("config.border.color");
144
+ border-top-color: transparent;
145
+ border-radius: 50%;
146
+ animation: spin 1s linear infinite;
147
+ }
148
+
149
+ @keyframes spin {
150
+ from {
151
+ transform: rotate(0deg);
152
+ }
153
+ to {
154
+ transform: rotate(360deg);
155
+ }
156
+ }
157
+ </style>
@@ -0,0 +1,138 @@
1
+ <template>
2
+ <label :class="['v-radio', { 'v-radio--disabled': isDisabled, 'v-radio--checked': isChecked }]" @click="handleChange">
3
+ <span class="v-radio-input">
4
+ <span v-if="isChecked" class="v-radio-inner"></span>
5
+ </span>
6
+ <span class="v-radio-label">
7
+ <slot>{{ item.label }}</slot>
8
+ </span>
9
+ </label>
10
+ </template>
11
+
12
+ <script setup lang="ts">
13
+ import { inject, computed } from 'vue';
14
+
15
+ interface RadioItem {
16
+ label: string;
17
+ value: string | number;
18
+ }
19
+
20
+ const props = defineProps({
21
+ value: {
22
+ type: [String, Number],
23
+ default: ''
24
+ },
25
+ item: {
26
+ type: Object as () => RadioItem,
27
+ required: true
28
+ },
29
+ disabled: {
30
+ type: Boolean,
31
+ default: false
32
+ }
33
+ });
34
+
35
+ const radioGroup = inject('radioGroup', null);
36
+ const isDisabled = computed(() => {
37
+ return radioGroup ? radioGroup.disabled.value || props.disabled : props.disabled;
38
+ });
39
+
40
+ const isChecked = computed(() => {
41
+ return radioGroup ? radioGroup.value.value === props.item.value : props.value === props.item.value;
42
+ });
43
+
44
+ // 获取 radioGroup 的默认值
45
+ const radioGroupDefaultValue = computed(() => {
46
+ return radioGroup ? (radioGroup as any).defaultValue : '';
47
+ });
48
+
49
+ const handleChange = () => {
50
+ if (isDisabled.value) return;
51
+
52
+ if (radioGroup) {
53
+ // 如果当前选中,则取消选择,设置为默认值
54
+ if (isChecked.value) {
55
+ radioGroup.emitUpdate(radioGroupDefaultValue.value);
56
+ } else {
57
+ radioGroup.emitUpdate(props.item.value);
58
+ }
59
+ } else {
60
+ // 如果是单独使用,直接更新值
61
+ const newValue = isChecked.value ? '' : props.item.value;
62
+ emit('update:value', newValue);
63
+ emit('change', newValue);
64
+ }
65
+ };
66
+
67
+ const emit = defineEmits(['update:value', 'change']);
68
+ </script>
69
+
70
+ <style lang="scss" scoped>
71
+ .v-radio {
72
+ display: flex;
73
+ align-items: center;
74
+ cursor: pointer;
75
+ font-size: 14px;
76
+ color: #606266;
77
+ transition: all 0.3s;
78
+ margin-right: 16px;
79
+
80
+ &:last-child {
81
+ margin-right: 0;
82
+ }
83
+
84
+ &--disabled {
85
+ color: #c0c4cc;
86
+ cursor: not-allowed;
87
+
88
+ .v-radio-input {
89
+ background-color: #f5f7fa;
90
+ border-color: #e4e7ed;
91
+ cursor: not-allowed;
92
+ }
93
+ }
94
+
95
+ &--checked {
96
+ .v-radio-input {
97
+ border-color: #287afa !important;
98
+
99
+ .v-radio-inner {
100
+ background-color: #287afa !important;
101
+ }
102
+ }
103
+ }
104
+
105
+ .v-radio-input {
106
+ display: inline-block;
107
+ width: 28rpx;
108
+ height: 28rpx;
109
+ border: 2px solid #dcdfe6;
110
+ border-radius: 50%;
111
+ background-color: transparent;
112
+ position: relative;
113
+ margin-right: 8px;
114
+ cursor: pointer;
115
+ transition: all 0.3s;
116
+ vertical-align: middle;
117
+ text-align: center;
118
+
119
+ .v-radio-inner {
120
+ display: inline-block;
121
+ width: 16rpx;
122
+ height: 16rpx;
123
+ border-radius: 50%;
124
+ background-color: #fff;
125
+ position: absolute;
126
+ left: 50%;
127
+ top: 50%;
128
+ transform: translate(-50%, -50%) scale(1);
129
+ transition: all 0.3s;
130
+ }
131
+ }
132
+
133
+ .v-radio-label {
134
+ display: inline-block;
135
+ font-size: 22rpx;
136
+ }
137
+ }
138
+ </style>
@@ -0,0 +1,169 @@
1
+ <template>
2
+ <view class="v-scroll-list-container">
3
+ <scroll-view
4
+ class="v-scroll-list"
5
+ scroll-y
6
+ :scroll-into-view="activeId"
7
+ :scroll-with-animation="true"
8
+ :show-scrollbar="false"
9
+ :style="{
10
+ height: scrollHeight + 'rpx'
11
+ }"
12
+ @scroll="handleScroll"
13
+ >
14
+ <view
15
+ v-for="(item, index) in options"
16
+ :key="index"
17
+ :id="`item_${item.value}`"
18
+ class="v-scroll-list-item"
19
+ :class="{ 'v-scroll-list-item-active': item.value === selectedValue }"
20
+ :style="{ height: itemHeight + 'rpx' }"
21
+ >
22
+ {{ item.label }}
23
+ </view>
24
+ </scroll-view>
25
+ <view class="v-scroll-list-mask" :style="maskStyle"></view>
26
+ </view>
27
+ </template>
28
+
29
+ <script lang="ts" setup>
30
+ import { ref, computed, watch, nextTick, inject } from 'vue';
31
+
32
+ interface ScrollListItem {
33
+ value: any;
34
+ label: string;
35
+ }
36
+
37
+ /**
38
+ * v-scroll-list
39
+ * @description 滚动选择列表
40
+ * @property {Array} options 列表数据
41
+ * @property {String|Number} value 当前选中值
42
+ * @property {Number} scrollHeight 滚动区域高度
43
+ * @property {Number} itemHeight 列表项高度
44
+ * @event {Function} update:value 选中值变化时触发
45
+ * @example
46
+ * <v-scroll-list :options="options" v-model="value" :scroll-height="500" :item-height="80"></v-scroll-list>
47
+ */
48
+ const props = defineProps({
49
+ options: {
50
+ type: Array as () => ScrollListItem[],
51
+ required: true
52
+ },
53
+ value: {
54
+ type: [String, Number],
55
+ default: ''
56
+ },
57
+ scrollHeight: {
58
+ type: Number,
59
+ default: 500
60
+ },
61
+ itemHeight: {
62
+ type: Number,
63
+ default: 80
64
+ }
65
+ });
66
+
67
+ const emit = defineEmits(['update:value']);
68
+
69
+ const config = inject<any>('config');
70
+ const selectedValue = ref(props.value);
71
+ const activeId = ref('');
72
+ const itemPositions = ref<number[]>([]);
73
+ let currentIndex = 0;
74
+ let currentTop = 0;
75
+ let isProgrammaticScroll = ref(true);
76
+
77
+ const maskHeight = computed(() => (props.scrollHeight - props.itemHeight) / 2);
78
+ const maskStyle = computed(() => ({
79
+ top: `0`,
80
+ height: `${props.itemHeight}rpx`
81
+ }));
82
+
83
+ const initPositions = async () => {
84
+ await nextTick();
85
+ activeId.value = `item_${selectedValue.value}`;
86
+ currentIndex = props.options.findIndex((item) => item.value === selectedValue.value) - 1;
87
+ };
88
+
89
+ const handleScroll = (event: any) => {
90
+ if (isProgrammaticScroll.value) {
91
+ const operation = event.detail.deltaY;
92
+
93
+ if (operation < 0) {
94
+ currentIndex++;
95
+ if (operation < -5) {
96
+ currentIndex += Math.trunc(Math.abs(operation) % 3);
97
+ }
98
+ } else {
99
+ currentIndex--;
100
+ if (operation > 5) {
101
+ currentIndex -= Math.trunc(Math.abs(operation) % 3);
102
+ }
103
+ }
104
+
105
+ if (currentIndex < 0) {
106
+ currentIndex = props.options.length - 1;
107
+ } else if (currentIndex >= props.options.length) {
108
+ currentIndex = 0;
109
+ }
110
+
111
+ selectedValue.value = props.options[currentIndex].value;
112
+ activeId.value = `item_${selectedValue.value}`;
113
+ emit('update:value', selectedValue.value);
114
+ isProgrammaticScroll.value = false;
115
+ } else {
116
+ isProgrammaticScroll.value = true;
117
+ }
118
+ };
119
+
120
+ // 选中项变化时自动居中
121
+ watch(selectedValue, (newVal) => {
122
+ if (isProgrammaticScroll.value) {
123
+ activeId.value = `item_${currentIndex}`;
124
+ isProgrammaticScroll.value = false;
125
+ }
126
+ });
127
+
128
+ watch(() => props.options, initPositions, { deep: true });
129
+
130
+ initPositions();
131
+ </script>
132
+
133
+ <style lang="scss" scoped>
134
+ .v-scroll-list-container {
135
+ position: relative;
136
+ height: 100%;
137
+ }
138
+
139
+ .v-scroll-list {
140
+ box-sizing: content-box;
141
+ }
142
+
143
+ .v-scroll-list-item {
144
+ display: flex;
145
+ align-items: center;
146
+ justify-content: center;
147
+ font-size: v-bind('config.fontSize.mediumText');
148
+ color: v-bind('config.fontColor.mainText');
149
+ transition: all 0.2s ease;
150
+ }
151
+ .v-scroll-list-item:last-child {
152
+ padding-bottom: 300rpx;
153
+ }
154
+ .v-scroll-list-item-active {
155
+ color: v-bind('config.fontColor.default');
156
+ font-weight: bold;
157
+ transform: scale(1.1);
158
+ }
159
+
160
+ .v-scroll-list-mask {
161
+ position: absolute;
162
+ left: 0;
163
+ right: 0;
164
+ pointer-events: none;
165
+ background: rgba(200, 200, 200, 0.1);
166
+ border-top: 1rpx solid v-bind('config.border.color');
167
+ border-bottom: 1rpx solid v-bind('config.border.color');
168
+ }
169
+ </style>
@@ -0,0 +1,253 @@
1
+ <template>
2
+ <view class="v-steps" :class="[direction]">
3
+ <view v-for="(step, index) in steps" :key="index" class="step-item" :class="[getStepStatus(index), { 'last-item': index === steps.length - 1 }]">
4
+ <view class="step-icon-container">
5
+ <view class="step-icon">
6
+ <text v-if="index < active" class="icon-check">✓</text>
7
+ <text v-else-if="index === active" class="step-number">{{ index + 1 }}</text>
8
+ <text v-else class="step-number">{{ index + 1 }}</text>
9
+ </view>
10
+ <view v-if="index !== steps.length - 1" class="step-line"></view>
11
+ </view>
12
+ <view class="step-content">
13
+ <text class="step-title">{{ step.title }}</text>
14
+ <text v-if="step.desc" class="step-desc">{{ step.desc }}</text>
15
+ </view>
16
+ </view>
17
+ </view>
18
+ </template>
19
+
20
+ <script lang="ts" setup>
21
+ import { computed, inject } from 'vue';
22
+
23
+ interface Step {
24
+ title: string;
25
+ desc?: string;
26
+ }
27
+
28
+ const props = defineProps({
29
+ steps: {
30
+ type: Array as () => Step[],
31
+ required: true
32
+ },
33
+ active: {
34
+ type: Number,
35
+ default: 0,
36
+ validator: (value: number) => value >= 0
37
+ },
38
+ direction: {
39
+ type: String,
40
+ default: 'horizontal',
41
+ validator: (value: string) => ['horizontal', 'vertical'].includes(value)
42
+ }
43
+ });
44
+ const config = inject<any>('config');
45
+ const getStepStatus = (index: number) => {
46
+ if (index < props.active) return 'finished';
47
+ if (index === props.active) return 'active';
48
+ return 'waiting';
49
+ };
50
+ </script>
51
+
52
+ <style lang="scss" scoped>
53
+ $primary-color: #287afa;
54
+ $success-color: #31d283;
55
+ $warning-color: #ff9628;
56
+ $info-color: #969696;
57
+ $text-primary: #333333;
58
+ $text-regular: #666666;
59
+ $text-placeholder: #c0c4cc;
60
+ $border-color: #969696;
61
+ $bg-color: #f5f7fa;
62
+
63
+ .v-steps {
64
+ display: flex;
65
+ width: 100%;
66
+ box-sizing: border-box;
67
+
68
+ &.horizontal {
69
+ flex-direction: row;
70
+ justify-content: space-between;
71
+ padding: 16rpx 0;
72
+
73
+ .step-item {
74
+ flex: 1;
75
+ display: flex;
76
+ flex-direction: column;
77
+ align-items: center;
78
+ position: relative;
79
+
80
+ .step-icon-container {
81
+ display: flex;
82
+ align-items: center;
83
+ justify-content: center;
84
+ position: relative;
85
+ width: 100%;
86
+
87
+ .step-line {
88
+ position: absolute;
89
+ left: 72.5%;
90
+ top: 50%;
91
+ transform: translateY(-50%);
92
+ width: calc(100% - 80rpx);
93
+ height: 4rpx;
94
+ background-color: v-bind('config.VSteps.lineBackgroundColor');
95
+ z-index: 1;
96
+ }
97
+ }
98
+
99
+ .step-content {
100
+ text-align: center;
101
+ margin-top: 16rpx;
102
+ padding: 0 20rpx;
103
+ }
104
+
105
+ &.last-item .step-line {
106
+ display: none;
107
+ }
108
+ }
109
+ }
110
+
111
+ &.vertical {
112
+ flex-direction: column;
113
+ padding: 16rpx 0;
114
+
115
+ .step-item {
116
+ display: flex;
117
+ position: relative;
118
+ padding-bottom: 40rpx;
119
+
120
+ &:last-child {
121
+ padding-bottom: 0;
122
+ }
123
+
124
+ .step-icon-container {
125
+ display: flex;
126
+ flex-direction: column;
127
+ align-items: center;
128
+ margin-right: 24rpx;
129
+
130
+ .step-line {
131
+ flex: 1;
132
+ width: 4rpx;
133
+ background-color: v-bind('config.VSteps.lineBackgroundColor');
134
+ margin-top: 16rpx;
135
+ }
136
+ }
137
+
138
+ .step-content {
139
+ flex: 1;
140
+ padding-bottom: 40rpx;
141
+ }
142
+
143
+ &.last-item .step-line {
144
+ display: none;
145
+ }
146
+ }
147
+ }
148
+
149
+ .step-icon {
150
+ width: 60rpx;
151
+ height: 60rpx;
152
+ border-radius: 50%;
153
+ display: flex;
154
+ align-items: center;
155
+ justify-content: center;
156
+ position: relative;
157
+ z-index: 2;
158
+ font-size: 32rpx;
159
+ font-weight: bold;
160
+ background-color: white;
161
+ border: 4rpx solid v-bind('config.border.color');
162
+ color: v-bind('config.fontColor.subTitle');
163
+ transition: all 0.3s ease;
164
+
165
+ .step-number {
166
+ line-height: 1;
167
+ }
168
+
169
+ .icon-check {
170
+ font-size: 36rpx;
171
+ font-weight: bold;
172
+ line-height: 1;
173
+ }
174
+ }
175
+
176
+ .step-title {
177
+ font-size: 32rpx;
178
+ font-weight: bold;
179
+ color: v-bind('config.fontColor.default');
180
+ display: block;
181
+ line-height: 1.4;
182
+ }
183
+
184
+ .step-desc {
185
+ font-size: 26rpx;
186
+ color: v-bind('config.fontColor.subTitle');
187
+ display: block;
188
+ margin-top: 8rpx;
189
+ line-height: 1.4;
190
+ }
191
+ .finished {
192
+ .step-icon {
193
+ border-color: v-bind('config.border.color');
194
+ background-color: v-bind('config.backgroundColor.succeed');
195
+ color: white;
196
+ }
197
+
198
+ .step-line {
199
+ background-color: v-bind('config.backgroundColor.succeed') !important;
200
+ }
201
+
202
+ .step-title {
203
+ color: v-bind('config.fontColor.default');
204
+ }
205
+ }
206
+ .active {
207
+ .step-icon {
208
+ border-color: v-bind('config.border.default');
209
+ background-color: v-bind('config.backgroundColor.default');
210
+ color: white;
211
+ }
212
+
213
+ .step-title {
214
+ color: v-bind('config.fontColor.default');
215
+ font-weight: bold;
216
+ }
217
+ }
218
+
219
+ .waiting {
220
+ .step-icon {
221
+ border-color: v-bind('config.border.color');
222
+ background-color: white;
223
+ color: v-bind('config.fontColor.text');
224
+ }
225
+
226
+ .step-title {
227
+ color: v-bind('config.fontColor.text');
228
+ }
229
+
230
+ .step-desc {
231
+ color: v-bind('config.fontColor.text');
232
+ }
233
+ }
234
+ }
235
+
236
+ @media (max-width: 768px) {
237
+ .v-steps.horizontal {
238
+ .step-item {
239
+ .step-content {
240
+ padding: 0 10rpx;
241
+ }
242
+
243
+ .step-title {
244
+ font-size: 28rpx;
245
+ }
246
+
247
+ .step-desc {
248
+ font-size: 24rpx;
249
+ }
250
+ }
251
+ }
252
+ }
253
+ </style>