企业级通用业务 Header 处理方案

目录

01: 处理 PC 端基础架构 

02: 通用组件:search 搜索框能力分析

03: 通用组件:search 搜索框样式处理

04: 通用组件:Button 按钮能力分析 

05: 通用组件:Button 按钮功能实现 

06: 通用组件:完善 search 基本能力

07: 通用组件:popover 气泡卡片能力分析

08: 通用组件:popover 气泡卡片基础功能实现 

09: 通用组件:popover 功能延伸,控制气泡展示位置 

10: 通用组件:处理慢速移动时,气泡消失问题 


01: 处理 PC 端基础架构 

- layout
- - components
- - - header
- - - - index.vue
- - - floating.vue
- - - main.vue
- - index.vue
// 设置 header 和 main 区域高度// tailwind.config.js
module.exports = {……theme: {extend: {……height: {header: '72px',main: 'calc(100vh - 72px)'}}}
}// 使用
// l-white => shadow-l-white
// height  => h-header<header-vue class="h-header"></>
<main-vue class="h-main"></>

02: 通用组件:search 搜索框能力分析

        既然是通用组件,就需要分析它的能力,它应该具备什么样的功能:

        1. 输入内容实现双向数据绑定

        2. 鼠标移入与获取焦点时的动画

        3. 一键清空文本功能

        4. 搜索触发功能

        5. 可控制,可填充的下拉展示区

        6. 监听到以下事件列表:

                1. clear:删除所有文本事件

                2. input:输入事件

                3. focus:获取焦点事件

                4. blur:失去焦点事件

                5. search:触发搜索(点击或回车)事件

03: 通用组件:search 搜索框样式处理

 

- libs
- - search
- - - index.vue
<template><divref="containerTarget"class="group relative p-0.5 rounded-xl border-white duration-500 hover:bg-red-100/40"><div><!-- 搜索图标 --><m-svg-iconclass="w-1.5 h-1.5 absolute translate-y-[-50%] top-[50%] left-2"name="search"color="#707070"/><!-- 输入框 --><inputclass="block w-full h-[44px] pl-4 text-sm outline-0 bg-zinc-100 dark:bg-zinc-800 caret-zinc-400 rounded-xl text-zinc-900 dark:text-zinc-200 tracking-wide font-semibold border border-zinc-100 dark:border-zinc-700 duration-500 group-hover:bg-white dark:group-hover:bg-zinc-900 group-hover:border-zinc-200 dark:group-hover:border-zinc-700 focus:border-red-300"type="text"placeholder="搜索"v-model="inputValue"@focus="onFocusHandler"@blur="onBlurHandler"@keyup.enter="onSearchHandlder"/><!-- 删除按钮 --><m-svg-iconv-show="inputValue"name="input-delete"class="h-1.5 w-1.5 absolute translate-y-[-50%] top-[50%] right-9 duration-500 cursor-pointer"@click="onClearClick"></m-svg-icon><!-- 分割线 --><divclass="opacity-0 h-1.5 w-[1px] absolute translate-y-[-50%] top-[50%] right-[62px] duration-500 bg-zinc-200 group-hover:opacity-100"></div><!-- TODO: 搜索按钮(通用组件) --><m-buttonclass="absolute translate-y-[-50%] top-[50%] right-1 rounded-xl duration-500 opacity-0 group-hover:opacity-100"icon="search"iconColor="#ffffff"@click="onSearchHandlder"></m-button></div><!-- 下拉区 --><transition name="slide"><divv-if="$slots.dropdown"v-show="isFocus"class="max-h-[368px] w-full text-base overflow-auto bg-white dark:bg-zinc-800 absolute z-20 left-0 top-[56px] p-2 rounded border border-zinc-200 dark:border-zinc-600 duration-200 hover:shadow-3xl scrollbar-thin scrollbar-thumb-zinc-200 dark:scrollbar-thumb-zinc-900 scrollbar-track-transparent"><slot name="dropdown" /></div></transition></div>
</template><script>
// 更新事件
const EMIT_UPDATE_MODELVALUE = 'update:modelValue'
// 触发搜索(点击或回车)事件
const EMIT_SEARCH = 'search'
// 删除所有文本事件
const EMIT_CLEAR = 'clear'
// 输入事件
const EMIT_INPUT = 'input'
// 获取焦点事件
const EMIT_FOCUS = 'focus'
// 失去焦点事件
const EMIT_BLUR = 'blur'
</script><script setup>
import { watch, ref } from 'vue'
import { useVModel, onClickOutside } from '@vueuse/core'const props = defineProps({modelValue: {type: String,required: true}
})const emits = defineEmits([EMIT_UPDATE_MODELVALUE,EMIT_CLEAR,EMIT_INPUT,EMIT_FOCUS,EMIT_BLUR,EMIT_SEARCH
])// 输入文本
const inputValue = useVModel(props)/*** 清空文本*/
const onClearClick = () => {inputValue.value = ''emits(EMIT_CLEAR, '')
}/*** 触发搜索*/
const onSearchHandlder = () => {emits(EMIT_SEARCH, inputValue.value)
}/*** 监听焦点行为*/
const isFocus = ref(false)
const onFocusHandler = () => {isFocus.value = trueemits(EMIT_FOCUS)
}/*** 失去焦点*/
const onBlurHandler = () => {emits(EMIT_BLUR)
}/*** 点击区域外隐藏 dropdown*/
const containerTarget = ref(null)
onClickOutside(containerTarget, () => {isFocus.value = false
})/*** 监听输入行为*/
watch(inputValue, (val) => {emits(EMIT_INPUT, val)
})
</script><style lang="scss" scoped>
.slide-enter-active {transition: all 0.5s;
}.slide-leave-active {transition: all 0.5s;
}.slide-enter-from,
.slide-leave-to {transform: translateY(40px);opacity: 0;
}
</style>

