前端开发笔记[2]-页面布局
摘要
使用vue.js和mdui进行简单页面布局,实现一个聊天网页的大致框架.
平台信息
- macOS
- safari:版本16.6 (18615.3.12.11.2)
- node.js
- "vue": "^2.5.2",
- "vue-router": "^3.0.1"
- "mdui": "^1.0.2",
- "eslint": "^4.15.0"
CSS的viewport相关单位
视口(viewport)相关的单位 vw
、vh
等,如 100vw = 100% 视口宽度。但视口宽度是多少呢?响应式布局页面中,视口所发挥的作用如下。
viewport(视口)是用户可以看到的 Web 页面视口区域。在早期,许多 Web 页面没有针对移动端优化。由于移动设备像素比较低,多为 320px,从而在打开 PC 端的页面时,会因为视口太窄导致页面布局错乱。为了解决这个问题,浏览器会在一个虚拟视口中渲染页面,通常会将 viewport 设置为一个较大的值(如 Safari 默认是 980px),这样 PC 端的网页在移动设备浏览器上都能在一屏内显示所有内容,只是页面元素看上去比较小。用户可能需要通过水平滚动、缩放等操作才能查看页面的不同区域,体验很差。
但是,移动互联网发展了这么长时间,已经不再像移动互联网初始阶段一样存在大量超过 980px 的页面,同时,很多移动端站点都会使用媒体查询等其他技术来适配较窄的屏幕,那么浏览器提供的虚拟视口就不再必要了。
为了支持响应式布局,我们首先需要通过 viewport meta 标签正确设置页面的 viewport。一个针对移动端做过优化的站点中,应该包含以下类似的内容:
<meta name="viewport" content="width=device-width, initial-scale=1">
其中 width 控制视口的宽度,可以设置为设备宽度 device-width,或是具体的像素值。在上面的例子中,viewport 的宽度设置为 device-width。这意味着,如果屏幕宽度是 375px(如 iPhone X),则浏览器也使用 375px 的视口宽度来渲染页面,而不是使用 980px 或其他宽度使页面缩小展示。initial-scale=1 设置了浏览器首次加载页面时的初始缩放级别。我们还可以通过 minimum-scale 和 maximum-scale 等属性控制用户的缩放操作,但这也可能导致页面的可访问性问题。
mdui进行页面布局
[https://www.mdui.org/docs/introduction]
[https://gitee.com/zdhxiong/mdui]
MDUI:漂亮、轻量且好用,它能让你更轻松地开发 Material Design 网页应用.
MDUI 提供了一套响应式、移动设备优先的 12 列网格布局系统。
- .mdui-col-xs-[1-12] 所有屏幕设备上都会生效,如手机、电脑等。
- .mdui-col-sm-[1-12] 在小屏幕及以上的设备上生效,如平板电脑。
- .mdui-col-md-[1-12] 在中等屏幕及以上的设备上生效,如笔记本电脑。
- .mdui-col-lg-[1-12] 在大屏幕及以上的设备上生效,如台式电脑。
- .mdui-col-xl-[1-12] 在特大屏幕设备上生效,如电视。
混用这些类,可以达到响应式的效果。
添加涟漪动画效果后,会在点击元素时,产生向外扩散的水波纹效果。
vue.js的页面构成
[https://www.yisu.com/zixun/624919.html]
一个vue页面由三部分组成:
- template标签包裹的界面展示代码(HTML代码)
- script标签包裹的业务实现代码(js脚本代码)
- style标签包裹的界面布局代码(css样式代码)
现在流行的Vue项目中,Vue页面都是采用组件套娃的形式,由一个一个的组件拼接而成整个页面。一个组件就是一个.vue文件。
设置node的localhost为0.0.0.0
[https://blog.csdn.net/chinatopno1/article/details/104035082]
[https://segmentfault.com/q/1010000012132756?sort=created]
[https://blog.csdn.net/Cookysurongbin/article/details/86077241]
修改项目下config目录下的index.js的文件.
vue-cli搭建的环境,用nginx做代理服务器,访问时显示:Invalid Host header
经查是因为新版的webpack-dev-server出于安全考虑,默认检查hostname,如果hostname不是配置内的就不能访问。这样有2种方法,一种是设置跳过host检查,一种是直接host设置成你的地址。
-
关闭host检查
可以在build目录下的webpack.dev.conf.js文件,devServer下添加disableHostCheck: true,跳过检查
同样的原理,可以在package.json文件修改scripts命令:webpack-dev-server --disableHostCheck=true -
设置成你的host,加入你的host是xxx.com,同样2中方法,修改配置文件,和script命令
在config目录下修改index.js文件的host,这个默认是localhost,可修改成 xxx.com
package.json的script语句: webpack-dev-server --host=xxx.com或者--public=xxx.com.
vim package.json
#修改scripts命令:webpack-dev-server --disableHostCheck=true
vim config/index.js
#host: '0.0.0.0'
#port: 8666
访问[127.0.0.1:8666]即可.
实现
目录结构
./components
├── MessageBubbleMe.vue
├── MessageBubbleShe.vue
├── test-cookie.vue
├── test-indexedDB.vue
├── test-mdui.vue
└── test-webStorage.vue
./pages
├── ChatPage.vue
├── HelloWorld.vue
├── LoginPage.vue
├── SettingsPage.vue
└── SideBarPage.vue
关键代码
ChatPage.vue
<!--主页面-->
<template>
<!--start 顶层容器-->
<div class="mdui-row">
<!--start 侧边栏容器-->
<div class="mdui-col-md-3" id="chatpage-sidebar" style="background-color:#c7edcc;height:100vh;opacity: 0.8;/*80%透明度*/">
<!-- 侧边栏内容 -->
<SideBarPage />
</div>
<!--end 侧边栏容器-->
<!--start 聊天区容器-->
<div class="mdui-col-sm-12 mdui-col-md-9" id="chatpage-content" style="background-color:white;height:100vh;opacity: 0.8;/*80%透明度*/">
<!-- start 消息区域 MessageArea -->
<div class="message-area">
<!-- 自动插入消息气泡 -->
<!-- end 消息区域 MessageArea -->
</div>
<!-- start 编辑区域 EditArea -->
<div class="edit-area">
<!-- start 编辑文本块 -->
<div class="edit-text-block mdui-textfield">
<textarea class="mdui-textfield-input mdui-ripple" rows="4" placeholder="在这里输入内容..."></textarea>
<!-- end 编辑文本块 -->
</div>
<!-- start 编辑小部件块 -->
<div class="edit-widgets-block">
<!-- start 添加图片按钮 -->
<button class="add-img-button mdui-btn mdui-ripple">
<i class="mdui-icon material-icons"></i>
<!-- end 添加图片按钮 -->
</button>
<!-- start 添加语音按钮 -->
<button class="add-voice-button mdui-btn mdui-ripple">
<i class="mdui-icon material-icons"></i>
<!-- start 添加语音按钮 -->
</button>
<!-- start 添加按钮 -->
<button class="add-button mdui-btn mdui-ripple">
<i class="mdui-icon material-icons"></i>
<!-- end 添加按钮 -->
</button>
<!-- start 发送按钮 -->
<button class="edit-send-button mdui-btn mdui-color-indigo mdui-ripple">
<i class="mdui-icon material-icons"></i>
<!-- end 发送按钮 -->
</button>
</div>
<!-- end 编辑小部件块 -->
</div>
<!-- end 编辑区域 EditArea -->
<!--end 聊天区容器-->
</div>
<!--end 顶层容器-->
</div>
</template>
<style>
/* 竖屏模式的侧边栏 */
#chatpage-sidebar {
display: none;
}
/* 横屏模式的侧边栏 */
@media (orientation: landscape) {
#chatpage-sidebar {
display: block;
}
}
/* 编辑区域 */
.edit-area{
position: absolute;
bottom: 0;
right: 0;
backdrop-filter: blur(10px); /* 调整模糊程度 */
/*background-color: rgba(0, 255, 255, 0.5); /* 调整背景颜色和透明度 */
}
</style>
<script>
/* eslint-disable */
import SideBarPage from '@/pages/SideBarPage'
import MessageBubbleMe from '@/components/MessageBubbleMe'
import MessageBubbleShe from '@/components/MessageBubbleShe'
import mdui from 'mdui';
//import { openDB } from 'idb';
export default {
name: 'MainPage',
components: {
SideBarPage,
MessageBubbleShe,
MessageBubbleMe
}
}
</script>
SideBarPage.vue
<template>
<!-- start 侧边栏容器 SideBarPage -->
<div class="mdui-row">
<!-- start SideBarSettings 区域 -->
<div class="mdui-col-xs-12 sidebar-settings-area">
<h2>侧边栏设置</h2>
<!-- start 清除对话历史按钮 -->
<button class="mdui-btn mdui-color-indigo mdui-col-xs-5 mdui-ripple" @click="clearChatHistory">
<i class="mdui-icon material-icons"></i>
<span class="mdui-text-truncate" style="max-width: 5px;">清除历史</span>
<!-- end 清除对话历史按钮 -->
</button>
<!-- start 设置对话API按钮 -->
<button class="mdui-btn mdui-color-indigo mdui-col-xs-5 mdui-ripple" @click="gotoChatApiSettings">
<i class="mdui-icon material-icons"></i>
<span>设置对话API</span>
<!-- end 设置对话API按钮 -->
</button>
<!-- start 进入设置页面按钮 -->
<button class="mdui-btn mdui-color-indigo mdui-col-xs-5 mdui-ripple" @click="gotoSettingsPage">
<i class="mdui-icon material-icons"></i>
<span>设置</span>
<!-- end 进入设置页面按钮 -->
</button>
<!-- end SideBarSettings 区域 -->
</div>
<!-- start 分割线 -->
<div class="mdui-divider">
<!-- end 分割线 -->
</div>
<!-- start Plugins 区域 -->
<div class="plugins-area mdui-col-xs-12">
<h2>插件</h2>
<!-- start MyMusicPlugin按钮 -->
<button class="mdui-btn mdui-color-indigo mdui-col-xs-5 mdui-ripple" @click="toggleMyMusicPlugin">
<i class="mdui-icon material-icons"></i>
<span>我的音乐</span>
<!-- end MyMusicPlugin按钮 -->
</button>
<!-- end Plugins 区域 -->
</div>
<!-- end 侧边栏容器 -->
</div>
</template>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
button{
margin:3px;
}
</style>
<script>
/* eslint-disable */
export default {
name: 'SideBarPage',
methods: {
clearChatHistory() {
console.log('ClearChatHistory');
},
gotoChatApiSettings() {
console.log('gotoChatApiSettings');
},
gotoSettingsPage() {
console.log('gotoSettingsPage');
},
toggleMyMusicPlugin() {
console.log('toggleMyMusicPlugin');
}
}
}
</script>
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>晓晓学习助手</title>
</head>
<style>
#main-background {
background-color: #c7edcc; /* 豆沙绿 */
background-image: url("./static/imgs/background-girl1.jpg");
background-size: cover;
background-repeat: no-repeat;
background-position: center;
width:100vw;
height:100vh;
}
/* 横屏时应用不同的背景图像 */
@media screen and (orientation: landscape) {
#main-background {
background-image: url("./static/imgs/background-girl2.jpg");
}
}
</style>
<body id="main-background">
<!--开始 MDUI 布局容器-->
<div class="mdui-container-fluid">
<!--开始 Vue.js自动插入内容-->
<div id="app"></div>
<!-- built files will be auto injected -->
<!--对应template中 id='app' 的内容-->
</div>
<!--结束 Vue.js自动插入内容-->
</div>
<!--结束 MDUI 布局容器-->
</body>
</html>
MessageBubbleShe.vue
<template>
<!-- start 她的消息气泡 MessageBubble-She -->
<div class="message-bubble message-bubble-she">
<!--start 她的消息文本 -->
<div class="message-text">
你好,我是你的 AI 朋友晓晓!希望我能帮到你。
<!--end 她的消息文本 -->
</div>
<!-- start 她的消息小部件 -->
<div class="message-widgets">
<!-- start 消息点赞按钮 -->
<button class="message-like-button mdui-btn">
<i class="mdui-icon material-icons"></i>
<!-- end 消息点赞按钮-->
</button>
<!-- start 消息不喜欢按钮 -->
<button class="message-unlike-button mdui-btn">
<i class="mdui-icon material-icons"></i>
<!-- end 消息不喜欢按钮 -->
</button>
<!-- start 消息复制按钮 -->
<button class="message-unlike-button mdui-btn">
<i class="mdui-icon material-icons"></i>
<!-- end 消息复制按钮 -->
</button>
<!-- 消息时间戳 -->
<b class="message-timestamp mdui-chip-title">2023-8-20 23:34:44</b>
<!-- start 消息状态 -->
<i class="mdui-icon material-icons message-status"></i>
<!-- <i class="mdui-icon material-icons"></i> -->
<!-- end 消息状态 -->
<!--end 她的消息小部件-->
</div>
<!-- end 她的消息气泡 MessageBubble-She -->
</div>
</template>
<style>
/* 她的消息气泡 */
.message-bubble-she {
text-align: left;
padding-right: 10%;
width:90%;
border-radius: 10px;
background-color: pink;
}
/* 气泡控件区 */
.message-widgets{
margin-right:0;
}
/* 气泡 */
.message-bubble{
/* 设置垂直间距为10像素 */
margin-bottom: 5vh;
margin-top: 5vh;
}
/* 气泡消息文本 */
.message-text{
padding: 5px;
}
</style>
<script>
/* eslint-disable */
export default {
name: 'MessageBubbleShe'
}
</script>
MessageBubbleMe.vue
<template>
<!-- start 我的消息气泡 MessageBubble-Me -->
<div class="message-bubble message-bubble-me">
<!-- start 我的消息文本 -->
<div class="message-text">
你好!
<!-- end 我的消息文本 -->
</div>
<!-- start 我的消息小部件 -->
<div class="message-widgets">
<!-- start 消息点赞按钮 -->
<button class="message-like-button mdui-btn">
<i class="mdui-icon material-icons"></i>
<!-- end 消息点赞按钮 -->
</button>
<!-- start 消息不喜欢按钮 -->
<button class="message-unlike-button mdui-btn">
<i class="mdui-icon material-icons"></i>
<!-- end 消息不喜欢按钮 -->
</button>
<!-- start 消息复制按钮 -->
<button class="message-unlike-button mdui-btn">
<i class="mdui-icon material-icons"></i>
<!-- end 消息复制按钮 -->
</button>
<!-- 消息时间戳 -->
<b class="message-timestamp mdui-chip-title">2023-8-20 23:33:33</b>
<!-- start 消息状态 -->
<i class="mdui-icon material-icons message-status"></i>
<!-- <i class="mdui-icon material-icons"></i> -->
<!-- end 消息状态 -->
<!--end 我的消息小部件-->
</div>
<!-- end 我的消息气泡 MessageBubble-Me -->
</div>
</template>
<style>
/* 我的消息气泡 */
.message-bubble-me {
text-align: right;
padding-left: 10%;
width:90%;
border-radius: 10px;
background-color: lightblue;
}
/* 气泡控件区 */
.message-widgets{
margin-right:0;
}
/* 气泡 */
.message-bubble{
/* 设置垂直间距为10像素 */
margin-bottom: 5vh;
margin-top: 5vh;
}
/* 气泡消息文本 */
.message-text{
padding: 5px;
}
</style>
<script>
/* eslint-disable */
export default {
name: 'MessageBubbleMe'
}
</script>
封装为docker镜像
docker pull node:lts-hydrogen
docker run -it --network=bridge -v $PWD:/srv -p 8666:8666 node:lts-hydrogen bash
export https_proxy=http://192.168.31.94:7890 http_proxy=http://192.168.31.94:7890 all_proxy=socks5://192.168.31.94:7890
cp -r /srv/learn-sys /home/node
cd /home/node/learn-sys
npm install
npm run dev
docker commit da5f9f8f1db0 qsbye/runtime-env2:v0.2
docker push qsbye/runtime-env2:v0.2
编译&运行
docker run -it -p 8666:8666 qsbye/runtime-env2:v0.2 bash -c "cd /home/node/learn-sys && npm run dev"
访问:[127.0.0.1:8666]
效果
有气泡 | 有侧边栏 |
---|---|
![]() |
![]() |
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek “源神”启动!「GitHub 热点速览」
· 我与微信审核的“相爱相杀”看个人小程序副业
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 如何使用 Uni-app 实现视频聊天(源码,支持安卓、iOS)
· C# 集成 DeepSeek 模型实现 AI 私有化(本地部署与 API 调用教程)