diff --git a/docs/examples-docs/goods-action.md b/docs/examples-docs/goods-action.md
new file mode 100644
index 000000000..328a48951
--- /dev/null
+++ b/docs/examples-docs/goods-action.md
@@ -0,0 +1,88 @@
+
+
+
+
+## GoodsAction 商品操作组件
+
+### 使用指南
+``` javascript
+import {
+ GoodsAction,
+ GoodsActionBigBtn,
+ GoodsActionMiniBtn
+} from 'vant';
+
+Vue.component(GoodsAction.name, GoodsAction);
+Vue.component(GoodsActionBigBtn.name, GoodsActionBigBtn);
+Vue.component(GoodsActionMiniBtn.name, GoodsActionMiniBtn);
+```
+
+### 代码演示
+
+:::demo
+```html
+
+
+ 客服
+
+
+ 购物车
+
+
+ 加入购物车
+
+
+ 立即购买
+
+
+```
+
+```javascript
+export default {
+ methods: {
+ onClickMiniBtn() {
+ Toast('点击图标');
+ },
+ onClickBigBtn() {
+ Toast('点击按钮');
+ }
+ }
+}
+```
+:::
+
+### API
+
+#### GoodsActionMiniBtn
+
+| 参数 | 说明 | 类型 | 默认值 | 可选值 |
+|-----------|-----------|-----------|-------------|-------------|
+| icon | 图标 | `String` | - | Icon 组件支持的所有图标 |
+| iconClass | 图标额外类名 | `String` | `''` | - |
+| url | 跳转链接 | `String` | `javascript:;` | - |
+
+#### GoodsActionBigBtn
+
+| 参数 | 说明 | 类型 | 默认值 | 可选值 |
+|-----------|-----------|-----------|-------------|-------------|
+| url | 跳转链接 | `String` | `javascript:;` | - |
+| primary | 是否主行动按钮,主行动按钮默认为红色 | `Boolean` | `false` | - |
diff --git a/docs/src/doc.config.js b/docs/src/doc.config.js
index 53912fd7d..d73a34ac3 100644
--- a/docs/src/doc.config.js
+++ b/docs/src/doc.config.js
@@ -189,6 +189,10 @@ module.exports = {
"path": "/express-way",
"title": "ExpressWay 配送方式"
},
+ {
+ "path": "/goods-action",
+ "title": "GoodsAction 商品操作"
+ },
{
"path": "/invalid-goods",
"title": "InvalidGoods 不可用商品列表"
diff --git a/packages/goods-action-big-btn/index.vue b/packages/goods-action-big-btn/index.vue
new file mode 100644
index 000000000..58bf06b07
--- /dev/null
+++ b/packages/goods-action-big-btn/index.vue
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
diff --git a/packages/goods-action-mini-btn/index.vue b/packages/goods-action-mini-btn/index.vue
new file mode 100644
index 000000000..4f91fd443
--- /dev/null
+++ b/packages/goods-action-mini-btn/index.vue
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
diff --git a/packages/goods-action/index.vue b/packages/goods-action/index.vue
new file mode 100644
index 000000000..8ab06d10a
--- /dev/null
+++ b/packages/goods-action/index.vue
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
diff --git a/packages/index.js b/packages/index.js
index bd8f2a69a..56cff9ada 100644
--- a/packages/index.js
+++ b/packages/index.js
@@ -13,6 +13,9 @@ import DatetimePicker from './datetime-picker';
import Dialog from './dialog';
import ExpressWay from './express-way';
import Field from './field';
+import GoodsAction from './goods-action';
+import GoodsActionBigBtn from './goods-action-big-btn';
+import GoodsActionMiniBtn from './goods-action-mini-btn';
import Icon from './icon';
import ImagePreview from './image-preview';
import InvalidGoods from './invalid-goods';
@@ -59,6 +62,9 @@ const components = [
DatetimePicker,
ExpressWay,
Field,
+ GoodsAction,
+ GoodsActionBigBtn,
+ GoodsActionMiniBtn,
Icon,
InvalidGoods,
Loading,
@@ -117,6 +123,9 @@ export {
Dialog,
ExpressWay,
Field,
+ GoodsAction,
+ GoodsActionBigBtn,
+ GoodsActionMiniBtn,
Icon,
ImagePreview,
InvalidGoods,
diff --git a/packages/vant-css/src/button.css b/packages/vant-css/src/button.css
index a3fd5f146..81ce54d89 100644
--- a/packages/vant-css/src/button.css
+++ b/packages/vant-css/src/button.css
@@ -103,6 +103,7 @@
line-height: 50px;
border: 0;
border-radius: 0;
+ font-size: 16px;
color: $bottom-action-button-default-color;
background-color: $bottom-action-button-default-background-color;
diff --git a/packages/vant-css/src/common/var.css b/packages/vant-css/src/common/var.css
index 2edb3809e..61de30818 100644
--- a/packages/vant-css/src/common/var.css
+++ b/packages/vant-css/src/common/var.css
@@ -13,6 +13,7 @@ $c-yellow-light: #fcff00;
$c-orange: #f60;
$c-orange-dark: #f15a0c;
$c-blue: #38f;
+$c-active: #e8e8e8;
$c-background: #f8f8f8;
diff --git a/packages/vant-css/src/goods-action.css b/packages/vant-css/src/goods-action.css
new file mode 100644
index 000000000..ca163bbdd
--- /dev/null
+++ b/packages/vant-css/src/goods-action.css
@@ -0,0 +1,40 @@
+@import './common/var.css';
+
+.van-goods-action {
+ left: 0;
+ right: 0;
+ bottom: 0;
+ display: flex;
+ position: fixed;
+
+ &__mini-btn {
+ color: #666;
+ display: flex;
+ height: 50px;
+ font-size: 10px;
+ min-width: 15%;
+ text-align: center;
+ background-color: #fff;
+ flex-direction: column;
+ justify-content: center;
+
+ &::after {
+ @mixin border-retina (top, right), #cacaca;
+ }
+
+ &:last-child {
+ &::after {
+ border-right-width: 0;
+ }
+ }
+
+ &:active {
+ background-color: $c-active;
+ }
+ }
+
+ &__mini-btn-icon {
+ font-size: 20px;
+ margin-bottom: 5px;
+ }
+}
diff --git a/packages/vant-css/src/index.css b/packages/vant-css/src/index.css
index 8731cbdb4..68b67d59c 100644
--- a/packages/vant-css/src/index.css
+++ b/packages/vant-css/src/index.css
@@ -36,3 +36,4 @@
@import './pay-order.css';
@import './order-goods.css';
@import './invalid-goods.css';
+@import './goods-action.css';
diff --git a/packages/vant-css/src/invalid-goods.css b/packages/vant-css/src/invalid-goods.css
index 43c0480ce..83317b9d3 100644
--- a/packages/vant-css/src/invalid-goods.css
+++ b/packages/vant-css/src/invalid-goods.css
@@ -1,4 +1,4 @@
-@import "./mixins/ellipsis";
+@import "./mixins/ellipsis.css";
$van-invalid-goods-photo-size: 90px;
diff --git a/packages/vant-css/src/order-goods.css b/packages/vant-css/src/order-goods.css
index fe80bfb3f..b40500e16 100644
--- a/packages/vant-css/src/order-goods.css
+++ b/packages/vant-css/src/order-goods.css
@@ -1,4 +1,4 @@
-@import "./mixins/border_retina";
+@import "./mixins/border_retina.css";
.van-order-goods {
background-color: #fff;
diff --git a/test/unit/components/goods-action.vue b/test/unit/components/goods-action.vue
new file mode 100644
index 000000000..bf867989c
--- /dev/null
+++ b/test/unit/components/goods-action.vue
@@ -0,0 +1,37 @@
+
+
+
+ 客服
+
+
+ 购物车
+
+
+ 加入购物车
+
+
+ 立即购买
+
+
+
+
+
diff --git a/test/unit/specs/goods-action.spec.js b/test/unit/specs/goods-action.spec.js
new file mode 100644
index 000000000..e86b69a08
--- /dev/null
+++ b/test/unit/specs/goods-action.spec.js
@@ -0,0 +1,47 @@
+import GoodsAction from '../components/goods-action';
+import GoodsActionBigBtn from 'packages/goods-action-big-btn';
+import GoodsActionMiniBtn from 'packages/goods-action-mini-btn';
+import { mount } from 'avoriaz';
+import { DOMChecker } from '../utils';
+
+describe('GoodsAction', () => {
+ let wrapper;
+
+ afterEach(() => {
+ wrapper && wrapper.destroy();
+ });
+
+ it('create a GoodsAction', () => {
+ wrapper = mount(GoodsAction, {});
+
+ DOMChecker(wrapper, {
+ count: {
+ '.van-goods-action__mini-btn': 2,
+ '.van-goods-action__big-btn': 2,
+ '.van-icon-chat': 1
+ }
+ });
+ });
+
+ it('click GoodsActionBigBtn', () => {
+ wrapper = mount(GoodsActionBigBtn, {});
+
+ const submitSpyFunc = sinon.spy();
+ wrapper.vm.$on('click', submitSpyFunc);
+ wrapper.trigger('click');
+ expect(submitSpyFunc.calledOnce).to.be.true;
+ });
+
+ it('click GoodsActionMiniBtn', () => {
+ wrapper = mount(GoodsActionMiniBtn, {
+ propsData: {
+ icon: 'card'
+ }
+ });
+
+ const submitSpyFunc = sinon.spy();
+ wrapper.vm.$on('click', submitSpyFunc);
+ wrapper.trigger('click');
+ expect(submitSpyFunc.calledOnce).to.be.true;
+ });
+});