04: 通用组件:Button 按钮能力分析 

对于这个按钮来说,我们期望拥有以下能力:

        1. 可以显示文字按钮,并提供 loading 功能

        2. 可以显示 icon 按钮,并可以任意指定 icon 颜色

        3. 可以开关的点击动画

        4. 可以指定各种风格和大小

        5. 当指定的风格或大小不符合预设时,需要给开发者以提示消息

05: 通用组件:Button 按钮功能实现 

- libs
- - button
- - - index.vue
/*** 实现步骤:* 1. 构建 type 风格可选项 和 size 大小可选项* 2. 通过 props 让开发者控制按钮* 3. 区分 icon button 和 text button* 4. 依据当前数据,实现视图* 5. 处理点击事件*/

 书写习惯:setup 是写逻辑的地方,不希望在这里写大量的常量。可以在 <script setup> 上面再去创建一个 <script>

// 定义 main 颜色
// tailwind.config.js
module.exports = {theme: {extend: {colors: {main: '#f44c58','hover-main': '#F2F9EC',}}}
}// 使用
class = "bg-main"
<script>
// type 可选项:表示按钮风格
const typeEnum = {primary:'text-white  bg-zinc-800 dark:bg-zinc-900  hover:bg-zinc-900 dark:hover:bg-zinc-700 active:bg-zinc-800 dark:active:bg-zinc-700',main: 'text-white  bg-main dark:bg-zinc-900  hover:bg-hover-main dark:hover:bg-zinc-700 active:bg-main dark:active:bg-zinc-700',info: 'text-zinc-800 dark:text-zinc-300  bg-zinc-200 dark:bg-zinc-700 hover:bg-zinc-300 dark:hover:bg-zinc-600 active:bg-zinc-200 dark:active:bg-zinc-700 '
}
// size 可选项:表示按钮大小。区分文字按钮和icon按钮
const sizeEnum = {default: {button: 'w-8 h-4 text-base',icon: ''},'icon-default': {button: 'w-4 h-4',icon: 'w-1.5 h-1.5'},small: {button: 'w-7 h-3 text-base',icon: ''},'icon-small': {button: 'w-3 h-3',icon: 'w-1.5 h-1.5'}
}
</script>
// 通过 props 让开发者控制按钮
<script setup>
const props = defineProps({// icon 图标名字icon: {type: String},// icon 图标颜色iconColor: {type: String},// icon 图标类名(匹配 tailwind)iconClass: {type: String},// 按钮风格type: {type: String,default: 'main',validator(val) {// 获取所有的可选的按钮风格const keys = Object.keys(typeEnum)// 开发者指定风格是否在可选风格中const result = keys.includes(val)// 如果不在则给开发者提示if (!result) {throw new Error(`你的 type 必须是 ${keys.join('、')} 中的一个`)}// 返回校验结果return result}},// 大小风格size: {type: String,default: 'default',validator(val) {// 获取所有的可选的大小(注意剔除 icon 开头的元素,因为我们期望开发者输入 size="default",但不期望开发者输入 size="icon-default")const keys = Object.keys(sizeEnum).filter((key) => !key.includes('icon'))// 开发者指定大小是否在可选大小中const result = keys.includes(val)// 如果不在则给开发者提示if (!result) {throw new Error(`你的 size 必须是 ${keys.join('、')} 中的一个`)}// 返回校验结果return result}},// 按钮在点击时是否需要动画isActiveAnim: {type: Boolean,default: true},// 加载状态loading: {type: Boolean,default: false}
})
</script>
// 区分 icon button 和 text button
// 传递了 icon props 则默认按钮类型为 icon button// 处理大小的 key 值
const sizeKey = computed(() => {return props.icon ? 'icon-' + props.size : props.size
})
// 依据当前的数据,实现视图
<template><buttonclass="text-sm text-center rounded duration-150 flex justify-center items-center":class="[typeEnum[type],sizeEnum[sizeKey].button,{ 'active:scale-105': isActiveAnim }]"@click.stop="onBtnClick"><!-- 展示 loading --><m-svg-iconv-if="loading"name="loading"class="w-2 h-2 animate-spin mr-1"></m-svg-icon><!-- icon 按钮 --><m-svg-iconv-if="icon":name="icon"class="m-auto":class="sizeEnum[sizeKey].icon":color="iconColor":fillClass="iconClass"></m-svg-icon><!-- 文字按钮 --><slot v-else /></button>
</template>
// 处理点击事件
const EMITS_CLICK = 'click'
const emits = defineEmits([EMITS_CLICK])
/*** 按钮点击事件处理*/
const onBtnClick = () => {if (props.loading) {return}emits(EMITS_CLICK)
}

