hy-app 0.4.12 → 0.4.15

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.
@@ -0,0 +1,136 @@
1
+ import type { CSSProperties } from "vue";
2
+
3
+ export type CouponStatus = 'normal' | 'disabled' | 'used' | 'expired';
4
+
5
+ export interface HyCouponProps {
6
+ /**
7
+ * 优惠券金额
8
+ * @type {string}
9
+ */
10
+ amount: string;
11
+
12
+ /**
13
+ * 优惠券标题
14
+ * @type {string}
15
+ */
16
+ title: string;
17
+
18
+ /**
19
+ * 优惠券副标题
20
+ * @type {string}
21
+ * @default ''
22
+ */
23
+ subTitle?: string;
24
+
25
+ /**
26
+ * 优惠券状态
27
+ * @type {'normal' | 'disabled' | 'used' | 'expired'}
28
+ * @default 'normal'
29
+ */
30
+ status?: CouponStatus;
31
+
32
+ /**
33
+ * 最小消费金额限制
34
+ * @type {number}
35
+ * @default 0
36
+ */
37
+ minSpend?: number;
38
+
39
+ /**
40
+ * 有效期
41
+ * @type {string}
42
+ * @default ''
43
+ */
44
+ validity?: string;
45
+
46
+ /**
47
+ * 是否显示左边界装饰
48
+ * @type {boolean}
49
+ * @default true
50
+ */
51
+ showLeftBorder?: boolean;
52
+
53
+ /**
54
+ * 是否显示右边界装饰
55
+ * @type {boolean}
56
+ * @default true
57
+ */
58
+ showRightBorder?: boolean;
59
+
60
+ /**
61
+ * 自定义样式
62
+ * @type {CSSProperties}
63
+ * @default {}
64
+ */
65
+ customStyle?: CSSProperties;
66
+
67
+ /**
68
+ * 自定义类名
69
+ * @type {string}
70
+ * @default ''
71
+ */
72
+ customClass?: string;
73
+
74
+ /**
75
+ * 提示文本
76
+ * @type {string}
77
+ * @default ''
78
+ */
79
+ tooltip?: string;
80
+
81
+ /**
82
+ * 是否显示操作按钮
83
+ * @type {boolean}
84
+ * @default false
85
+ */
86
+ showActionButton?: boolean;
87
+
88
+ /**
89
+ * 自定义按钮文本
90
+ * @type {string}
91
+ * @default ''
92
+ */
93
+ customButtonText?: string;
94
+
95
+ /**
96
+ * 是否为专属优惠券
97
+ * @type {boolean}
98
+ * @default false
99
+ */
100
+ isExclusive?: boolean;
101
+
102
+ /**
103
+ * 每用户限领数量
104
+ * @type {number}
105
+ * @default 0
106
+ */
107
+ limitPerUser?: number;
108
+
109
+ /**
110
+ * 使用日期(已使用状态时显示)
111
+ * @type {string}
112
+ * @default ''
113
+ */
114
+ usedDate?: string;
115
+
116
+ /**
117
+ * 过期日期(已过期状态时显示)
118
+ * @type {string}
119
+ * @default ''
120
+ */
121
+ expireDate?: string;
122
+
123
+ /**
124
+ * 禁用原因(已禁用状态时显示)
125
+ * @type {string}
126
+ * @default ''
127
+ */
128
+ disabledReason?: string;
129
+
130
+ /**
131
+ * 点击时是否触发receive事件
132
+ * @type {boolean}
133
+ * @default true
134
+ */
135
+ triggerReceiveOnClick?: boolean;
136
+ }
@@ -1,397 +1,149 @@
1
1
  <template>
2
2
  <view class="hy-folding-panel">
