Introduction

image
今天小试牛刀,试着写一个task-panel,不过涉及的组件有点多,先写好组件部分,再把他们拼装成task-panel

设计需求

结构说明

  1. 组件由三个div模块组成,分别为head, operatorcard-grid
  2. head标题任务个数,和一个dropdown组件构成
  3. operator由一个长条按钮creator编辑框editor组成,这两个部分不能同时存在
  4. card-grid由两种卡片组成,一种是未完成的,一种是已完成

组件说明

dropdown

dropdown包含三个操作,

  • 修改task-panel的标题
  • 在此task-panel后新增一个task-panel
  • 删除此task-panel

注意这个组件与父组件相关联

creator

creator的功能是显示editor编辑框,同时自身消失

editor

editor提供两种按钮,取消确定,在其中输入文本后传递给父组件

card

card代表一项任务

组件实现

dropdown

在组件中,需要为每个menu-item设置好:text, :action@click事件绑定
其中action是为了统一@click处理的方式,不然menu-item多了,要为每个组件绑定一个不同的处理函数,详细请看下方行为处理

设计需求

  1. 主要功能

    • 显示一个菜单,这个菜单有三个功能,分别是

      • 修改task-panel的标题
      • 在此task-panel后新增一个task-panel
      • 删除此task-panel
    • 这三个功能通过emit传递给父组件
  2. 组件修饰

    • 鼠标悬浮div.entry的图标时,图标的颜色加深
  3. 接口

    • 通过active属性控制菜单的显示

[active == false] 初始状态

image

    <div class="entry" @click="toggleHandler">
      <svg t="1608216593558" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3281" width="48" height="48"><path d="M110.336 572.330667a85.333333 85.333333 0 1 0 120.661333-120.661334 85.333333 85.333333 0 0 0-120.661333 120.661334zM451.669333 572.330667a85.333333 85.333333 0 1 0 120.661334-120.661334 85.333333 85.333333 0 0 0-120.661334 120.661334zM793.002667 572.330667a85.333333 85.333333 0 1 0 120.661333-120.661334 85.333333 85.333333 0 0 0-120.661333 120.661334z" p-id="3282"></path></svg>
  </div>

[active == true] 激活状态

image

    <div class="menu" v-show="active">
      <menu-item text="edit" action="edit" @click="handler">
    <svg class="icon" viewBox="0 0 1024 1024">
      <path d="M856.576 290.304l-63.488 63.488-122.88-122.88 63.488-63.488a11.776 11.776 0 0 1 8.704-3.584 11.264 11.264 0 0 1 8.704 3.584l105.472 105.472a12.8 12.8 0 0 1 0 17.408zM208.896 716.8L307.2 815.616l-131.584 32.768 33.28-131.584z m153.6 65.536l-122.88-120.832 387.072-387.072 122.88 122.368-388.096 386.56z m537.6-552.96l-105.472-105.472a74.24 74.24 0 0 0-102.4 0L174.592 640a68.608 68.608 0 0 0-18.432 32.256L102.4 883.2a31.744 31.744 0 0 0 8.192 29.696 30.208 30.208 0 0 0 22.528 8.704h7.68l210.944-51.2a66.048 66.048 0 0 0 32.256-18.432L900.096 333.824a74.24 74.24 0 0 0 0-102.4z"></path>
        </svg>
      </menu-item>

      <menu-item text="add" action="add" @click="handler">
    <svg class="icon" viewBox="0 0 1024 1024">
      <path d="M476.16 476.16V191.0272c0-20.6848 16.0256-37.4272 35.84-37.4272 19.8144 0 35.84 16.7424 35.84 37.4272V476.16h285.1328c20.6848 0 37.4272 16.0256 37.4272 35.84 0 19.8144-16.7424 35.84-37.4272 35.84H547.84v285.1328c0 20.6848-16.0256 37.4272-35.84 37.4272-19.8144 0-35.84-16.7424-35.84-37.4272V547.84H191.0272C170.3424 547.84 153.6 531.8144 153.6 512c0-19.8144 16.7424-35.84 37.4272-35.84H476.16z"></path>
        </svg>
      </menu-item>

      <menu-item text="delete" action="delete" @click="handler">
    <svg class="icon" viewBox="0 0 1024 1024">
      <path d="M890.88 241.9712h-230.2976V202.752c0-55.296-45.056-100.352-100.352-100.352H463.7696c-55.3472 0-100.352 45.056-100.352 100.352v39.2192H133.12a30.72 30.72 0 0 0 0 61.4912h66.3552L296.2432 855.04c2.56 14.6944 15.3088 25.3952 30.2592 25.3952h370.9952a30.72 30.72 0 0 0 30.2592-25.3952l96.768-551.5776H890.88a30.72 30.72 0 0 0 0-61.4912M261.8368 303.4624h103.68l37.2224 515.584h-50.432L261.8368 303.4624m202.496 515.584l-37.1712-515.584h169.6768l-37.1712 515.584H464.3328m207.36 0h-50.432l37.1712-515.584h103.7312l-90.4704 515.584M463.7696 163.8912h96.4608c21.4528 0 38.912 17.408 38.912 38.8608v39.2192H424.8576V202.752a38.912 38.912 0 0 1 38.912-38.8608"></path>
        </svg>
      </menu-item>

    </div>

