Merge pull request #79 from eleme/feat/next-steps

Add Steps component
This commit is contained in:
FuryBean 2016-08-22 14:30:06 +08:00 committed by GitHub
commit d837afd7ce
14 changed files with 659 additions and 7 deletions

View File

@ -154,5 +154,11 @@
],
"rate": [
"./packages/rate/index.js"
],
"steps": [
"./packages/steps/index.js"
],
"step": [
"./packages/step/index.js"
]
}

183
examples/docs/steps.md Normal file
View File

@ -0,0 +1,183 @@
## 基础用法
简单的步骤条。
### 定宽步骤条
<el-steps :space="100" :active="active" finish-status="success">
<el-step title="步骤 1"></el-step>
<el-step title="步骤 2"></el-step>
<el-step title="步骤 3"></el-step>
</el-steps>
<el-button @click.native="next">下一步</el-button>
<script>
export default {
data() {
return {
active: 0
};
},
methods: {
next() {
if (this.active++ > 2) this.active = 0;
}
}
}
</script>
```html
<el-steps :space="100" :active="active" finish-status="success">
<el-step title="步骤 1"></el-step>
<el-step title="步骤 2"></el-step>
<el-step title="步骤 3"></el-step>
</el-steps>
<el-button @click.native="next">下一步</el-button>
<script>
export default {
data() {
return {
active: 0
};
},
methods: {
next() {
if (this.active++ > 2) this.active = 0;
}
}
}
</script>
```
### 自适应步骤条
<el-steps :active="1">
<el-step title="步骤 1"></el-step>
<el-step title="步骤 2"></el-step>
<el-step title="步骤 3"></el-step>
</el-steps>
```html
<el-steps :active="1">
<el-step title="步骤 1"></el-step>
<el-step title="步骤 2"></el-step>
<el-step title="步骤 3"></el-step>
</el-steps>
```
## 含状态步骤
每一步骤显示出该步骤的状态。
<el-steps :space="100" :active="1" finish-status="success">
<el-step title="已完成"></el-step>
<el-step title="进行中"></el-step>
<el-step title="步骤 3"></el-step>
</el-steps>
```html
<el-steps :space="100" :active="1" finish-status="success">
<el-step title="已完成"></el-step>
<el-step title="进行中"></el-step>
<el-step title="步骤 3"></el-step>
</el-steps>
```
## 有描述的步骤条
每个步骤有其对应的步骤状态描述。
<el-steps :space="200" :active="1">
<el-step title="步骤 1" description="这是一段很长很长很长的描述性文字"></el-step>
<el-step title="步骤 2" description="这是一段很长很长很长的描述性文字"></el-step>
<el-step title="步骤 3" description="这是一段很长很长很长的描述性文字"></el-step>
</el-steps>
```html
<el-steps :space="200" :active="1">
<el-step title="步骤 1" description="这是一段很长很长很长的描述性文字"></el-step>
<el-step title="步骤 2" description="这是一段很长很长很长的描述性文字"></el-step>
<el-step title="步骤 3" description="这是一段很长很长很长的描述性文字"></el-step>
</el-steps>
```
## 带图标的步骤条
步骤条内可以启用各种自定义的图标。
<el-steps :space="100" :active="1">
<el-step title="步骤 1" icon="edit"></el-step>
<el-step title="步骤 2" icon="upload"></el-step>
<el-step title="步骤 3" icon="picture"></el-step>
</el-steps>
```html
<el-steps :space="100" :active="1">
<el-step title="步骤 1" icon="edit"></el-step>
<el-step title="步骤 2" icon="upload"></el-step>
<el-step title="步骤 3" icon="picture"></el-step>
</el-steps>
```
## 竖式步骤条
竖直方向的步骤条。
<el-steps :space="100" direction="vertical" :active="1">
<el-step title="步骤 1"></el-step>
<el-step title="步骤 2"></el-step>
<el-step title="步骤 3"></el-step>
</el-steps>
```html
<el-steps :space="100" direction="vertical" :active="1">
<el-step title="步骤 1"></el-step>
<el-step title="步骤 2"></el-step>
<el-step title="步骤 3"></el-step>
</el-steps>
```
## 步骤错误提示
每一步骤显示出该步骤的状态。
<el-steps :space="100" :active="1" finish-status="success" process-status="error">
<el-step title="已完成"></el-step>
<el-step title="审核失败"></el-step>
<el-step title="步骤 3"></el-step>
</el-steps>
```html
<el-steps :space="100" :active="1" finish-status="success" process-status="error">
<el-step title="已完成"></el-step>
<el-step title="审核失败"></el-step>
<el-step title="步骤 3"></el-step>
</el-steps>
```
## API
### Steps.props
| 参数 | 说明 | 类型 | 可选值 | 默认值 |
|---------- |-------- |---------- |------------- |-------- |
| space | 每个 step 的间距,不填写将自适应间距 | Number | | |
| direction | 显示方向 | String | `vertical` `horizontal` | `horizontal` |
| active | 设置当前激活步骤 | Number | | 0 |
| process-status | 设置当前步骤的状态 | String | `wait` `process` `finish` `error` `success` | `process` |
| finish-status | 设置结束步骤的状态 | String | `wait` `process` `finish` `error` `success` | `finish` |
### Step.props
| 参数 | 说明 | 类型 | 可选值 | 默认值 |
|---------- |-------- |---------- |------------- |-------- |
| title | 标题 | String | | |
| description | 描述性文字 | String | | |
| icon | 图标 | Element Icon 提供的图标,如果要使用自定义图标可以通过 slot 方式写入 | String | |
### Step.slot
| name | 说明 |
|----|----|
| icon | 图标 |
| title | 标题 |
| description | 描述性文字 |

