{'新手指引'}
+
+
diff --git a/mini.project.json b/mini.project.json
new file mode 100644
index 000000000..d7f35a072
--- /dev/null
+++ b/mini.project.json
@@ -0,0 +1,8 @@
+{
+ "enableAppxNg": true,
+ "miniprogramRoot": "demo",
+ "axmlStrictCheck": true,
+ "component2": true,
+ "exclude": ["dist/**/*"],
+ "enableNodeModuleBabelTransform": true
+}
diff --git a/package.json b/package.json
new file mode 100644
index 000000000..49c9b7bce
--- /dev/null
+++ b/package.json
@@ -0,0 +1,97 @@
+{
+ "name": "antd-mini",
+ "version": "0.0.1-beta.2",
+ "scripts": {
+ "dev": "minidev dev --less --typescript --no-source-map",
+ "build": "npm run build:component && npm run build:site",
+ "build:component": "NODE_ENV=production node scripts/compile.js",
+ "build:site": "dumi build",
+ "build:demo": "minidev build --output=$PWD/dist --typescript --less --no-source-map",
+ "deploy:docs": "gh-pages -d docs-dist",
+ "lint:code": "eslint \"demo/pages/**/*.js\" \"scripts/*.js\" \"src/**/*.ts\"",
+ "lint:code:fix": "eslint \"demo/pages/**/*.js\" \"scripts/*.js\" \"src/**/*.ts\" --fix",
+ "changelog": "conventional-changelog -p angular -i CHANGELOG.md -s",
+ "prepublishOnly": "npm run build:component"
+ },
+ "gitHooks": {
+ "pre-commit": "lint-staged",
+ "commit-msg": "node scripts/verifyCommitMsg.js"
+ },
+ "lint-staged": {
+ "*.ts": [
+ "eslint --ext .ts"
+ ],
+ "*.js": [
+ "eslint --ext .js"
+ ]
+ },
+ "dependencies": {
+ "@babel/runtime": "^7.17.2",
+ "async-validator": "^4.0.7",
+ "fast-deep-equal": "3.1.3"
+ },
+ "devDependencies": {
+ "@babel/core": "^7.6.4",
+ "@babel/plugin-syntax-dynamic-import": "^7.0.0",
+ "@babel/plugin-transform-runtime": "^7.11.5",
+ "@babel/preset-env": "^7.6.3",
+ "@babel/preset-typescript": "^7.6.0",
+ "@mini-types/alipay": "^1.0.2",
+ "@typescript-eslint/eslint-plugin": "^5.6.0",
+ "@typescript-eslint/parser": "^5.6.0",
+ "alipay-style": "^0.1.0",
+ "axios": "^0.24.0",
+ "chalk": "^4.1.0",
+ "conventional-changelog-cli": "^2.1.1",
+ "detect-port": "1.3.0",
+ "dingtalk-design-cli": "^0.20.2",
+ "dumi": "^1.1.35",
+ "dumi-theme-mobile": "^1.1.17",
+ "eslint": "^8.4.1",
+ "eslint-config-ali": "^13.0.0",
+ "eslint-config-prettier": "^8.3.0",
+ "eslint-plugin-import": "^2.25.3",
+ "fs": "^0.0.1-security",
+ "gh-pages": "^3.0.0",
+ "gulp": "^4.0.2",
+ "gulp-babel": "^8.0.0",
+ "gulp-clean-css": "^4.0.0",
+ "gulp-if": "^3.0.0",
+ "gulp-inject-envs": "^1.0.0",
+ "gulp-less": "^4.0.1",
+ "gulp-rename": "^1.2.3",
+ "lint-staged": "^10.0.7",
+ "minidev": "^1.0.7",
+ "path": "^0.12.7",
+ "postcss-less": "^6.0.0",
+ "pre-commit": "^1.2.2",
+ "prettier": "^2.2.1",
+ "react": "^16.12.0 || ^17.0.0",
+ "request": "^2.88.2",
+ "stylelint": "^14.1.0",
+ "stylelint-config-standard": "^24.0.0",
+ "typescript": "^4.0.0",
+ "yorkie": "^2.0.0"
+ },
+ "files": [
+ "es"
+ ],
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/ant-design/ant-design-mini.git",
+ "branch": "master",
+ "platform": "github"
+ },
+ "bugs": {
+ "url": "https://github.com/ant-design/ant-design-mini/issues"
+ },
+ "keywords": [
+ "ant-mini",
+ "mini-program",
+ "mini-ali-ui",
+ "mini-antui",
+ "alipay"
+ ],
+ "license": "MIT",
+ "homepage": "https://github.com/ant-design/ant-design-mini"
+}
diff --git a/plugin/index.ts b/plugin/index.ts
new file mode 100644
index 000000000..27888496a
--- /dev/null
+++ b/plugin/index.ts
@@ -0,0 +1,126 @@
+import { IApi } from 'dumi';
+import * as path from 'path';
+import * as fs from 'fs';
+
+export default (api: IApi) => {
+ process.env.DUMI_THEME = path.resolve(
+ require.resolve('dumi-theme-mobile/package.json'),
+ '../es',
+ );
+ api.register({
+ key: 'dumi.registerCompiletime',
+ fn: () => ({
+ name: 'CustomDemoPreviewer',
+ component: path.join(__dirname, './render.js'),
+ transformer(opts) {
+ if (opts.attrs && opts.attrs.src && opts.attrs.src.endsWith('.tsx')) {
+ return null;
+ }
+ // 检测 demo 文件夹
+ if (!fs.existsSync(path.join(process.cwd(), 'demo/pages'))) {
+ throw new Error(
+ '组件库 demo 文件夹:缺失',
+ );
+ }
+ const sourcesPath = parseAlias(
+ process.cwd(),
+ opts.attrs.src,
+ opts.mdAbsPath,
+ );
+ return {
+ rendererProps: getRenderProps(sourcesPath),
+ previewerProps: {
+ sources: getBlockDepsFiles(require(`${process.cwd()}/package.json`).name, sourcesPath),
+ dependencies: getBlockDepsNPM(`${process.cwd()}/package.json`),
+ hideActions: ['CSB', 'RIDDLE'],
+ simulator: false,
+ },
+ };
+ },
+ }),
+ });
+};
+
+export function getRenderProps(path: string) {
+ const prefix = path.match(/.*(\/demo\/)(pages\/.+)/)[2];
+ const demoAxmlFile = fs.readdirSync(path).find((file) => file.endsWith('.axml'));
+ const tail = demoAxmlFile.match(/(.+)\.axml$/)[1];
+ const pages = `${prefix}/${tail}`;
+
+ const props
+ = process.env.NODE_ENV === 'development'
+ ? {
+ appCdnBaseUrl: `http://localhost:${process.argv[3]}/`,
+ pages,
+ }
+ : { appCdnBaseUrl:"https://gw.alipayobjects.com/os/gzmsfesa-sffminipkg_prod/package/alipay/com_alipay_alipaywallet/2021001172665758/0_1_2202171349_9/", pages };
+
+ return props;
+}
+
+export function getBlockDepsFiles(
+ pkgName: string,
+ blockRootPath: string,
+ parentPath = '',
+) {
+ const files = fs.readdirSync(path.join(blockRootPath, parentPath));
+ return files.reduce((r, file) => {
+ const fileRltPath = path.join(parentPath, file);
+ const fileAbsPath = path.join(blockRootPath, fileRltPath);
+
+ if (fs.lstatSync(fileAbsPath).isDirectory()) {
+ Object.assign(r, getBlockDepsFiles(blockRootPath, fileRltPath));
+ } else if (/\.(ts|js|axml|acss)$/.test(fileAbsPath)) {
+ r[fileRltPath] = {
+ path: fileAbsPath,
+ };
+ } else if (/\.json$/.test(fileAbsPath)) {
+ r[fileRltPath] = {
+ path: fileAbsPath,
+ content: CorrentPathInJson(fileAbsPath, pkgName),
+ };
+ }
+ return r;
+ }, {} as Record
+
+### 带有 name 和 desc
+
+
+
+
+## API
+
+### 属性
+
+| 属性 | 类型 | 必填 | 默认值 | 说明 |
+| --- | --- | --- | --- | --- |
+| size | `'x-small'` | `'small'` | `'medium'` | `'large'` | 否 | "medium" | x-small(80 x 80);small(88 x 88);medium(104 x 104);large(120 x 120) |
+| src | string | 否 | - | 头像地址,默认为灰色的内置图片 |
+| name | string | 否 | - | 第一行信息 |
+| desc | string | 否 | - | 第二行补充信息,当 name 不存在时,不显示;当 size=x-small,不显示 |
+| className | string | 否 | - | 类名 |
+
+### 样式类
+| 类名 | 说明 |
+| -----|-----|
+| amd-avatar | 整体样式 |
+| amd-avatar-src | 图片样式 |
+| amd-avatar-content | 头像描述样式 |
+| amd-avatar-name | name 样式 |
+| amd-avatar-desc | desc 样式 |
+
+
\ No newline at end of file
diff --git a/src/Avatar/index.ts b/src/Avatar/index.ts
new file mode 100644
index 000000000..28f5fb61d
--- /dev/null
+++ b/src/Avatar/index.ts
@@ -0,0 +1,5 @@
+import { AvatarDefaultProps } from './props';
+
+Component({
+ props: AvatarDefaultProps,
+});
diff --git a/src/Avatar/props.d.ts b/src/Avatar/props.d.ts
new file mode 100644
index 000000000..de038fc93
--- /dev/null
+++ b/src/Avatar/props.d.ts
@@ -0,0 +1,25 @@
+import { IBaseProps } from '../_base';
+
+/**
+ * @description 头像,可展示头像以及用户名等简要信息。
+ */
+export interface IAvatarProps extends IBaseProps {
+ /**
+ * @description 尺寸,x-small(80*80) small(88*88) medium(104*104) large(120*120)
+ * @default "medium"
+ */
+ size?: 'x-small' | 'small' | 'medium' | 'large';
+ /**
+ * @description 头像地址,默认为灰色的内置图片
+ */
+ src?: string;
+ /**
+ * @description 第一行信息
+ */
+ name?: string;
+ /**
+ * @description 第二行补充信息,当 name 不存在时,不显示;当 size=x-small,不显示
+ */
+ desc?: string;
+}
+export declare const AvatarDefaultProps: Partial
+
+## API
+
+### 属性
+
+| 属性 | 类型 | 必填 | 默认值 | 说明 |
+| -----|-----|-----|-----|----- |
+| text | string | number | 否 | - | 红点内容,为空时表示只显示红点;可以是数字,也可以是文字;如果是数字,超过 99 会自动变成 ... |
+| bubble | boolean | 否 | false | 是否为气泡形态(带箭头) |
+| placement | 'top-left' | 'top-right' | 否 | "top-right" | 相对于 children 所在访问,left-top(左上角) top-right(右上角) |
+| stroke | boolean | 否 | false | 是否有描边 |
+| className | string | 否 | - | 类名 |
+
+### 样式类
+| 类名 | 说明 |
+| -----|-----|
+| amd-badge | 整体样式 |
+| amd-badge-inner-text | 内部文本样式 |
+| amd-badge-text | 文本样式 |
+| amd-badge-dot | 红点样式 |
+| amd-badge-icon | 图标样式 |
+
+
\ No newline at end of file
diff --git a/src/Badge/index.ts b/src/Badge/index.ts
new file mode 100644
index 000000000..bb0a58ef2
--- /dev/null
+++ b/src/Badge/index.ts
@@ -0,0 +1,19 @@
+import { BadgeDefaultProps } from './props';
+import computed from '../mixins/computed';
+
+Component({
+ mixins: [computed],
+ props: BadgeDefaultProps,
+ methods: {
+ computed(props) {
+ const { text } = props;
+ let overCount = false;
+ if (typeof text === 'number') {
+ if (text >= 100) {
+ overCount = true;
+ }
+ }
+ return { overCount };
+ },
+ },
+});
diff --git a/src/Badge/props.d.ts b/src/Badge/props.d.ts
new file mode 100644
index 000000000..181d132c3
--- /dev/null
+++ b/src/Badge/props.d.ts
@@ -0,0 +1,36 @@
+import { IBaseProps } from '../_base';
+/**
+ * @description 徽标,红点、数字或文字。用于告诉用户待处理的事物或更新数。
+ */
+
+export interface IBadgeProps extends IBaseProps {
+ /**
+ * @description badge 类型
+ * @default dot
+ */
+ type?: 'dot' | 'number' | 'text' | 'bubble';
+ /**
+ * @description 数字内容,超过 99 会自动变成 99+
+ */
+ number?: number;
+ /**
+ * @description 红点内容,为空时表示只显示红点;可以是数字,也可以是文字;如果是数字,超过 99 会自动变成 ...
+ */
+ text?: string | number;
+ /**
+ * @description 是否为气泡形态(带箭头)
+ * @default false
+ */
+ bubble?: boolean;
+ /**
+ * @description 相对于 children 所在访问,left-top(左上角) top-right(右上角)
+ * @default "top-right"
+ */
+ placement?: 'top-left' | 'top-center' | 'top-right' | 'left' | 'right' | 'bottom-left' | 'bottom-center' | 'bottom-right' ;
+ /**
+ * @description 是否有描边
+ * @default false
+ */
+ stroke?: boolean;
+}
+export declare const BadgeDefaultProps: Partial
+
+### 内联按钮
+
+
+
+### 自定义Icon
+
+
+### 辅助按钮
+
+
+## API
+
+### 属性
+
+| 属性 | 类型 | 必填 | 默认值 | 说明 |
+| -----|-----|-----|-----|----- |
+| publicId | string | 否 | - | 生活号 id,必须是当前小程序同主体且已关联的生活号,open-type="lifestyle" 时有效。 |
+| openType | string | 否 | - | 开放能力。 |
+| scope | string | 否 | - | 当 openType 为 getAuthorize 时有效。 |
+| type | 'default' | 'primary' | 'danger' | 'ghost' | 'danger-ghost' | 'light' | 否 | "default" | 按钮类型 default=辅助按钮 primary=品牌色按钮 danger=危险按钮 ghost=primary+ghost danger-ghost=danger+ghost light=弱按钮 |
+| disabled | boolean | 否 | false | 是否禁用 |
+| activeClassName | string | 否 | - | 按下时的类名 |
+| subText | string | 否 | - | 辅助文字,显示在第二行 |
+| inline | boolean | 否 | false | 内联,不撑满父级宽度 |
+| inlineSize | 'small' | 'medium' | 'large' | 否 | "medium" | 内联尺寸 |
+| icon | string | 否 | - | 按钮左侧图标 |
+| loading | boolean | 否 | false | 是否加载中,加载中时不可点击 |
+| loadingText | string | 否 | - | 加载中时的文字 |
+| htmlType | 'button' | 'submit' | 'reset' | 否 | "button" | 按钮原生类型,在表单提交时有效 |
+| mode | string | 否 | - | 结合表单使用时,设置 mode 值为 'form' |
+| form | string | 否 | - | 结合表单使用时,需要设置为所在表单组件的 form 值 |
+| className | string | 否 | - | 类名 |
+
+### 事件
+
+| 事件名 | 说明 | 类型 |
+| -----|-----|-----|
+| onTap | 点击按钮,触发此回调 | ( e: [`Event`](https://opendocs.alipay.com/mini/framework/event-object) ) => void |
+
+### 插槽
+| 名称 | 说明 |
+| ----|----|
+| icon | 图标插槽 |
+
+### 样式类
+
+| 类名 | 说明 |
+| -----|-----|
+| amd-button | 整体样式 |
+| amd-button-content | 按钮内容样式 |
+| amd-button-loading-container | 加载区域样式 |
+| amd-button-loading-text | 加载区域文字样式 |
+| amd-button-loading | 加载动画样式 |
+| amd-button-wrap | 加载区域右侧样式 |
+| amd-button-icon | 图标样式 |
+| amd-button-text | 按钮文字样式 |
+| amd-button-subtext | 副标题样式 |
+
+
\ No newline at end of file
diff --git a/src/Button/index.ts b/src/Button/index.ts
new file mode 100644
index 000000000..8cfad6660
--- /dev/null
+++ b/src/Button/index.ts
@@ -0,0 +1,20 @@
+import { ButtonDefaultProps } from './props';
+import fmtEvent from '../_util/fmtEvent';
+import htmlType from '../mixins/htmlType';
+
+Component({
+ props: ButtonDefaultProps,
+ mixins: [htmlType()],
+ methods: {
+ onTap(e) {
+ const { onTap, disabled, loading, _submit } = this.props;
+ if (typeof _submit === 'function' && !disabled && !loading) {
+ _submit();
+ }
+ if (onTap && !disabled && !loading) {
+ const event = fmtEvent(this.props, e);
+ return onTap(event);
+ }
+ },
+ },
+});
diff --git a/src/Button/props.d.ts b/src/Button/props.d.ts
new file mode 100644
index 000000000..997082971
--- /dev/null
+++ b/src/Button/props.d.ts
@@ -0,0 +1,78 @@
+/* eslint-disable @typescript-eslint/no-explicit-any */
+import { IBaseProps, IconType } from '../_base';
+
+/**
+ * @description 按钮,用户只需单击一下即可执行操作并做出选择。
+ * 常用于表单提交、界面跳转、模块引导点击。具体用法和小程序框架中 button 保持一致,在 button 基础上做了样式的封装。
+ * 封装后的按钮可改变按钮形态、增加 loading,以及内置了几种不同样式的按钮。
+ */
+
+export interface IButtonProps extends IBaseProps {
+ /**
+ * @description 按钮类型
+ * @default "default"
+ */
+ type?: 'default' | 'primary' | 'danger' | 'ghost' | 'danger-ghost' | 'light';
+ /**
+ * @description 是否禁用
+ * @default false
+ */
+ disabled?: boolean;
+ /**
+ * @description 按下时的类名
+ */
+ activeClassName?: string;
+ /**
+ * @description 辅助文字,显示在第二行
+ */
+ subText?: string;
+ /**
+ * @description 内联,不撑满父级宽度
+ * @default false
+ */
+ inline?: boolean;
+ /**
+ * @description 内联尺寸
+ * @default "medium"
+ */
+ inlineSize?: 'x-small' | 'small' | 'medium' | 'large' | 'x-large';
+ /**
+ * @description 按钮左侧图标
+ */
+ icon?: IconType;
+ /**
+ * @description 是否加载中,加载中时不可点击
+ * @default false
+ */
+ loading?: boolean;
+ /**
+ * @description 加载中时的文字
+ */
+ loadingText?: string;
+ /**
+ * @description 按钮原生类型,在表单提交时有效
+ * @default "button"
+ */
+ htmlType?: 'button' | 'submit' | 'reset';
+ /**
+ * @description 点击回调
+ */
+ onTap?: (event?: any) => void;
+ /**
+ * @description 生活号 id,必须是当前小程序同主体且已关联的生活号,open-type="lifestyle" 时有效。
+ */
+ publicId?: string;
+ /**
+ * @description 开放能力。
+ */
+ openType?: string;
+ /**
+ * @description 当 openType 为 getAuthorize 时有效。
+ */
+ scope?: string;
+ /**
+ * @description 当 openType 为 getAuthorize 时有效。
+ */
+ fill?: 'outline' | 'solid' | 'none';
+}
+export declare const ButtonDefaultProps: Partial
+
+### 受控模式
+
+
+
+## API
+
+### 属性
+
+| 属性 | 类型 | 必填 | 默认值 | 说明 |
+| -----|-----|-----|-----|----- |
+| value | string | 否 | - | checkbox 携带的 value 值, 在原生 form 表单提交的时候有用;在 CheckboxGroup 时亦有用 |
+| checked | boolean | 否 | - | checkbox 是否选中 |
+| id | string | 否 | - | 表单元素 id |
+| name | string | 否 | - | 表单元素 name |
+| disabled | boolean | 否 | false | 是否禁用 |
+| mode | 'noraml' | 'form' | 否 | normal | 配合From/FormItem组件使用时,需设置为 from |
+| className | string | 否 | - | 类名 |
+
+
+### 事件
+| 事件名 | 说明 | 类型 |
+| -----|-----|-----|
+| onChange | 选中状态改变,触发回调 | (e: Event) => void|
+
+### 样式类
+| 类名 | 说明 |
+| -----|-----|
+| amd-checkbox | 标签样式 |
+| amd-checkbox-base | 原始 checkbox 样式 |
+| amd-checkbox-fake | checkbox 组件未选中样式 |
+| amd-checkbox-disabled | checkbox 组件选中后样式 |
+
+
\ No newline at end of file
diff --git a/src/Checkbox/index.ts b/src/Checkbox/index.ts
new file mode 100644
index 000000000..f1b3cfc99
--- /dev/null
+++ b/src/Checkbox/index.ts
@@ -0,0 +1,14 @@
+import { CheckboxDefaultProps } from './props';
+import controlled from '../mixins/controlled';
+import formMixin from '../mixins/form';
+
+Component({
+ props: CheckboxDefaultProps,
+ mixins: [controlled('checked'), formMixin()],
+ methods: {
+ onChange(v) {
+ const { value } = v.detail;
+ this.cOnChange(value);
+ },
+ },
+});
diff --git a/src/Checkbox/props.d.ts b/src/Checkbox/props.d.ts
new file mode 100644
index 000000000..d505e97a9
--- /dev/null
+++ b/src/Checkbox/props.d.ts
@@ -0,0 +1,17 @@
+import { IBaseFormItemPropsWithOutFocus } from '../_base';
+/**
+ * @description 复选框,表单组件。
+ */
+
+export interface ICheckboxProps extends IBaseFormItemPropsWithOutFocus
+
+## API
+
+### 属性
+
+#### CheckboxGroup
+| 属性 | 类型 | 必填 | 默认值 | 说明 |
+| -----|-----|-----|-----|----- |
+| value | string[] | 否 | - | CheckboxGroup 的值,决定子元素是否勾选 |
+| radius | boolean | 否 | false | 是否带圆角 |
+| header | string | 否 | - | 头部说明 |
+| footer | string | 否 | - | 底部说明 |
+| className | string | 否 | - | 类名 |
+| mode | 'noraml' | 'form' | 否 | normal | 配合From/FormItem组件使用时,需设置为 from |
+| uid | string | 是 | - | `必须全局唯一`,需与内部的 CheckboxItem 组件的 uid 一致 |
+
+#### CheckboxItem
+
+| 属性 | 类型 | 必填 | 默认值 | 说明 |
+| -----|-----|-----|-----|----- |
+| value | string | 否 | - | checkbox 携带的 value 值, 在原生 form 表单提交的时候有用;在 CheckboxGroup 时亦有用 |
+| disabled | boolean | 否 | false | 是否禁用 |
+| className | string | 否 | - | 类名 |
+| uid | string | 是 | - | `必须全局唯一`,需与内部的 CheckboxGroup 组件的 uid 一致 |
+
+### 事件
+
+#### CheckboxGroup
+| 事件名 | 说明 | 类型 |
+| -----|-----|-----|
+| onChange | 勾选状态变化时,触发此函数 |-----|
+
+### 插槽
+
+#### CheckboxGroup
+| 名称 | 说明 |
+| ----|----|
+| header | 头部内容插槽 |
+| footer | 底部内容插槽 |
+
+### 样式类
+
+#### CheckboxGroup
+| 类名 | 说明 |
+| -----|-----|
+| amd-checkbox-group |整体样式|
+| amd-list-header | 头部内容样式 |
+| amd-list-body | 内部内容样式 |
+| amd-list-footer |底部内容样式|
+
+#### CheckboxItem
+
+| 类名 | 说明 |
+| -----|-----|
+| amd-checkbox-item | 整体样式 |
+| amd-checkbox | 原始 checkbox 整体样式 |
+| amd-checkbox-base | 原始 checkbox 样式 |
+| amd-checkbox-fake | 未选中 checkbox 样式 |
+| amd-checkbox-checked | 选中 checkbox 样式 |
+
+
\ No newline at end of file
diff --git a/src/CheckboxGroup/index.ts b/src/CheckboxGroup/index.ts
new file mode 100644
index 000000000..21d32a890
--- /dev/null
+++ b/src/CheckboxGroup/index.ts
@@ -0,0 +1,59 @@
+import { CheckboxGroupDefaultProps } from './props';
+import equal from 'fast-deep-equal';
+import { store } from './context';
+import formMixin from '../mixins/form';
+
+Component({
+ props: CheckboxGroupDefaultProps,
+ mixins: [formMixin()],
+ didMount() {
+ const { uid, value } = this.props;
+ const getGroupPropsVal = (key: string) => {
+ switch (key) {
+ case 'onChange':
+ if (this.onChange) {
+ return this.onChange.bind(this);
+ }
+ return this.props.onChange;
+ case 'value':
+ if (Array.isArray(this.props.value)) {
+ return this.props.value;
+ }
+ return [];
+ default:
+ return this.props[key];
+ }
+ };
+ store.setGroupPropsVal(this.props.uid, getGroupPropsVal);
+ if (Array.isArray(value) && value.length > 0) {
+ store.updateGroupValue(uid);
+ }
+ },
+ didUpdate(prevProps) {
+ const { uid: newUID, disabled: newDisabled, value: newValue } = this.props;
+ const { uid: oldUID, disabled: oldDisabled, value: oldValue } = prevProps;
+ store.updateGroup(
+ newUID,
+ {
+ isUIDChanged: newUID !== oldUID,
+ isDisabledChanged: newDisabled !== oldDisabled,
+ isValueChange: !equal(newValue, oldValue),
+ },
+ { oldUID },
+ );
+ },
+ didUnmount() {
+ const { uid } = this.props;
+ store.removeGroup(uid);
+ },
+ methods: {
+ onChange(val) {
+ if (this.props.onChange) {
+ this.props.onChange.call(this, val);
+ }
+ },
+ _updateFieldValue(v) {
+ store.updateGroupValue(this.props.uid, v);
+ },
+ },
+});
diff --git a/src/CheckboxGroup/props.d.ts b/src/CheckboxGroup/props.d.ts
new file mode 100644
index 000000000..1e067ff77
--- /dev/null
+++ b/src/CheckboxGroup/props.d.ts
@@ -0,0 +1,31 @@
+
+import { IBaseFormItemPropsWithOutFocus, IBaseProps } from '../_base';
+
+export interface IListProps extends IBaseProps {
+ /**
+ * @description 是否带圆角
+ * @default false
+ */
+ radius?: boolean;
+ /**
+ * @description 头部说明
+ */
+
+ header?: string;
+ /**
+ * @description 底部说明
+ */
+
+ footer?: string;
+}
+/**
+ * @description 复选框组合,内部由多个 CheckboxItem 组成。
+ */
+
+export interface ICheckboxGroupProps extends Omit
+
+### 手风琴模式
+
+
+
+## API
+
+### 属性
+
+#### Collapse
+| 属性 | 类型 | 必填 | 默认值 | 说明 |
+| -----|-----|-----|-----|----- |
+| name | string[] | 否 | [] | 当前激活的索引 |
+| accordion | boolean | 否 | - | 是否是手风琴模式,仅一个内容被展开 |
+| className | string | 否 | - | 类名 |
+| uid | string | 是 | - | `必须全局唯一`,需与内部的 CollapseItem 组件的 uid 一致 |
+
+#### CollapseItem
+| 属性 | 类型 | 必填 | 默认值 | 说明 |
+| -----|-----|-----|-----|----- |
+| title | `string` | 否 | - | 标题栏内容 |
+| name | string | 是 | - | 标识,必须唯一 |
+| className | string | 否 | - | 类名 |
+| uid | string | 是 | - | `必须全局唯一`,需与外部 Collapse 组件的 uid 一致 |
+
+### 事件
+#### Collapse
+| 事件名 | 说明 | 类型 |
+| -----|-----|-----|
+| onChange | 面板展开/收缩时,获取当前展开的面板 | ( value : `string[]`) => void |
+
+### 插槽
+#### CollapseItem
+| 插槽名称 | 说明 |
+| -----|-----|
+| title | CollapseItem 组件标题插槽,当 title 属性存在时,插槽不生效 |
+
+### 样式类
+#### Collapse
+| 类名 | 说明 |
+| -----|-----|
+| amd-avatar | 整体样式 |
+| amd-avatar-src | 图片样式 |
+| amd-avatar-content | 头像描述样式 |
+| amd-avatar-name | name 样式 |
+| amd-avatar-desc | desc 样式 |
+
+#### CollapseItem
+| 类名 | 说明 |
+| -----|-----|
+| amd-avatar | 整体样式 |
+| amd-avatar-src | 图片样式 |
+| amd-avatar-content | 头像描述样式 |
+| amd-avatar-name | name 样式 |
+| amd-avatar-desc | desc 样式 |
+
+
\ No newline at end of file
diff --git a/src/Collapse/index.ts b/src/Collapse/index.ts
new file mode 100644
index 000000000..ccabbb8d8
--- /dev/null
+++ b/src/Collapse/index.ts
@@ -0,0 +1,47 @@
+import { CollapseDefaultProps } from './props';
+import { context } from './context';
+import equal from 'fast-deep-equal';
+
+Component({
+ props: CollapseDefaultProps,
+ data: {
+ supportSjs: my.canIUse('sjs.event'),
+ },
+ didMount() {
+ const { uid, name } = this.props;
+ const getGroupPropsVal = (key: string) => {
+ switch (key) {
+ case 'onChange':
+ if (this.onChange) {
+ return this.onChange.bind(this);
+ }
+ return this.props.onChange;
+ case 'name':
+ if (Array.isArray(this.props.name)) {
+ return this.props.name;
+ }
+ return [];
+ default:
+ return this.props[key];
+ }
+ };
+ context.addGroup(uid);
+ context.setGroupPropsVal(uid, getGroupPropsVal);
+ context.setItemsAccordion(uid);
+ if (Array.isArray(name)) {
+ context.updateGroupValue(uid, true);
+ }
+ },
+ didUnmount() {
+ context.removeGroup(this.props.uid);
+ },
+ didUpdate(prevProps) {
+ const { uid: newUID, name: newName = [], accordion: newAccordion = false } = this.props;
+ const { uid: oldUID, name: oldName = [], accordion: oldAccordion = false } = prevProps;
+ context.updateGroup(newUID, {
+ isNameChanged: !equal(newName, oldName),
+ isUIDChanged: newUID !== oldUID,
+ isAccordionChanged: newAccordion !== oldAccordion,
+ }, { oldUID });
+ },
+});
diff --git a/src/Collapse/props.d.ts b/src/Collapse/props.d.ts
new file mode 100644
index 000000000..d1b96d9e8
--- /dev/null
+++ b/src/Collapse/props.d.ts
@@ -0,0 +1,23 @@
+import { IBaseProps } from '../_base';
+/**
+ * @description 手风琴,内部由多个 CollapseItem 组成。
+ */
+
+export interface ICollapseProps extends IBaseProps {
+ /**
+ * @description 当前激活的索引
+ * @default []
+ */
+ name?: string[];
+ /**
+ * @description collapse 切换时的回调
+ */
+
+ onChange?: (index: string[]) => void;
+ /**
+ * @description 是否是手风琴模式,仅一个内容被展开
+ */
+
+ accordion?: boolean;
+}
+export declare const CollapseDefaultProps: Partial
+
+## API
+
+### 属性
+
+| 属性 | 类型 | 必填 | 默认值 | 说明 |
+| -----|-----|-----|-----|----- |
+| direction | 'vertical' | 'horizontal' | 否 | 'vertical' | 按钮排列方向 |
+| title | string | 否 | - | 标题文字 |
+| content | string | 否 | - | 内容文字 |
+| visible | boolean | 是 | false | 是否可见,受控模式 |
+| duration | number | 否 | 300 | 过渡动画时长,单位毫秒 |
+| maskClosable | boolean | 否 | true | 点击蒙层关闭 |
+| className | string | 否 | - | 类名 |
+
+### 事件
+
+| 事件名 | 说明 | 类型 |
+| -----|-----|-----|
+| onButtonTap | 点击 Modal 组件内部按钮,触发回调 | (index: number ) => void |
+| onClose | 组件关闭回调 | () => void |
+
+### 插槽
+| 名称 | 说明 |
+| ----|----|
+| content | 弹窗内容 |
+
+### 样式类
+| 类名 | 说明 |
+| ----|----|
+| amd-dialog | 整体样式 |
+| amd-dialog-vertical | 整体样式 |
+| amd-dialog-horizontal | 整体样式 |
+| amd-dialog-content | 内容整体样式 |
+| amd-dialog-content-title | 标题样式 |
+| amd-dialog-content-content | 内容样式 |
+| amd-dialog-content-button-container | 按钮区域样式 |
+| amd-dialog-content-button-container-vertical | 按钮区域样式 |
+| amd-dialog-content-button-container-horizontal | 按钮区域样式 |
+| amd-dialog-content-button | 按钮样式 |
+
+
+
\ No newline at end of file
diff --git a/src/Dialog/index.ts b/src/Dialog/index.ts
new file mode 100644
index 000000000..213bef7d2
--- /dev/null
+++ b/src/Dialog/index.ts
@@ -0,0 +1,19 @@
+import { DialogDefaultProps } from './props';
+
+Component({
+ props: DialogDefaultProps,
+ methods: {
+ onButtonTap(e) {
+ const { onButtonTap } = this.props;
+ if (typeof onButtonTap === 'function') {
+ const { index } = e.currentTarget.dataset;
+ return onButtonTap(index);
+ }
+ },
+ onClose() {
+ if (typeof this.props.onClose === 'function') {
+ this.props.onClose();
+ }
+ },
+ },
+});
diff --git a/src/Dialog/props.d.ts b/src/Dialog/props.d.ts
new file mode 100644
index 000000000..e619bc99d
--- /dev/null
+++ b/src/Dialog/props.d.ts
@@ -0,0 +1,20 @@
+import { IBaseProps } from '../_base';
+/**
+ * @description 对话框
+ */
+export interface IDialogProps extends IBaseProps {
+ /**
+ * @description 对话框操作按钮文本
+ */
+ buttonText: string[];
+ /**
+ * @description 对话框按钮排列方向
+ */
+ direction: 'vertical' | 'horizontal';
+ /**
+ * @description 点击蒙层关闭对话框
+ * @default true
+ */
+ maskClosable: boolean
+}
+export declare const DialogDefaultProps: Partial
+
+## API
+### 属性
+#### Filter
+| 属性 | 类型 | 必填 | 默认值 | 说明 |
+| -----|-----|-----|-----|----- |
+| className | string | 否 | - | 类名 |
+
+#### FilterItem
+| 属性 | 类型 | 必填 | 默认值 | 说明 |
+| -----|-----|-----|-----|----- |
+| type | `'default'` | `'multiple'` | 否 | "default" | 类型 default=单选 multiple=多选 |
+| value | any | 否 | - | 每一项的值,该组件仅支持受控模式 |
+| items | {value: string; text: string; subText: string}[] | 否 | - | type=default type=multiple 有效|
+| placeholder | string | 否 | - | 当该项值为空的时候显示文案 |
+| className | string | 否 | - | 类名 |
+
+### 事件
+#### FilterItem
+
+| 事件名 | 说明 | 类型 |
+| -----|-----|-----|
+| onChange | 选中的选项变更后,触发此回调 | ( changedFields: `Record
+
+### 表单校验
+
+
+### 结合 a:for 指令
+
+
+### 结合 a:if 指令
+
+
+### 实例方法使用
+
+
+### 结合表单组件
+
+
+## API
+
+### 属性
+
+#### Form
+| 属性 | 类型 | 必填 | 默认值 | 说明 |
+| -----|:-----:|:-----:|:-----:|----- |
+| Form | string | 是 | [] | 表单 uid |
+| initialValues | Record
+
+## API
+### 属性
+| 属性 | 类型 | 必填 | 默认值 | 说明 |
+| -----|:-----:|:-----:|:-----:|----- |
+| type | string | 是 | "" | icon 图标的类型 |
+| size | `'x-small'` | `'small'` | `'medium'` | `'large'` | `'x-large'` | 否 | "medium" | icon 的大小,x-small(16)、small(32)、medium(48)、large(64)、x-large(128), |
+| color | string | 否 | - | icon 的颜色,即 CSS 中 color 属性的值 |
+| className | string | 否 | - | 类名 |
+
+### 事件
+
+| 事件名 | 说明 | 类型 |
+| -----|-----|-----|
+| onTap | 点击图标,触发此回调 | ( e: [`Event`](https://opendocs.alipay.com/mini/framework/event-object) ) => void |
+
+### 样式类
+| 类名 | 说明 |
+| -----|-----|
+| amd-icon | 整体样式 |
+
+
\ No newline at end of file
diff --git a/src/Icon/index.sjs b/src/Icon/index.sjs
new file mode 100644
index 000000000..d788be754
--- /dev/null
+++ b/src/Icon/index.sjs
@@ -0,0 +1,3 @@
+const getSize = x => 'amd-icon-size-' + x;
+
+export default { getSize };
diff --git a/src/Icon/index.ts b/src/Icon/index.ts
new file mode 100644
index 000000000..d2e491d09
--- /dev/null
+++ b/src/Icon/index.ts
@@ -0,0 +1,15 @@
+import { IconDefaultProps } from './props';
+import fmtEvent from '../_util/fmtEvent';
+
+Component({
+ props: IconDefaultProps,
+ methods: {
+ onTap(e) {
+ const { onTap } = this.props;
+ if (onTap) {
+ const event = fmtEvent(this.props, e);
+ return onTap(event);
+ }
+ },
+ },
+});
diff --git a/src/Icon/props.d.ts b/src/Icon/props.d.ts
new file mode 100644
index 000000000..fe6543018
--- /dev/null
+++ b/src/Icon/props.d.ts
@@ -0,0 +1,31 @@
+/* eslint-disable @typescript-eslint/no-explicit-any */
+import { IBaseProps } from '../_base';
+/**
+ * @description 图标,内置丰富的图标可以选择。
+ */
+
+export interface IIconProps
+
+## API
+
+### 属性
+| 属性 | 类型 | 必填 | 默认值 | 说明 |
+| -----|-----|-----|-----|----- |
+| label | string | slot | 否 | - | 标签文案 |
+| controlled | boolean | 否 | false | 是否受控模式 |
+| type | 'text' | 'number' | 'idcard' | 'digit' | 'numberpad' | 'digitpad' | 'idcardpad' | 否 | "text" | 输入框的类型 |
+| password | boolean | 否 | false | 是否是密码类型。 |
+| placeholder | string | 否 | - | 占位符。 |
+| placeholderClass | string | 否 | - | 指定 placeholder 的样式类。 |
+| placeholderStyle | string | 否 | - | 指定 placeholder 的样式,可设置间距。 |
+| maxLength | number | 否 | 140 | 最大长度。 |
+| confirmType | 'done' | 'go' | 'next' | 'search' | 'send' | 否 | "done" | 设置键盘右下角按钮的文字,有效值:done(显示“完成”)、go(显示“前往”)、next(显示“下一个”)、search(显示“搜索”)、send(显示“发送”),平台不同显示的文字略有差异。注意:只有在 type=text 时有效。 |
+| confirmHold | boolean | 否 | false | 点击键盘右下角按钮时是否保持键盘不收起状态。 |
+| cursor | number | 否 | - | 指定 focus 时的光标位置。 |
+| selectionStart | number | 否 | -1 | 获取光标时,选中文本对应的焦点光标起始位置,需要和 selection-end 配合使用。 |
+| selectionEnd | number | 否 | -1 | 获取光标时,选中文本对应的焦点光标结束位置,需要和 selection-start 配合使用。 |
+| randomNumber | boolean | 否 | false | 当 type 为 number, digit, idcard 数字键盘是否随机排列。 |
+| enableNative | boolean | 否 | - | 是否启用 Native 渲染 |
+| layer | 'horizontal' | 'vertical' | 否 | "horizontal" | input 排列位置 |
+| inputCls | string | 否 | - | input 输入框的样式类名 |
+| labelCls | string | 否 | - | label 区域的样式类名 |
+| value | string | 否 | - | 输入框的值 |
+| clear | boolean | 否 | true | 显示清除图标 |
+| autoFocus | boolean | 否 | false | 自动聚焦,ios 可能会失效 |
+| ref | React.Ref | 否 | - | 用于操作表单的实例,有 focus 和 blur 两个方法 |
+| id | string | 否 | - | 表单元素 id |
+| name | string | 否 | - | 表单元素 name |
+| disabled | boolean | 否 | false | 是否禁用 |
+| mode | 'noraml' | 'form' | 否 | normal | 配合From/FormItem组件使用时,需设置为 from |
+| className | string | 否 | - | 类名 |
+
+### 事件
+| 事件名 | 说明 | 类型 |
+| -----|-----|-----|
+| onConfirm | 点击键盘完成时触发此回调 | (v: string) => void |
+| onClear | 清除输入内容时触发此回调 | (v: string) => void |
+| onFocus | 聚焦时触发触发此回调 | (v: string) => void |
+| onBlur | 失焦时触发此回调 | (v: string) => void |
+| onChange | 输入时触发此回调 | (v: string) => void |
+
+### 样式类
+| 类名 | 说明 |
+| -----|-----|
+| amd-input-item | 整体样式 |
+| amd-input-item-line | 整体样式 |
+| amd-input-item-layer | 左侧内容区域样式 |
+| amd-input-item-layer-vertical | 左侧内容区域样式 |
+| amd-input-item-layer-normal | 左侧内容区域样式 |
+| amd-input-item-label | 标签样式 |
+| amd-input-item-content | Input 组件样式 |
+| amd-input-item-clear | 清除图标区域样式 |
+| amd-input-item-clear-icon | 清除图标样式 |
+| amd-input-item-extra | 额外区域样式 |
+
+
\ No newline at end of file
diff --git a/src/InputItem/index.ts b/src/InputItem/index.ts
new file mode 100644
index 000000000..1996b29d3
--- /dev/null
+++ b/src/InputItem/index.ts
@@ -0,0 +1,82 @@
+import { InputItemDefaultProps } from './props';
+import controlled from '../mixins/controlled';
+import formMixin from '../mixins/form';
+import { store } from '../Form/store';
+
+Component({
+ mixins: [controlled(), formMixin()],
+ props: InputItemDefaultProps,
+ data: {
+ showClear: false,
+ },
+ methods: {
+ hideClear() {
+ this.setData({
+ showClear: false,
+ });
+ },
+ showClear() {
+ this.setData({
+ showClear: true,
+ });
+ },
+
+ // 按钮消失
+ onBlur(e) {
+ this.hideClear();
+ const { onBlur } = this.props;
+ if (onBlur) {
+ const { value } = e.detail;
+ onBlur(value);
+ }
+ },
+ // 按钮消失
+ onConfirm(e) {
+ this.hideClear();
+ const { onConfirm } = this.props;
+ if (onConfirm) {
+ const { value } = e.detail;
+ onConfirm(value);
+ }
+ },
+ // 展示无需蒙层
+ onFocus(e) {
+ this.showClear();
+ const { onFocus } = this.props;
+ if (onFocus) {
+ const { value } = e.detail;
+ onFocus(value);
+ }
+ },
+ onChange(e) {
+ const { onChange } = this.props;
+ if (onChange) {
+ const { value } = e.detail;
+ this.cOnChange(value);
+ }
+ },
+ triggerOnValuesChange() {
+ // 触发
+ const { form: formFn, field: fieldFn } = this.props._getCurrentField();
+ const form = formFn();
+ const field = fieldFn();
+ if (form && field) {
+ store.trigger(form, field, '');
+ }
+ },
+ onClear() {
+ this.hideClear();
+ const { onClear, controlled } = this.props;
+ if (onClear) {
+ onClear('');
+ }
+ if (!controlled) {
+ this.setData({
+ cValue: '',
+ });
+ // 非受控模式下生效
+ this.triggerOnValuesChange();
+ }
+ },
+ },
+});
diff --git a/src/InputItem/props.d.ts b/src/InputItem/props.d.ts
new file mode 100644
index 000000000..56884e3a0
--- /dev/null
+++ b/src/InputItem/props.d.ts
@@ -0,0 +1,23 @@
+import { IBaseFormItemProps } from '../_base';
+/**
+ * @description 输入框。
+ */
+
+export interface IInputItemProps extends IBaseFormItemProps {
+ /**
+ * @description 输入框的值
+ */
+ value?: string;
+ /**
+ * @description 提示文字
+ */
+
+ placeholder?: string;
+ /**
+ * @description 显示清除图标
+ * @default true
+ */
+
+ clear?: boolean;
+}
+export declare const InputItemDefaultProps: Partial
+
+## API
+
+### 属性
+
+#### List
+| 属性 | 类型 | 必填 | 默认值 | 说明 |
+| -----|-----|-----|-----|----- |
+| radius | boolean | 否 | false | 是否带圆角 |
+| header | `string` | 否 | - | 头部说明 |
+| footer | `string` | 否 | - | 底部说明 |
+| className | string | 否 | - | 类名 |
+
+#### ListItem
+
+| 属性 | 类型 | 必填 | 默认值 | 说明 |
+| -----|-----|-----|-----|----- |
+| image | `string` | 否 | - | 左侧图片 |
+| arrow | `'right'` | `'up'` | `'down'` | 否 | - | 箭头方向,不传表示没有箭头 |
+| extra | `string` | 否 | - | 右侧额外内容 |
+| brief | `string` | 否 | - | 第二行信息 |
+| className | string | 否 | - | 类名 |
+
+
+### 插槽
+
+#### List
+| 名称 | 说明 |
+| ----|----|
+| header | 头部内容插槽 |
+| footer | 尾部内容插槽 |
+
+#### ListItem
+| 名称 | 说明 |
+| ----|----|
+| brief | 下方简介内容插槽 |
+| extra | 右侧内容插槽 |
+| image | 左侧图标插槽 |
+
+### 事件
+
+#### ListItem
+| 事件名 | 说明 | 类型 |
+| -----|-----|-----|
+| onTap | 点击图标,触发此回调 | ( e: [`Event`](https://opendocs.alipay.com/mini/framework/event-object) ) => void |
+
+### 样式类
+#### List
+| 类名 | 说明 |
+| -----|-----|
+| amd-list | 整体样式 |
+| amd-list-header | header 样式 |
+| amd-list-body | 内部内容样式 |
+| amd-list-footer | footer 样式 |
+
+#### ListItem
+| 类名 | 说明 |
+| -----|-----|
+| amd-list-item | 整体样式 |
+| amd-list-item-line | 内容样式 |
+| amd-list-item-content | 除 extra、brief 外内容样式 |
+| amd-list-item-content-main | 主要内容样式 |
+| amd-list-item-image | 左侧图片样式 |
+| amd-list-item-brief | brief 样式 |
+| amd-list-item-extra | extra 样式 |
+| amd-list-item-arrow| 右侧 arrow 样式 |
+
+
\ No newline at end of file
diff --git a/src/List/index.ts b/src/List/index.ts
new file mode 100644
index 000000000..e4591e8b4
--- /dev/null
+++ b/src/List/index.ts
@@ -0,0 +1,5 @@
+import { ListDefaultProps } from './props';
+
+Component({
+ props: ListDefaultProps,
+});
diff --git a/src/List/props.d.ts b/src/List/props.d.ts
new file mode 100644
index 000000000..1d90499c6
--- /dev/null
+++ b/src/List/props.d.ts
@@ -0,0 +1,24 @@
+
+import { IBaseProps } from '../_base';
+/**
+ * @description 列表,内部配合 ListItem 使用。
+ */
+
+export interface IListProps extends IBaseProps {
+ /**
+ * @description 是否带圆角
+ * @default false
+ */
+ radius?: boolean;
+ /**
+ * @description 头部说明
+ */
+
+ header?: string;
+ /**
+ * @description 底部说明
+ */
+
+ footer?: string;
+}
+export declare const ListDefaultProps: Partial
+
+## API
+
+### 属性
+| 属性 | 类型 | 必填 | 默认值 | 说明 |
+| -----|-----|-----|-----|----- |
+| type | 'spin' | 'mini' | 否 | "spin" | 组件类型 |
+| delay | number | 否 | - | 延时显示加载状态,单位 ms |
+| loading | boolean | 否 | true | 是否加载中 |
+| color | string | 否 | "#999" | 点的颜色,仅限 mini loading |
+| size | `'x-large'` | `'large'` | `'medium'` | `'small'` | 否 | "medium" | type 为 spin 时,组件的尺寸 |
+| miniSize | string | 否 | "200rpx" | type 为 mini 时,组件的尺寸 |
+| text | string | 否 | - | 加载中文案,仅限 spin loading |
+| indicator | `string` | 否 | - | 自定义加载中的指示器,仅限 spin loading |
+| theme | `'dark'` | `'light'` | 否 | "dark" | 颜色,dark/深色、 light/浅色,仅限 spin loading |
+| className | string | 否 | - | 类名 |
+
+### 插槽
+| 名称 | 说明 |
+| ----|----|
+| indicator | 自定义加载中的指示器 |
+
+### 样式类
+| 类名 | 说明 |
+| ----|----|
+| amd-loading | 整体样式 |
+| amd-loading-spin | 内容区域样式 |
+| amd-loading-spin-dark | 内容区域样式 |
+| amd-loading-spin-light | 内容区域样式 |
+| amd-loading-spin-icon | 加载图标样式 |
+| amd-loading-spin-icon-small | 加载图标样式 |
+| amd-loading-spin-icon-medium | 加载图标样式 |
+| amd-loading-spin-icon-large | 加载图标样式 |
+| amd-loading-spin-icon-x-large | 加载图标样式 |
+| amd-loading-text | 加载文字样式 |
+| amd-loading-text-dark | 加载文字样式 |
+| amd-loading-text-light | 加载文字样式 |
+
+
\ No newline at end of file
diff --git a/src/Loading/index.sjs b/src/Loading/index.sjs
new file mode 100644
index 000000000..c8a8fc541
--- /dev/null
+++ b/src/Loading/index.sjs
@@ -0,0 +1,7 @@
+export function getLoadingColor(theme) {
+ if (theme === 'light') {
+ return '%23FFF';
+ }
+
+ return '%23999';
+}
diff --git a/src/Loading/index.ts b/src/Loading/index.ts
new file mode 100644
index 000000000..989c658e4
--- /dev/null
+++ b/src/Loading/index.ts
@@ -0,0 +1,41 @@
+import { LoadingDefaultProps } from './props';
+
+/**
+ * 注意,delay 的变更不能实时生效
+ */
+Component({
+ props: LoadingDefaultProps,
+ data: {
+ // 决定实际是否渲染加载中状态
+ _loading: false,
+ },
+ didMount() {
+ this.setLoading();
+ },
+ didUpdate(prevProps) {
+ if (prevProps.loading !== this.props.loading) {
+ if (this.timeout) {
+ clearTimeout(this.timeout);
+ }
+ this.setLoading();
+ }
+ },
+ methods: {
+ setLoading() {
+ const { delay, loading } = this.props;
+ const realDelay: number = isNaN(delay) ? 0 : delay;
+
+ if (loading && !this.data._loading) {
+ this.timeout = setTimeout(() => {
+ this.setData({
+ _loading: true,
+ });
+ }, realDelay);
+ } else if (!loading && this.data._loading) {
+ this.setData({
+ _loading: false,
+ });
+ }
+ },
+ },
+});
diff --git a/src/Loading/props.d.ts b/src/Loading/props.d.ts
new file mode 100644
index 000000000..f04e30abc
--- /dev/null
+++ b/src/Loading/props.d.ts
@@ -0,0 +1,32 @@
+
+import { IBaseProps } from '../_base';
+/**
+ * @description 加载,用于提示局部或页面在加载中。
+ */
+
+export interface ILoadingProps extends IBaseProps {
+ /**
+ * @description 是否加载中
+ * @default true
+ */
+ loading?: boolean;
+ /**
+ * @description 尺寸
+ * @default "medium"
+ */
+ size?: 'x-large' | 'large' | 'medium' | 'small';
+ /**
+ * @description 延时显示加载状态,单位 ms
+ */
+ delay?: number;
+ /**
+ * @description 加载中文案
+ */
+ text?: string;
+ /**
+ * @description 颜色,dark/深色, light/浅色
+ * @default "dark"
+ */
+ theme?: 'dark' | 'light';
+}
+export declare const LoadingDefaultProps: Partial
+
+## API
+
+### 属性
+
+| 属性 | 类型 | 必填 | 默认值 | 说明 |
+| -----|-----|-----|-----|----- |
+| title | string | 否 | - | 标题 |
+| content | string | 是 | - | 内容 |
+| image | string | 否 | - | 缩略图 |
+| imageSize | 'medium' | 'large' | 'x-large' | 否 | "medium" | 缩略图尺寸 |
+| visible | boolean | 是 | false | 是否可见,受控模式 |
+| duration | number | 否 | - | 过渡动画时长,单位毫秒 |
+| mainButtonText | string | 否 | '主操作' | 主按钮 |
+| addonButtonText | string | 否 | '辅助操作' | 辅助按钮,第二个按钮 |
+| maskClosable | boolean | 否 | true | 点击蒙层关闭 |
+| className | string | 否 | - | 类名 |
+
+### 事件
+
+| 事件名 | 说明 | 类型 |
+| -----|-----|-----|
+| onButtonTap | 点击 Modal 组件内部按钮,触发回调 | (type: 'marin' | 'addon' ) => void |
+| onClose | 点击 close 图标触发回调 | () => void |
+
+### 插槽
+| 名称 | 说明 |
+| ----|----|
+| content | 弹窗内容 |
+
+### 样式类
+| 类名 | 说明 |
+| ----|----|
+| amd-modal | 整体样式 |
+| amd-modal-content | 弹窗主体内容样式 |
+| amd-modal-content-image | 弹窗图片样式 |
+| amd-modal-content-image-medium | 弹窗图片样式 |
+| amd-modal-content-image-large | 弹窗图片样式 |
+| amd-modal-content-title | 弹窗标题样式 |
+| amd-modal-content-content | 弹窗内容样式 |
+| amd-modal-buttons-container | 弹窗按钮区域整体样式 |
+| amd-modal-buttons-addon | 辅助按钮样式 |
+| amd-modal-close | close 图标样式 |
+
+
\ No newline at end of file
diff --git a/src/Modal/index.ts b/src/Modal/index.ts
new file mode 100644
index 000000000..7d7fe577d
--- /dev/null
+++ b/src/Modal/index.ts
@@ -0,0 +1,23 @@
+import { ModalDefaultProps } from './props';
+
+Component({
+ props: ModalDefaultProps,
+ data: {
+ isShowBtn: true,
+ },
+ methods: {
+ onClose() {
+ const { onClose } = this.props;
+ if (typeof onClose === 'function') {
+ return onClose();
+ }
+ },
+ onButtonTap(e) {
+ const { onButtonTap } = this.props;
+ if (typeof onButtonTap === 'function') {
+ const { type } = e.currentTarget.dataset;
+ return onButtonTap(type);
+ }
+ },
+ },
+});
diff --git a/src/Modal/props.d.ts b/src/Modal/props.d.ts
new file mode 100644
index 000000000..f767eb48b
--- /dev/null
+++ b/src/Modal/props.d.ts
@@ -0,0 +1,61 @@
+
+import { IBaseProps } from '../_base';
+/**
+ * @description 对话框,当应用中需要比较明显的对用户当前的操作行为进行警示或提醒时,可以使用对话框。用户需要针对对话框进行操作后方可结束。
+ */
+
+export interface IModalProps extends IBaseProps {
+ /**
+ * @description 标题
+ */
+ title: string;
+ /**
+ * @description 内容
+ */
+ content: string;
+ /**
+ * @description 缩略图
+ */
+ image?: string;
+ /**
+ * @description 缩略图尺寸
+ * @default "medium"
+ */
+ imageSize?: 'medium' | 'large' | 'x-large';
+ /**
+ * @description 是否可见,受控模式
+ * @default false
+ */
+ visible: boolean;
+ /**
+ * @description 点击蒙层关闭
+ * @default true
+ */
+ maskClosable: boolean;
+ /**
+ * @description 主按钮文本
+ */
+ mainButtonText: string
+ /**
+ * @description 辅助按钮文本
+ */
+ addonButtonText: string
+ /**
+ * @description 关闭图标的颜色,用于在深色和浅色背景上切换,不传时表示无关闭图标
+ */
+ closeType?: 'dark' | 'light';
+ /**
+ * @description 弹窗动画时长,单位 ms
+ * @default 300
+ */
+ duration: number;
+ /**
+ * @description 触发关闭时回调
+ */
+ onClose?: () => void;
+ /**
+ * @description 点击按钮回调
+ */
+ onButtonTap?: (type: 'main' | 'addon' | 'cancel') => void;
+}
+export declare const ModalDefaultProps: Partial
+
+## API
+### 属性
+| 属性 | 类型 | 必填 | 默认值 | 说明 |
+| -----|-----|-----|-----|----- |
+| mode | 'link' | 'closeable' | 否 | - | 通告类型,link 表示连接,整行可点;closeable 表示点击 x 可以关闭;不填时表示你右侧没有图标 |
+| actions | string[] | 否 | - | 行动点,最多两个行动点,当有 action 时,mode 失效 |
+| showIcon | boolean | 否 | false | 是否显示左侧的图标 |
+| enableMarquee | boolean | 否 | false | 是否开启滚动动画 |
+| loop | boolean | 否 | false | 是否循环滚动,enableMarquee 为 true 时有效 |
+| type | 'default' | 'info' | 'error' | 'primary' | 否 | "default" | 提示类型 default 橙色,info 灰色,error 红色,primary 蓝色 |
+| className | string | 否 | - | 类名 |
+
+### 事件
+
+| 事件名 | 说明 | 类型 |
+| -----|-----|-----|
+| onTap | 点击通知栏右侧的图标(箭头或者叉),触发回调 | () => void |
+| onActionTap | 点击右侧操作区域文本,触发回调 | (index: number) => void |
+
+
+### 样式类
+| 类名 | 说明 |
+| -----|-----|
+| amd-notice-bar | 整体样式 |
+| amd-notice-bar-default | 整体样式 |
+| amd-notice-bar-danger | 整体样式 |
+| amd-notice-bar-primary | 整体样式 |
+| amd-notice-bar-transparent | 整体样式 |
+| amd-notice-bar-content | 内部区域样式 |
+| amd-notice-bar-scroll-left | 左侧阴影渐变区域样式 |
+| amd-notice-bar-scroll-right | 右侧阴影渐变区域样式 |
+| amd-notice-bar-marquee | 文本展示区域样式 |
+| amd-notice-bar-operation | 右侧操作区域样式 |
+| amd-notice-bar-operation-icon | 右侧操作区域内图标样式 |
+| amd-notice-bar-operation-text | 右侧操作区域文字样式 |
+
+
+
\ No newline at end of file
diff --git a/src/NoticeBar/index.ts b/src/NoticeBar/index.ts
new file mode 100644
index 000000000..3529f91b4
--- /dev/null
+++ b/src/NoticeBar/index.ts
@@ -0,0 +1,197 @@
+import { NoticeBarDefaultProps } from './props';
+import { log } from '../_util/console';
+
+// eslint-disable-next-line @typescript-eslint/no-empty-function
+const noop = () => { };
+const _canIUseTransitionEnd = my.canIUse('view.onTransitionEnd');
+
+Component({
+ props: NoticeBarDefaultProps,
+ data: {
+ _show: true,
+ _canIUseTransitionEnd,
+ _marqueeStyle: '',
+ _animatedWidth: 0,
+ _overflowWidth: 0,
+ _duration: 0,
+ _viewWidth: 0,
+ },
+ didMount() {
+ const { enableMarquee } = this.props;
+ this.showError();
+
+ if (enableMarquee) {
+ if (!_canIUseTransitionEnd) {
+ this._measureText();
+ this._startAnimation();
+ } else {
+ this._measureText(this.startMarquee.bind(this));
+ }
+ }
+ },
+
+ didUpdate() {
+ const { enableMarquee } = this.props;
+ this.showError();
+
+ // 这里更新处理的原因是防止notice内容在动画过程中发生改变。
+ if (enableMarquee) {
+ if (!this._marqueeTimer && !_canIUseTransitionEnd) {
+ this._measureText();
+ this._startAnimation();
+ } else {
+ this._measureText(this.startMarquee.bind(this));
+ }
+ }
+ },
+
+ didUnmount() {
+ if (this._marqueeTimer) {
+ clearTimeout(this._marqueeTimer);
+ this._marqueeTimer = null;
+ }
+ },
+ methods: {
+ showError() {
+ const { actions } = this.props;
+ if (!Array.isArray(actions) && typeof actions !== 'undefined') {
+ log.warn('NoticeBar', `当前定义的 actions 的类型为 ${typeof actions},不符合属性定义,应该为数组,如:actions="{{['值', '值']}}`);
+ }
+ },
+ onTap() {
+ const { mode, onTap } = this.props;
+ if (mode === 'link' && typeof onTap === 'function') {
+ return onTap();
+ }
+ if (mode === 'closeable' && typeof onTap === 'function') {
+ this.setData({
+ _show: false,
+ });
+ return onTap();
+ }
+ },
+ onActionTap(e) {
+ const { onActionTap } = this.props;
+ if (onActionTap) {
+ const { index } = e.currentTarget.dataset;
+ return onActionTap(index);
+ } else {
+ log.error('NoticeBar', '缺少 onActionTap 回调。');
+ }
+ },
+ // 文本滚动的计算
+ resetMarquee() {
+ const { loop } = this.props;
+ const { _viewWidth } = this.data;
+ let showMarqueeWidth = '0px';
+ if (loop) {
+ showMarqueeWidth = `${_viewWidth}px`;
+ }
+ const _marqueeStyle = `transform: translate3d(${showMarqueeWidth}, 0, 0); transition: 0s all linear;`;
+ this.setData({
+ _marqueeStyle,
+ });
+ },
+ startMarquee() {
+ const { loop } = this.props;
+ const leading = 500;
+ const { _duration, _overflowWidth, _viewWidth } = this.data;
+ let marqueeScrollWidth = _overflowWidth;
+ if (loop) {
+ marqueeScrollWidth = _overflowWidth + _viewWidth;
+ }
+ const _marqueeStyle = `transform: translate3d(${-marqueeScrollWidth}px, 0, 0); transition: ${_duration}s all linear ${typeof leading === 'number' ? `${leading / 1000}s` : '0s'};`;
+ if (this.data._marqueeStyle !== _marqueeStyle) {
+ this.setData({
+ _marqueeStyle,
+ });
+ }
+ },
+ onTransitionEnd() {
+ const { loop } = this.props;
+ const trailing = 800;
+ if (loop) {
+ setTimeout(() => {
+ this.resetMarquee();
+ this._measureText(this.startMarquee.bind(this));
+ }, typeof trailing === 'number' ? trailing : 0);
+ }
+ },
+ _measureText(callback = noop) {
+ const fps = 40;
+ const { loop } = this.props;
+ // 计算文本所占据的宽度,计算需要滚动的宽度
+ setTimeout(() => {
+ my.createSelectorQuery()
+ .select(`.amd-notice-bar-marquee-${this.$id}`)
+ .boundingClientRect()
+ .select(`.amd-notice-bar-content-${this.$id}`)
+ .boundingClientRect()
+ .exec((ret) => {
+ // eslint-disable-next-line max-len
+ const _overflowWidth = (ret && ret[0] && ret[1] && ((
+
+## API
+
+### 属性
+| 属性 | 类型 | 必填 | 默认值 | 说明 |
+| -----|-----|-----|-----|----- |
+| controlled | boolean | 否 | false | - |
+| data | {value: string | number; label: string}| 是 | - | picker 数据 |
+| value | (string | number)[] | 否 | - | picker 数据 |
+| cols | number | 否 | 1 | 显示多少列 |
+| title | string | 否 | - | 标题 |
+| okText | string | 否 | "确定" | 确认按钮文案 |
+| dismissText | string | 否 | "取消" | 取消文案 |
+| placeholder | string | 否 | - | 提示文案 |
+| id | string | 否 | - | 表单元素 id |
+| name | string | 否 | - | 表单元素 name |
+| disabled | boolean | 否 | false | 是否禁用 |
+| mode | 'noraml' | 'form' | 否 | normal | 配合From/FormItem组件使用时,需设置为 from |
+| className | string | 否 | - | 类名 |
+
+### 事件
+| 事件名 | 说明 | 类型 |
+| -----|-----|-----|
+| onFormat | 选中值的文本格式 | (v: (string | number)[]) => string |
+| onOk | 点击确定按钮,触发回调 | (v: (string | number)[]) => void |
+| onDismiss | 点击取消按钮,触发回调 | () => void |
+| onChange | 选中项发生变化,触发回调 | (v: (string | number)[]) => void |
+
+## 插槽
+| 名称 | 说明 |
+| -----|-----|
+| label | 文本区域标签名称 |
+| title | 弹窗窗体标题名称 |
+
+### 样式类
+| 类名 | 说明 |
+| -----|-----|
+| amd-picker | 文本展示区域样式 |
+| amd-picker-popup | 弹窗整体样式 |
+| amd-picker-header | 弹窗头部区域样式 |
+| amd-picker-header-item | 弹窗头部区域文本样式 |
+| amd-picker-content | 选择区域样式 |
+| amd-picker-content-item | 选择区域单个选项样式 |
+
+
+
\ No newline at end of file
diff --git a/src/Picker/index.ts b/src/Picker/index.ts
new file mode 100644
index 000000000..5a098caed
--- /dev/null
+++ b/src/Picker/index.ts
@@ -0,0 +1,183 @@
+import { PickerDefaultProps } from './props';
+import computed from '../mixins/computed';
+import controlled from '../mixins/controlled';
+import equal from 'fast-deep-equal';
+import formMixin from '../mixins/form';
+import _extends from '@babel/runtime/helpers/extends';
+import { store } from '../Form/store';
+
+const noop = (v) => {
+ return v.join(',');
+};
+
+Component({
+ mixins: [computed, controlled(), formMixin()],
+ props: _extends({}, { onFormat: noop }, PickerDefaultProps),
+ data: {
+ visible: false,
+ cValue: [],
+ showValue: [],
+ valueIndex: [],
+ changeValue: [],
+ },
+ onInit() {
+ const updatePickerFiledValue = (v) => {
+ const changeValue = v.map((item, index) => {
+ return this.props.data[index].findIndex((target) => item === target);
+ });
+ this.formatValue = v;
+ const showValue = this.props.onFormat ? this.props.onFormat(v) : noop(v);
+ this.setData({
+ showValue,
+ valueIndex: changeValue,
+ });
+ };
+ if (!this.$page.data._getCurrentField) return;
+ const { form: formFn, field: fieldFn } = this.$page.data._getCurrentField();
+ const form = formFn();
+ const field = fieldFn();
+ store.addUpdateFiledValue(form, field, updatePickerFiledValue);
+ },
+ didMount() {
+ const { data } = this.props;
+ this.dataList = data;
+ this.transferValue();
+ },
+ didUpdate(prevProps) {
+ if (!equal(prevProps.value, this.props.value)) {
+ const pValueIndex = [];
+ for (let i = 0; i < this.props.value.length; i++) {
+ pValueIndex.push(this.dataList[i].indexOf(this.props.value[i]));
+ }
+ this.formatValueFn(pValueIndex);
+ this.setData({
+ valueIndex: pValueIndex,
+ });
+ }
+ this.correctChangeValue(prevProps.data, this.props.data);
+ },
+ methods: {
+ correctChangeValue(prevData, curData) {
+ const shouldCorrectChangeIndex =
+ prevData.some((col, index) => col.length !== curData[index].length);
+ if (shouldCorrectChangeIndex) {
+ const newChangedValue = this.data.changeValue.map((colIndex, index) => {
+ // 长度 大 -> 小 取小
+ if (prevData[index].length > curData[index].length) {
+ return curData[index].length - 1;
+ }
+ return colIndex;
+ });
+ this.setData({
+ changeValue: newChangedValue,
+ });
+ }
+ },
+ transferValue() {
+ const { cValue } = this.data;
+ const newValue = [];
+ if (this.dataList && cValue) {
+ for (let i = 0; i < cValue.length; i++) {
+ newValue.push(this.dataList[i].indexOf(cValue[i]));
+ }
+ }
+ this.formatValueFn(newValue);
+ },
+ computed(props) {
+ const { value } = props;
+ return {
+ cValue: value,
+ showValue: this.props?.onFormat(this.formatValue ? this.formatValue : []),
+ };
+ },
+ triggerPicker() {
+ const { disabled, onTriggerPicker } = this.props;
+ const { valueIndex, cValue } = this.data;
+ if (this.dataList && valueIndex.length <= 0) {
+ const vIndex = [];
+ for (let i = 0; i < cValue.length; i++) {
+ vIndex.push(this.dataList[i].indexOf(cValue[i]));
+ }
+ this.setData({
+ valueIndex: vIndex,
+ });
+ }
+ if (!disabled) {
+ this.setData({
+ visible: true,
+ });
+ }
+ if (onTriggerPicker) {
+ onTriggerPicker.call(this.props);
+ }
+ },
+ onDismiss() {
+ const { onDismiss } = this.props;
+ if (onDismiss) {
+ this.setData({
+ changeValue: this.data.valueIndex,
+ visible: false,
+ });
+ return onDismiss();
+ }
+ },
+ onChange(v) {
+ const { onChange } = this.props;
+
+ if (onChange) {
+ const { value } = v.detail;
+ const valueTemp = [];
+ if (this.dataList) {
+ for (let i = 0; i < value.length; i++) {
+ valueTemp.push(this.dataList[i][value[i]]);
+ }
+ }
+ this.setData({
+ changeValue: value,
+ cValue: valueTemp,
+ });
+ onChange.call(this.props, valueTemp);
+ }
+ },
+ onOk() {
+ const { onOk, isDatePicker, onChange } = this.props;
+ const { changeValue } = this.data;
+ let pickerValueChanged = false;
+ if (changeValue?.length) {
+ for (let i = 0; i < changeValue.length; i++) {
+ if (changeValue[i] !== this.data.valueIndex[i]) {
+ pickerValueChanged = true;
+ }
+ }
+ }
+ if (pickerValueChanged || isDatePicker) {
+ this.formatValueFn(changeValue);
+ this.setData({
+ cValue: this.formatValue,
+ showValue: this.props.onFormat(this.formatValue),
+ valueIndex: changeValue,
+ visible: false,
+ });
+ onOk?.(changeValue);
+ const { _getCurrentField } = this.props;
+ if (_getCurrentField) {
+ onChange.call(this.props, this.formatValue);
+ } else {
+ onChange?.(changeValue);
+ }
+
+ pickerValueChanged = false;
+ } else {
+ this.setData({
+ visible: false,
+ });
+ }
+ },
+ formatValueFn(nv) {
+ this.formatValue = [];
+ for (let i = 0; i < nv.length; i++) {
+ this.formatValue.push(this.dataList[i][nv[i]]);
+ }
+ },
+ },
+});
diff --git a/src/Picker/props.d.ts b/src/Picker/props.d.ts
new file mode 100644
index 000000000..8eae37e4f
--- /dev/null
+++ b/src/Picker/props.d.ts
@@ -0,0 +1,66 @@
+import { IBaseFormItemPropsWithOutFocus } from '../_base';
+
+export interface PickerData {
+ value: PickerValue[0];
+ label: string;
+ children?: PickerData[];
+}
+export declare type PickerValue = (string | number)[];
+/**
+ * @description 选择器,包括一个或多个不同值的可滚动列表,每个值可以在视图的中心以较暗的文本形式显示。当用户激活 **Picker** 后,将会从底部弹出。
+ */
+export interface IPickerProps extends IBaseFormItemPropsWithOutFocus
+
+### 结合 PopoverItem 组件使用
+
+
+## API
+
+### 属性
+
+#### Popover
+| 属性 | 类型 | 必填 | 默认值 | 说明 |
+| -----|-----|-----|-----|----- |
+| visible | boolean | 否 | false | 是否可见 |
+| mode | `'dark'` | `'light'` | 否 | 'dark' | 组件显示模式 |
+| placement | `'top'` | `'top-right'` | `'top-left'` | `'bottom'` | `'bottom-left'` | `'bottom-right'` | `'left'` | `'left-top'` | `'left-bottom'` | `'right'` | `'right-top'` | `'right-bottom'` | 否 | "bottom-right" | 方向 |
+| className | string | 否 | - | 类名 |
+
+#### PopoverItem
+| 属性 | 类型 | 必填 | 默认值 | 说明 |
+| -----|-----|-----|-----|----- |
+| icon | string | 否 | "" | 图标类型 |
+| className | string | 否 | - | 类名 |
+
+### 事件
+#### Popover
+| 事件名 | 说明 | 类型 |
+| -----|-----|-----|
+| onVisibleChange | 组件隐藏/显示切换,触发回调 | ( visible: boolean ) => void |
+
+#### PopoverItem
+| 事件名 | 说明 | 类型 |
+| -----|-----|-----|
+| onTap | 点击组件,触发回调 | () => void |
+
+### 插槽
+#### Popover
+| 名称 | 说明 |
+| ----|----|
+| items | tooltip 提示插槽,可以使用 PopoverItem 渲染列表 |
+
+#### PopoverItem
+| 名称 | 说明 |
+| -----|-----|
+| icon | 图标插槽 |
+
+### 样式类
+#### Popover
+| 类名 | 说明 |
+| ----|----|
+| amd-popover | 整体样式 |
+| amd-popover-container | 主体内容样式 |
+| amd-popover-content | 内容样式 |
+| amd-popover-arrow | 箭头样式 |
+| amd-popover-inner | 内部内容样式 |
+| amd-popover-inner-pseudo | tootip 内容整体样式 |
+
+#### PopoverItem
+| 类名 | 说明 |
+| ----|----|
+| amd-popover-item | 整体样式|
+| amd-popover-item-icon | 图标样式|
+| amd-popover-item-icon-item | 图标样式|
+| amd-popover-item-text | 文字样式|
+
+
\ No newline at end of file
diff --git a/src/Popover/index.ts b/src/Popover/index.ts
new file mode 100644
index 000000000..8c4f844da
--- /dev/null
+++ b/src/Popover/index.ts
@@ -0,0 +1,21 @@
+import { PopoverDefaultProps } from './props';
+
+Component({
+ props: PopoverDefaultProps,
+ methods: {
+ onVisibleChange() {
+ const { onVisibleChange, visible } = this.props;
+ if (onVisibleChange) {
+ return onVisibleChange(!visible);
+ }
+ },
+ onMaskTap() {
+ const { maskClosable } = this.props;
+ if (maskClosable) {
+ this.onVisibleChange();
+ } else {
+ return false;
+ }
+ },
+ },
+});
diff --git a/src/Popover/props.d.ts b/src/Popover/props.d.ts
new file mode 100644
index 000000000..1f90c9d6b
--- /dev/null
+++ b/src/Popover/props.d.ts
@@ -0,0 +1,28 @@
+
+import { IBaseProps } from '../_base';
+/**
+ * @description 气泡,内部配合 PopoverItem 使用。
+ */
+
+export interface IPopoverProps extends IBaseProps {
+ /**
+ * @description 是否可见
+ * @default false
+ */
+ visible?: boolean;
+ /**
+ * @description visible 变更时回调
+ */
+ onVisibleChange?: (visible: boolean) => void;
+ /**
+ * @description 方向
+ * @default "bottom-right"
+ */
+ placement?: 'top' | 'top-right' | 'top-left' | 'bottom' | 'bottom-left' | 'bottom-right' | 'left' | 'left-top' | 'left-bottom' | 'right' | 'right-top' | 'right-bottom';
+ /**
+ * @description 组件显示模式
+ * @default dark
+ */
+ mode?: 'dark' | 'light'
+}
+export declare const PopoverDefaultProps: Partial
+
+## API
+
+### 属性
+
+| 属性 | 类型 | 必填 | 默认值 | 说明 |
+| -----|-----|-----|-----|----- |
+| className | string | 否 | - | 类名 |
+| visible | boolean | 否 | false | 是否显示 |
+| maskClosable | boolean | 否 | false | 点击蒙层是否可以关闭 |
+| disableScroll | boolean | 否 | true | 弹窗展示时,是否禁止页面滚动 |
+| animation | boolean | 否 | true | 是否开启过渡动画 |
+| duration | number | 否 | 300 | 过渡动画时长,单位毫秒 |
+| position | `'center'` | `'top'` | `'bottom'` | `'left'` | `'right'` | 否 | "center" | 弹窗布局 |
+| zIndex | number | 否 | 998 | 弹窗层级 |
+
+
+
+### 事件
+
+| 事件名 | 说明 | 类型 |
+| -----|-----|-----|
+| onClose | 弹窗关闭时,触发回调 | ( visible: boolean ) => void |
+
+### 样式类
+
+| 类名 | 说明 |
+| ----|----|
+| amd-popup | 整体样式 |
+| amd-popup-mask | 遮罩层样式 |
+| amd-popup-disable-scroll | 禁用滚动样式 |
+| amd-popup-animation | 开启过渡动画样式 |
+| amd-popup-content | 内容样式 |
+| amd-popup-top | 内容样式 |
+| amd-popup-bottom | 内容样式 |
+| amd-popup-left | 内容样式 |
+| amd-popup-right | 内容样式 |
+| amd-popup-center | 内容样式 |
+| amd-popup-close-container | 关闭图标区域样式 |
+| amd-popup-close-container | 关闭图标样式 |
+
+
+
diff --git a/src/Popup/index.sjs b/src/Popup/index.sjs
new file mode 100644
index 000000000..a7449aa2d
--- /dev/null
+++ b/src/Popup/index.sjs
@@ -0,0 +1,12 @@
+function disableScrollEvent(event) {
+ event.preventDefault();
+}
+
+function enableScrollEvent(event) {
+ event.stopPropagation();
+}
+
+export default {
+ disableScrollEvent,
+ enableScrollEvent,
+};
diff --git a/src/Popup/index.ts b/src/Popup/index.ts
new file mode 100644
index 000000000..272a36bff
--- /dev/null
+++ b/src/Popup/index.ts
@@ -0,0 +1,33 @@
+import { PopupDefaultProps } from './props';
+
+Component({
+ props: PopupDefaultProps,
+ data: {
+ classNames: '',
+ supportSjs: my.canIUse('sjs.event'),
+ },
+ deriveDataFromProps(nextProps) {
+ const classNames = [
+ nextProps.className || '',
+ nextProps.disableScroll ? 'amd-popup-disable-scroll' : '',
+ nextProps.animation ? 'amd-popup-animation' : '',
+ ];
+ this.setData({
+ classNames: classNames.join(' ').trim(),
+ });
+ },
+ methods: {
+ onMaskClose() {
+ const { onClose, maskClosable } = this.props;
+ if (maskClosable) {
+ onClose?.();
+ }
+ },
+ onClose() {
+ const { onClose } = this.props;
+ if (onClose) {
+ onClose?.();
+ }
+ },
+ },
+});
diff --git a/src/Popup/props.d.ts b/src/Popup/props.d.ts
new file mode 100644
index 000000000..94c27b3d2
--- /dev/null
+++ b/src/Popup/props.d.ts
@@ -0,0 +1,31 @@
+import { IBaseProps } from '../_base';
+/**
+ * @description 弹窗,可在其中加入具体内容,展示更多信息供用户使用。
+ */
+export interface IPopupProps extends IBaseProps {
+ /**
+ * @description 是否显示
+ * @default false
+ */
+ visible?: boolean;
+ /**
+ * @description 点击蒙层是否可以关闭
+ * @default false
+ */
+ maskClosable?: boolean;
+ /**
+ * @description 关闭时回调
+ */
+ onClose?: () => void;
+ /**
+ * @description 弹窗布局
+ * @default "center"
+ */
+ position?: 'center' | 'top' | 'bottom' | 'left' | 'right';
+ /**
+ * @description 展示关闭图标
+ * @default false
+ */
+ showCloseIcon?: boolean;
+}
+export declare const PopupDefaultProps: Partial
+
+## API
+
+### 属性
+#### RadioGroup
+| 属性 | 类型 | 必填 | 默认值 | 说明 |
+| -----|-----|-----|-----|----- |
+| value | string | 否 | - | RadioGroup 的值,决定子元素是否勾选 |
+| radius | boolean | 否 | false | 是否带圆角 |
+| header | string | 否 | - | 头部说明 |
+| footer | string | 否 | - | 底部说明 |
+| className | string | 否 | - | 类名 |
+| mode | 'noraml' | 'form' | 否 | normal | 配合From/FormItem组件使用时,需设置为 from |
+| uid | string | 是 | - | `必须全局唯一`,需与内部的 RadioItem 组件的 uid 一致 |
+
+#### RadioItem
+| 属性 | 类型 | 必填 | 默认值 | 说明 |
+| -----|-----|-----|-----|----- |
+| value | string | 否 | - | Radio 携带的 value 值, 在原生 form 表单提交的时候有用;在 RadioGroup 时亦有用 |
+| disabled | boolean | 否 | false | 是否禁用 |
+| className | string | 否 | - | 类名 |
+| uid | string | 是 | - | `必须全局唯一`,需与外部的 RadioGroup 组件的 uid 一致 |
+
+### 事件
+#### RadioGroup
+| 事件名 | 说明 | 类型 |
+| -----|-----|-----|
+| onChange | 选中项发生变化,触发回调 | (v: string) => void |
+
+### 插槽
+#### RadioGroup
+| 插槽名 | 说明 |
+| -----|-----|
+| header | 头部说明 |
+| footer | 底部说明 |
+
+### 样式类
+#### RadioGroup
+| 类名 | 说明 |
+| -----|-----|
+| amd-radio-group | 整体样式 |
+| amd-list-header | 头部说明区域样式 |
+| amd-list-body | radio-group 区域样式 |
+| amd-list-footer | 底部说明区域样式 |
+
+#### RadioItem
+| 类名 | 说明 |
+| -----|-----|
+| amd-radio-item-wrap | 整体样式 |
+| amd-radio-item-base | radio 组件样式 |
+| amd-radio-item-fake | 选中状态下 radio 组件样式 |
+
+
\ No newline at end of file
diff --git a/src/RadioGroup/index.ts b/src/RadioGroup/index.ts
new file mode 100644
index 000000000..c101d6c66
--- /dev/null
+++ b/src/RadioGroup/index.ts
@@ -0,0 +1,33 @@
+import { RadioGroupDefaultProps } from './props';
+import { componentContext, componentDisabled, componentValue } from './context';
+import controlled from '../mixins/controlled';
+import formMixin from '../mixins/form';
+
+Component({
+ props: RadioGroupDefaultProps,
+ mixins: [controlled(), formMixin()],
+ didMount() {
+ const { uid, value, disabled } = this.props;
+ // 用于触发 item.checked 更新
+ componentValue.update(uid, value);
+ // 用于触发 item.disabled 更新
+ componentDisabled.update(uid, disabled);
+
+ componentContext.onUpdate(uid, (v) => {
+ this.cOnChange(v);
+ });
+ },
+ didUpdate(prevProps) {
+ const { disabled, uid } = this.props;
+ if (disabled !== prevProps.disabled) {
+ componentDisabled.update(uid, disabled);
+ }
+ componentValue.update(uid, this.data.cValue);
+ },
+ didUnmount() {
+ const { uid } = this.props;
+ componentContext.clearEvent(uid);
+ componentDisabled.clearEvent(uid);
+ componentValue.clearEvent(uid);
+ },
+});
diff --git a/src/RadioGroup/props.d.ts b/src/RadioGroup/props.d.ts
new file mode 100644
index 000000000..26f58cd8f
--- /dev/null
+++ b/src/RadioGroup/props.d.ts
@@ -0,0 +1,28 @@
+import { IBaseFormItemPropsWithOutFocus, IBaseProps } from '../_base';
+
+export interface IListProps extends IBaseProps {
+ /**
+ * @description 是否带圆角
+ * @default false
+ */
+ radius?: boolean;
+ /**
+ * @description 头部说明
+ */
+ header?: string
+ /**
+ * @description 底部说明
+ */
+ footer?: string
+}
+/**
+ * @description 单选,内部配合 RadioItem 使用。
+ */
+
+export interface IRadioGroupProps extends Omit
+
+## API
+
+### 属性
+| 属性 | 类型 | 必填 | 默认值 | 说明 |
+| -----|-----|-----|-----|----- |
+| type | `'success'` | `'danger'` | `'info'` | `'warn'` | `'wait'` | 否 | - | 内置类型 success=成功 danger=错误/危险 info=信息提示 wait=等待处理 |
+| image | `string` | `slot` | `string` | 否 | - | 自定义图片,如果配置了 type, 则不生效 |
+| title | `string` | `slot` | 否 | - | 主文案 |
+| message | `string` | `slot` | 否 | - | 副文案 |
+| buttons | {text: string; type: 'default' | 'primary' | 'danger' | 'ghost' | 'danger-ghost' | 'light'}[] | 否 | - | 按钮类型 |
+| className | string | 否 | - | 类名 |
+
+### 事件
+
+| 事件名 | 说明 | 类型 |
+| -----|-----|-----|
+| onButtonTap | 弹窗关闭时,触发回调 | (idx: number) => void |
+
+### 样式类
+
+| 类名 | 说明 |
+| ----|----|
+| amd-result | 整体样式 |
+| amd-result-main | 内容展示区域 |
+| amd-result-image | icon 区域样式 |
+| amd-result-buttons | 按钮区域样式 |
diff --git a/src/Result/index.sjs b/src/Result/index.sjs
new file mode 100644
index 000000000..ff390fff5
--- /dev/null
+++ b/src/Result/index.sjs
@@ -0,0 +1,13 @@
+const iconMap = {
+ 'success': 'CheckCircleFill',
+ 'danger': 'CloseCircleFill',
+ 'info': 'InformationCircleFill',
+ 'warn': 'ExclamationCircleFill',
+ 'wait': 'ClockCircleFill',
+};
+
+const iconType = t => {
+ return iconMap[t];
+}
+
+export default { iconType };
diff --git a/src/Result/index.ts b/src/Result/index.ts
new file mode 100644
index 000000000..bc07d4942
--- /dev/null
+++ b/src/Result/index.ts
@@ -0,0 +1,14 @@
+import { ResultDefaultProps } from './props';
+
+Component({
+ props: ResultDefaultProps,
+ methods: {
+ onButtonTap(e) {
+ const { onButtonTap } = this.props;
+ if (onButtonTap) {
+ const { index } = e.currentTarget.dataset;
+ return onButtonTap(index);
+ }
+ },
+ },
+});
diff --git a/src/Result/props.d.ts b/src/Result/props.d.ts
new file mode 100644
index 000000000..1cc66289a
--- /dev/null
+++ b/src/Result/props.d.ts
@@ -0,0 +1,50 @@
+import { IBaseProps, IconType } from '../_base';
+
+export interface IButtonProps extends IBaseProps {
+ /**
+ * @description 按钮类型 default=辅助按钮 primary=品牌色按钮 danger=危险按钮 ghost=primary+ghost danger-ghost=danger+ghost light=弱按钮
+ * @default "default"
+ */
+ type?: 'default' | 'primary' | 'danger' | 'ghost' | 'danger-ghost' | 'light';
+}
+/**
+ * @description 结果页,用于展示用户操作后的信息反馈。
+ */
+
+export interface IResultProps extends IBaseProps {
+ /**
+ * @description 内置类型 success=成功 danger=错误/危险 info=信息提示 wait=等待处理
+ */
+ type?: 'success' | 'danger' | 'info' | 'warn' | 'wait';
+ /**
+ * @description 自定义图片,如果配置了 type, 则不生效
+ */
+
+ image?: IconType;
+ /**
+ * @description 主文案
+ */
+
+ title?: string;
+ /**
+ * @description 副文案
+ */
+
+ message?: string;
+ /**
+ * @description 按钮
+ */
+
+ buttons?: ({
+ /**
+ * @description 按钮文案
+ */
+ text?: string;
+ } & IButtonProps)[];
+
+ /**
+ * @description 按钮点击回调
+ */
+ onButtonTap?: (index: number) => void;
+}
+export declare const ResultDefaultProps: Partial
+
+## API
+### 属性
+| 属性 | 类型 | 必填 | 默认值 | 说明 |
+| -----|-----|-----|-----|----- |
+| position | `'bottom'` | `'top'` | `'both'` | 否 | both | 安全区位置 |
+| className | string | 否 | '' | 类名 |
+
+
+### 样式类
+| 类名 | 说明 |
+| -----|-----|
+| amd-safe-area | 整体样式 |
+| amd-safe-area-top | 顶部安全区域 |
+| amd-safe-area-bottom | 底部安全区域 |
+
+
\ No newline at end of file
diff --git a/src/SafeArea/index.ts b/src/SafeArea/index.ts
new file mode 100644
index 000000000..e076f4643
--- /dev/null
+++ b/src/SafeArea/index.ts
@@ -0,0 +1,3 @@
+Component({
+ props: { className: '', position: 'both' },
+});
diff --git a/src/SearchBar/index.axml b/src/SearchBar/index.axml
new file mode 100644
index 000000000..c2390c8be
--- /dev/null
+++ b/src/SearchBar/index.axml
@@ -0,0 +1,51 @@
+
+
+## API
+### 属性
+| 属性 | 类型 | 必填 | 默认值 | 说明 |
+| -----|-----|-----|-----|----- |
+| controlled | boolean | 否 | false | 是否受控模式 |
+| borderColor | string | "" | false | 输入框边框颜色 |
+| enableNative | boolean | 否 | - | 是否启用 Native 渲染 |
+| value | string | 否 | - | 搜索框的值 |
+| placeholder | string | 否 | - | 提示文字 |
+| showCancelButton | boolean | 否 | false | 是否显示取消按钮 |
+| cancelText | string | 否 | "取消" | 取消按钮文案 |
+| maxLength | number | 否 | - | 最大长度 |
+| showBizIcon | boolean | 否 | false | 是否展示额外图标 |
+| autoFocus | boolean | 否 | false | 自动聚焦,ios 可能会失效 |
+| id | string | 否 | - | 表单元素 id |
+| name | string | 否 | - | 表单元素 name |
+| disabled | boolean | 否 | false | 是否禁用 |
+| className | string | 否 | - | 类名 |
+
+### 事件
+| 事件名 | 说明 | 类型 |
+| -----|-----|-----|
+| onVoiceTap | 点击话筒图标,触发此回调 | () => void |
+| onInput | 监听输入框输入动作,触发此回调 | ( v: string ) => void |
+| onSubmit | input 组件的 onConfirm 事件 | ( v: string ) => void |
+| onCancel | 点击最右侧取消按钮,触发此回调 | ( v: string ) => void |
+| onClear | 点击清除图标,触发此回调 | ( v: string ) => void |
+| onFocus | 输入框对焦,触发此回调 | ( v: string ) => void |
+| onBlur | 输入框失焦,触发此回调 | ( v: string ) => void |
+| onChange | 输入文本,触发此回调 | ( v: string ) => void |
+| onBizIconTap | 点击辅助图标,触发此回调 | ( ) => void |
+
+### 样式类
+| 类名 | 说明 |
+| -----|-----|
+| amd-search-bar | 整体样式 |
+| amd-search-bar-input | 内部输入框样式 |
+| amd-search-bar-synthetic | search 图标样式 |
+| amd-search-bar-synthetic-icon | search 图标样式 |
+| amd-search-bar-value | input 组件样式 |
+| amd-search-bar-clear-icon | 清除图标样式 |
+| amd-search-bar-biz-icon | 额外图标样式 |
+
+
\ No newline at end of file
diff --git a/src/SearchBar/index.ts b/src/SearchBar/index.ts
new file mode 100644
index 000000000..c84f43241
--- /dev/null
+++ b/src/SearchBar/index.ts
@@ -0,0 +1,75 @@
+import { SearchBarDefaultProps } from './props';
+import controlled from '../mixins/controlled';
+
+Component({
+ mixins: [controlled()],
+ props: SearchBarDefaultProps,
+ data: {
+ focus: false,
+ },
+ methods: {
+ onSubmit(e) {
+ const { onSubmit } = this.props;
+ if (onSubmit) {
+ const { value } = e.detail;
+ onSubmit(value);
+ }
+ },
+ onInput(e) {
+ const { onInput } = this.props;
+ if (onInput) {
+ const { value } = e.detail;
+ this.cOnInput(value);
+ }
+ },
+ onClear() {
+ const { onClear } = this.props;
+ if (onClear) {
+ onClear('');
+ }
+ this.clearcValue();
+ },
+ clearcValue() {
+ const { controlled: propsControlled } = this.props;
+ if (!propsControlled) {
+ this.setData({
+ cValue: '',
+ });
+ }
+ },
+ onCancel() {
+ const { onCancel } = this.props;
+ if (onCancel) {
+ onCancel('');
+ }
+ this.clearcValue();
+ },
+ onBizIconTap() {
+ if (typeof this.props.onBizIconTap === 'function') {
+ this.props.onBizIconTap();
+ } else if (typeof this.props.onVoiceTap === 'function') {
+ this.props.onVoiceTap();
+ }
+ },
+ onFocus(e) {
+ this.setData({
+ focus: true,
+ });
+ const { onFocus } = this.props;
+ if (onFocus) {
+ const { value } = e.detail;
+ onFocus(value);
+ }
+ },
+ onBlur(e) {
+ this.setData({
+ focus: false,
+ });
+ const { onBlur } = this.props;
+ if (onBlur) {
+ const { value } = e.detail;
+ onBlur(value);
+ }
+ },
+ },
+});
diff --git a/src/SearchBar/props.d.ts b/src/SearchBar/props.d.ts
new file mode 100644
index 000000000..f02b592dc
--- /dev/null
+++ b/src/SearchBar/props.d.ts
@@ -0,0 +1,81 @@
+import { IBaseFormItemProps } from '../_base';
+/**
+ * @description 搜索框。
+ */
+
+export interface ISearchBarProps extends IBaseFormItemProps
+
+## API
+
+### 属性
+| 属性 | 类型 | 必填 | 默认值 | 说明 |
+| -----|-----|-----|-----|----- |
+| controlled | boolean | 否 | false | 是否受控 |
+| value | string[] | 否 | - | 已选择项, 取 items 每一项的 value |
+| items | {text:string; value:string; subText: string; disabled: boolean}[] | 是 | - | 可选项 |
+| activeItemClassName | string | 否 | - | 每一项激活时新加类名 |
+| multiple | boolean | 否 | false | 是否允许多选,标签栏显示的时候会显示当前单选/多选的状态 |
+| title | string | 否 | '' | 标签栏标题 |
+| desc | string | 否 | '' | 标签栏说明|
+| id | string | 否 | - | 表单元素 id |
+| name | string | 否 | - | 表单元素 name |
+| disabled | boolean | 否 | false | 是否禁用 |
+| mode | 'noraml' | 'form' | 否 | normal | 配合From/FormItem组件使用时,需设置为 from |
+| className | string | 否 | - | 类名 |
+
+### 事件
+| 事件名 | 说明 | 类型 |
+| -----|-----|-----|
+| onChange | 选中值发生变化,触发回调 | (v: string | string[]) => void |
+
+### 样式类
+| 类名 | 说明 |
+| -----|-----|
+| amd-selector | 整体样式 |
+| amd-selector-disabled | 禁用状态下的整体样式 |
+| amd-selector-content | 单个选项样式 |
+| amd-selector-item | 单个选项样式 |
+| amd-selector-item-active | 激活状态下单个样式 |
+| amd-selector-item-disabled | 禁用状态下单个选项样式 |
+| amd-selector-item-text | 文本样式 |
+| amd-selector-item-subtext | 副文本样式 |
+| amd-selector-item-badge-active | 激活状态下徽标样式 |
+
+
\ No newline at end of file
diff --git a/src/Selector/index.sjs b/src/Selector/index.sjs
new file mode 100644
index 000000000..c0e000e84
--- /dev/null
+++ b/src/Selector/index.sjs
@@ -0,0 +1,19 @@
+const getFixedValue = (value, multiple) => {
+ let fixedValue;
+ if (multiple) {
+ fixedValue = value;
+ } else {
+ fixedValue = value && value.slice(0, 1);
+ }
+
+ if (fixedValue) {
+ return fixedValue;
+ }
+
+ // 如果不是数组, 返回数组兜底
+ return [];
+};
+
+export default {
+ getFixedValue,
+};
diff --git a/src/Selector/index.ts b/src/Selector/index.ts
new file mode 100644
index 000000000..0a0bc1322
--- /dev/null
+++ b/src/Selector/index.ts
@@ -0,0 +1,61 @@
+import { SelectorDefaultProps } from './props';
+import controlled from '../mixins/controlled';
+import formMixin from '../mixins/form';
+
+const getFixedValue = (value, multiple) => {
+ let fixedValue;
+ if (multiple) {
+ fixedValue = value;
+ } else {
+ fixedValue = value?.slice(0, 1);
+ }
+
+ return fixedValue;
+};
+
+Component({
+ mixins: [controlled(), formMixin()],
+ props: SelectorDefaultProps,
+ data: {} as {
+ cValue?: string[];
+ items: [];
+ },
+ methods: {
+ onChange(e) {
+ const { disabled, value, text } = e.currentTarget.dataset;
+ const { multiple, items } = this.props;
+ if (!disabled && !this.props.disabled) {
+ let nextValue: string[];
+ const fixedValue = getFixedValue(this.data.cValue, multiple);
+ if (multiple) {
+ // 之前已经选中,删除它
+ if (fixedValue?.indexOf(value) !== -1) {
+ nextValue = fixedValue?.filter((item) => {
+ return item !== value;
+ });
+ } else {
+ // 之前未选中,增加
+ nextValue = [...fixedValue, value];
+ }
+ // 将 value 重新按 options 排序
+ const sortValue = (v: string[]) => {
+ return items.map((item) => item.value).filter((item) => v.indexOf(item) !== -1);
+ };
+ nextValue = sortValue(nextValue);
+ this.cOnChange(nextValue);
+ } else {
+ // 单选
+ // 取消选中
+ // eslint-disable-next-line no-lonely-if
+ if (fixedValue?.[0] === value) {
+ nextValue = '';
+ } else {
+ // 选中
+ nextValue = value;
+ }
+ this.cOnChange(nextValue, text);
+ }
+ }
+ },
+ },
+});
diff --git a/src/Selector/props.d.ts b/src/Selector/props.d.ts
new file mode 100644
index 000000000..1ccf54c8c
--- /dev/null
+++ b/src/Selector/props.d.ts
@@ -0,0 +1,56 @@
+import { IBaseFormItemPropsWithOutFocus } from '../_base';
+
+export interface ISelectorItem {
+ /**
+ * @description 主文案
+ */
+ text: string;
+ /**
+ * @description 选项值,在同一个 Selector 中唯一
+ */
+
+ value: string;
+ /**
+ * @description 辅助文案
+ */
+
+ subText?: string;
+ /**
+ * @description 单个禁用
+ */
+
+ disabled?: boolean;
+}
+/**
+ * @description 筛选器,可供用户进行单选或者多选。
+ */
+
+export interface ISelectorProps extends IBaseFormItemPropsWithOutFocus
+
+## API
+
+### 属性
+
+| 属性 | 类型 | 必填 | 默认值 | 说明 |
+| -----|-----|-----|-----|----- |
+| controlled | boolean | 否 | false | 是否受控 |
+| value | number | 否 | - | 输入框的值, 表单提交的时候有效 |
+| type | 'number' | 'digit' | 否 | - | 输入框的值, 表单提交的时候有效 |
+| min | number | 否 | - | 最小值 |
+| max | number | 否 | - | 最大值 |
+| step | number | 否 | 1 | 每次加减的值 |
+| inputWidth | string | 否 | - | 输入框宽度 |
+| precision | number | 否 | - | 计算精度,保留几位小数
+
+## API
+### 属性
+
+#### Steps
+| 属性 | 类型 | 必填 | 默认值 | 说明 |
+| -----|-----|-----|-----|----- |
+| index | number | 否 | 0 | 当前步骤, 受控 |
+| direction | `'horizontal'` | `'vertical'` | 否 | "horizontal" | 方向 |
+| className | string | 否 | - | 类名 |
+
+#### StepItem
+
+| 属性 | 类型 | 必填 | 默认值 | 说明 |
+| -----|-----|-----|-----|----- |
+| index | number | 是 | - | 小程序必填,用于标记当前是第几步,必须按顺序递增 |
+| title | `string` | `slot` | 是 | - | 标题 |
+| desc | `string` | `slot` | 否 | - | 补充信息 |
+| fail | boolean | 否 | false | 是否失败步骤 |
+| icon | `string` | `slot` | `string` | 否 | - | 图标,横向和纵向都有各自的默认图标 |
+| activeIcon | `string` | `slot` | `string` | 否 | - | 激活步骤图标,横向和纵向都有各自的默认图标 |
+| failIcon | `string` | `slot` | `string` | 否 | - | 失败步骤图标,横向和纵向都有各自的默认图标 |
+| className | string | 否 | - | 类名 |
+
+### 插槽
+
+#### StepItem
+| 名称 | 说明 |
+| ----|----|
+| title | 标题内容插槽 |
+| desc | 补充内容插槽 |
+| icon | 默认图标插槽 |
+| activeIcon | 激活步骤图标插槽 |
+| failIcon | 失败步骤图标插槽 |
+
+## 样式类
+#### Steps
+
+| 类名 | 说明 |
+| -------------------------- | ---------------- |
+| amd-steps | 整体样式 |
+| amd-steps-horizontal | 整体样式 |
+| amd-steps-vertical | 整体样式 |
+
+#### StepItem
+
+| 类名 | 说明 |
+| -------------------------- | ---------------- |
+| amd-steps-item | 整体样式 |
+| amd-steps-item-horizontal | 整体样式 |
+| amd-steps-item-vertical | 整体样式 |
+| amd-steps-item-line | 步骤条样式 |
+| amd-steps-item-line-fail | 错误步骤条样式 |
+| amd-steps-item-icon | 图标样式 |
+| amd-steps-item-text | 文字区域样式 |
+| amd-steps-item-title | 标题样式 |
+| amd-steps-item-desc | 补充说明样式 |
+
+
\ No newline at end of file
diff --git a/src/Steps/index.ts b/src/Steps/index.ts
new file mode 100644
index 000000000..1698a7480
--- /dev/null
+++ b/src/Steps/index.ts
@@ -0,0 +1,22 @@
+import { StepsDefaultProps } from './props';
+import { context } from './context';
+
+Component({
+ props: StepsDefaultProps,
+ didMount() {
+ this.updateItemData();
+ },
+ didUpdate() {
+ this.updateItemData();
+ },
+ didUnmount() {
+ context.removeGroup(this.props.uid);
+ },
+ methods: {
+ updateItemData() {
+ const { index, direction, uid } = this.props;
+ context.updateItemIndex(uid, index);
+ context.updateItemDirection(uid, direction);
+ },
+ },
+});
diff --git a/src/Steps/props.d.ts b/src/Steps/props.d.ts
new file mode 100644
index 000000000..8706d2c53
--- /dev/null
+++ b/src/Steps/props.d.ts
@@ -0,0 +1,19 @@
+import { IBaseProps } from '../_base';
+/**
+ * @description 步骤条,分步展示当前进展,配合 StepItem 使用
+ */
+
+export interface IStepsProps extends IBaseProps {
+ /**
+ * @description 当前步骤, 受控
+ * @default 0
+ */
+ index?: number;
+ /**
+ * @description 方向
+ * @default "horizontal"
+ */
+
+ direction?: 'horizontal' | 'vertical';
+}
+export declare const StepsDefaultProps: Partial
+
+## API
+
+### 属性
+| 属性 | 类型 | 必填 | 默认值 | 说明 |
+| -----|-----|-----|-----|----- |
+| autoClose | boolean | 否 | false | 点击按钮是是否会自动收起 |
+| disabled | boolean | 否 | false | 是否禁止操作 |
+| left | { text: string, type: 'default' | 'primary' | 'danger'; className: string } [] | 否 | - | 右滑漏出左侧操作区 |
+| right | { text: string, type: 'default' | 'primary' | 'danger'; className: string }[] | 否 | - | 左滑漏出右侧操作区 |
+| className | string | 否 | - | 类名 |
+
+### 事件
+| 事件名 | 说明 | 类型 | 补充 |
+| -----|-----|-----|-----|
+| onLeftButtonTap | 点击左侧按钮,触发回调 | (v: number) => void | 从左往右起,第 n 个按钮 |
+| onRightButtonTap | 点击右侧按钮,触发回调 | (v: number) => void | 从左往右起,第 n 个按钮 |
+
+### 样式类
+| 类名 | 说明 |
+| ----|----|
+| amd-swipe-action| 整体样式 |
+| amd-swipe-action-closeSwipe | 整体样式 |
+| amd-swipe-action-wrap | 整体内容样式 |
+| amd-swipe-action-left | 右侧按钮区域样式 |
+| amd-swipe-action-right | 按钮区域样式 |
+| amd-swipe-action-btn | 按钮样式 |
+| amd-swipe-action-btn-text | 按钮文字样式 |
+| amd-swipe-action-content | 表层区域样式 |
+| amd-swipe-action-item | 表层区域内容样式 |
+
+
\ No newline at end of file
diff --git a/src/SwipeAction/index.ts b/src/SwipeAction/index.ts
new file mode 100644
index 000000000..4040f77e8
--- /dev/null
+++ b/src/SwipeAction/index.ts
@@ -0,0 +1,181 @@
+import { SwipeActionDefaultProps } from './props';
+import { ComponentContext } from '../_util/context';
+
+const swipeIdContext = new ComponentContext();
+
+Component({
+ props: SwipeActionDefaultProps,
+ data: {
+ itemPosition: 0,
+ leftBtn: false,
+ rightBtn: false,
+ prevId: null,
+ },
+ didMount() {
+ this.setBtnWidth();
+ this.swipeActionItemId = (value) => {
+ if (value !== this.$id && this.data.itemPosition !== 0) {
+ this.setData({
+ itemPosition: 0,
+ });
+ }
+ };
+ swipeIdContext.onUpdate(this.swipeActionItemId);
+ },
+ didUnmount() {
+ swipeIdContext.offUpdate(this.swipeActionItemId);
+ },
+ methods: {
+ setBtnWidth() {
+ my.createSelectorQuery()
+ .select(`.amd-swipe-action-right-${this.$id}`)
+ .boundingClientRect()
+ .exec((ret) => {
+ this.rightBtnWidth = (ret && ret[0] && (
+
+## API
+
+### 属性
+
+| 属性 | 类型 | 必填 | 默认值 | 说明 |
+| -----|-----|-----|-----|----- |
+| controlled | boolean | 否 | false | 是否受控模式 |
+| checked | boolean | 否 | - | 是否勾选 |
+| id | string | 否 | - | 表单元素 id |
+| name | string | 否 | - | 表单元素 name |
+| disabled | boolean | 否 | false | 是否禁用 |
+| mode | 'noraml' | 'form' | 否 | normal | 配合From/FormItem组件使用时,需设置为 from |
+| size | 'medium' | 'small' | 'x-small' | 否 | medium | 组件尺寸 |
+| className | string | 否 | - | 类名 |
+
+### 事件
+| 事件名 | 说明 | 类型 |
+| -----|-----|-----|
+| onChange | 点击 switch ,触发此回调 | ( e: boolean ) => void |
+
+
+### 样式类
+| 类名 | 说明 |
+| -----|-----|
+| amd-switch | 整体样式 |
+
+
\ No newline at end of file
diff --git a/src/Switch/index.ts b/src/Switch/index.ts
new file mode 100644
index 000000000..b86632769
--- /dev/null
+++ b/src/Switch/index.ts
@@ -0,0 +1,14 @@
+import { SwitchDefaultProps } from './props';
+import controlled from '../mixins/controlled';
+import formMixin from '../mixins/form';
+
+Component({
+ props: SwitchDefaultProps,
+ mixins: [controlled('checked'), formMixin()],
+ methods: {
+ onChange(v) {
+ const { value } = v.detail;
+ this.cOnChange(value);
+ },
+ },
+});
diff --git a/src/Switch/props.d.ts b/src/Switch/props.d.ts
new file mode 100644
index 000000000..052269821
--- /dev/null
+++ b/src/Switch/props.d.ts
@@ -0,0 +1,22 @@
+import { IBaseFormItemPropsWithOutFocus } from '../_base';
+/**
+ * @description 开关。
+ */
+
+export interface ISwitchProps extends IBaseFormItemPropsWithOutFocus
+
+## API
+
+### 属性
+
+#### Tabs
+| 属性 | 类型 | 必填 | 默认值 | 说明 |
+| -----|-----|-----|-----|----- |
+| type | 'basis' | 'capsule' | 'mixin' | 'title' | 否 | "basis" | 类型,basis(基础),capsule(胶囊),mixin(混合) |
+| index | number | 否 | 0 | 当前激活的索引 |
+| animation | boolean | 否 | false | 是否有过渡动画 |
+| swipeable | boolean | 否 | false | 是否支持手势切换 |
+| sticky | boolean | 否 | false | 是否支持吸顶 |
+| plus | string | slot | 否 | - | 右上角操作按钮,自定义节点 |
+| title | slot-scope | 否 | - | 自定义 tab 标题样式,仅在 type 为 basis 时可用 |
+| className | string | 否 | - | 类名 |
+
+#### TabItem
+| 属性 | 类型 | 必填 | 默认值 | 说明 |
+| -----|-----|-----|-----|----- |
+| tab | {title: string; subTitle?: string; badge?: number; disabled?: boolean}[]| 是 | - | 每一项 tab 内容 |
+| className | string | 否 | - | 类名 |
+
+### 事件
+
+#### Tabs
+| 事件名 | 说明 | 类型 |
+| -----|-----|-----|
+| onChange | 面板切换时候,触发回调 |(index: number) => void|
+
+### 插槽
+#### Tabs
+| 名称 | 说明 |
+| ----|----|
+| plus | 表单项额外内容 |
+
+### 作用域插槽
+#### Tabs
+| 名称 | 说明 |
+| ----|----|
+| title | 自定义 tab 标题样式 |
+
+### 样式类
+#### Tabs
+| 类名 | 说明 |
+| -----|-----|
+| amd-tabs | 整体样式 |
+| amd-tabs-bar | 上方标题区域样式 |
+| `amd-tabs-bar-active` | `上方标题区域激活时样式` |
+| `amd-tabs-bar-active:after` | `上方标题区域激活时 indicator 样式` |
+| amd-tabs-bar | 上方标题区域样式 |
+| amd-tabs-bar-sticky | 吸顶状态样式 |
+| amd-tabs-bar-plus | 右上角标签样式 |
+| amd-tabs-bar-fade | 两侧渐淡效果样式 |
+| amd-tabs-bar-fade-left | 左侧渐淡效果样式 |
+| amd-tabs-bar-fade-right | 左侧渐淡效果样式 |
+| amd-tabs-bar-scroll-view | 内部ScrollView组件样式 |
+| amd-tabs-bar-wrap| 每个标题的样式 |
+| amd-tabs-bar-item | 每个标题的样式 |
+| amd-tabs-bar-basis | type 为 basis 时,每个标题的样式 |
+| amd-tabs-bar-capsule | type 为 capsule 时,每个标题的样式 |
+| amd-tabs-bar-capsule-title | type 为 capsule 时,每个标题内部文字的样式 |
+| amd-tabs-bar-capsule-badge | type 为 capsule 时,每个标题内部 badge 的样式 |
+| amd-tabs-bar-mixin| 内type 为 mixin 时,每个标题的样式 |
+| amd-tabs-bar-mixin-title| 内type 为 mixin 时,每个标题的文字样式 |
+| amd-tabs-bar-mixin-subtitle| 内type 为 mixin 时,每个标题的副标题样式 |
+| amd-tabs-bar-disabled | 禁用态选项卡样式 |
+
+#### TabItem
+| 类名 | 说明 |
+| -----|-----|
+| amd-tabs-item | 整体样式 |
+| amd-tabs-item-pane | 整体样式 |
+
+
+
diff --git a/src/Tabs/index.ts b/src/Tabs/index.ts
new file mode 100644
index 000000000..f52ce42ea
--- /dev/null
+++ b/src/Tabs/index.ts
@@ -0,0 +1,252 @@
+/* eslint-disable @typescript-eslint/no-explicit-any */
+import { TabsDefaultProps } from './props';
+import { getTabArray, componentContext } from './context';
+import { log } from '../_util/console';
+import { objectValues } from '../_util/tools';
+import { compareVersion } from '../_util/compareVersion';
+
+const canSwipeable = my.canIUse('swiper.disable-touch');
+const component2 = my.canIUse('component2');
+let hasTab = false;
+
+const isShouldNotCalHeight = component2 && compareVersion(my.SDKVersion, '2.6.4') >= 0;
+
+const isForceUpdate = compareVersion(my.SDKVersion, '2.6.4') >= 0 && compareVersion(my.SDKVersion, '2.7.5') === -1;
+
+const isMoreThan275 = compareVersion(my.SDKVersion, '2.7.5') >= 0;
+
+Component({
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
+ props: TabsDefaultProps,
+ data: {
+ _tabs: {},
+ _leftFade: true,
+ _rightFade: true,
+ _tabsViewportWidth: 0,
+ _scrollLeft: 0,
+ _swipeable: false,
+ _swipeableIndex: 0,
+ _swipeableAnimation: false,
+ _tabContentHeight: 0,
+ currentIndex: 0,
+ component2,
+ _forceRefreshSwiper: 0,
+ _isForceUpdate: isForceUpdate,
+ },
+ didMount() {
+ if (hasTab) {
+ log.error('Tabs', '目前仅支持在一个页面中使用一个 Tabs 组件。');
+ }
+ hasTab = true;
+ componentContext.onUpdate((value) => {
+ this.setData({
+ _tabs: value,
+ });
+ });
+ const { index, animation } = this.props;
+ this.setData({
+ _tabs: objectValues(getTabArray),
+ currentIndex: index,
+ });
+ this._getTabsWidth();
+ this._useSwipeable(this.props.swipeable);
+ if (typeof index !== 'number') {
+ // 如果当前索引值的类型不对给警告提示
+ log.error('Tabs', `当前激活的索引值类型非 number 类型,修改当前 index 的 ${typeof index} 类型,以保证展示的正确性。`);
+ } else {
+ my.createSelectorQuery()
+ .select(`#amd-tabs-bar-item-${index}`)
+ .boundingClientRect()
+ .exec((ret) => {
+ const { _tabsViewportWidth } = this.data;
+ if (!(
+
+## API
+
+### 属性
+| 属性 | 类型 | 必填 | 默认值 | 说明 |
+| -----|-----|-----|-----|----- |
+| type | 'outline' | 'fill' | 'fill-light' | 否 | "fill" | 类型 |
+| color | 'primary' | 'success' | 'warn' | 'danger' | 否 | "primary" | 标签颜色, 内建 primary(蓝), success(绿), warn(黄), danger(红) |
+| icon | string| 否 | - | 图标 |
+| className | string | 否 | - | 类名 |
+
+### 插槽
+| 名称 | 说明 |
+| ----|----|
+| icon | 图标插槽 |
+
+### 样式类
+| 类名 | 说明 |
+| ----|----|
+| amd-tag | 整体样式 |
+| amd-tag-outline | 整体样式 |
+| amd-tag-fill | 整体样式 |
+| amd-tag-fill-light | 整体样式 |
+| amd-tag-primary | 整体样式 |
+| amd-tag-success | 整体样式 |
+| amd-tag-warn | 整体样式 |
+| amd-tag-danger | 整体样式 |
+| amd-tag-icon-container | 图标区域样式 |
+| amd-tag-content | 默认插槽内容样式 |
+
+
+
+
\ No newline at end of file
diff --git a/src/Tag/index.ts b/src/Tag/index.ts
new file mode 100644
index 000000000..3d8a9839e
--- /dev/null
+++ b/src/Tag/index.ts
@@ -0,0 +1,5 @@
+import { TagDefaultProps } from './props';
+
+Component({
+ props: TagDefaultProps,
+});
diff --git a/src/Tag/props.d.ts b/src/Tag/props.d.ts
new file mode 100644
index 000000000..a2be28414
--- /dev/null
+++ b/src/Tag/props.d.ts
@@ -0,0 +1,24 @@
+import { IBaseProps, IconType } from '../_base';
+/**
+ * @description 标签,突出利益点、以及属性说明。
+ */
+
+export interface ITagProps extends IBaseProps {
+ /**
+ * @description 类型
+ * @default "fill"
+ */
+ type?: 'outline' | 'fill' | 'fill-light';
+ /**
+ * @description 标签颜色, 内建 primary(蓝), success(绿), warn(黄), danger(红)
+ * @default "primary"
+ */
+
+ color?: 'image' | 'primary' | 'success' | 'warn' | 'danger';
+ /**
+ * @description 图标
+ */
+
+ icon?: IconType;
+}
+export declare const TagDefaultProps: Partial
+
+## API
+
+### 属性
+
+| 属性 | 类型 | 必填 | 默认值 | 说明 |
+| -----|-----|-----|-----|----- |
+| fixed | boolean | 否 | false | 是否固定在底部 |
+| hasCheckbox | boolean | 否 | true | 是否需要勾选框 |
+| disabled | boolean | 否 | false | 是否禁用 |
+| mainButtonText | string | 是 | '同意' | 主按钮文本 |
+| addonButtonText | string | 否 | | 辅助按钮文本 |
+| className | string | 否 | - | 类名 |
+
+
+### 事件
+| 事件名 | 说明 | 类型 |
+| -----|-----|-----|
+| onChange | 点击 checkbox ,触发此回调 | ( v : boolean ) => void |
+| onMainBtnTap | 点击主按钮,触发此回调 | () => void |
+| onSubBtnTap | 点击辅助按钮,触发此回调 | () => void |
+### 插槽
+| 名称 | 说明 |
+| ----|----|
+| 默认插槽 | 带协议的文案, 比如 同意《用户授权协议》 |
+
+### 样式类
+| 类名 | 说明 |
+| -----|-----|
+| amd-terms | 整体样式 |
+| amd-terms-fixed | 整体样式 |
+| amd-terms-term | term 区域样式 |
+| amd-terms-term-checkbox | checkbox 组件样式 |
+| amd-terms-content | 内容区域样式 |
+
+
\ No newline at end of file
diff --git a/src/Terms/index.ts b/src/Terms/index.ts
new file mode 100644
index 000000000..9b447516d
--- /dev/null
+++ b/src/Terms/index.ts
@@ -0,0 +1,41 @@
+import { TermsDefaultProps } from './props';
+
+Component({
+ props: TermsDefaultProps,
+ data: {
+ disabled: false,
+ },
+ didMount() {
+ this.updateDisabled(false);
+ },
+ methods: {
+ updateDisabled(v) {
+ if (this.props.hasCheckbox) {
+ this.setData({
+ disabled: !v,
+ });
+ }
+ },
+ onChange(v) {
+ const { onChange, _getCurrentField } = this.props;
+ this.updateDisabled(v);
+ if (onChange) {
+ if (_getCurrentField) {
+ return onChange.call(this.props, v);
+ } else {
+ return onChange(v);
+ }
+ }
+ },
+ onMainBtnTap() {
+ if (typeof this.props.onMainBtnTap === 'function') {
+ this.props.onMainBtnTap();
+ }
+ },
+ onSubBtnTap() {
+ if (typeof this.props.onSubBtnTap === 'function') {
+ this.props.onSubBtnTap();
+ }
+ },
+ },
+});
diff --git a/src/Terms/props.d.ts b/src/Terms/props.d.ts
new file mode 100644
index 000000000..c5ceb1e63
--- /dev/null
+++ b/src/Terms/props.d.ts
@@ -0,0 +1,33 @@
+
+import { IBaseFormItemPropsWithOutFocus } from '../_base';
+/**
+ * @description 协议。
+ */
+
+export interface ITermsProps extends IBaseFormItemPropsWithOutFocus
+
+## API
+
+### 属性
+| 属性 | 类型 | 必填 | 默认值 | 说明 |
+| -----|-----|-----|-----|----- |
+| image | string | 否 | - | 需要使用的图片 url |
+| title | string | 是 | - | 提示文字 |
+| arrowPosition | 'top-left' | 'top-center' | 'top-right' | 'left' | 'right' | 'bottom-left' | 'bottom-center' | 'bottom-right' | 否 | - | 箭头位置,不传时表示没有箭头 |
+| buttonText | string | 否 | - | 按钮文字 |
+| showClose | boolean | 否 | false | 是否有关闭按钮 |
+| buttonPosition | 'right' | 'bottom' | 否 | 'right' | 文字按钮的位置,默认为右边 |
+| className | string | 否 | - | 类名 |
+
+### 事件
+| 事件名 | 说明 | 类型 |
+| -----|-----|-----|
+| onButtonTap | 点击按钮,触发回调 | () => void |
+
+### 样式类
+| 类名 | 说明 |
+| ----|----|
+| amd-tips | 整体样式 |
+| amd-tips-wrap | 内部样式 |
+| amd-tips-wrap-pseudo | 表面内容区域样式 |
+| amd-tips-wrap-pseudo-content | 表面内容区域样式 |
+| amd-tips-arrow | 箭头样式 |
+| amd-tips-wrap-real | 真实内容区域样式 |
+
+
+
\ No newline at end of file
diff --git a/src/Tips/index.ts b/src/Tips/index.ts
new file mode 100644
index 000000000..24441ebbd
--- /dev/null
+++ b/src/Tips/index.ts
@@ -0,0 +1,34 @@
+import { TipsDefaultProps } from './props';
+import { log } from '../_util/console';
+
+Component({
+ props: TipsDefaultProps,
+ data: {
+ _show: true,
+ },
+ didMount() {
+ this.showError();
+ },
+ didUpdate() {
+ this.showError();
+ },
+ methods: {
+ showError() {
+ const { title } = this.props;
+ if (!title) {
+ log.warn('Tips', '缺少 title 属性。');
+ }
+ },
+ onButtonTap() {
+ const { onButtonTap } = this.props;
+ if (onButtonTap) {
+ return onButtonTap();
+ }
+ },
+ onHideTips(t) {
+ this.setData({
+ _show: t,
+ });
+ },
+ },
+});
diff --git a/src/Tips/props.d.ts b/src/Tips/props.d.ts
new file mode 100644
index 000000000..f3a0d1ac2
--- /dev/null
+++ b/src/Tips/props.d.ts
@@ -0,0 +1,42 @@
+import { IBaseProps } from '../_base';
+/**
+ * @description 向导提示。
+ */
+
+export interface ITipsProps extends IBaseProps {
+ /**
+ * @description 需要使用的图片 url
+ */
+ image?: string;
+ /**
+ * @description 提示文字
+ */
+ title: string;
+ /**
+ * @description 箭头位置,不传时表示没有箭头
+ */
+ arrowPosition?: 'top-left' | 'top-center' | 'top-right' | 'left' | 'right' | 'bottom-left' | 'bottom-center' | 'bottom-right';
+ /**
+ * @description 按钮文字
+ */
+ buttonText?: string;
+ /**
+ * @description 是否有关闭按钮
+ * @default false
+ */
+ showClose?: boolean;
+ /**
+ * @description 关闭按钮位置
+ * @default right
+ */
+ buttonPosition?: 'right' | 'bottom';
+ /**
+ * @description 点击关闭的回调
+ */
+ onClose?: () => void;
+ /**
+ * @description 点击按钮回调
+ */
+ onButtonTap?: () => void;
+}
+export declare const TipsDefaultProps: Partial
+
+## API
+
+### 属性
+
+#### VTabs
+
+| 属性 | 类型 | 必填 | 默认值 | 说明 |
+| -----|-----|-----|-----|----- |
+| index | number | 否 | 0 | 当前激活的索引 |
+| className | string | 否 | - | 类名 |
+
+### VTabsItem
+| 属性 | 类型 | 必填 | 默认值 | 说明 |
+| -----|-----|-----|-----|----- |
+| tab | {title: string; disabled?: boolean, badge?: {type: 'dot' | 'number' | 'text', text: number | string }}[] | 是 | - | 每一项 tab 内容 |
+| className | string | 否 | - | 类名 |
+
+### 事件
+
+#### VTabs
+| 事件名 | 说明 | 类型 |
+| -----|-----|-----|
+| onChange | 面板切换时候,触发回调 |(index: number) => void|
+
+### 插槽
+### VTab
+| 名称 | 说明 | 类型 |
+| title | 自定义标题内标题样式 | 作用域插槽 |
+| icon | 自定义标题内图标样式 | 作用域插槽 |
+
+### 样式类
+
+
+#### VTabs
+| 类名 | 说明 |
+| -----|-----|
+| amd-vtabs | 整体样式 |
+| amd-vtabs-bar | 左侧标题区域样式 |
+| amd-vtabs-bar-scroll-view | 左侧标题区域样式 |
+| amd-vtabs-bar-item-wrap | 左侧标题样式 |
+| amd-vtabs-bar-item | 左侧标题样式 |
+| `amd-vtabs-bar-item-active` | `标题激活状态样式` |
+| amd-vtabs-bar-item-disabled | 标题禁用状态样式 |
+| amd-vtabs-bar-item-title | 标题内 title 的样式 |
+| amd-vtabs-bar-item-count | 标题内 badge 的样式 |
+| amd-vtabs-bar-item-icon | 标题内 icon 插槽的样式 |
+| amd-vtabs-content | 右侧内容区域样式 |
+| amd-vtabs-content-slides| 右侧单个内容区域样式 |
+
+
+#### VTabItem
+| 类名 | 说明 |
+| -----|-----|
+| amd-vtabs-item | 整体样式 |
+
+
+
diff --git a/src/VTabs/index.ts b/src/VTabs/index.ts
new file mode 100644
index 000000000..57539d4a7
--- /dev/null
+++ b/src/VTabs/index.ts
@@ -0,0 +1,188 @@
+import { VTabsDefaultProps } from './props';
+import { getTabArray, componentContext } from './context';
+import { objectValues } from '../_util/tools';
+
+Component({
+ props: VTabsDefaultProps,
+ data: {
+ tabTop: 0,
+ _tabs: {},
+ _index: 0,
+ wrapScrollTop: null,
+ wrapScrollHeight: null,
+ },
+ async didMount() {
+ this.isScrolling = false;
+ this.onlyChangeTab = false;
+ this.timerId = null;
+ await this.calcHeight();
+
+ componentContext.onUpdate((value) => {
+ this.setData({
+ _tabs: value,
+ });
+ });
+ const { index } = this.props;
+ this.setData({
+ _tabs: objectValues(getTabArray),
+ wrapScrollHeight: this.scrollWrapHeight,
+ });
+ // 初次加载时的情况
+ if (this.data._index !== index) {
+ this.onChange(index);
+ this.setData({
+ _index: index,
+ });
+ }
+ },
+ didUpdate(prevProps, prevData) {
+ const { index } = this.props;
+ const { _index } = this.data;
+ if (prevProps.index !== index && prevData._index === _index) {
+ this.onChange(index);
+ }
+ },
+ didUnmount() {
+ componentContext.clearEvent();
+ if (this.timerId) {
+ clearTimeout(this.timerId);
+ this.timerId = null;
+ }
+ },
+ methods: {
+ async onWrapTouchStart() {
+ await this.calcHeight();
+ },
+ async calcHeight() {
+ this.anchorMap = {};
+ this.indexTop = {};
+ this.indexMap = {};
+
+ this.wrapHeight = await new Promise((resolve) => {
+ my.createSelectorQuery()
+ .select(`#amd-vtabs-content-slides-${this.$id}`)
+ .boundingClientRect()
+ .exec((ret) => {
+ resolve((