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.
@@ -1,68 +1,57 @@
1
- import type { CSSProperties } from 'vue'
2
- import type { CellContentVo } from '../hy-cell/typing'
3
-
4
- export interface PanelVo extends CellContentVo {
5
- /**
6
- * @description 是否展示
7
- * */
8
- spread?: boolean
9
- /**
10
- * @description 展示面板里自定义值
11
- * */
12
- content?: string
13
- }
14
- export default interface HyFoldingPanelProps {
15
- /**
16
- * @description 数据集
17
- * */
18
- list: PanelVo[]
19
- /**
20
- * @description 是否手风琴模式( 默认 false )
21
- * */
22
- accordion?: boolean
23
- /**
24
- * @description 头部标题
25
- * */
26
- title?: string
27
- /**
28
- * @description 是否显示头部底部边框
29
- * */
30
- titleBorder?: boolean
31
- /**
32
- * @description 是否显示cell下边框 (默认 true )
33
- * */
34
- border?: boolean
35
- /**
36
- * @description 标题前缀竖线颜色
37
- * */
38
- verticalColor?: string
39
- /**
40
- * @description 是否显示标题前缀竖线
41
- * */
42
- showVertical?: boolean
43
- /**
44
- * @description 是否禁用(默认false)
45
- * */
46
- disabled?: boolean
47
- /**
48
- * @description 单元的大小,可选值为 large,medium,small
49
- * */
50
- size?: HyApp.SizeType
51
- /**
52
- * @description 内容面板高度
53
- * */
54
- contentHeight?: string | number
55
- /**
56
- * @description 定义需要用到的外部样式
57
- * */
58
- customStyle?: CSSProperties
59
- }
60
-
61
- export interface IFoldingPanel {
62
- /** 打开面板触发 */
63
- (e: 'open', temp: PanelVo, index: number): void
64
- /** 关闭面板触发 */
65
- (e: 'close', temp: PanelVo, index: number): void
66
- /** 改成面板开关状态触发 */
67
- (e: 'change', temp: PanelVo, index: number): void
68
- }
1
+ import type { Ref, ToRefs } from "vue";
2
+
3
+ // 折叠面板组组件的Props接口
4
+ export interface HyFoldingPanelGroupProps {
5
+ /**
6
+ * 当前激活的面板索引,支持v-model
7
+ */
8
+ modelValue?: number;
9
+ /**
10
+ * 是否手风琴模式,默认false
11
+ */
12
+ accordion?: boolean;
13
+ /**
14
+ * 是否禁用整个折叠面板组
15
+ */
16
+ disabled?: boolean;
17
+ /**
18
+ * 是否显示边框,默认true
19
+ */
20
+ border?: boolean;
21
+ /**
22
+ * 面板大小 large, medium, small
23
+ */
24
+ size?: HyApp.SizeType;
25
+ }
26
+
27
+ // 父组件提供给子组件的配置接口
28
+ export interface HyFoldingPanelGroupConfig extends ToRefs<HyFoldingPanelGroupProps> {
29
+ /**
30
+ * 当前激活的索引
31
+ */
32
+ activeIndex: Ref<number | string>;
33
+ /**
34
+ * 更新激活索引的方法
35
+ */
36
+ updateActiveIndex: (index: number | string) => void;
37
+ }
38
+
39
+ // 折叠面板组组件的事件接口
40
+ export interface HyFoldingPanelGroupEmits {
41
+ /**
42
+ * v-model 同步事件
43
+ */
44
+ (e: "update:modelValue", value: number | string): void;
45
+ /**
46
+ * 面板状态改变时触发
47
+ */
48
+ (e: "change", index: number | string): void;
49
+ /**
50
+ * 面板打开时触发
51
+ */
52
+ (e: "open", index: number | string): void;
53
+ /**
54
+ * 面板关闭时触发
55
+ */
56
+ (e: "close", index: number | string): void;
57
+ }
@@ -1,228 +1,278 @@
1
- <template>
2
- <view class="hy-folding-panel-item">
3
- <HyCell
4
- :title="title"
5
- :value="value"
6
- :icon="icon"
7
- :border="border"
8
- :size="size"
9
- :disabled="disabled"
10
- :clickable="!disabled"
11
- @click="handleClick"
12
- >
13
- <template #icon="{ icon }">
14
- <slot name="icon" :icon="icon"></slot>
15
- </template>
16
- <template #title="{ title }">
17
- <slot name="title" :title="title">{{ title }}</slot>
18
- </template>
19
- <template #value="{ value }">
20
- <slot name="value" :value="value">
21
- <text v-if="value">{{ value }}</text>
22
- <text v-else class="hy-folding-panel-item__arrow" :class="{ 'hy-folding-panel-item__arrow--up': spread }">
23
- {{ spread ? '↑' : '↓' }}
24
- </text>
25
- </slot>
26
- </template>
27
- <template #bottom>
28
- <transition name="hy-folding-panel-item__transition">
29
- <view
30
- v-show="spread"
31
- class="hy-folding-panel-item__content"
32
- :style="[
33
- customStyle,
34
- { height: contentHeight }
35
- ]"
36
- >
37
- <slot />
38
- </view>
39
- </transition>
40
- <HyLine v-if="spread && showBottomLine" />
41
- </template>
42
- </HyCell>
43
- </view>
44
- </template>
45
-
46
- <script lang="ts">
47
- export default {
48
- name: "hy-folding-panel-item",
49
- options: {
50
- addGlobalClass: true,
51
- virtualHost: true,
52
- styleIsolation: "shared",
53
- },
54
- };
55
- </script>
56
-
57
- <script setup lang="ts">
58
- import { ref, computed, watch, onMounted, inject } from "vue";
59
- import type { CSSProperties } from "vue";
60
- import { addUnit } from "../../libs";
61
- import HyCell from "../hy-cell/hy-cell.vue";
62
- import HyLine from "../hy-line/hy-line.vue";
63
-
64
- interface IProps {
65
- /** 标题 */
66
- title?: string;
67
- /** 标题左侧的图标 */
68
- icon?: string;
69
- /** 右侧的值 */
70
- value?: string;
71
- /** 是否显示下边框 */
72
- border?: boolean;
73
- /** 单元的大小 */
74
- size?: "large" | "medium" | "small";
75
- /** 是否禁用 */
76
- disabled?: boolean;
77
- /** 内容面板高度 */
78
- contentHeight?: string | number;
79
- /** 是否默认展开 */
80
- defaultOpen?: boolean;
81
- /** 是否显示底部线条 */
82
- showBottomLine?: boolean;
83
- /** 定义需要用到的外部样式 */
84
- customStyle?: CSSProperties;
85
- /** 面板索引(由父组件提供) */
86
- index?: number;
87
- }
88
-
89
- const props = withDefaults(defineProps<IProps>(), {
90
- title: "",
91
- icon: "",
92
- value: "",
93
- border: true,
94
- size: "medium",
95
- disabled: false,
96
- contentHeight: 120,
97
- defaultOpen: false,
98
- showBottomLine: true,
99
- customStyle: () => ({}),
100
- index: -1,
101
- });
102
-
103
- const emit = defineEmits<{
104
- (e: "click", value: boolean): void;
105
- (e: "open"): void;
106
- (e: "close"): void;
107
- // 用于手风琴模式的特殊事件
108
- (e: "child-click", index: number, isOpen: boolean): void;
109
- }>();
110
-
111
- // 尝试从父组件注入手风琴模式配置
112
- const accordion = inject<boolean>('hy-folding-panel-accordion', false);
113
-
114
- const spread = ref(props.defaultOpen);
115
- const contentHeight = computed(() => {
116
- return typeof props.contentHeight === 'number'
117
- ? `${props.contentHeight}px`
118
- : props.contentHeight;
119
- });
120
-
121
- // 监听默认展开状态
122
- watch(
123
- () => props.defaultOpen,
124
- (newValue) => {
125
- spread.value = newValue;
126
- },
127
- { immediate: true }
128
- );
129
-
130
- // 处理点击事件
131
- const handleClick = () => {
132
- if (props.disabled) return;
133
-
134
- spread.value = !spread.value;
135
- emit("click", spread.value);
136
-
137
- // 向父组件通知子组件点击事件(用于手风琴模式)
138
- if (props.index >= 0) {
139
- emit('child-click', props.index, spread.value);
140
- }
141
-
142
- if (spread.value) {
143
- emit("open");
144
- } else {
145
- emit("close");
146
- }
147
- };
148
-
149
- // 提供给父组件控制展开/收起的方法
150
- defineExpose({
151
- open: () => {
152
- if (!props.disabled && !spread.value) {
153
- spread.value = true;
154
- emit("click", true);
155
- emit("open");
156
-
157
- // 通知父组件
158
- if (props.index >= 0) {
159
- emit('child-click', props.index, true);
160
- }
161
- }
162
- },
163
- close: () => {
164
- if (!props.disabled && spread.value) {
165
- spread.value = false;
166
- emit("click", false);
167
- emit("close");
168
-
169
- // 通知父组件
170
- if (props.index >= 0) {
171
- emit('child-click', props.index, false);
172
- }
173
- }
174
- },
175
- toggle: () => {
176
- handleClick();
177
- },
178
- getSpread: () => spread.value,
179
- });
180
-
181
- // 组件挂载时,如果是手风琴模式且默认打开,通知父组件
182
- onMounted(() => {
183
- if (spread.value && accordion && props.index >= 0) {
184
- emit('child-click', props.index, true);
185
- }
186
- });
187
- </script>
188
-
189
- <style lang="scss" scoped>
190
- @import "./index.scss";
191
-
192
- .hy-folding-panel-item {
193
- &__arrow {
194
- font-size: 16px;
195
- color: #999;
196
- transition: transform 0.3s;
197
-
198
- &--up {
199
- transform: rotate(0deg);
200
- }
201
- }
202
-
203
- &__content {
204
- padding: 12px 16px;
205
- box-sizing: border-box;
206
- overflow: hidden;
207
- background-color: #f8f8f8;
208
- }
209
-
210
- &__transition-enter-active,
211
- &__transition-leave-active {
212
- transition: all 0.3s ease;
213
- overflow: hidden;
214
- }
215
-
216
- &__transition-enter-from,
217
- &__transition-leave-to {
218
- opacity: 0;
219
- max-height: 0;
220
- }
221
-
222
- &__transition-enter-to,
223
- &__transition-leave-from {
224
- opacity: 1;
225
- max-height: 1000px; /* 足够大的值,确保内容能完全展开 */
226
- }
227
- }
228
- </style>
1
+ <template>
2
+ <view
3
+ class="hy-folding-panel-item"
4
+ :class="{
5
+ 'hy-folding-panel-item--disabled':
6
+ groupConfig?.disabled?.value || disabled,
7
+ 'is-active': isExpanded,
8
+ 'hy-folding-panel-item--border': groupConfig?.border?.value,
9
+ }"
10
+ >
11
+ <!-- 面板头部 -->
12
+ <view
13
+ :class="[
14
+ 'hy-folding-panel-item__header',
15
+ `hy-folding-panel-item--${groupConfig?.size?.value}`,
16
+ ]"
17
+ @click="handleClick"
18
+ >
19
+ <slot v-if="$slots.header" name="header"></slot>
20
+ <template v-else>
21
+ <view class="hy-folding-panel-item__left">
22
+ <slot v-if="$slots.title" name="title"></slot>
23
+ <template v-else>
24
+ <!-- 图标 -->
25
+ <view v-if="icon" class="hy-folding-panel-item__icon">
26
+ <hy-icon
27
+ v-if="icon"
28
+ :src="icon"
29
+ :color="iconColor"
30
+ :size="iconSize"
31
+ />
32
+ </view>
33
+ <!-- 标题 -->
34
+ <text class="hy-folding-panel-item__title">{{ title }}</text>
35
+ </template>
36
+ </view>
37
+ <view class="hy-folding-panel-item__right">
38
+ <!-- 右侧值 -->
39
+ <text v-if="value" class="hy-folding-panel-item__value">{{
40
+ value
41
+ }}</text>
42
+ <!-- 箭头 -->
43
+ <view
44
+ class="hy-folding-panel-item__arrow"
45
+ :class="{ 'hy-folding-panel-item__arrow--up': isExpanded }"
46
+ >
47
+ <hy-icon :name="IconConfig.DOWN"></hy-icon>
48
+ </view>
49
+ </view>
50
+ </template>
51
+ </view>
52
+
53
+ <!-- 面板内容 -->
54
+ <view
55
+ class="hy-folding-panel-item__content"
56
+ :style="[
57
+ customStyle,
58
+ {
59
+ height: isExpanded
60
+ ? contentHeight
61
+ ? addUnit(contentHeight)
62
+ : 'auto'
63
+ : '0px',
64
+ overflow: 'hidden',
65
+ },
66
+ ]"
67
+ >
68
+ <slot v-if="$slots.default"></slot>
69
+ <text v-else>{{ content || "" }}</text>
70
+ </view>
71
+ </view>
72
+ </template>
73
+
74
+ <script lang="ts">
75
+ export default {
76
+ name: "hy-folding-panel-item",
77
+ };
78
+ </script>
79
+
80
+ <script setup lang="ts">
81
+ import { ref, computed, inject, onMounted } from "vue";
82
+ import type { CSSProperties, PropType } from "vue";
83
+ import type { HyFoldingPanelItemEmits } from "./typing";
84
+ import type { HyFoldingPanelGroupConfig } from "../hy-folding-panel/typing";
85
+ import { addUnit, IconConfig } from "../../libs";
86
+
87
+ // 组件
88
+ import HyIcon from "../hy-icon/hy-icon.vue";
89
+
90
+ /**
91
+ * 折叠面板项组件
92
+ * 用于展示可展开/折叠的单个面板项
93
+ * @displayName hy-folding-panel-item
94
+ */
95
+
96
+ const props = defineProps({
97
+ /**
98
+ * 面板索引(由父组件自动设置)
99
+ */
100
+ index: {
101
+ type: [Number, String],
102
+ default: -1,
103
+ required: true,
104
+ },
105
+ /**
106
+ * 面板标题
107
+ */
108
+ title: {
109
+ type: String,
110
+ default: "",
111
+ },
112
+ /**
113
+ * 右侧显示的值
114
+ */
115
+ value: {
116
+ type: String,
117
+ default: "",
118
+ },
119
+ /**
120
+ * 左侧图标
121
+ */
122
+ icon: {
123
+ type: String,
124
+ default: "",
125
+ },
126
+ /**
127
+ * 左侧图标颜色
128
+ */
129
+ iconColor: {
130
+ type: String,
131
+ default: "",
132
+ },
133
+ /**
134
+ * 左侧图标大小
135
+ */
136
+ iconSize: {
137
+ type: [Number, String],
138
+ default: "",
139
+ },
140
+ /**
141
+ * 面板内容
142
+ */
143
+ content: {
144
+ type: String,
145
+ default: "",
146
+ },
147
+ /**
148
+ * 是否禁用
149
+ */
150
+ disabled: {
151
+ type: Boolean,
152
+ default: false,
153
+ },
154
+ /**
155
+ * 默认是否展开
156
+ */
157
+ defaultOpen: {
158
+ type: Boolean,
159
+ default: false,
160
+ },
161
+ /**
162
+ * 内容区域最大高度
163
+ */
164
+ contentHeight: {
165
+ type: [Number, String],
166
+ default: 150,
167
+ },
168
+ /**
169
+ * 自定义样式
170
+ */
171
+ customStyle: {
172
+ type: Object as PropType<CSSProperties>,
173
+ default: () => ({}),
174
+ },
175
+ });
176
+
177
+ // 事件定义
178
+ const emit = defineEmits<HyFoldingPanelItemEmits>();
179
+
180
+ // 尝试从父组件注入配置
181
+ const groupConfig = inject<HyFoldingPanelGroupConfig>("hy-folding-panel");
182
+
183
+ // 内部展开状态
184
+ const expanded = ref(props.defaultOpen);
185
+
186
+ // 计算当前是否展开
187
+ const isExpanded = computed(() => {
188
+ if (groupConfig?.accordion?.value) {
189
+ console.log(groupConfig.activeIndex.value, props.index);
190
+ // 如果在group中,根据group的activeIndex判断
191
+ return groupConfig.activeIndex.value === props.index;
192
+ }
193
+ // 独立使用时,使用内部状态
194
+ return expanded.value;
195
+ });
196
+
197
+ // 处理点击事件
198
+ const handleClick = () => {
199
+ if (props.disabled) return;
200
+ console.log(groupConfig?.accordion?.value);
201
+
202
+ if (groupConfig?.accordion?.value) {
203
+ // 在group中时,通知group更新激活索引
204
+ groupConfig.updateActiveIndex(props.index);
205
+ emit("child-click", props.index);
206
+ } else {
207
+ // 独立使用时,切换内部状态
208
+ expanded.value = !expanded.value;
209
+ }
210
+
211
+ // 触发事件
212
+ emit("click", props.index);
213
+ if (isExpanded.value) {
214
+ emit("close", props.index);
215
+ } else {
216
+ emit("open", props.index);
217
+ }
218
+ emit("change", !isExpanded.value, props.index);
219
+ };
220
+
221
+ // 对外暴露的方法
222
+ defineExpose({
223
+ /**
224
+ * 打开面板
225
+ */
226
+ open: () => {
227
+ if (props.disabled) return;
228
+
229
+ if (groupConfig) {
230
+ groupConfig.activeIndex.value = props.index;
231
+ } else {
232
+ expanded.value = true;
233
+ }
234
+ emit("open", props.index);
235
+ emit("change", true, props.index);
236
+ },
237
+
238
+ /**
239
+ * 关闭面板
240
+ */
241
+ close: () => {
242
+ if (groupConfig) {
243
+ if (groupConfig?.accordion?.value) {
244
+ groupConfig.activeIndex.value = -1;
245
+ }
246
+ } else {
247
+ expanded.value = false;
248
+ }
249
+ emit("close", props.index);
250
+ emit("change", false, props.index);
251
+ },
252
+
253
+ /**
254
+ * 切换面板状态
255
+ */
256
+ toggle: () => {
257
+ handleClick();
258
+ },
259
+
260
+ /**
261
+ * 获取当前展开状态
262
+ */
263
+ getExpanded: () => {
264
+ return isExpanded.value;
265
+ },
266
+ });
267
+
268
+ // 初始化时如果默认打开且在group中,通知group
269
+ onMounted(() => {
270
+ if (props.defaultOpen && groupConfig && props.index !== -1) {
271
+ groupConfig.updateActiveIndex(props.index);
272
+ }
273
+ });
274
+ </script>
275
+
276
+ <style lang="scss" scoped>
277
+ @import "./index.scss";
278
+ </style>