浏览代码

Merge pull request #447 from ailululu/next

steps
love_forever 4 年之前
父节点
当前提交
f9506d7074

+ 18 - 0
src/config.json

@@ -373,6 +373,24 @@
           "sort": 16,
           "show": true,
           "author": "Drjingfubo"
+        },
+        {
+          "name": "Steps",
+          "sort": 17,
+          "cName": "步骤条",
+          "type": "component",
+          "show": true,
+          "desc": "步骤条",
+          "author": "ailululu"
+        },
+        {
+          "name": "Step",
+          "sort": 17,
+          "cName": "步骤条子组件",
+          "type": "component",
+          "show": false,
+          "desc": "步骤条子组件",
+          "author": "ailululu"
         }
       ]
     },

+ 200 - 0
src/packages/step/index.scss

@@ -0,0 +1,200 @@
+.nut-step {
+  flex-grow: 0;
+  flex-shrink: 0;
+  flex-basis: 33.34%;
+  text-align: center;
+  font-size: 0;
+  &-head {
+    position: relative;
+    display: block;
+    margin-bottom: 10px;
+  }
+  &-line {
+    position: absolute;
+    top: 11px;
+    left: 50%;
+    right: -50%;
+    display: inline-block;
+    height: 1px;
+    background: #909ca4;
+  }
+  &-icon {
+    position: relative;
+    display: inline-block;
+    width: 25px;
+    height: 25px;
+    line-height: 25px;
+    font-family: PingFangSC-Medium;
+    font-size: 13px;
+    // border-color: #909CA4;
+    z-index: 1;
+    .nut-icon {
+      width: 100%;
+      height: 100%;
+    }
+    &.is-text {
+      border-radius: 50%;
+      border-width: 1px;
+      border-style: solid;
+    }
+    &.is-icon {
+      border-radius: 50%;
+      border-width: 1px;
+      border-style: solid;
+      background-color: transparent;
+    }
+  }
+  &-main {
+    display: inline-block;
+    padding-left: 10%;
+    padding-right: 10%;
+    text-align: center;
+  }
+  &-title {
+    display: block;
+    margin-bottom: 10px;
+    font-size: 14px;
+    color: #909ca4;
+  }
+  &-content {
+    display: block;
+    font-size: 14px;
+    color: #666;
+  }
+  &:last-child {
+    .nut-step-line {
+      display: none;
+    }
+  }
+  &.nut-step-finish {
+    .nut-step-head {
+      color: #3790ff;
+      border-color: #3790ff;
+    }
+    .nut-step-icon.is-text {
+      background-color: #fff;
+    }
+    .nut-step-icon.is-icon {
+      background-color: #fff;
+    }
+    .nut-step-line {
+      background: #3790ff;
+    }
+    .nut-step-title {
+      color: #3790ff;
+    }
+  }
+  &.nut-step-process {
+    .nut-step-head {
+      color: #fff;
+      border-color: #3790ff;
+    }
+    .nut-step-icon.is-text {
+      background-color: #3790ff;
+    }
+    .nut-step-icon.is-icon {
+      background-color: #3790ff;
+    }
+    .nut-step-title {
+      color: #3790ff;
+    }
+  }
+  &.nut-step-wait {
+    .nut-step-head {
+      color: #909ca4;
+      border-color: #909ca4;
+    }
+    .nut-step-icon.is-text {
+      background-color: #fff;
+    }
+    .nut-step-icon.is-icon {
+      background-color: #fff;
+    }
+    .nut-step-content {
+      color: #909ca4;
+    }
+  }
+  &.nut-step-error {
+    .nut-step-head {
+      color: #fff;
+      border-color: #fa2c19;
+    }
+    .nut-step-icon.is-text {
+      background-color: #fa2c19;
+    }
+    .nut-step-icon.is-icon {
+      background-color: #fa2c19;
+    }
+    .nut-step-line {
+      background: #3790ff;
+    }
+  }
+}
+.nut-steps-vertical {
+  .nut-step {
+    display: flex;
+    height: 33.34%;
+  }
+  .nut-step-line {
+    position: absolute;
+    display: inline-block;
+    width: 1px;
+    height: 100%;
+    background: #909ca4;
+  }
+  .nut-step-main {
+    display: inline-block;
+    padding-left: 6%;
+    text-align: left;
+  }
+  &.nut-steps-dot {
+    .nut-step-head {
+      margin-top: 7px;
+      margin-bottom: 0;
+    }
+    .nut-step-line {
+      top: 7px;
+      left: 50%;
+      right: -50%;
+    }
+    .nut-step-icon {
+      width: 8px;
+      height: 8px;
+      background: #3790ff;
+      border-radius: 50%;
+      box-sizing: content-box;
+    }
+    .nut-step-wait {
+      .nut-step-icon {
+        background-color: #959fb1;
+      }
+      .nut-step-content {
+        color: #909ca4;
+      }
+    }
+    .nut-step-finish {
+      .nut-step-icon {
+        background-color: #3790ff;
+      }
+    }
+    .nut-step-process {
+      .nut-step-icon {
+        position: relative;
+        background-color: #3790ff;
+        &:before {
+          content: '';
+          display: inline-block;
+          position: absolute;
+          left: 50%;
+          top: 50%;
+          margin-left: -7px;
+          margin-top: -7px;
+          width: 14px;
+          height: 14px;
+          background-color: rgba(55, 144, 255, 0.23);
+          border-radius: 50%;
+        }
+      }
+    }
+  }
+}