06: 通用组件:完善 search 基本能力

/*** 1. 输入内容实现双向数据绑定* 2. 搜索按钮在 hover 时展示* 3. 一键清空文本功能* 4. 触发搜索* 5. 控制下拉展示区的展示* 6. 事件处理*/
// 事件处理:
//     双向绑定
//     search 搜索
//     删除所有文本
//     输入事件
//     获取焦点事件
//     失去焦点事件

07: 通用组件:popover 气泡卡片能力分析

/*** 具备两个插槽。*     第一个插槽描述触发弹出层的视图。这个视图可以定为具名插槽。*     第二个插槽描述弹出层内容。这个内容可以定为匿名插槽。* 弹出层气泡可以在指定位置弹出。*/

08: 通用组件:popover 气泡卡片基础功能实现 

- libs
- - popover
- - - index.vue
<template><div class="relative" @mouseleave="onMouseleave" @mouseenter="onMouseenter"><div ref="referenceTarget"><!-- 具名插槽 --><slot name="reference" /></div><!-- 气泡展示动画 --><transition name="slide"><divv-show="isVisable"ref="contentTarget"class="absolute p-1 z-20 bg-white dark:bg-zinc-900 border rounded-md dark:border-zinc-700":style="contentStyle"><!-- 匿名插槽 --><slot /></div></transition></div>
</template><script>
// 延迟关闭时长
const DELAY_TIME = 100const PROP_TOP_LEFT = 'top-left'
const PROP_TOP_RIGHT = 'top-right'
const PROP_BOTTOM_LEFT = 'bottom-left'
const PROP_BOTTOM_RIGHT = 'bottom-right'// 定义指定位置的 Enum
const placementEnum = [PROP_TOP_LEFT,PROP_TOP_RIGHT,PROP_BOTTOM_LEFT,PROP_BOTTOM_RIGHT
]
</script><script setup>
import { ref, watch, nextTick } from 'vue'const props = defineProps({// 控制气泡弹出位置,并给出开发者错误的提示placement: {type: String,default: 'bottom-left',validator(val) {const result = placementEnum.includes(val)if (!result) {throw new Error(`你的 placement 必须是 ${placementEnum.join('、')} 中的一个`)}return result}}
})// 控制 menu 展示
const isVisable = ref(false)// 控制延迟关闭
let timeout = null
/*** 鼠标移入的触发行为*/
const onMouseenter = () => {isVisable.value = true// 再次触发时,清理延时装置if (timeout) {clearTimeout(timeout)}
}
/*** 鼠标移出的触发行为*/
const onMouseleave = () => {// 延时装置timeout = setTimeout(() => {isVisable.value = falsetimeout = null}, DELAY_TIME)
}/*** 计算元素尺寸*/
const referenceTarget = ref(null)
const contentTarget = ref(null)
const useElementSize = (target) => {if (!target) return {}return {width: target.offsetWidth,height: target.offsetHeight}
}/*** 计算弹层位置*/
const contentStyle = ref({top: 0,left: 0
})/*** 监听展示的变化,在展示时计算气泡位置*/
watch(isVisable, (val) => {if (!val) {return}// 等待渲染成功之后nextTick(() => {switch (props.placement) {// 左上case PROP_TOP_LEFT:contentStyle.value.top = 0contentStyle.value.left =-useElementSize(contentTarget.value).width + 'px'break// 右上case PROP_TOP_RIGHT:contentStyle.value.top = 0contentStyle.value.left =useElementSize(referenceTarget.value).width + 'px'break// 左下case PROP_BOTTOM_LEFT:contentStyle.value.top =useElementSize(referenceTarget.value).height + 'px'contentStyle.value.left =-useElementSize(contentTarget.value).width + 'px'break// 右下case PROP_BOTTOM_RIGHT:contentStyle.value.top =useElementSize(referenceTarget.value).height + 'px'contentStyle.value.left =useElementSize(referenceTarget.value).width + 'px'break}})
})
</script><style lang="scss" scoped>
// slide 展示动画
.slide-enter-active {transition: opacity 0.3s, transform 0.3s;
}.slide-leave-active {transition: opacity 0.3s, transform 0.3s;
}.slide-enter-from,
.slide-leave-to {transform: translateY(20px);opacity: 0;
}
</style>

