Appearance
趋势标记-图标的组合使用实现上升下降趋势
components/trend/src/index.vue
html
<template>
<div class="trend">
<div class="text">{{ text }}</div>
<div class="icon">
<el-icon-arrowup
v-if="type === 'up'"
:style="{ color: upIconColor }"
></el-icon-arrowup>
<el-icon-arrowdown
v-else
:style="{ color: downIconColor }"
></el-icon-arrowdown>
</div>
</div>
</template>
<script lang="ts" setup>
defineProps({
type: {
type: String,
default: "up",
},
text: {
type: String,
default: "文字",
},
upIconColor: {
type: String,
default: "#f5222d",
},
downIconColor: {
type: String,
default: "#52c41a",
},
});
</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>views/trend/index.vue
html
<template>
<div class="flex">
<m-trend text="营业额"></m-trend>
<m-trend text="销售额" type="down"></m-trend>
</div>
<div class="flex">
<m-trend text="营业额" upIconColor="blue"></m-trend>
<m-trend text="销售额" type="down" downIconColor="#123456"></m-trend>
</div>
</template>
<script lang="ts" setup></script>
<style lang="scss" scoped>
.flex {
display: flex;
div {
margin-right: 10px;
}
}
</style>趋势标记-动态绑定class的妙用实现颜色反转
子组件接收的text可以是插槽,也可以是父组件通过props传递过去的,可以使用useSlots进行区分
颜色反转只有是默认颜色的时候才生效
html
<template>
<div class="trend">
<div class="text">
<slot v-if="slots.default"></slot>
<div v-else>{{ text }}</div>
</div>
<div class="icon">
<el-icon-arrowup
v-if="type === 'up'"
:style="{ color: !reverseColor ? upIconColor : '#52c41a' }"
></el-icon-arrowup>
<el-icon-arrowdown
v-else
:style="{ color: !reverseColor ? downIconColor : '#f5222d' }"
></el-icon-arrowdown>
</div>
</div>
</template>
<script lang="ts" setup>
import { useSlots } from "vue";
defineProps({
type: {
type: String,
default: "up",
},
text: {
type: String,
default: "文字",
},
upIconColor: {
type: String,
default: "#f5222d",
},
downIconColor: {
type: String,
default: "#52c41a",
},
reverseColor: {
type: Boolean,
default: false,
},
});
const slots = useSlots();
</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>html
<template>
<!-- <div class="flex">
<m-trend text="营业额"></m-trend>
<m-trend text="销售额" type="down"></m-trend>
</div>
<div class="flex">
<m-trend text="营业额" upIconColor="blue"></m-trend>
<m-trend text="销售额" type="down" downIconColor="#123456"></m-trend>
</div> -->
<!-- <m-trend>营业额</m-trend> -->
<m-trend text="营业额" reverseColor></m-trend>
<m-trend text="销售额" type="down" downIconColor="purple"></m-trend>
</template>
<script lang="ts" setup></script>
<style lang="scss" scoped>
.flex {
display: flex;
div {
margin-right: 10px;
}
}
</style>趋势标记-计算属性的妙用实现文字颜色
html
<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
v-if="type === 'up'"
:is="`el-icon-${toLine(upIcon)}`"
:style="{ color: !reverseColor ? upIconColor : '#52c41a' }"
></component>
<component
v-else
:is="`el-icon-${toLine(downIcon)}`"
:style="{ color: !reverseColor ? downIconColor : '#f5222d' }"
></component>
</div>
</div>
</template>
<script lang="ts" setup>
import { useSlots, computed } from "vue";
import { toLine } from "../../../utils";
const props = defineProps({
type: {
type: String,
default: "up",
},
text: {
type: String,
default: "文字",
},
upIconColor: {
type: String,
default: "#f5222d",
},
downIconColor: {
type: String,
default: "#52c41a",
},
reverseColor: {
type: Boolean,
default: false,
},
upTextColor: {
type: String,
default: "rgb(0,0,0)",
},
downTextColor: {
type: String,
default: "rgb(0,0,0)",
},
upIcon: {
type: String,
default: "ArrowUp",
},
downIcon: {
type: String,
default: "ArrowDown",
},
});
const slots = useSlots();
const 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>html
<template>
<!-- <div class="flex">
<m-trend text="营业额"></m-trend>
<m-trend text="销售额" type="down"></m-trend>
</div>
<div class="flex">
<m-trend text="营业额" upIconColor="blue"></m-trend>
<m-trend text="销售额" type="down" downIconColor="#123456"></m-trend>
</div> -->
<!-- <m-trend>营业额</m-trend> -->
<!-- <m-trend text="营业额" reverseColor></m-trend>
<m-trend text="销售额" type="down" downIconColor="purple"></m-trend> -->
<!-- <m-trend text="营业额" upTextColor="blue"></m-trend>
<m-trend text="销售额" type="down" downTextColor="yellow"></m-trend> -->
<m-trend text="营业额" upIcon="CaretTop"></m-trend>
<m-trend text="销售额" type="down" downIcon="CaretBottom"></m-trend>
</template>
<script lang="ts" setup></script>
<style lang="scss" scoped>
.flex {
display: flex;
div {
margin-right: 10px;
}
}
</style>通知菜单-icon和badge组件的组合使用
html
<template>
<el-badge :value="value" :max="max" :is-dot="isDot">
<component :is="`el-icon-${toLine(icon)}`"></component>
</el-badge>
</template>
<script lang="ts" setup>
import { toLine } from "../../../utils";
defineProps({
icon: {
type: String,
default: "Bell",
},
value: {
type: [String, Number],
defalut: "",
},
max: {
type: Number,
},
isDot: {
type: Boolean,
default: false,
},
});
</script>
<style lang="scss" scoped></style>html
<template>
<m-notification :value="50"></m-notification><br />
<m-notification :value="50" :max="30"></m-notification><br />
<m-notification :value="50" isDot></m-notification><br />
<m-notification :value="50" icon="ChatLineRound"></m-notification>
</template>
<script lang="ts" setup></script>
<style lang="scss" scoped></style>通知菜单-封装一个列表组件(上)
components/notification/src/index.vue
html
<template>
<el-popover placement="bottom" :width="300" trigger="hover">
<template #default>
<slot></slot>
</template>
<template #reference>
<el-badge :value="value" :max="max" :is-dot="isDot">
<component :is="`el-icon-${toLine(icon)}`"></component>
</el-badge>
</template>
</el-popover>
</template>views/notification/index.vue
html
<template>
<!-- <m-notification :value="50"></m-notification><br />
<m-notification :value="50" :max="30"></m-notification><br />
<m-notification :value="50" isDot></m-notification><br />
<m-notification :value="50" icon="ChatLineRound"></m-notification> -->
<m-notification :value="1">
<template #default>
<m-list></m-list>
</template>
</m-notification>
</template>封装m-list
components/list/src/index.vue
html
<template>
<div>list</div>
</template>
<script lang="ts" setup>
import { PropType } from "vue";
import { ListOptions, ActionOptions } from "./types";
defineProps({
list: {
type: Array as PropType<ListOptions[]>,
required: true,
},
actions: {
type: Array as PropType<ActionOptions[]>,
default: () => [],
},
});
</script>
<style lang="scss" scoped></style>components/list/src/types.ts
typescript
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;
}通知菜单-封装一个列表组件(下)
先准备数据
views/notification/data.ts
typescript
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'
},
]components/list/src/index.vue
html
<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";
defineProps({
// 列表的内容
list: {
type: Array as PropType<ListOptions[]>,
required: true,
},
// 操作的内容
actions: {
type: Array as PropType<ActionOptions[]>,
default: () => [],
},
});
const emits = defineEmits(["clickItem", "clickAction"]);
const clickItem = (item: ListItem, index: number) => {
emits("clickItem", { item, index });
};
const 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>style/ui.scss
scss
.list-tabs__item {
.el-tabs__nav {
width: 100%;
display: flex;
}
.el-tabs__item {
flex: 1;
text-align: center;
}
}views/notification/index.vue
html
<template>
<!-- <m-notification :value="50"></m-notification><br />
<m-notification :value="50" :max="30"></m-notification><br />
<m-notification :value="50" isDot></m-notification><br />
<m-notification :value="50" icon="ChatLineRound"></m-notification> -->
<m-notification :value="1">
<template #default>
<m-list
@clickItem="clickItem"
@clickAction="clickAction"
:list="list"
:actions="actions"
></m-list>
</template>
</m-notification>
</template>
<script lang="ts" setup>
import { list, actions } from "./data";
const clickItem = (val: any) => {
console.log(val);
};
const clickAction = (val: any) => {
console.log(val);
};
</script>
<style lang="scss" scoped></style>