+ 88 - 0
src/packages/step/index.vue

@@ -0,0 +1,88 @@
+<template>
+  <view :class="classes">
+    <view class="nut-step-head">
+      <view class="nut-step-line"></view>
+      <view
+        class="nut-step-icon"
+        :class="[!state.dot ? (icon ? 'is-icon' : 'is-text') : '']"
+      >
+        <template v-if="icon">
+          <nut-icon class="nut-step-icon-inner" :class="icon" />
+        </template>
+        <template v-else-if="state.dot"></template>
+        <template v-else>
+          <view class="nut-step-inner">{{ state.index }}</view>
+        </template>
+      </view>
+    </view>
+    <view class="nut-step-main">
+      <view class="nut-step-title">
+        {{ title }}
+      </view>
+      <view class="nut-step-content">
+        {{ content }}
+      </view>
+    </view>
+  </view>
+</template>
+
+<script lang="ts">
+import { reactive, computed, inject } from 'vue';
+import { createComponent } from '@/utils/create';
+const { create, componentName } = createComponent('step');
+
+export default create({
+  props: {
+    title: {
+      type: String,
+      default: ''
+    },
+    content: {
+      type: String,
+      default: ''
+    },
+    icon: {
+      type: String,
+      default: null
+    },
+    status: {
+      type: String,
+      default: null
+    },
+    data: {
+      type: String,
+      default: null
+    }
+  },
+  setup(props, context) {
+    const steps: any = inject('stepsParent');
+    const defaults = context.slots?.default();
+    console.log('defaults', context.slots);
+    console.log('steps', steps.props.progressDot);
+    const state = reactive({
+      data: [],
+      index: context.slots.default()[0]?.children - 1,
+      dot: steps.props.progressDot
+    });
+    console.log('dot', state.dot);
+    // console.log('context', steps.state.steps[state.index])
+    const classes = computed(() => {
+      const prefixCls = componentName;
+      return {
+        [prefixCls]: true,
+        [props.status
+          ? 'nut-step-' + props.status
+          : steps.state.steps[state.index].currentStatus]: true
+      };
+    });
+    return {
+      state,
+      classes
+    };
+  }
+});
+</script>
+
+<style scoped lang="scss">
+@import 'index.scss';
+</style>

+ 85 - 0
src/packages/steps/demo.vue