09: 通用组件:popover 功能延伸,控制气泡展示位置 

/*** 步骤:* 1. 指定所有可选位置的常量,并生成 enum* 2. 通过 prop 控制指定位置* 3. 获取元素的 DOM;创建读取元素尺寸的方法* 4. 生成气泡的样式对象,用来控制每个位置对应的样式* 5. 根据 prop,计算样式对象*/

10: 通用组件:处理慢速移动时,气泡消失问题 

        想要解决这个问题,可以利用 类似于防抖(debounce)的概念。

        也就是:鼠标刚离开时,不去立刻修改 isVisible,而是延迟一段时间,如果在这段时间之内,再次触发了鼠标移入事件,则不再修改 isVisible。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/1420736.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

Photoshop 2022 for Mac/win:释放创意,打造专业级的图像编辑体验

在数字图像编辑的世界里&#xff0c;Adobe Photoshop 2022无疑是那颗璀璨的明星。这款专为Mac和Windows用户设计的图像处理软件&#xff0c;以其卓越的性能和丰富的功能&#xff0c;赢得了全球数百万创作者的青睐。 Photoshop 2022在继承前代版本强大功能的基础上&#xff0c;…

Appium 2.x 安装及使用

由于安全问题&#xff0c;Appium 1.x 版本不再被维护&#xff0c;但想要继续使用Appium进行自动化可以使用 Appium 2.x。 1. 安装Appium 2.x 在过往文章中有介绍过Appium 1.x 的安装&#xff0c;所以一些必备的软件(如&#xff1a;JDK、SDK、node.js、Python)安装就不再细嗦&…