View File

@ -205,6 +205,12 @@
"path": "/dropdown",
"name": "下拉菜单 (dropdown)",
"title": "Dropdown 下拉菜单"
},
{
"path": "/steps",
"name": "步骤条 (steps)",
"title": "Steps 步骤",
"description": "引导用户按照流程完成任务的分步导航条,可根据实际应用场景设定步骤,步骤不得少于 2 步。"
}
]
},

7
packages/step/index.js Normal file
View File

@ -0,0 +1,7 @@
const Step = require('../steps/src/step');
Step.install = function(Vue) {
Vue.component(Step.name, Step);
};
module.exports = Step;

View File

@ -0,0 +1,31 @@
var cooking = require('cooking');
var path = require('path');
cooking.set({
entry: {
index: path.join(__dirname, 'index.js')
},
dist: path.join(__dirname, 'lib'),
template: false,
format: 'umd',
moduleName: 'ElSteps',
extractCSS: 'style.css',
extends: ['vue', 'saladcss']
});
cooking.add('resolve.alias', {
'main': path.join(__dirname, '../../src'),
'packages': path.join(__dirname, '../../packages')
});
cooking.add('externals', {
vue: {
root: 'Vue',
commonjs: 'vue',
commonjs2: 'vue',
amd: 'vue'
}
});
module.exports = cooking.resolve();

7
packages/steps/index.js Normal file
View File

@ -0,0 +1,7 @@
const Steps = require('./src/steps');
Steps.install = function(Vue) {
Vue.component(Steps.name, Steps);
};
module.exports = Steps;

View File

@ -0,0 +1,15 @@
{
"name": "el-steps",
"version": "0.0.0",
"description": "A steps component for Vue.js.",
"keywords": [
"element",
"vue",
"component"
],
"main": "./lib/index.js",
"repository": "https://github.com/element-component/element/tree/master/packages/steps",
"author": "elemefe",
"license": "MIT",
"dependencies": {}
}

124
packages/steps/src/step.vue Normal file
View File