[行为1] 点击初始状态下的dropdown

方法 toggleHandler

    toggleHandler() {
      this.$emit('toggle')
    },

[行为2] 点击菜单中的条目

    handler(action) {
      this.$emit('toggle-menu', action)
    }

组件修饰 <style scoped>

.entry svg.icon:hover {
  opacity: 1;
  cursor: pointer;
}
// 图标设置
.entry svg.icon {
  width: 1em;
  height: 1em;
  opacity: 0.5;
}

// 设置为相对布局,这样整个菜单的显示以这个为参照
.entry svg.icon {
  position: relative;
}

// 菜单栏
div.menu {
  /* border: 1px solid black; */
  box-shadow: 0 12px 32px 0 rgba(38,38,38,0.16);
  border-radius: 4px;
  background-color: white;

  width: 194px;

  position: absolute;
  top: 31.5px;
}

// 整体布局
div.dropdown {
  display: flex;
  flex-direction: column;
  align-items: center;
}

creator

image

设计需求

  1. 主要功能

    • 点击后使editor组件显示,同时自身消失,此时active == true
    • 当退出editor时,creator恢复,此时active == false
  2. 组件修饰

    • 鼠标悬浮时,box-shadow颜色加深,内置图标变蓝
  3. 接口设计

    • active控制editor的显示

基本结构

  <div class="creator"
       v-show='!active'
       @click="handler">
    <svg id="at-plus" viewBox="0 0 1024 1024">
      <path d="M476.16 476.16V191.0272c0-20.6848 16.0256-37.4272 35.84-37.4272 19.8144 0 35.84 16.7424 35.84 37.4272V476.16h285.1328c20.6848 0 37.4272 16.0256 37.4272 35.84 0 19.8144-16.7424 35.84-37.4272 35.84H547.84v285.1328c0 20.6848-16.0256 37.4272-35.84 37.4272-19.8144 0-35.84-16.7424-35.84-37.4272V547.84H191.0272C170.3424 547.84 153.6 531.8144 153.6 512c0-19.8144 16.7424-35.84 37.4272-35.84H476.16z">
      </path>
        </svg>
  </div>

[行为] 处理点击事件

    handler() {
      this.$emit('toggle')
    },

组件修饰 <style scoped>

// icon
.creator svg {
  width: 20px;
  height: 20px;
  color: #8C8C8C;
}
</style>

// 整体布局
div.creator {
  height: 30px;
  box-shadow: 0 1px 2px rgba(0,0,0,0.1);
  border-radius: 4px;
  
  display: flex;
  align-items: center;
  justify-content: center;
  /* text-align: center; */

  background-color: white;

  margin: 8px 0;
}

// 鼠标悬浮
div.creator:hover {
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.15);
  cursor: pointer;
}

div.creator:hover svg#at-plus path {
  fill: #309fee;
}

editor

image
_
image

设计需求

  1. 主要功能

    • 提供输入文本框和两个按钮控制
    • 将输入的文本传递给父组件
  2. 组件修饰

    • 针对textarea标签,鼠标点击进入时,边框变为蓝色
    • 对两个button按钮背景颜色和文字颜色进行设置
    • 鼠标悬浮在button上时,需要button变换背景色,也可以使用transition属性添加动画
  3. 接口设计

    • holder控制textareaplaceholder的内容
    • rows 控制textarearows属性大小
    • active控制自身的显示,v-show="active"

基本结构

  <div class="editor" v-show="active">

    <div class="body">
      <textarea 
          @keyup.enter="submitEditorText"
          v-model="inputText"
          :placeholder="holder"
          data-real="true"
          :rows="rows">

      </textarea>
    </div>

    <div class="footer">
      <button class="cancel" @click="cancelHandler">
          <span class="next-btn">取消</span>
      </button>

      <button class="submit" :disable="inputText.length == 0" 
          @click="submitEditorText">
          <span class="next-btn">确定</span>
      </button>

    </div>

  </div>

组件修饰 <style scoped>

  1. 主要样式
.footer button.cancel {
  color: #1b9aee;
  border-color: #ccecff;
  background-color: transparent;
}

.footer button.cancel:hover {
  color: #1b9aee;
  background-color: #f2fbff;
  border-color: #ccecff;
  cursor: pointer;
}

.footer button.submit {
  margin-left: 12px;
  border-style: solid;
  background-color: #1b9aee;
  border-color: transparent;
  
  color: rgb(255, 255, 255);
}

.footer button.submit:hover {
  cursor: pointer;
  background-color: #0171c2;
}

.footer button.submit[disabled] {
  cursor: not-allowed;
  background-color: #bfbfbf;
  border-color: #bfbfbf;
}
  1. 次要样式
.editor {
  width: 100%;
  /* height: 216px; */
  background-color: #fff;
  box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.1);

}