STM32串口通信入门

文章目录 一、串口协议和RS-232标准&#xff0c;以及RS232电平与TTL电平的区别1.串口通信协议2.RS-232标准3.RS232电平与TTL电平的区别4.USB/TTL转232“模块&#xff08;CH340芯片为例&#xff09; 二、补充实验&#xff08;一&#xff09;几个常见的库函数、结构体1.时钟配置函…

C++小程序:同一路由器下两台计算机间简单通信(2/2)——客户端

客户端的程序结构前半部分与服务器端基本相同&#xff0c;后半部分也相对简单。相关函数的解释可以参考前文服务器端的内容。有关客户端的内容除个别地方外&#xff0c;就不再做长篇大论的解释。强调一点&#xff0c;如果将此程序移到其它电脑上运行&#xff0c;编译需要releas…

Vue路由拆分

1.在src下建立router&#xff0c;在router中建立文件index 2.将main.js中部分内容复制 App <template> <div><a href"#/friend">朋友</a><br><a href"#/info">信息</a><br><a href"#/music&quo…

支持播放h265的插件

插件源码地址&#xff1a;GitCode - 开发者的代码家园https://gitcode.com/mirrors/nanguantong/flv-h265.js/overview 1. 下载代码 运行以下命令 npm i npm run build npm run build 后生成 flv.min.js 文件&#xff0c;引入使用 2. 调用 js 文件&#xff1a; let flvj…

完全背包问题(c++)

完全背包问题 当前有 N 种物品&#xff0c;第 i 种物品的体积是 ci​&#xff0c;价值是 wi​。 每种物品的数量都是无限的&#xff0c;可以选择任意数量放入背包。 现有容量为 V 的背包&#xff0c;请你放入若干物品&#xff0c;使总体积不超过 V&#xff0c;并且总价值尽可…

msvcp140dll怎么修复,分享5种有效的解决方法

MSVCP140.dll文件丢失这一现象究竟是何缘由&#xff0c;又会引发哪些令人头疼的问题呢&#xff1f;在探索这个问题的答案之前&#xff0c;我们先来深入了解这个神秘的DLL文件。MSVCP140.dll是Microsoft Visual C Redistributable Package的一部分&#xff0c;它扮演着至关重要的…

景源畅信:抖音小店的商品怎么同步到橱窗?

在数字营销的海洋中&#xff0c;抖音小店与橱窗的同步操作无疑是商家们关注的焦点。这不仅能增加商品的曝光度&#xff0c;还能提高交易的可能性。那么&#xff0c;如何将抖音小店的商品同步到橱窗呢? 一、核心步骤解析 要实现商品从抖音小店同步到橱窗&#xff0c;你需要确保…

汇昌联信:做拼多多网点需要具备什么能力?

在当前电商行业高速发展的背景下&#xff0c;拼多多以其独特的商业模式迅速崛起&#xff0c;成为众多创业者和商家关注的焦点。想要运营一家成功的拼多多网点&#xff0c;不仅需要对平台规则有深入的了解&#xff0c;还需要具备多方面的能力。这些能力是确保网点稳定运营并实现…

VBA_NZ系列工具NZ07:日期录入控件