3
- <!-- 容器模式 - 通过 slot 支持自定义内容 -->
4
- <view class="hy-folding-panel__wrapper">
5
- <!-- 头部标题 -->
6
- <view v-if="title" class="hy-folding-panel__title">
7
- <text class="hy-folding-panel__title-text">{{ title }}</text>
8
- </view>
9
-
10
- <!-- 面板内容区域 -->
11
- <view class="hy-folding-panel__body">
12
- <!-- 列表模式 -->
13
- <view v-if="list && list.length" class="hy-folding-panel__list">
14
- <view
15
- v-for="(item, index) in panelItems"
16
- :key="index"
17
- class="hy-folding-panel__item"
18
- :class="{
19
- 'hy-folding-panel__item--disabled': disabled || item.disabled,
20
- 'hy-folding-panel__item--active': item.expanded,
21
- 'hy-folding-panel__item--border': border,
22
- [`hy-folding-panel__item--${size}`]: true
23
- }"
24
- >
25
- <!-- 面板头部 -->
26
- <view class="hy-folding-panel__item-header" @click="toggleItem(index)">
27
- <view class="hy-folding-panel__item-left">
28
- <!-- 图标 -->
29
- <view v-if="item.icon" class="hy-folding-panel__item-icon">
30
- <image :src="item.icon" mode="aspectFit" />
31
- </view>
32
- <!-- 标题 -->
33
- <text class="hy-folding-panel__item-title">{{ item.title }}</text>
34
- </view>
35
- <view class="hy-folding-panel__item-right">
36
- <!-- 右侧值 -->
37
- <text v-if="item.value" class="hy-folding-panel__item-value">{{ item.value }}</text>
38
- <!-- 箭头 -->
39
- <view class="hy-folding-panel__item-arrow" :class="{ 'hy-folding-panel__item-arrow--up': item.expanded }">
40
- <text>{{ item.expanded ? '↑' : '↓' }}</text>
41
- </view>
42
- </view>
43
- </view>
44
-
45
- <!-- 面板内容 -->
46
- <view
47
- class="hy-folding-panel__item-content"
48
- :style="[
49
- customStyle,
50
- {
51
- height: item.expanded ? (contentHeight ? addUnit(contentHeight) : 'auto') : '0px',
52
- overflow: 'hidden'
53
- }
54
- ]"
55
- >
56
- <slot name="item-content" :item="item" :index="index">
57
- <text>{{ item.content || '' }}</text>
58
- </slot>
59
- </view>
60
- </view>
61
- </view>
62
-
63
- <!-- 自定义内容 -->
64
- <slot v-else></slot>
65
- </view>
66
- </view>
3
+ <slot></slot>
67
4
  </view>
68
5
  </template>
69
6
 
70
7
  <script lang="ts">
71
8
  export default {
72
- name: "hy-folding-panel"
9
+ name: "hy-folding-panel",
73
10
  };
74
11
  </script>
75
12
 
76
13
  <script setup lang="ts">
77
- import { ref, computed, watch } from "vue";
78
- import type { CSSProperties, PropType } from "vue";
79
- import type { IFoldingPanel, PanelVo } from "./typing";
80
- import { addUnit } from "../../libs";
81
-
82
- /**
83
- * 折叠面板组件
84
- * 用于展示可展开/折叠的内容区域
85
- * @displayName hy-folding-panel
86
- */
14
+ import { provide, ref, watch, toRefs } from "vue";
15
+ import type { PropType } from "vue";
16
+ import type { HyFoldingPanelGroupEmits } from "./typing";
87
17
 