// textarea
textarea {
  color: #262626;
  /* padding: 4px 8px; */
  font-size: 14px;
  font-weight: 400;

  outline: none;

  border-radius: 4px;
  border: none;

  line-height: 20px;
  resize: vertical;
  vertical-align: middle;


  /* width: 230px; */
  width: 95%;
  height: 60px;
  padding: 4px 8px;
}

textarea {
  line-height: 1;
  vertical-align: middle;
  border: 1px solid #d9d9d9;
}

textarea:focus {
  border-color: #1b9aee;
}


// 设置 position与display
.editor .body {
  padding: 12px 12px 16px 12px;
  display: flex;
  justify-content: center;
}

// footer
.footer {
  padding: 12px;
  text-align: right;
  border-top: 1px solid #E5E5E5;
}

// 通用button样式
.footer button {
  border-style: solid;

  border-radius: 4px;
  padding: 0 7px;
  height: 28px;
  line-height: 26px;
  font-size: 14px;
  border-width: 1px;
  min-width: 52px;

  display: inline-block;
  text-align: center;
  white-space: nowrap;
  vertical-align: middle;
  transition: background-color 0.3s ease,color 0.3s ease,border-color 0.3s ease;
}

行为设计

    submitEditorText() {
      this.$emit('submit', this.inputText)
      this.inputText = ''
    },

    cancelHandler() {
      this.$emit('cancel')
    },

card

image

设计需求

  1. 主要功能

    • 显示任务的状态与任务的描述
    • 在组件中,通过点击span.check-box图标来控制任务的状态
    • 不提供细节服务
  2. 组件修饰

    • done == 'true'时,card整体变灰

      • 同时span.check-box打勾
      • 同时横线穿过card的文字内容
    • 鼠标悬浮时

      • box-shadow变深
      • 整体向下偏移一点
      • 同时用动画修饰这些变化
  3. 接口设计

    • done表示card的状态,<div class="card" :class="{'done': done}">
    • content表示card的任务内容

基本结构

  <div class="card" :class="{'done': done}">
    <span class="check-box" @click="toggleHandler">
      <svg viewBox="0 0 1024 1024">
            <path v-show="!done" d="M804.5568 102.4H219.4432A117.3504 117.3504 0 0 0 102.4 219.4432v585.1136A117.3504 117.3504 0 0 0 219.4432 921.6h585.1136A117.3504 117.3504 0 0 0 921.6 804.5568V219.4432A117.3504 117.3504 0 0 0 804.5568 102.4m0 70.1952c25.3952 0 46.848 21.4528 46.848 46.848v585.1136a47.4112 47.4112 0 0 1-46.848 46.848H219.4432a47.4112 47.4112 0 0 1-46.848-46.848V219.4432c0-25.3952 21.4528-46.848 46.848-46.848h585.1136">
    </path>

            <path v-show="done" d="M804.5568 102.4H219.4432A117.3504 117.3504 0 0 0 102.4 219.4432v585.1136A117.3504 117.3504 0 0 0 219.4432 921.6h585.1136A117.3504 117.3504 0 0 0 921.6 804.5568V219.4432A117.3504 117.3504 0 0 0 804.5568 102.4m0 70.1952c25.3952 0 46.848 21.4528 46.848 46.848v585.1136a47.4112 47.4112 0 0 1-46.848 46.848H219.4432a47.4112 47.4112 0 0 1-46.848-46.848V219.4432c0-25.3952 21.4528-46.848 46.848-46.848h585.1136M449.024 683.6736a34.9184 34.9184 0 0 1-24.7808-10.2912L276.992 526.2336a35.1744 35.1744 0 0 1 49.664-49.664l122.368 122.3168 248.1664-248.2176a35.072 35.072 0 1 1 49.664 49.6128l-273.0496 273.1008a34.9184 34.9184 0 0 1-24.832 10.24">
    </path>

            </svg>
    </span>

    <span class="task-content">
      {{ content }}
    </span>

  </div>

行为设计

    toggleHandler() {
      this.$emit('toggle', this.done)
    },

组件修饰 <style scoped>

  1. 主要部分
/* when card is done */
.card.done .task-content {
  color: rgb(140, 140, 140);
  text-decoration: line-through;
}

.card.done .check-box {
  fill: rgb(140, 140, 140);
}

.card:hover {
  box-shadow: 0 1px 2px 2px rgba(0, 0, 0, 0.1);
  transform: translateY(2px);
  cursor: pointer;
}

.check-box:hover svg {
  fill: #595959;
}
  1. 次要部分
.card {
  display: flex;
  align-items: center;
  font-size: 14px;
  height: 48px;
}

.card span.check-box {
  width: 20px;
  height: 20px;

  margin: 0 12px;
}


// style of task content 

/* structure of task content */
.task-content {
  max-width: 172px;
  font-size: 14px;
  line-height: 20px;
}

/* display of task content */
.task-content {
  display: flex;
  flex-grow: 1;
  flex-shrink: 1;
}


// whole card color 
.card {
  box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.1);
  background-color: white;
  border-radius: 4px;
  transition: transform 0.2s;
}

// last: distance of each card 
.card {
  margin-bottom: 8px;
}

nesteiner
1 声望0 粉丝

油腻的大学生