我的教程一共九套及VBA汉英手册一部&#xff0c;分为初级、中级、高级三大部分。是对VBA的系统讲解&#xff0c;从简单的入门&#xff0c;到数据库&#xff0c;到字典&#xff0c;到高级的网抓及类的应用。大家在学习的过程中可能会存在困惑&#xff0c;这么多知识点该如何组织…

Multisim 14 常见电子仪器的使用和Multisim的使用

multisim multisim&#xff0c;即电子电路仿真设计软件。Multisim是美国国家仪器&#xff08;NI&#xff09;有限公司推出的以Windows为基础的仿真工具&#xff0c;适用于板级的模拟/数字电路板的设计工作。它包含了电路原理图的图形输入、电路硬件描述语言输入方式&#xff0…

利用PS在不伤背景的前提下根据颜色去除图像上不想要的内容

下面为一个例子&#xff0c;去除图像上红色的虚线 Step1.用套索工具框选带有颜色的部分 Step2.切换到魔术棒工具&#xff0c;上端选项中&#xff0c;点击与选区交叉&#xff0c;连续这一项不要勾选 Step3.在需要去除的部分点击一下即可在框选范围内选中所有同颜色的区域&#x…

# 从浅入深 学习 SpringCloud 微服务架构(十七)--Spring Cloud config(1)

从浅入深 学习 SpringCloud 微服务架构&#xff08;十七&#xff09;–Spring Cloud config&#xff08;1&#xff09; 一、配置中心的 概述 1、配置中心概述 对于传统的单体应用而言&#xff0c;常使用配置文件来管理所有配置&#xff0c;比如 SpringBoot 的 application.y…

事件高级。

一、注册事件&#xff08;绑定事件&#xff09; 就是给元素添加事件 注册事件有两种方式&#xff1a;传统方式和方法监听注册方式 1 传统注册方式 方法监听注册事件 2、 addEventListener 事件监听方式 里面的事件类型是字符串&#xff0c;必定加引号&#xff0c;而且不带o…

VScode 修改 Markdown Preview Enhanced 主题与字体

VScode 修改 Markdown Preview Enhanced 主题与字体 1. 修改前后效果对比2. 修改主题2.1 更改默认主题2.2 修改背景色 3. 修改字体 VS Code基础入门使用可查看&#xff1a; VS Code 基础入门使用&#xff08;配置&#xff09;教程 其他Vs Code 配置可关注查看&#xff1a; Vs C…

C++:9.scanf扩展——原来这么好用!

——scanf&#xff1a;我**不常用了&#xff1f; 有一天看到了一道题&#xff1a; C 输入一个时间&#xff0c;输出它属于&#xff0c;白天&#xff0c;下午还是黑夜。 输入样例&#xff1a; 15:20 00:00 13:14 05:20 11:45 14:00 ……??? 大胆题目小瞧我的编程水平!!!!!…

[图解]实现领域驱动设计译文暴露的问题02

0 00:00:00,500 --> 00:00:02,190 今天呢&#xff0c;我们 1 00:00:02,470 --> 00:00:04,820 继续来谈一谈 2 00:00:05,150 --> 00:00:07,790 实现领域驱动设计的中译本 3 00:00:08,630 --> 00:00:09,690 的译文 4 00:00:10,070 --> 00:00:11,710 暴露的问题…

BUU-二维码

题目 解题 打开是一张图片&#xff0c;扫描二维码后显示 secret is here 一时没有思路&#xff0c;看别人的wp https://blog.csdn.net/wusimin432503/article/details/125692459https://blog.csdn.net/weixin_45728231/article/details/120988424?spm1001.2101.3001.6661.1…

1.1. 离散时间鞅-条件期望

1.1. 离散时间鞅-条件期望 条件期望1. 条件期望的定义1.1. 条件期望的定义1.2. 条件期望的存在唯一性 2. 条件期望的示例2.1. X ∈ F X \in \mathcal{F} X∈F&#xff0c; X X X与 F \mathcal{F} F独立的情形2.2. X X X是有限 σ \sigma σ代数情形2.3. X X X是随机变量生成…