diff --git a/packages/opendesign/src/collapse/OCollapse.vue b/packages/opendesign/src/collapse/OCollapse.vue
new file mode 100644
index 0000000000000000000000000000000000000000..39556012177382681cf9e4f4aa17af0d8a470c32
--- /dev/null
+++ b/packages/opendesign/src/collapse/OCollapse.vue
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
diff --git a/packages/opendesign/src/collapse/OCollapseItem.vue b/packages/opendesign/src/collapse/OCollapseItem.vue
new file mode 100644
index 0000000000000000000000000000000000000000..843f4b7ccec215d05e38d7a6a8c879dee74e0252
--- /dev/null
+++ b/packages/opendesign/src/collapse/OCollapseItem.vue
@@ -0,0 +1,90 @@
+
+
+
+
+
diff --git a/packages/opendesign/src/collapse/__demo__/CollapseBasic.vue b/packages/opendesign/src/collapse/__demo__/CollapseBasic.vue
new file mode 100644
index 0000000000000000000000000000000000000000..e5b189ada3ecf478e7fd41f1ae1b08fa8bf42ec6
--- /dev/null
+++ b/packages/opendesign/src/collapse/__demo__/CollapseBasic.vue
@@ -0,0 +1,30 @@
+
+
+
+ 基础用法
+
+
+
+
diff --git a/packages/opendesign/src/collapse/__demo__/TheIndex.vue b/packages/opendesign/src/collapse/__demo__/TheIndex.vue
new file mode 100644
index 0000000000000000000000000000000000000000..e2f39908aa2d377ed8995889878b861b73f5c1df
--- /dev/null
+++ b/packages/opendesign/src/collapse/__demo__/TheIndex.vue
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
diff --git a/packages/opendesign/src/collapse/index.ts b/packages/opendesign/src/collapse/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..545196c4abfea54cffa6fa5e7cc0cca131072870
--- /dev/null
+++ b/packages/opendesign/src/collapse/index.ts
@@ -0,0 +1,20 @@
+import type { App } from 'vue';
+
+import _OCollapse from './OCollapse.vue';
+import _OCollapseItem from './OCollapseItem.vue';
+
+export * from './types';
+
+const OCollapse = Object.assign(_OCollapse, {
+ install(app: App) {
+ app.component(_OCollapse.name, _OCollapse);
+ },
+});
+
+const OCollapseItem = Object.assign(_OCollapseItem, {
+ install(app: App) {
+ app.component(_OCollapseItem.name, _OCollapseItem);
+ },
+});
+
+export { OCollapse, OCollapseItem };
diff --git a/packages/opendesign/src/collapse/provide.ts b/packages/opendesign/src/collapse/provide.ts
new file mode 100644
index 0000000000000000000000000000000000000000..396d82cccd1f5a46986038c719bbcf7cf9524224
--- /dev/null
+++ b/packages/opendesign/src/collapse/provide.ts
@@ -0,0 +1,8 @@
+import { InjectionKey, Ref } from 'vue';
+
+export const collapseInjectKey: InjectionKey<{
+ realValue: Ref>;
+ accordion: Ref;
+ updateModelValue: (val: Array) => void;
+ onChange: (val: string | number, ev: Event) => void;
+}> = Symbol('provide-collapse');
diff --git a/packages/opendesign/src/collapse/style/index.scss b/packages/opendesign/src/collapse/style/index.scss
new file mode 100644
index 0000000000000000000000000000000000000000..504d59c44eea7243e3faa4e454b7675ea8b98768
--- /dev/null
+++ b/packages/opendesign/src/collapse/style/index.scss
@@ -0,0 +1,2 @@
+@use './style.scss' as *;
+@use './media.scss' as *;
diff --git a/packages/opendesign/src/collapse/style/index.ts b/packages/opendesign/src/collapse/style/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..786fce9c52bcc5902ae5ad430cbd07f8ead8a699
--- /dev/null
+++ b/packages/opendesign/src/collapse/style/index.ts
@@ -0,0 +1,2 @@
+import '../../_styles';
+import './index.scss';
diff --git a/packages/opendesign/src/collapse/style/media.scss b/packages/opendesign/src/collapse/style/media.scss
new file mode 100644
index 0000000000000000000000000000000000000000..b27842ad42bbb0089923e7ff03d70a88b27f373b
--- /dev/null
+++ b/packages/opendesign/src/collapse/style/media.scss
@@ -0,0 +1,3 @@
+@use '../../_styles/mixin.scss' as *;
+@include respond-to('phone-pad') {
+}
diff --git a/packages/opendesign/src/collapse/style/style.scss b/packages/opendesign/src/collapse/style/style.scss
new file mode 100644
index 0000000000000000000000000000000000000000..38a5c2997a17285ce676086dd5b6a10a42894018
--- /dev/null
+++ b/packages/opendesign/src/collapse/style/style.scss
@@ -0,0 +1,53 @@
+@use '../../_styles/mixin.scss' as *;
+@use './var.scss';
+
+.o-collapse {
+ background-color: var(--collapse-bg-color);
+ padding: var(--collapse-padding);
+ border-radius: var(--collapse-radius);
+}
+
+.o-collapse-item + .o-collapse-item {
+ border-top: 1px solid var(--collapse-division-color);
+}
+
+.o-collapse-item-header {
+ padding: var(--collapse-item-header-padding);
+ display: flex;
+ flex-direction: row-reverse;
+ justify-content: space-between;
+ cursor: pointer;
+}
+
+.o-collapse-item-title {
+ margin: 0;
+ color: var(--o-color-info1);
+ font-size: var(--o-font_size-h3);
+ line-height: var(--o-line_height-h3);
+ overflow: hidden;
+ white-space: nowrap;
+ text-overflow: ellipsis;
+}
+
+.o-collapse-item-icon {
+ flex-shrink: 0;
+ color: var(--collapse-item-icon-color);
+ font-size: var(--collapse-item-icon-size);
+ transform: rotate(90deg);
+ transition: transform var(--o-duration-m2) var(--o-easing-standard);
+}
+
+.o-collapse-item-body {
+ margin-bottom: 24px;
+ overflow: hidden;
+ transition: height var(--o-duration-m2) var(--o-easing-standard);
+}
+
+.o-collapse-item-expanded {
+ .o-collapse-item-title {
+ font-weight: 500;
+ }
+ .o-collapse-item-icon {
+ transform: rotate(-90deg);
+ }
+}
diff --git a/packages/opendesign/src/collapse/style/var.scss b/packages/opendesign/src/collapse/style/var.scss
new file mode 100644
index 0000000000000000000000000000000000000000..801a6a6470b6895baec9d2efd0dc4e3d78b25567
--- /dev/null
+++ b/packages/opendesign/src/collapse/style/var.scss
@@ -0,0 +1,13 @@
+.o-collapse {
+ --collapse-radius: var(--o-radius_control-l);
+ --collapse-bg-color: var(--o-color-fill2);
+ --collapse-padding: 4px 32px;
+ --collapse-division-color: var(--o-color-control1);
+}
+
+.o-collapse-item {
+ --collapse-item-header-padding: 24px 0px;
+ --collapse-item-title-color: var(--o-color-info1);
+ --collapse-item-icon-color: var(--o-color-info1);
+ --collapse-item-icon-size: var(--o-icon_size-xs);
+}
diff --git a/packages/opendesign/src/collapse/types.ts b/packages/opendesign/src/collapse/types.ts
new file mode 100644
index 0000000000000000000000000000000000000000..1c0c86317fff8702c4e7daa30f52f7e53aa10b12
--- /dev/null
+++ b/packages/opendesign/src/collapse/types.ts
@@ -0,0 +1,44 @@
+import { ExtractPropTypes, PropType } from 'vue';
+
+export const collapseProps = {
+ /**
+ * 是否开启手风琴模式
+ */
+ accordion: {
+ type: Boolean,
+ default: false,
+ },
+
+ /**
+ * 折叠面板双向绑定值
+ */
+ modelValue: {
+ type: Array as PropType>,
+ },
+ /**
+ * 非受控模式时,默认展开的面板值
+ */
+ defaultValue: {
+ type: Array as PropType>,
+ default: () => [],
+ },
+};
+
+export const collapseItemProps = {
+ /**
+ * 折叠面板value
+ */
+ value: {
+ type: [String, Number],
+ required: true,
+ },
+ /**
+ * 折叠面板标题
+ */
+ title: {
+ type: String,
+ },
+};
+
+export type CollapsePropsT = ExtractPropTypes;
+export type CollapseItemPropsT = ExtractPropTypes;
diff --git a/packages/opendesign/src/index.scss b/packages/opendesign/src/index.scss
index c741adeddf02d3af480295a9f21f98fe131a0d67..f8f9040274d8565294c8193dcf869b6a9478a8d5 100644
--- a/packages/opendesign/src/index.scss
+++ b/packages/opendesign/src/index.scss
@@ -41,3 +41,4 @@
@use './upload/style/index.scss' as *;
@use './toggle/style/index.scss' as *;
@use './anchor/style/index.scss' as *;
+@use './collapse/style/index.scss' as *;
diff --git a/packages/opendesign/src/index.ts b/packages/opendesign/src/index.ts
index b4b5f5b68ae16384d818cd51ac9aff5b06ed4d47..6e9b3d2e92123f9d96b7b2151d33081d7a62a118 100644
--- a/packages/opendesign/src/index.ts
+++ b/packages/opendesign/src/index.ts
@@ -44,6 +44,7 @@ export * from './scroller';
export * from './upload';
export * from './toggle';
export * from './anchor';
+export * from './collapse';
export * from './intersection-observer';
export * from './resize-observer';
diff --git a/packages/portal/src/router.ts b/packages/portal/src/router.ts
index a772d41d9106e425342b9e870f9f0409c4286654..e6ec213499a78fb5e9ab79c5b7c73ba381242f8c 100644
--- a/packages/portal/src/router.ts
+++ b/packages/portal/src/router.ts
@@ -255,6 +255,12 @@ export const routes = [
label: '锚点 Anchor',
component: () => import('@components/anchor/__demo__/TheIndex.vue'),
},
+ {
+ path: '/collapse',
+ name: 'Collapse',
+ label: '折叠面板 Collapse',
+ component: () => import('@components/collapse/__demo__/TheIndex.vue'),
+ },
{
path: '/resize-observer',
name: 'ResizeObserver',