vue3自定义流程组件

在这里插入图片描述效果图

<template>
  <div
    class="time-line"
    :style="{
      'margin-bottom': processList.length % 2 === 0 ? 84 + Math.floor(processList.length / 2) * 18 + 'px' : (processList.length / 2) * 18 + 'px'
    }"
  >
    <template v-for="(item, index) in processList" :key="index">
      <div
        :class="{ 'first-item': index === 0, 'comment-end': item.type == 'comment_end', 'comment-start': item.type == 'comment_start' }"
        class="item"
        :style="{
          top: index % 2 === 1 ? 66 + Math.floor(index / 2) * 18 + 'px' : (Math.floor(index / 2) - 1 > 0 ? Math.floor(index / 2) - 1 : 0.05) * 18 + 'px'
        }"
      >
        <div class="time-num" v-if="index !== 0">{{ processList.length - index }}</div>
        <div class="title">{{ item.operationModule }}</div>
        <div class="content" v-if="item.type != 'comment_start' && item.type != 'comment_end'">
          <div class="item-box">
            <span class="label">操作人:</span>
            <span class="value" :title="item?.assigneeName">{{ item?.assigneeName }}</span>
          </div>
          <div class="item-box">
            <span class="label">时间:</span>
            <span class="value">{{ item?.completedTime }}</span>
          </div>
          <div class="item-box">
            <span class="label">意见:</span>
            <span class="value" :title="item.message ? item.message : ''">{{ item?.message }}</span>
          </div>
        </div>
        <div class="btn-box" :title="item?.activityName">{{ item?.activityName }}</div>
        <div class="arrow"></div>
      </div>
    </template>
  </div>
