diff --git a/packages/opendesign/src/components/dropdown/ODropdown.vue b/packages/opendesign/src/components/dropdown/ODropdown.vue
index ee5c145d1d4ed66b4dc26e86aa9dfa358509b8fc..cf48944f6d4b93570559810bcab948ded9aaab1c 100644
--- a/packages/opendesign/src/components/dropdown/ODropdown.vue
+++ b/packages/opendesign/src/components/dropdown/ODropdown.vue
@@ -1,17 +1,59 @@
-
+
+
+
diff --git a/packages/opendesign/src/components/dropdown/ODropdownItem.vue b/packages/opendesign/src/components/dropdown/ODropdownItem.vue
new file mode 100644
index 0000000000000000000000000000000000000000..54c0ca1cc8eaccb15ecf14bcf4e7cac95d70f539
--- /dev/null
+++ b/packages/opendesign/src/components/dropdown/ODropdownItem.vue
@@ -0,0 +1,24 @@
+
+
+
+
+ {{ props.label || `${props.value}` }}
+
+
+
+
diff --git a/packages/opendesign/src/components/dropdown/__demo__/DropdownBasic.vue b/packages/opendesign/src/components/dropdown/__demo__/DropdownBasic.vue
new file mode 100644
index 0000000000000000000000000000000000000000..3227b4dc823d75258fa630dd0edcdddfafdd0a8d
--- /dev/null
+++ b/packages/opendesign/src/components/dropdown/__demo__/DropdownBasic.vue
@@ -0,0 +1,25 @@
+
+
+
+ 基础用法
+
+
diff --git a/packages/opendesign/src/components/dropdown/__demo__/IndexDropdown.vue b/packages/opendesign/src/components/dropdown/__demo__/IndexDropdown.vue
new file mode 100644
index 0000000000000000000000000000000000000000..0408b4ca437a2da23827efd7c8734a3f65cdabbe
--- /dev/null
+++ b/packages/opendesign/src/components/dropdown/__demo__/IndexDropdown.vue
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
diff --git a/packages/opendesign/src/components/dropdown/index.ts b/packages/opendesign/src/components/dropdown/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..b6ff5169abe181f9f220a0209b82a35463505b62
--- /dev/null
+++ b/packages/opendesign/src/components/dropdown/index.ts
@@ -0,0 +1,18 @@
+import type { App } from 'vue';
+
+import _ODropdown from './ODropdown.vue';
+import _ODropdownItem from './ODropdownItem.vue';
+
+const ODropdown = Object.assign(_ODropdown, {
+ install(app: App) {
+ app.component(_ODropdown.name, _ODropdown);
+ },
+});
+
+const ODropdownItem = Object.assign(_ODropdownItem, {
+ install(app: App) {
+ app.component(_ODropdownItem.name, _ODropdownItem);
+ },
+});
+
+export { ODropdown, ODropdownItem };
diff --git a/packages/opendesign/src/components/dropdown/provide.ts b/packages/opendesign/src/components/dropdown/provide.ts
new file mode 100644
index 0000000000000000000000000000000000000000..77ee794c3efdb77d91172c71015085f74fd938a8
--- /dev/null
+++ b/packages/opendesign/src/components/dropdown/provide.ts
@@ -0,0 +1,5 @@
+import { InjectionKey } from 'vue';
+
+export const dropdownInjectKey: InjectionKey<{
+ updateVisible: (val: boolean) => void;
+}> = Symbol('provide-dropdown');
diff --git a/packages/opendesign/src/components/dropdown/style/index.scss b/packages/opendesign/src/components/dropdown/style/index.scss
new file mode 100644
index 0000000000000000000000000000000000000000..efd9d3489fe3c79dc85736017210d0421dfec716
--- /dev/null
+++ b/packages/opendesign/src/components/dropdown/style/index.scss
@@ -0,0 +1,32 @@
+@use './var.scss';
+
+.o-dropdown-list {
+ list-style: none;
+ margin: 0;
+ padding: 0;
+ background-color: var(--dropdown-list-bg);
+ box-shadow: var(--dropdown-list-shadow);
+ border-radius: var(--dropdown-list-radius);
+ border: var(--dropdown-list-bd);
+}
+
+.o-dropdown-item {
+ display: flex;
+ align-items: center;
+ padding: var(--dropdown-item-padding);
+ background-color: var(--dropdown-item-bg-color);
+ font-size: var(--dropdown-item-text-size);
+ line-height: var(--dropdown-item-text-height);
+ cursor: pointer;
+
+ &:not(.o-dropdown-item-disabled) {
+ &:hover {
+ background-color: var(--dropdown-item-bg-color-hover);
+ color: var(--dropdown-item-color-hover);
+ }
+ &.active {
+ color: var(--dropdown-item-color-active);
+ background-color: var(--dropdown-item-bg-color-active);
+ }
+ }
+}
diff --git a/packages/opendesign/src/components/dropdown/style/index.ts b/packages/opendesign/src/components/dropdown/style/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..97806f75ad147dd20bd3802a7001ea21d7911191
--- /dev/null
+++ b/packages/opendesign/src/components/dropdown/style/index.ts
@@ -0,0 +1,3 @@
+import '../../style';
+import '../../popup/style';
+import './index.scss';
diff --git a/packages/opendesign/src/components/dropdown/style/var.scss b/packages/opendesign/src/components/dropdown/style/var.scss
new file mode 100644
index 0000000000000000000000000000000000000000..702a01358a471b41164f4c48aad1f94f09f4217b
--- /dev/null
+++ b/packages/opendesign/src/components/dropdown/style/var.scss
@@ -0,0 +1,25 @@
+.o-dropdown-list {
+ --dropdown-list-bg: var(--o-color-control-light);
+ --dropdown-list-shadow: none;
+ --dropdown-list-bd: 1px solid var(--o-color-primary2);
+ --dropdown-list-radius: var(--o-radius-s);
+}
+
+.o-dropdown-item {
+ --dropdown-item-color: var(--o-color-info2);
+ --dropdown-item-color-hover: var(--o-color-info1-inverse);
+ --dropdown-item-color-active: var(--o-color-info1-inverse);
+ --dropdown-item-color-disabled: var(--o-color-info4);
+
+ --dropdown-item-bg-color: transparent;
+ --dropdown-item-bg-color-hover: var(--o-color-primary3-light);
+ --dropdown-item-bg-color-active: var(--o-color-primary2-light);
+ --dropdown-item-bg-color-disabled: var(--o-color-control4-light);
+
+ --dropdown-item-text-size: var(--o-font_size-text);
+ --dropdown-item-text-height: var(--o-line_height-text);
+
+ --dropdown-item-padding: 5px 12px;
+ --dropdown-item-text-size: var(--o-font_size-text);
+ --dropdown-item-text-height: var(--o-line_height-text);
+}
diff --git a/packages/opendesign/src/components/dropdown/types.ts b/packages/opendesign/src/components/dropdown/types.ts
new file mode 100644
index 0000000000000000000000000000000000000000..6db69939c0ac6fade965ea66a391c823f3dc1c59
--- /dev/null
+++ b/packages/opendesign/src/components/dropdown/types.ts
@@ -0,0 +1,100 @@
+import { PopupPositionT, PopupTriggerT } from '../popup';
+import { ExtractPropTypes, PropType } from 'vue';
+import type { SizeT, RoundT } from '../_shared/global';
+
+export const dropdownProps = {
+ /**
+ * 弹出框是否可见
+ */
+ visible: {
+ type: Boolean,
+ },
+ /**
+ * 非受控模式,弹出框是否默认可见
+ */
+ defaultVisible: {
+ type: Boolean,
+ default: false,
+ },
+ /**
+ * 大小
+ */
+ size: {
+ type: String as PropType,
+ },
+ /**
+ * 圆角值
+ */
+ round: {
+ type: String as PropType,
+ },
+ /**
+ * 下拉选项触发方式
+ */
+ trigger: {
+ type: String as PropType,
+ default: 'click',
+ },
+ /**
+ * 下拉选项位置
+ */
+ optionPosition: {
+ type: String as PropType,
+ default: 'bl',
+ },
+ /**
+ * 下拉选项宽度自适应规则
+ * 'auto':自动 | 'min-width':最小宽度与选择框一致 | 'width': 宽度与选择框一致
+ */
+ optionWidthMode: {
+ type: String as PropType<'auto' | 'min-width' | 'width'>,
+ default: 'min-width',
+ },
+ /**
+ * 下拉容器自定义类
+ */
+ optionWrapClass: {
+ type: String,
+ },
+ /**
+ * 是否在结束选择时,卸载下拉选项
+ * v-model
+ */
+ unmountOnHide: {
+ type: Boolean,
+ default: true,
+ },
+ /**
+ * 过渡名称
+ */
+ transition: {
+ type: String,
+ },
+};
+
+export type DropdownPropsT = ExtractPropTypes;
+
+export const dropdownItemProps = {
+ /**
+ * 显示文本
+ */
+ label: {
+ type: String,
+ default: '',
+ },
+ /**
+ * 选项值
+ */
+ value: {
+ type: [String, Number] as PropType,
+ default: '',
+ },
+ /**
+ * 是否禁用
+ */
+ disabled: {
+ type: Boolean,
+ },
+};
+
+export type DropdownItemPropsT = ExtractPropTypes;
diff --git a/packages/portal/src/router.ts b/packages/portal/src/router.ts
index 4f1533f3c28469cc30d0494558d166078488ef9c..1e512dd62b751fc1a773314113dc37a0aa217b39 100644
--- a/packages/portal/src/router.ts
+++ b/packages/portal/src/router.ts
@@ -153,6 +153,12 @@ export const routes = [
label: '菜单',
component: () => import('@components/menu/__demo__/IndexMenu.vue'),
},
+ {
+ path: '/dropdown',
+ name: 'ODropdown',
+ label: '下拉菜单',
+ component: () => import('@components/dropdown/__demo__/IndexDropdown.vue'),
+ },
];
export const router = createRouter({