@@ -0,0 +1,85 @@
+<template>
+  <div class="demo">
+    <h2>基本用法</h2>
+    <nut-steps current="1">
+      <nut-step title="进行中">1</nut-step>
+      <nut-step title="未开始">2</nut-step>
+      <nut-step title="未开始">3</nut-step>
+    </nut-steps>
+    <h2>标题和描述信息</h2>
+    <nut-steps current="2">
+      <nut-step
+        title="已完成"
+        content="步骤描述"
+        icon="nutui-iconfont nut-icon-wanshangshide"
+        >1</nut-step
+      >
+      <nut-step title="进行中" content="步骤描述">2</nut-step>
+      <nut-step title="未开始" content="步骤描述">3</nut-step>
+    </nut-steps>
+    <h2>自定义图标</h2>
+    <nut-steps current="1">
+      <nut-step
+        title="已完成"
+        icon="nutui-iconfont nut-icon-wanshangshide"
+        status="error"
+        >1</nut-step
+      >
+      <nut-step title="进行中" icon="nutui-iconfont nut-icon-notice"
+        >2</nut-step
+      >
+      <nut-step
+        class="nut-step-wait"
+        title="未开始"
+        icon="nutui-iconfont nut-icon-notice"
+        >3</nut-step
+      >
+    </nut-steps>
+    <h2>竖向步骤条</h2>
+    <div style="height: 300px">
+      <nut-steps direction="vertical" current="2">
+        <nut-step
+          title="已完成"
+          icon="nutui-iconfont nut-icon-wanshangshide"
+          content="您的订单已经打包完成,商品已发出"
+          >1</nut-step
+        >
+        <nut-step title="进行中" content="您的订单正在配送途中">2</nut-step>
+        <nut-step
+          title="未开始"
+          content="收货地址为:北京市经济技术开发区科创十一街18号院京东大厦"
+          >3</nut-step
+        >
+      </nut-steps>
+    </div>
+    <div style="height: 300px">
+      <nut-steps direction="vertical" progressDot current="2">
+        <nut-step title="已完成" content="您的订单已经打包完成,商品已发出"
+          >1</nut-step
+        >
+        <nut-step title="进行中" content="您的订单正在配送途中">2</nut-step>
+        <nut-step
+          title="未开始"
+          content="收货地址为:北京市经济技术开发区科创十一街18号院京东大厦"
+          >3</nut-step
+        >
+      </nut-steps>
+    </div>
+  </div>
+</template>
+
+<script lang="ts">
+import { createComponent } from '@/utils/create';
+const { createDemo } = createComponent('steps');
+export default createDemo({
+  props: {},
+  setup() {
+    return {};
+  }
+});
+</script>
+
+<style lang="scss" scoped>
+.demo-box {
+}
+</style>

+ 90 - 0
src/packages/steps/doc.md

