1.趋势标记
--components/trend
--src/index.vue
--index.ts
src/index.vue
<template>
<div class="trend">
<div class="text" :style="{ color: textColor }">
<slot v-if="slots.default"></slot>
<div v-else>{{ text }}</div>
</div>
<div class="icon">
<!-- 动态显示图标 -->
<component
:is="`el-icon-${toLine(upIcon)}`"
:style="{ color: !reverseColor ? upIconColor : '#52c41a' }"
v-if="type === 'up'"
></component>
<component
:is="`el-icon-${toLine(downIcon)}`"
:style="{ color: !reverseColor ? downIconColor : '#f5222d' }"
v-else
></component>
</div>
</div>
</template>
<script lang='ts' setup>
import { useSlots, computed } from 'vue'
import { toLine } from '../../../utils'
let props = defineProps({
// 标记当前趋势是上升(up)还是下降(down)
type: {
type: String,
default: 'up'
},
// 上升趋势显示的图标
upIcon: {
type: String,
default: 'ArrowUp'
},
// 下降趋势显示的图标
downIcon: {
type: String,
default: 'ArrowDown'
},
// 趋势显示的文字
// 1. 父组件传递过来的数据
// 2. 插槽
text: {
type: String,
default: '文字'
},
// 颜色翻转只在默认的颜色下生效 如果使用了自定义颜色 这个属性就不生效了
reverseColor: {
type: Boolean,
default: false
},
// 上升趋势图标颜色
upIconColor: {
type: String,
default: '#f5222d'
},
// 下降趋势的图标颜色
downIconColor: {
type: String,
default: '#52c41a'
},
// 上升趋势文字颜色
upTextColor: {
type: String,
default: 'rgb(0,0,0)'
},
// 下降趋势的文字颜色
downTextColor: {
type: String,
default: 'rgb(0,0,0)'
}
})
// 获取插槽内容
let slots = useSlots()
// 文字颜色
let textColor = computed(() => {
return props.type === 'up' ? props.upTextColor : props.downTextColor
})
</script>
<style lang='scss' scoped>
.trend {
display: flex;
align-items: center;
.text {
font-size: 12px;
margin-right: 4px;
}
.icon {
svg {
width: 0.8em;
height: 0.8em;
}
}
}
</style>
index.ts
import { App } from 'vue'
import trend from './src/index.vue'
// 让这个组件可以通过use的形式使用
export default {
install(app: App) {
app.component('m-trend', trend)
}
}
components/index.ts注册
import { App } from 'vue'
import trend from './trend'
const components = [
trend
]
export default {
install(app: App) {
components.map(item => {
app.use(item)
})
}
}
在views/trend中使用
views
--trend/index.vue
trend/index.vue
<template>
<div>
<el-divider>正常趋势</el-divider>
<div class="flex">
<div>
<m-trend text="营业额"></m-trend>
</div>
<div>
<m-trend text="销售额" type="down"></m-trend>
</div>
</div>
<br />
<el-divider>颜色翻转</el-divider>
<div class="flex">
<div><m-trend text="销售额" reverseColor></m-trend></div>
<div><m-trend text="营业额" type="down" reverseColor></m-trend></div>
</div>
<br />
<el-divider>自定义图标颜色</el-divider>
<div class="flex">
<div>
<m-trend text="营业额" upIconColor="blue"></m-trend>
</div>
<div>
<m-trend text="销售额" type="down" downIconColor="#123456"></m-trend>
</div>
</div>
<br />
<el-divider>自定义文字颜色</el-divider>
<div class="flex">
<div><m-trend text="营业额" upTextColor="blue"></m-trend></div>
<div><m-trend text="销售额" type="down" downTextColor="yellow"></m-trend></div>
</div>
<br />
<el-divider>自定义图标</el-divider>
<div class="flex">
<div><m-trend upIcon="CaretTop">营业额</m-trend></div>
<div><m-trend type="down" downIcon="CaretBottom">销售额</m-trend></div>
</div>
</div>
</template>
<script lang='ts' setup>
</script>
<style lang='scss' scoped>
.flex {
display: flex;
div {
margin-right: 10px;
}
}
</style>
2.list列表组件
--components/list
--src/index.vue
--src/types.ts
--index.ts
src/index.vue
<template>
<div class="list-tabs__item">
<el-tabs>
<el-tab-pane v-for="(item, index) in list" :key="index" :label="item.title">
<el-scrollbar max-height="300px">
<div
class="container"
@click="clickItem(item1, index1)"
v-for="(item1, index1) in item.content"
:key="index1"
>
<div class="avatar" v-if="item1.avatar">
<el-avatar size="small" :src="item1.avatar"></el-avatar>
</div>
<div class="content">
<div v-if="item1.title" class="title">
<div>{{ item1.title }}</div>
<el-tag v-if="item1.tag" size="mini" :type="item1.tagType">{{ item1.tag }}</el-tag>
</div>
<div class="time" v-if="item1.desc">{{ item1.desc }}</div>
<div class="time" v-if="item1.time">{{ item1.time }}</div>
</div>
</div>
<div class="actions">
<div
class="a-item"
:class="{ 'border': i !== actions.length }"
v-for="(action, i) in actions"
:key="i"
@click="clickAction(action, i)"
>
<div class="a-icon" v-if="action.icon">
<component :is="`el-icon-${toLine(action.icon)}`"></component>
</div>
<div class="a-text">{{ action.text }}</div>
</div>
</div>
</el-scrollbar>
</el-tab-pane>
</el-tabs>
</div>
</template>
<script lang='ts' setup>
import { PropType } from 'vue'
import { ListOptions, ActionOptions, ListItem } from './types'
import { toLine } from '../../../utils'
let props = defineProps({
// 列表的内容
list: {
type: Array as PropType<ListOptions[]>,
required: true
},
// 操作的内容
actions: {
type: Array as PropType<ActionOptions[]>,
default: () => []
}
})
let emits = defineEmits(['clickItem', 'clickAction'])
let clickItem = (item: ListItem, index: number) => {
emits('clickItem', { item, index })
}
let clickAction = (item: ActionOptions, index: number) => {
emits('clickAction', { item, index })
}
</script>
<style lang='scss' scoped>
.container {
display: flex;
align-items: center;
padding: 12px 20px;
cursor: pointer;
&:hover {
background: #e6f6ff;
}
.avatar {
flex: 1;
}
.content {
flex: 3;
.title {
display: flex;
align-items: center;
justify-content: space-between;
}
.time {
font-size: 12px;
color: #999;
margin-top: 4px;
}
}
}
.actions {
height: 50px;
display: flex;
align-items: center;
border-top: 1px solid #eee;
.a-item {
height: 50px;
flex: 1;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
.a-icon {
margin-right: 4px;
position: relative;
top: 2px;
}
}
}
.border {
border-right: 1px solid #eee;
}
</style>
src/types.ts
// 列表的每一项
export interface ListItem {
// 头像
avatar?: string,
// 标题
title?: string,
// 描述
desc?: string,
// 时间
time?: string,
// 标签内容
tag?: string,
tagType?: '' | 'success' | 'info' | 'warning' | 'danger'
}
// 列表
export interface ListOptions {
title: string,
content: ListItem[]
}
// 操作选项
export interface ActionOptions {
text: string,
icon?: string
}
在components/index.ts中注册
import { App } from 'vue'
import list from './list'
const components = [
list
]
export default {
install(app: App) {
components.map(item => {
app.use(item)
})
}
}
3.通知菜单
-- components/notification
--src/index.vue
--index.ts
src/index.vue
<template>
<el-popover popper-class="notification-popper-class" placement="bottom" :width="300" trigger="click">
<!-- 弹出层插槽 -->
<template #default>
<slot></slot>
</template>
<template #reference>
<!-- 角标 -->
<el-badge style="cursor: pointer;" :value="value" :max="max" :is-dot="isDot">
<component :is="`el-icon-${toLine(icon)}`"></component>
</el-badge>
</template>
</el-popover>
</template>
<script lang='ts' setup>
import { toLine } from '../../../utils'
let props = defineProps({
// 显示的图标
icon: {
type: String,
default: 'Bell'
},
// 通知数量
value: {
type: [String, Number],
default: ''
},
// 最大值
max: {
type: Number
},
// 是否显示小圆点
isDot: {
type: Boolean,
default: false
},
})
</script>
<style lang='scss' scoped>
svg {
width: 1.5em;
height: 1.5em;
}
</style>
index.ts
import { App } from 'vue'
import notification from './src/index.vue'
// 让这个组件可以通过use的形式使用
export default {
install(app: App) {
app.component('m-notification', notification)
}
}
在components/index.ts中注册
import { App } from 'vue'
import notification from './notification'
const components = [
notification
]
export default {
install(app: App) {
components.map(item => {
app.use(item)
})
}
}
4.list(列表) + notification(通知)的使用
--views/notification
--index.vue
--data.ts
data.ts
export const list = [
{
title: '通知',
content: [
{
title: '蒂姆·库克回复了你的邮件',
time: '2019-05-08 14:33:18',
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/ThXAXghbEsBCCSDihZxY.png'
},
{
title: '乔纳森·伊夫邀请你参加会议',
time: '2019-05-08 14:33:18',
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/OKJXDXrmkNshAMvwtvhu.png'
},
{
title: '斯蒂夫·沃兹尼亚克已批准了你的休假申请',
time: '2019-05-08 14:33:18',
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/kISTdvpyTAhtGxpovNWd.png'
}
],
},
{
title: '关注',
content: [
{
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/fcHMVNCjPOsbUGdEduuv.jpeg',
title: '曲丽丽 评论了你',
desc: '描述信息描述信息描述信息',
time: '3小时前'
},
{
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/fcHMVNCjPOsbUGdEduuv.jpeg',
title: '曲丽丽 评论了你',
desc: '描述信息描述信息描述信息',
time: '3小时前'
},
{
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/fcHMVNCjPOsbUGdEduuv.jpeg',
title: '曲丽丽 评论了你',
desc: '描述信息描述信息描述信息',
time: '3小时前'
}
]
},
{
title: '代办',
content: [
{
title: '任务名称',
desc: '任务需要在 2017-01-12 20:00 前启动',
tag: '未开始',
tagType: ''
},
{
title: '第三方紧急代码变更',
desc: '冠霖提交于 2017-01-06,需在 2017-01-07 前完成代码变更任务',
tag: '马上到期',
tagType: 'danger'
},
{
title: '信息安全考试',
desc: '指派竹尔于 2017-01-09 前完成更新并发布',
tag: '已耗时8天',
tagType: 'warning'
}
]
},
]
export const actions = [
{
text: '清空代办',
icon: 'delete'
},
{
text: '查看更多',
icon: 'edit'
},
]
index.vue
<template>
<div>
<!-- <m-notification :value="50"></m-notification>
<br />
<br />
<m-notification :value="50" :max="30"></m-notification>
<br />
<br />
<m-notification :value="50" isDot></m-notification>
<br />
<br />
<m-notification icon="ChatRound" :value="50"></m-notification>-->
<m-notification :value="50">
<template #default>
<m-list @clickItem="clickItem" @clickAction="clickAction" :list="list" :actions="actions"></m-list>
</template>
</m-notification>
</div>
</template>
<script lang='ts' setup>
import { list, actions } from './data'
let clickItem = (val: any) => {
console.log(val)
}
let clickAction = (val: any) => {
console.log(val)
}
</script>
<style lang='scss' scoped>
</style>
5.总结 :
趋势标记组件
列表组件
通知菜单组件
通知菜单和列表组件联动
组件自定义属性