【JS】[Vue笔记] 仿照`Teambition`手写`task-panel` ---- 组件篇
Introduction
今天小试牛刀,试着写一个task-panel
,不过涉及的组件有点多,先写好组件部分,再把他们拼装成task-panel
设计需求
结构说明
- 组件由三个
div
模块组成,分别为head
,operator
和card-grid
head
由标题,任务个数,和一个dropdown
组件构成operator
由一个长条按钮creator
和编辑框editor
组成,这两个部分不能同时存在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
多了,要为每个组件绑定一个不同的处理函数,详细请看下方行为处理
设计需求
主要功能
显示一个菜单,这个菜单有三个功能,分别是
- 修改
task-panel
的标题 - 在此
task-panel
后新增一个task-panel
- 删除此
task-panel
- 修改
- 这三个功能通过
emit
传递给父组件
组件修饰
- 鼠标悬浮
div.entry
的图标时,图标的颜色加深
- 鼠标悬浮
接口
- 通过
active
属性控制菜单的显示
- 通过
[active == false] 初始状态
<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] 激活状态
<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
设计需求
主要功能
- 点击后使
editor
组件显示,同时自身消失,此时active == true
- 当退出
editor
时,creator
恢复,此时active == false
- 点击后使
组件修饰
- 鼠标悬浮时,
box-shadow
颜色加深,内置图标变蓝
- 鼠标悬浮时,
接口设计
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
_
设计需求
主要功能
- 提供输入文本框和两个按钮控制
- 将输入的文本传递给父组件
组件修饰
- 针对
textarea
标签,鼠标点击进入时,边框变为蓝色 - 对两个
button
按钮背景颜色和文字颜色进行设置 - 鼠标悬浮在
button
上时,需要button
变换背景色,也可以使用transition
属性添加动画
- 针对
接口设计
holder
控制textarea
中placeholder
的内容rows
控制textarea
的rows
属性大小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>
- 主要样式
.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;
}
- 次要样式
.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
设计需求
主要功能
- 显示任务的状态与任务的描述
- 在组件中,通过点击
span.check-box
图标来控制任务的状态 - 不提供细节服务
组件修饰
当
done == 'true'
时,card
整体变灰- 同时
span.check-box
打勾 - 同时横线穿过
card
的文字内容
- 同时
鼠标悬浮时
box-shadow
变深- 整体向下偏移一点
- 同时用动画修饰这些变化
接口设计
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>
- 主要部分
/* 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;
}
- 次要部分
.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;
}
以上是 【JS】[Vue笔记] 仿照`Teambition`手写`task-panel` ---- 组件篇 的全部内容, 来源链接: utcz.com/a/87243.html