@@ -0,0 +1,90 @@
+# Steps 步骤条 组件
+
+### 介绍
+
+拆分展示某项流程的步骤,引导用户按流程完成任务或向用户展示当前状态。
+
+### 安装
+
+```javascript
+import { createApp } from 'vue';
+import { Steps } from '@nutui/nutui';
+
+const app = createApp();
+app.use(Steps);
+```
+
+## 代码演示
+
+### 基本用法
+
+```html
+<nut-steps current="1">
+  <nut-step title="进行中">1</nut-step>
+  <nut-step title="未开始">2</nut-step>
+  <nut-step title="未开始">3</nut-step>
+</nut-steps>
+```
+
+### 标题和描述信息
+
+```html
+<nut-steps current="2">
+  <nut-step title="已完成" content="步骤描述" icon="nutui-iconfont nut-icon-wanshangshide">1</nut-step>
+  <nut-step title="进行中" content="步骤描述">2</nut-step>
+  <nut-step title="未开始" content="步骤描述">3</nut-step>
+</nut-steps>
+```
+
+### 自定义图标
+
+```html
+<nut-steps current="1">
+  <nut-step title="已完成" icon="nutui-iconfont nut-icon-wanshangshide" status="error">1</nut-step>
+  <nut-step title="进行中" icon="nutui-iconfont nut-icon-notice">2</nut-step> 
+  <nut-step class="nut-step-wait" title="未开始" icon="nutui-iconfont nut-icon-notice">3</nut-step>
+</nut-steps>
+```
+
+### 竖向步骤条
+
+```html
+<nut-steps direction="vertical" current="2">
+  <nut-step title="已完成" icon="nutui-iconfont nut-icon-wanshangshide" content="您的订单已经打包完成,商品已发出" >1</nut-step>
+  <nut-step title="进行中" content="您的订单正在配送途中" >2</nut-step>
+  <nut-step title="未开始" content="收货地址为:北京市经济技术开发区科创十一街18号院京东大厦">3</nut-step>
+</nut-steps>
+```
+
+### 点状步骤和垂直方向
+```html
+<nut-steps direction="vertical" progressDot current="2">
+  <nut-step title="已完成" content="您的订单已经打包完成,商品已发出" >1</nut-step>
+  <nut-step title="进行中" content="您的订单正在配送途中">2</nut-step>
+  <nut-step title="未开始" content="收货地址为:北京市经济技术开发区科创十一街18号院京东大厦">3</nut-step>
+</nut-steps>
+```
+
+
+## API
+
+### Props
+
+#### nut-steps
+
+| 参数                   | 说明                                                        | 类型           | 默认值      |
+| ---------------------- | ----------------------------------------------------------- | -------------- | ----------- |
+| direction	             | 	显示方向,`horizontal`,`vertical`  | String        | horizontal  | 
+| current	               | 	当前所在的步骤           | Number、String        | 0       |
+| progressDot            |  点状步骤条     | Boolean | false         |
+
+
+
+#### nut-step
+
+| 参数           | 说明                   | 类型     | 默认值      |
+| ---------------- | ---------------------- | ------------ | ----------- |
+| title            | 流程步骤的标题         | String | 步骤 |
+| content          | 流程步骤的描述性文字       | String | 步骤描述 |
+| icon          | 图标       | String | - |
+| status           | 流程状态       | String | String、Number | "process"(可选值 "wait"、"process"、"finish"、"error" |

+ 7 - 0
src/packages/steps/index.scss

@@ -0,0 +1,7 @@
+.nut-steps {
+  display: flex;
+}
+.nut-steps-vertical {
+  height: 100%;
+  flex-flow: column;
+}

+ 131 - 0
src/packages/steps/index.vue

@@ -0,0 +1,131 @@
+<template>
+  <view :class="classes">
+    <slot></slot>
+  </view>
+</template>
+
+<script lang="ts">
+import {
+  provide,
+  computed,
+  onMounted,
+  reactive,
+  watch,
+  onBeforeMount
+} from 'vue';
+import step from '@/packages/step/index.vue';
+import { createComponent } from '@/utils/create';
+const { create, componentName } = createComponent('steps');
+
+export default create({
+  children: [step],
+  props: {
+    direction: {
+      type: String,
+      default: 'horizontal'
+    },
+    current: {
+      type: String,
+      default: 'false'
+    },
+    status: {
+      validator(value) {
+        return ['wait', 'process', 'finish', 'error'].includes(value);
+      },
+      default: 'process'
+    },
+    progressDot: {
+      type: Boolean,
+      default: false
+    }
+  },
+  setup(props, { emit, slots }) {
+    const state = reactive({
+      steps: {},
+      children: []
+    });
+    const classes = computed(() => {
+      const prefixCls = componentName;
+      return {
+        [prefixCls]: true,
+        ['nut-steps-' + props.direction]: true,
+        [props.progressDot ? 'nut-steps-dot' : '']: true
+      };
+    });
+    onBeforeMount(() => {
+      console.log('onBeforeMount');
+      init();
+    });
+    onMounted(() => {
+      console.log('onMounted');
+      // init();
+    });
+    // watch(
+    //   () => props.current,
+    //   val => {
+    //     console.log()
+    //     states();
+    //   }
+    // );
+    // watch(
+    //   () =>  props.source,
+    //   val => {
+    //     init();
+    //   }
+    // )
+    const init = () => {
+      state.steps = (slots as any)?.default();
+      stepStates();
+    };
+    const stepStates = () => {
+      if (props.progressDot) {
+        console.log('state.steps', state.steps);
+        state.steps.dot = true;
+      }
+      const total = state.steps.length;
+      state.steps.forEach((child, index) => {
+        child.stepNumber = index + 1;
+
+        state.children = index;
+        // console.log('data', state.children)
+
+        if (props.direction === 'horizontal') {
+          child.total = total;
+        }
+        // console.log('父', child);
+        if (!child.currentStatus) {
+          if (index == props.current - 1) {
+            if (props.status != 'error') {
+              child.currentStatus = 'nut-step-process';
+            } else {
+              child.currentStatus = 'error';
+            }
+          } else if (index < props.current) {
+            child.currentStatus = 'nut-step-finish';
+          } else {
+            child.currentStatus = 'nut-step-wait';
+          }
+        }
+        if (index + 1 === total) {
+          child.currentStatus += ' nut-step-last';
+        }
+      });
+    };
+    // provide('parent', {
+    //   slots
+    // });
+    provide('stepsParent', {
+      props,
+      state
+    });
+    return {
+      classes,
+      stepStates
+    };
+  }
+});
+</script>
+
+<style scoped lang="scss">
+@import 'index.scss';
+</style>