</template>
<script>
import { defineComponent, ref, onMounted } from 'vue'
export default defineComponent({
  name: 'proccess-component',
  props: {
  // 后台获取的数据,根据实际情况修改element的key
    processList: {
      type: Array,
      required: true,
      default: () => []
    }
  },
})
</script>
<style lang="scss" scoped>
.time-line {
  width: 800px;
  position: relative;
  display: flex;
  flex-wrap: wrap;
  flex-direction: row-reverse;
  position: relative;
  top: 15px;
  .item {
    width: 400px;
    height: 150px;
    background-image: url('@/assets/images/group-bj3.png');
    background-size: 98.875% 100%;
    background-repeat: no-repeat;
    position: relative;
    .time-num {
      width: 32px;
      height: 32px;
      border-radius: 50%;
      line-height: 32px;
      text-align: center;
      background-image: linear-gradient(243deg, #0971da 5%, #5cadff 100%);
      color: #fff;
    }
    .btn-box {
      position: absolute;
      width: 73px;
      height: 26px;
      border-radius: 13px;
      text-align: center;
      line-height: 26px;
      color: #fff;
      text-overflow: ellipsis;
      overflow: hidden;
      white-space: nowrap;
      padding: 0 8px;
    }
    .arrow {
      width: 20px;
      height: 18px;
      background-size: 100% 100%;
      position: absolute;
      background-repeat: no-repeat;
    }
    .content {
      position: absolute;
      font-size: 12px;
      width: 281px;
      .item-box {
        margin-top: 4px;
        display: flex;
        align-items: center;
        font-size: 14px;
        height: 20px;
        line-height: 20px;
        .value {
          flex: 1;
          text-overflow: ellipsis;
          overflow: hidden;
          white-space: nowrap;
        }
        .label {
          display: inline-block;
          width: 64px;
          text-align: right;
          color: rgba(0, 0, 0, 0.65);
        }
        &:first-child {
          margin-top: 0;
        }
      }
    }
    .title {
      color: #000;
      position: absolute;
      font-size: 20px;
    }
    &:nth-child(odd) {
      background-image: url('@/assets/images/group-bj4.png');
      background-position: 4px 0;
      .time-num {
        position: relative;
        bottom: 14px;
        right: 16px;
      }
      .btn-box {
        right: 0;
        background: #419af6;
        top: 70px;
      }
      .arrow {
        background-image: url('@/assets/images/group-icon2.png');
        top: 86px;
        left: 384px;
      }
      .title {
        left: 36px;
        top: 50px;
      }
      .content {
        top: 94px;
        left: 24px;
      }
    }
    &:nth-child(odd).comment-start {
      background-image: url('@/assets/images/group-bj4-1.png');
      background-size: 18% 100%;
      background-repeat: no-repeat;
      .btn-box {
        left: 75px;
        background: #419af6;
        top: 70px;
      }
      .arrow {
        background-image: url('@/assets/images/group-icon2.png');
        left: 147px;
      }
    }

    &:nth-child(even) {
      background-position: 1px 0;
      .time-num {
        position: relative;
        left: 384px;
        bottom: 13px;
      }
      .btn-box {
        left: 0;
        background: #419af6;
        top: 70px;
      }
      .arrow {
        background-image: url('@/assets/images/group-icon4.png');
        top: 86px;
        left: -4px;
      }
      .title {
        right: 36px;
        top: 50px;
      }
      .content {
        top: 94px;
        left: 81px;
      }
    }
    &:nth-child(even).comment-start {
      background-image: url('@/assets/images/group-bj3-1.png');
      background-repeat: no-repeat;
      background-position: 324px 0;
      background-size: 18% 100%;
      .btn-box {
        right: 75px;
        left: auto;
      }
      .arrow {
        right: 149px;
        left: auto;
      }
    }
    &:nth-child(2) {
      .time-num {
        background-image: linear-gradient(136deg, #fc7887 0%, #f8434f 100%);
      }
    }
  }
  .item.first-item {
    background-image: url('@/assets/images/group-bj1.png');
    background-position: 1px 0;
    background-size: 98.875% 100%;
    .btn-box {
      background: #fb7482;
      top: 54px;
    }
    .arrow {
      background-image: url('@/assets/images/group-icon1.png');
      top: 70px;
      left: 384px;
    }
    .title {
      color: #ff606c;
      top: 36px;
    }
    .content {
      top: 80px;
    }
  }
  .item.comment-end {
    background-image: url('@/assets/images/group-end.png');
    background-repeat: no-repeat;
    background-size: 18% 100%;
    background-position: 1px 0;
    .btn-box {
      background: #fb7482;
      top: 54px;
      left: 72px;
    }
    .arrow {
      left: 145px;
    }
  }
}
</style>

切图
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

### 创建和使用 Vue.js 自定义组件 #### 1. 全局组件注册 通过 `Vue.component` 方法可以注册全局组件。这种方式使得该组件可以在任何地方被调用而无需额外导入。 ```javascript // 定义一个名为 my-component 的全局组件 Vue.component(&#39;my-component&#39;, { template: &#39;<div>这是一个全局组件</div>&#39; }); ``` 当需要在模板中使用此组件时,可以直接书写 `<my-component></my-component>` 即可[^1]。 #### 2. 局部组件注册 局部组件仅限于特定的 Vue 实例内部有效。可以通过将组件对象作为属性传递给 Vue 实例中的 components 字段完成注册。 ```javascript const LocalComponent = { template: &#39;<div>这是局部组件</div>&#39;, }; new Vue({ el: &#39;#app&#39;, components: { &#39;local-component&#39;: LocalComponent, }, }); ``` 此时,在 HTML 中就可以这样使用:`<local-component></local-component>`。 #### 3. 动态组件切换 利用 `<component>` 标签配合 `is` 特性能够实现动态加载不同的组件。 ```html <div id="dynamic-example"> <button @click="currentTab = &#39;Home&#39;">首页</button> <button @click="currentTab = &#39;About&#39;">关于</button> <!-- 动态绑定到 currentTab --> <component :is="currentTab"></component> </div> ``` 对应的 JavaScript 部分如下: ```javascript new Vue({ el: "#dynamic-example", data() { return { currentTab: "Home", // 默认显示 Home 组件 }; }, components: { Home: { /* ... */ }, About: { /* ... */ } }, }); ``` 这里展示了如何基于按钮点击事件改变当前展示的内容。 #### 4. 父子组件间通信机制 对于父子关系之间的数据流动有两种主要形式——从父级向子级以及反方向传输。 - **父 -> 子**: 使用 props 将数据传入子组件。 ```html <child-component msg="来自父亲的消息"></child-component> ``` 在子组件内接收参数: ```javascript export default { props: [&#39;msg&#39;], mounted() { console.log(this.msg); // 输出 “来自父亲的消息” } } ``` - **子 -> 父**: 利用 `$emit` 发送自定义事件通知父类更新状态或其他操作。 ```javascript this.$emit(&#39;updateMessage&#39;, newValue); ``` 上述代码片段表示子组件触发了一个带有新值的通知给监听它的上级节点处理。 --- ### 示例代码综合演示 下面提供一段完整的例子来说明以上知识点的实际应用情况: ```html <!-- index.html --> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Vue Component Example</title> <script src="https://cdn.jsdelivr.net/npm/vue@2"></script> </head> <body> <div id="app"> <parent-component /> </div> <script type="text/javascript" src="./main.js"></script> </body> </html> ``` ```javascript // main.js 文件内容 // 注册全局组件 ParentComponent 和 ChildComponent Vue.component(&#39;parent-component&#39;, { template: ` <div class="container"> <h2>Parent Component</h2> <input v-model="message"/> <p>{{ message }}</p> <!-- 调用本地定义好的 child component 并传递消息过去 --> <child-component :initial-message="message" @updatedMessage="(val) => updatedText(val)"> </child-component> </div>` , data(){ return{ message:"初始文本" } }, methods:{ updatedText(newVal){ alert(`收到子组件反馈的新信息:${ newVal }`); this.message=newVal; } } }); function ChildComponent () { return { props:[&#39;initialMessage&#39;], // 接收外部输入的信息 initialMessage template:`<div><h3>Child Component</h3> <textarea rows=5 cols=30 v-model="internalMsg"></textarea> <br/> <button @click="$emit(&#39;updatedMessage&#39;, internalMsg)">发送回父母</button> </div>`, data(){return{internalMsg:this.initialMessage}} } } new Vue({el:&#39;#app&#39;}); ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值