@ -0,0 +1,124 @@
<template>
<div
class="el-step"
:style="style"
:class="['is-' + $parent.direction]">
<div
class="el-step__head"
:class="['is-' + currentStatus, { 'is-text': !icon }]">
<div
class="el-step__line"
:class="['is-' + $parent.direction,{ 'is-icon': icon }]">
<i class="el-step__line-inner" :style="lineStyle"></i>
</div>
<slot
v-if="currentStatus !== 'success' && currentStatus !== 'error'"
name="icon">
<i v-if="icon" :class="['el-step__icon', 'el-icon-' + icon]"></i>
<div v-else>{{ index + 1 }}</div>
</slot>
<i
v-else
class="el-step__icon"
:class="['el-icon-' + (currentStatus === 'success' ? 'check' : 'close')]">
</i>
</div>
<div
class="el-step__main"
:style="{ marginLeft: mainOffset }">
<div
class="el-step__title"
ref="title"
:class="['is-' + currentStatus]">
<slot name="title">{{ title }}</slot>
</div>
<div
class="el-step__description"
:class="['is-' + currentStatus]">
<slot name="description">{{ description }}</slot>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'el-step',
props: {
title: String,
icon: String,
description: String,
status: {
type: String,
default: 'wait'
}
},
data() {
return {
index: -1,
style: { width: 0, height: 0 },
lineStyle: { width: 0, height: 0 },
mainOffset: 0,
currentStatus: this.status
};
},
created() {
this.$parent.steps.push(this);
},
methods: {
updateStatus(val) {
const prevChild = this.$parent.$children[this.index - 1];
if (val > this.index) {
this.currentStatus = this.$parent.finishStatus;
} else if (val === this.index) {
this.currentStatus = this.$parent.processStatus;
} else {
this.currentStatus = 'wait';
}
if (prevChild) prevChild.calcProgress(this.currentStatus);
},
calcProgress(status) {
let step = 100;
this.lineStyle.transitionDelay = 150 * this.index + 'ms';
if (status === this.$parent.processStatus) {
step = 50;
} else if (status === 'wait') {
step = 0;
this.lineStyle.transitionDelay = (-150 * this.index) + 'ms';
}
this.$parent.direction === 'vertical'
? this.lineStyle.height = step + '%'
: this.lineStyle.width = step + '%';
}
},
mounted() {
const parent = this.$parent;
const space = parent.space
? parent.space + 'px'
: 100 / parent.steps.length + '%';
if (parent.direction === 'horizontal') {
this.style = { width: space };
this.mainOffset = -this.$refs.title.getBoundingClientRect().width / 2 + 16 + 'px';
} else {
this.style = { height: space };
}
const unwatch = this.$watch('index', val => {
this.$watch('$parent.active', this.updateStatus, { immediate: true });
unwatch();
});
}
};
</script>

View File

@ -0,0 +1,38 @@
<template>
<div class="el-steps" :class="['is-' + direction]"><slot></slot></div>
</template>
<script>
export default {
name: 'el-steps',
props: {
space: Number,
active: Number,
direction: {
type: String,
default: 'horizontal'
},
finishStatus: {
type: String,
default: 'finish'
},
processStatus: {
type: String,
default: 'process'
}
},
data() {
return {
steps: []
};
},
mounted() {
this.steps.forEach((child, index) => {
child.index = index;
});
}
};
</script>

View File

@ -372,4 +372,10 @@
--card-border-color: var(--disabled-border-base);
--card-border-radius: 4px;
--card-padding: 20px;
/*Steps
--------------------------*/
--steps-border-color: var(--disabled-border-base);
--steps-border-radius: 4px;
--steps-padding: 20px;
}

View File

@ -35,3 +35,5 @@
@import "./badge.css";
@import "./card.css";
@import "./rate.css";
@import "./steps.css";
@import "./step.css";

View File