18
+ // Props定义
88
19
  const props = defineProps({
89
20
  /**
90
- * 数据集
21
+ * 当前激活的面板索引,支持v-model
91
22
  */
92
- list: {
93
- type: Array as PropType<PanelVo[]>,
94
- default: () => []
23
+ modelValue: {
24
+ type: [Number, String],
25
+ default: -1,
95
26
  },
96
27
  /**
97
- * 是否手风琴模式
28
+ * 是否手风琴模式,默认false
98
29
  */
99
30
  accordion: {
100
31
  type: Boolean,
101
- default: false
32
+ default: false,
102
33
  },
103
34
  /**
104
- * 面板标题
35
+ * 是否禁用整个折叠面板组
105
36
  */
106
- title: {
107
- type: String,
108
- default: ''
37
+ disabled: {
38
+ type: Boolean,
39
+ default: false,
109
40
  },
110
41
  /**
111
42
  * 是否显示边框
112
43
  */
113
44
  border: {
114
45
  type: Boolean,
115
- default: true
46
+ default: true,
116
47
  },
117
48
  /**
118
- * 是否禁用
119
- */
120
- disabled: {
121
- type: Boolean,
122
- default: false
123
- },
124
- /**
125
- * 面板大小 large, medium, small
49
+ * 面板头部大小 large, medium, small
50
+ * @values large, medium, small
126
51
  */
127
52
  size: {
128
- type: String as PropType<'large' | 'medium' | 'small'>,
129
- default: 'medium'
53
+ type: String as PropType<HyApp.SizeType>,
54
+ default: "medium",
130
55
  },
131
- /**
132
- * 内容区域高度
133
- */
134
- contentHeight: {
135
- type: [Number, String],
136
- default: 150
137
- },
138
- /**
139
- * 自定义样式
140
- */
141
- customStyle: {
142
- type: Object as PropType<CSSProperties>,
143
- default: () => ({})
144
- }
145
56
  });
146
57
 
147
58
  // 事件定义
148
- const emit = defineEmits<IFoldingPanel>();
59
+ const emit = defineEmits<HyFoldingPanelGroupEmits>();
149
60
 
150
- // 内部面板状态
151
- interface PanelItem extends PanelVo {
152
- expanded: boolean;
153
- }
61
+ // 内部激活索引
62
+ const activeIndex = ref(props.modelValue);
154
63
 
155
- // 计算的面板项,包含展开状态
156
- const panelItems = ref<PanelItem[]>([]);
64
+ // 监听v-model变化
65
+ watch(
66
+ () => props.modelValue,
67
+ (newVal) => {
68
+ activeIndex.value = newVal;
69
+ },
70
+ );
157
71
 
158
- // 初始化面板数据
159
- const initializePanels = () => {
160
- panelItems.value = props.list.map(item => ({
161
- ...item,
162
- expanded: !!item.spread
163
- }));
164
- };
72
+ // 监听内部激活索引变化
73
+ watch(activeIndex, (newVal) => {
74
+ emit("update:modelValue", newVal);
75
+ emit("change", newVal);
76
+ });
165
77
 
166
- // 监听列表数据变化
167
- watch(() => props.list, () => {
168
- initializePanels();
169
- }, { deep: true, immediate: true });
78
+ // 提供给子组件的方法
79
+ const updateActiveIndex = (index: number) => {
80
+ if (props.disabled) return;
170
81
 
171
- // 切换面板展开状态
172
- const toggleItem = (index: number) => {
173
- if (props.disabled || panelItems.value[index].disabled) {
174
- return;
175
- }
176
-
177
- const currentItem = panelItems.value[index];
178
- const isCurrentlyExpanded = currentItem.expanded;
179
-
180
- // 手风琴模式下,关闭其他面板
181
82
  if (props.accordion) {
182
- panelItems.value.forEach((item, i) => {
183
- if (i !== index) {
184
- item.expanded = false;
185
- }
186
- });
83
+ // 手风琴模式下,如果点击的是当前激活的索引,则关闭(设为-1)
84
+ const wasActive = activeIndex.value === index;
85
+ activeIndex.value = wasActive ? -1 : index;
86
+
87
+ // 触发相应的事件
88
+ if (!wasActive) {
89
+ emit("open", index);
90
+ } else {
91
+ emit("close", index);
92
+ }
93
+ } else {
94
+ // 非手风琴模式下,这里不做特殊处理,由子组件自己控制
95
+ activeIndex.value = index;
187
96
  }
188
-
189
- // 切换当前面板状态
190
- currentItem.expanded = !isCurrentlyExpanded;
191
-
192
- // 触发事件
193
- emit('change', currentItem, index);
194
- emit(currentItem.expanded ? 'open' : 'close', currentItem, index);
195
97
  };
196
98
 
99
+ // 提供给子组件的配置
100
+ provide("hy-folding-panel", {
101
+ ...toRefs(props),
102
+ activeIndex,
103
+ updateActiveIndex,
104
+ });
197
105
  // 对外暴露的方法
198
106
  defineExpose({
199
107
  /**
200
108
  * 打开指定索引的面板
201
109
  */
202
- open: (index: number) => {
203
- if (index >= 0 && index < panelItems.value.length) {
204
- if (props.accordion) {
205
- panelItems.value.forEach((item, i) => {
206
- item.expanded = i === index;
207
- });
208
- } else {
209
- panelItems.value[index].expanded = true;
210
- }
211
-
212
- const item = panelItems.value[index];
213
- emit('change', item, index);
214
- emit('open', item, index);
215
- }
110
+ open: (index: number | string) => {
111
+ if (props.disabled) return;
112
+ activeIndex.value = index;
113
+ emit("open", index);
216
114
  },
217
-
115
+
218
116
  /**
219
- * 关闭指定索引的面板
117
+ * 关闭所有面板
220
118
  */
221
- close: (index: number) => {
222
- if (index >= 0 && index < panelItems.value.length) {
223
- panelItems.value[index].expanded = false;
224
-
225
- const item = panelItems.value[index];
226
- emit('change', item, index);
227
- emit('close', item, index);
119
+ closeAll: () => {
120
+ if (props.disabled) return;
121
+ const prevIndex = activeIndex.value;
122
+ activeIndex.value = -1;
123
+ if (prevIndex !== -1) {
124
+ emit("close", prevIndex);
228
125
  }
229
126
  },
230
-
127
+
231
128
  /**
232
- * 切换指定索引的面板
129
+ * 切换指定索引面板的状态
233
130
  */
234
- toggle: (index: number) => {
235
- toggleItem(index);
131
+ toggle: (index: number | string) => {
132
+ if (props.disabled) return;
133
+ updateActiveIndex(index);
236
134
  },
237
-
135
+
238
136
  /**
239
- * 关闭所有面板
137
+ * 关闭指定索引的面板
240
138
  */
241
- closeAll: () => {
242
- panelItems.value.forEach((item, index) => {
243
- item.expanded = false;
244
- emit('change', item, index);
245
- emit('close', item, index);
246
- });
247
- }
139
+ close: (index: number | string) => {
140
+ if (props.disabled) return;
141
+ if (activeIndex.value === index) {
142
+ activeIndex.value = -1;
143
+ emit("close", index);
144
+ }
145
+ },
248
146
  });
249
147
  </script>
250
148
 
251
- <style lang="scss" scoped>
252
- .hy-folding-panel {
253
- width: 100%;
254
-
255
- &__wrapper {
256
- background-color: #ffffff;
257
- border-radius: 8px;
258
- overflow: hidden;
259
- }
260
-
261
- &__title {
262
- padding: 16px;
263
- font-size: 16px;
264
- font-weight: 600;
265
- color: #333333;
266
- border-bottom: 1px solid #f0f0f0;
267
- }
268
-
269
- &__body {
270
- // 主体容器样式
271
- }
272
-
273
- &__list {
274
- // 列表容器样式
275
- }
276
-
277
- &__item {
278
- position: relative;
279
-
280
- &--disabled {
281
- opacity: 0.6;
282
- }
283
-
284
- &--border {
285
- border-bottom: 1px solid #f0f0f0;
286
-
287
- &:last-child {
288
- border-bottom: none;
289
- }
290
- }
291
-
292
- &--large {
293
- .hy-folding-panel__item-header {
294
- padding: 20px 16px;
295
-
296
- .hy-folding-panel__item-title {
297
- font-size: 16px;
298
- }
299
-
300
- .hy-folding-panel__item-value {
301
- font-size: 14px;
302
- }
303
- }
304
- }
305
-
306
- &--medium {
307
- .hy-folding-panel__item-header {
308
- padding: 16px;
309
-
310
- .hy-folding-panel__item-title {
311
- font-size: 15px;
312
- }
313
-
314
- .hy-folding-panel__item-value {
315
- font-size: 13px;
316
- }
317
- }
318
- }
319
-
320
- &--small {
321
- .hy-folding-panel__item-header {
322
- padding: 12px 16px;
323
-
324
- .hy-folding-panel__item-title {
325
- font-size: 14px;
326
- }
327
-
328
- .hy-folding-panel__item-value {
329
- font-size: 12px;
330
- }
331
- }
332
- }
333
- }
334
-
335
- &__item-header {
336
- display: flex;
337
- justify-content: space-between;
338
- align-items: center;
339
- background-color: #ffffff;
340
- transition: background-color 0.3s;
341
-
342
- &:active {
343
- background-color: #f5f5f5;
344
- }
345
- }
346
-
347
- &__item-left {
348
- display: flex;
349
- align-items: center;
350
- flex: 1;
351
- }
352
-
353
- &__item-icon {
354
- width: 24px;
355
- height: 24px;
356
- margin-right: 8px;
357
-
358
- image {
359
- width: 100%;
360
- height: 100%;
361
- }
362
- }
363
-
364
- &__item-title {
365
- font-size: 15px;
366
- color: #333333;
367
- flex: 1;
368
- }
369
-
370
- &__item-right {
371
- display: flex;
372
- align-items: center;
373
- }
374
-
375
- &__item-value {
376
- font-size: 13px;
377
- color: #999999;
378
- margin-right: 8px;
379
- }
380
-
381
- &__item-arrow {
382
- font-size: 12px;
383
- color: #cccccc;
384
- transition: transform 0.3s;
385
-
386
- &--up {
387
- transform: rotate(0deg);
388
- }
389
- }
390
-
391
- &__item-content {
392
- background-color: #fafafa;
393
- transition: height 0.3s ease;
394
- box-sizing: border-box;
395
- }
396
- }
397
- </style>
149
+ <style lang="scss" scoped></style>