@ -0,0 +1,204 @@
@charset "UTF-8";
@import "./common/var.css";
@component-namespace el {
@b step {
position: relative;
vertical-align: top;
@when horizontal {
display: inline-block;
}
@when vertical {
& .el-step__head,
& .el-step__main {
display: inline-block;
}
& .el-step__main {
padding-left: 10px;
}
}
@e line {
display: inline-block;
position: absolute;
border-color: inherit;
background-color: #c0ccda;
z-index: -1;
@when icon {
@when horizontal {
width: 55%;
margin-left: 35px;
}
@when vertical {
height: 55%;
margin-top: 35px;
}
}
@when horizontal {
top: 15px;
height: 2px;
width: 100%;
margin-right: -14px;
}
@when vertical {
width: 2px;
height: 100%;
box-sizing: border-box;
padding-top: 28px;
}
}
@e line-inner {
display: inherit;
border-width: 1px;
border-style: solid;
border-color: inherit;
transition: all 150ms;
width: 0;
height: 0;
}
@e icon {
line-height: 28px;
}
@e head {
circle: 28px transparent;
text-align: center;
line-height: 28px;
font-size: 28px;
vertical-align: top;
transition: all 150ms;
@when text {
font-size: 14px;
border-width: 2px;
border-style: solid;
@when process {
color: #fff;
background-color: #c0ccda;
border-color: #c0ccda;
}
@when wait {
color: #c0ccda;
background-color: #fff;
border-color: #c0ccda;
}
@when success {
color: #fff;
background-color: #13ce66;
border-color: #13ce66;
}
@when error {
color: #fff;
background-color: #ff4949;
border-color: #ff4949;
}
@when finish {
color: #fff;
background-color: #20a0ff;
border-color: #20a0ff;
}
}
@when process {
color: #c0ccda;
border-color: #c0ccda;
}
@when wait {
color: #c0ccda;
border-color: #c0ccda;
}
@when success {
color: #13ce66;
border-color: #13ce66;
}
@when error {
color: #ff4949;
border-color: #ff4949;
}
@when finish {
color: #20a0ff;
border-color: #20a0ff;
}
}
@e main {
white-space: normal;
padding-right: 10px;
}
@e title {
font-size: 14px;
margin-top: 5px;
display: inline-block;
@when process {
font-weight: 700;
color: #475669;
}
@when wait {
font-weight: normal;
color: #99a9bf;
}
@when success {
font-weight: 700;
color: #13ce66;
}
@when error {
font-weight: 700;
color: #ff4949;
}
@when finish {
font-weight: 700;
color: #20a0ff;
}
}
@e description {
font-size: 12px;
font-weight: normal;
line-height: 14px;
@when process {
color: #8492a6;
}
@when wait {
color: #c0ccda;
}
@when success {
color: #13ce66;
}
@when error {
color: #ff4949;
}
@when finish {
color: #20a0ff;
}
}
}
}

View File

@ -0,0 +1,17 @@
@charset "UTF-8";
@import "./common/var.css";
@component-namespace el {
@b steps {
overflow: hidden;
font-size: 0;
> :last-child .el-step__line {
display: none;
}
@when horizontal {
white-space: nowrap;
}
}
}

View File

@ -43,13 +43,15 @@ import Loading from '../packages/loading/index.js';
import Icon from '../packages/icon/index.js';
import Row from '../packages/row/index.js';
import Col from '../packages/col/index.js';
import Upload from '../packages/upload/index.js';
// import Upload from '../packages/upload/index.js';
import Progress from '../packages/progress/index.js';
import Spinner from '../packages/spinner/index.js';
import Message from '../packages/message/index.js';
import Badge from '../packages/badge/index.js';
import Card from '../packages/card/index.js';
import Rate from '../packages/rate/index.js';
import Badge from '../packages/badge/index.js';
import Steps from '../packages/steps/index.js';
import Step from '../packages/step/index.js';
const install = function(Vue) {
if (install.installed) return;
@ -96,13 +98,15 @@ const install = function(Vue) {
Vue.component(Icon.name, Icon);
Vue.component(Row.name, Row);
Vue.component(Col.name, Col);
Vue.component(Upload.name, Upload);
// Vue.component(Upload.name, Upload);
Vue.component(Progress.name, Progress);
Vue.component(Spinner.name, Spinner);
Vue.component(Message.name, Message);
Vue.component(Badge.name, Badge);
Vue.component(Card.name, Card);
Vue.component(Rate.name, Rate);
Vue.component(Badge.name, Badge);
Vue.component(Steps.name, Steps);
Vue.component(Step.name, Step);
Vue.use(Loading);
@ -166,11 +170,13 @@ module.exports = {
Icon,
Row,
Col,
Upload,
// Upload,
Progress,
Spinner,
Message,
Badge,
Card,
Rate,
Message,
Badge
Steps,
Step
};