跳到主要内容

7 篇博文 含有标签「网站开发」

查看所有标签

· 阅读需 7 分钟
tjuzyp

本系列文章适用于前端开发初学者,我将从环境搭建开始,逐步介绍如何开发一个基于 Vue 3.x & Ant Design Vue 4.x 的网站,如果哪位朋友发现其中存在技术错误或其他不当之处,请及时指出,如果有任何疑问或需要帮助,也可以在评论区留言,我将尽我所能一一解答,谢谢。

本篇文章将介绍如何添加网站的加载等待动画、页面水印与编辑信息脚注。

  1. 当网站所在服务器的性能较差、网络带宽较低或其他技术原因导致网站的加载速度变慢时,加载动画能在一定程度上提高用户体验
  2. 页面水印可以在一定程度上增加页面的美观性,然而,过度使用或不当使用水印也可能引起用户的反感,常见的目的是用来保护版权和防止内容被未经授权地复制或分发
  3. 编辑信息脚注标识了内容的最后更新者与更新时间,它的主要作用是提供对页面中内容的补充说明

本篇文章我将结合三国史诗馆网站的页面优化进行介绍,代码实现较简单,主要考验的是审美,一不小心便会画蛇添足。

加载等待动画

加载等待动画等待的是什么?它常用于等待页面网络请求、处理数据、渲染组件及其他异步操作。

它的作用域包括页面整体等待、页面局部等待及组件自身等待等。

它的实现方式也多种多样,可以使用 setTimeout() 函数延迟加载、使用 jQuery 的 $(document).ready()、使用 window.onload()、使用 css 变换 display 元素、使用 loading 动画……

这次我介绍的是使用 loading 动画实现页面的整体等待,它是最基础最普遍的一种应用场景。

在 Vue 项目中,我们可以看到以下三段与此相关的代码:

  • 页面入口,创建 id="app" 的 div 元素
/public/index.html
...
<body>
<div id="app" />
</body>
  • 定义应用主程序,使用路由
/src/App.vue
<template>
<router-view/>
</template>
...
  • 创建并启动应用主程序
/src/main.js
...
// 引入主程序
import App from './App.vue';

// 创建应用
const app = createApp(App);
...
// 启动
app.mount('#app');

在上述代码中,app.mount('#app') 是将 Vue 应用挂载到具有 id 为 "app" 的 <div> 元素上,这意味着当执行该代码时,Vue 应用将替换 <div id="app" /> 元素的内容。

所以,我们可以在 <div id="app" /> 中增加一个动画,就实现了等待时显示、加载后销毁,代码如下:

/public/index.html
...
<link rel="stylesheet" href="/loading.css"/>
...
<div id="app">
<div class="fantastic-admin-home">
<div class="loading">
<div class="square"></div>
<div class="square"></div>
<div class="square"></div>
<div class="square"></div>
</div>
<div class="text">服务器性能有限, 请耐心等待</div>
</div>
</div>
...

以上代码是使用 css 画了一个动画,css 代码如下:

/public/loading.css
#app {
height: 100%;
}

.fantastic-admin-home {
position: absolute;
z-index: 10000;
top: 0;
left: 0;
height: 100%;
width: 100%;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
user-select: none;
color: #736477;
background-color: snow;
}

.fantastic-admin-home .loading {
height: 40px;
width: 40px;
display: flex;
align-items: center;
justify-content: center;
flex-wrap: wrap;
}

.fantastic-admin-home .loading .square {
display: flex;
align-items: center;
justify-content: center;
height: 20px;
width: 20px;
}

.fantastic-admin-home .loading .square::before {
content: "";
width: 10px;
height: 10px;
border-radius: 15%;
border: 3px solid #8c858f;
animation: square-to-dot-animation 2s linear infinite;
}

.fantastic-admin-home .loading .square:nth-child(1)::before {
animation-delay: calc(150ms * 1);
}

.fantastic-admin-home .loading .square:nth-child(2)::before {
animation-delay: calc(150ms * 2);
}

.fantastic-admin-home .loading .square:nth-child(3)::before {
animation-delay: calc(150ms * 3);
}

.fantastic-admin-home .loading .square:nth-child(4)::before {
animation-delay: calc(150ms * 4);
}

@keyframes square-to-dot-animation {
15%,
25% {
border-radius: 100%;
width: 0;
height: 0;
margin: 5px;
border-width: 5px;
}

40% {
border-radius: 15%;
width: 10px;
height: 10px;
margin: initial;
border-width: 3px;
}
}

.fantastic-admin-home .text {
position: relative;
font-size: 18px;
margin-top: 20px;
}

.fantastic-admin-home .text::after {
content: "....";
position: absolute;
padding-left: 1px;
}

效果如下:

加载等待动画

页面水印

页面水印是指在网页背景上添加的特定的文字/图案,它通常是半透明的形式,或者颜色非常浅,对网页原始内容的可读性不造成影响。

使用 Antdv 框架时,添加水印很容易,它已经帮我们封装好了,只需要在页面元素最外层增加 a-watermark 即可,代码如下:

/src/layout/index.vue
<template>
<a-watermark :height="30" :width="130" :image="require('@/assets/logo-full-watermark.png')">
// 原 a-layout 代码段
...
</a-watermark>
</template>
...

效果如下:

页面水印

编辑信息脚注

编辑信息脚注是我随便起的一个名字,它用于在一段内容的结尾处标记最后编辑信息,实现方式各种各样,我只用了最直接的一行代码,如下所示:

/src/views/record/personage/detail.vue
...
<p class="editInfo">最后由 xxx 编辑于 xxx</p>
...
.editInfo {
float: right;
color: lightgrey;
font-style: italic;
}

效果如下:

编辑信息脚注

总结

页面设计时尽量少加花里胡哨的东西,它会影响页面要展示的主体内容,也容易引起用户的反感。


本篇文章到这里就结束了,欢迎关注后续更新。

· 阅读需 11 分钟
tjuzyp

本系列文章适用于前端开发初学者,我将从环境搭建开始,逐步介绍如何开发一个基于 Vue 3.x & Ant Design Vue 4.x 的网站,如果哪位朋友发现其中存在技术错误或其他不当之处,请及时指出,如果有任何疑问或需要帮助,也可以在评论区留言,我将尽我所能一一解答,谢谢。

本篇文章将介绍关于不同尺寸终端的自动适配,由于终端尺寸复杂多样,而且设备/系统/应用兼容性存在差异,导致适配工作经常百密一疏(其实往往是一密百疏),所以它一直是前端开发最头疼的问题之一。

为确保网站、应用或其他界面在各种设备上都能够良好的呈现和使用,开发人员经常使用以下方法进行适配:

  1. 流式布局:它使用百分比单位和自适应容器来创建流式布局,使页面元素根据屏幕尺寸自动调整大小
  2. 弹性布局:它使用相对单位(如百分比、vw、em、rem 等)来定义元素的相对尺寸,以便元素可以根据父容器的大小进行伸缩
  3. 移动优先:首先考虑移动设备的设计风格及操作交互,然后再尽量适配大屏幕
  4. 使用 Viewport 参数控制页面在移动设备上的缩放显示
  5. 使用第三方组件库:例如 Vant、Mint UI 等,这些组件库已经做好了移动端大部分的适配工作

在具体的实现上,以上方法一般会组合使用,原本设计好了统一的风格,但它不能百分百适配所有情况,只能发现一个问题解决一个问题,结果就像打补丁,补丁接补丁、补丁摞补丁。

本篇文章我将结合三国史诗馆网站的终端适配进行介绍,包括以下部分:

  1. 常用配置/元素/单位的基本概念
  2. 三国史诗馆网站 PC 端与手机端的显示差异
  3. 主要代码改动

常用配置/元素/单位的基本概念

下方表格列举了一些比较常见的适配相关的前端名词:

名词解释使用示例
width/height定义元素的宽度/高度style="width:800px;height:600px;"
scrollWidth/scrollHeight对象的内容的实际宽度/高度let scrollWidth = document.body.scrollWidth;
clientWidth/clientHeight对象的内容的可视宽度/高度let clientWidth = document.body.clientWidth;
offsetWidth/offsetHeight对象的整体的可视宽度/高度(包括滚动条等)let offsetWidth = document.body.offsetWidth;
innerWidth/innerHeight浏览器窗口的内部宽度/高度let innerWidth = document.body.innerWidth;
font-size字体大小style="font-size:12px;"
px以像素为单位定义元素大小style="font-size:12px;"
pt以磅为单位定义元素大小style="font-size:12pt;"
vw相对于视口宽度(Viewport Width)的百分比style="width:10vw;" // 相当于视窗宽度的 10%
vh相对于视口高度(Viewport Width)的百分比style="width:10vh;" // 相当于视窗高度的 10%
em相对于当前元素字体大小的相对长度单位style="width:10em;"
rem相对于根元素字体大小的相对长度单位style="width:10rem;"
%相对于父元素的百分比值style="width:10%;"
@media媒体查询:根据不同的媒体类型和条件来区分各种设备media="screen" 或 @media screen
calc()一个可以进行数值基本运算的函数height:calc(100vh-64px);
span栅格布局中的栅格占位格数span="8" // 元素占父元素的 8/24 栅格
xs视窗宽度 <576px 时栅格占位格数xs="8" // 视窗宽度 <576px 时元素占父元素的 8/24 栅格
sm视窗宽度 ≥576px 时栅格占位格数sm="8" // 视窗宽度 ≥576px 时元素占父元素的 8/24 栅格
md视窗宽度 ≥768px 时栅格占位格数md="8" // 视窗宽度 ≥768px 时元素占父元素的 8/24 栅格
lg视窗宽度 ≥992px 时栅格占位格数lg="8" // 视窗宽度 ≥992px 时元素占父元素的 8/24 栅格
xl视窗宽度 ≥1200px 时栅格占位格数xl="8" // 视窗宽度 ≥1200px 时元素占父元素的 8/24 栅格
xxl视窗宽度 ≥1600px 时栅格占位格数xxl="8" // 视窗宽度 ≥1600px 时元素占父元素的 8/24 栅格

除常见名词外,其中我主要用到了以下几个:

  1. offsetWidth:使用它作为判断尺寸大小的依据
  2. vw:使用百分比宽度设置页面元素的 padding/margin,可以使元素间距在大小屏幕上都很协调
  3. vh:主要用于设置页面的最小高度,避免在宽高比不协调的设备中显示异常
  4. em:主要用于设置文字的字号与间距等
  5. calc():用于计算页面最小高度

显示差异及代码实现

整体布局

整体上主要是页面边距的差异,将在人物大全页面中进行说明。

在页面入口文件 App.vue 中获取页面可视宽度,将其存储到全局变量中,用于其他页面的样式选择判断,实现代码如下:

/src/App.vue
const storage = useStore();
// 更新页面宽度
const onResize = () => {
storage.commit('updateClientWidth', document.body.offsetWidth);
};

// 加载页面
onMounted(() => {
// 更新页面宽度
onResize();
window.addEventListener("resize", onResize);
});
// 销毁页面
onBeforeUnmount(() => {
window.removeEventListener("resize", onResize);
});

人物大全页面

  • 左上角的 logo 图片在 PC 端距页面左侧较远,手机端较近,可对比下方两个图片,实现代码如下:

左上角 logo边距(PC 端)

左上角 logo边距(手机端)

/src/layout/index.vue
...
<!-- 使用相对宽度 3.5vw 适配不同宽度页面 -->
<a-layout-header class="clear-fix" style="background-color: white; padding: 0 3.5vw">
...
  • 页面标题的边距、内容区的边距,同见上图,实现方式基本相同,略

  • 每行的卡片数量不同,在 PC 端有 6 个,而手机端只有 2 个(跟设备无关,跟页面尺寸有关),同见上图,实现代码如下:

/src/views/record/personage/index.vue
...
<!-- 使用 xs/sm/lg 适配不同宽度页面 -->
<a-col v-for="personage in personageList" :xs="12" :sm="8" :lg="4">
...
  • 卡片中人物头像尺寸不同,同见上图,实现代码如下:
/src/views/record/personage/index.vue
...
<!-- 综合页面宽度、卡片个数、最大宽度/高度,得出最终的宽度/高度 -->
<a-avatar :src="personage.recordPersonageEntity?.avatar" :style="{width: scrollWidth() * cardProportion() * 0.32 + 'px', height: scrollWidth() * cardProportion() * 0.32 + 'px', 'max-width': '66px', 'max-height': '66px'}"/>
...
  • 分页器样式不同,在 PC 端功能更丰富,可对比下方两个图片,实现代码如下:

分页器样式(PC 端)

分页器样式(手机端)

/src/views/record/personage/index.vue
...
<!-- 根据页面宽度选择使用不同样式的分页器 -->
<a-pagination v-if="scrollWidth() >= 800" :current="query.pageNum" :page-size="query.pageSize" showSizeChanger style="margin-top: 2vh; text-align: center;" :total="personageTotal" :show-total="(total, range) => `${range[0]}-${range[1]} of ${total} items`" @change="paginationChange"/>
<a-pagination v-else :current="query.pageNum" :page-size="query.pageSize" style="margin-top: 2vh; text-align: center;" :total="personageTotal" simple @change="paginationChange"/>
...

人物详情页面

人物详情页面的锚点菜单,在 PC 端存在但手机端不存在,可对比下方两个图片,实现代码如下:

人物详情页面(PC 端)

人物详情页面(手机端)

/src/views/record/personage/detail.vue
...
<!-- 根据页面宽度选择内容区所占列数 -->
<a-col :span="scrollWidth() >= 800 ? 20 : 24">
...
<!-- 根据页面宽度选择是否显示锚点菜单 -->
<a-col v-if="scrollWidth() >= 800" span="4" style="background-color: white;">
<a-anchor :affix="true" :offsetTop="30" style="margin-top: 8px; margin-left: 10px" :items="anchorItems"></a-anchor>
</a-col>
...

总结

目前我的网站的页面较少,而且页面较简单,所以做终端适配很容易,涉及到的技术也不多,但是其方法和核心思想是通用的,我的建议是:

  1. 尽量不使用 px 等固定单位,推荐使用百分比单位(%、vw、vh)与相对单位(em、rem)
  2. 一定不要追求 PC 端与手机端的功能一致,手机端界面尽量简洁、操作尽量方便
  3. 一定不要追求技术实现方案统一,也一定不要反复打补丁,要设计先行、随机应变

本篇文章到这里就结束了,欢迎关注后续更新。

· 阅读需 9 分钟
tjuzyp

本系列文章适用于前端开发初学者,我将从环境搭建开始,逐步介绍如何开发一个基于 Vue 3.x & Ant Design Vue 4.x 的网站,如果哪位朋友发现其中存在技术错误或其他不当之处,请及时指出,如果有任何疑问或需要帮助,也可以在评论区留言,我将尽我所能一一解答,谢谢。

本篇文章将介绍 ant-design 的几个组件的使用方法,同时我开发完成了三国演义人物详情页面,大家可以在三国史诗馆网站看到更新。

  1. 卡片 Card:通用卡片容器
  2. 分页 Pagination:采用分页的形式分隔长列表
  3. 排版 Typography:文本的基本格式
  4. 锚点 Anchor:用于跳转到页面指定位置
  5. 骨架屏 Skeleton:在需要等待加载内容的位置提供一个占位图形组合
  6. 描述列表 Descriptions:成组展示多个只读字段

卡片 Card

卡片可承载标题、文字、图片及按钮等内容,常用于概览页面,我用它展示人物的基本信息,代码如下:

/src/views/record/personage/index.vue
<a-card :loading="personage.loading" class="card" @click="detail(personage)" hoverable>
<a-card-meta :title="personage.recordPersonageEntity?.lastName + personage.recordPersonageEntity?.firstName" :description="personage.recordPersonageEntity?.courtesyName ? '字 ' + personage.recordPersonageEntity?.courtesyName : ''">
<template #avatar>
<a-avatar :src="personage.recordPersonageEntity?.avatar" style="height: 66px; width: 66px;"/>
</template>
</a-card-meta>
<br>
<a-typography-paragraph style="height: 4.5rem; line-height: 1.5rem;" :ellipsis="{rows: 3, expandable: false}" :content="personage.recordPersonageEntity?.introduction"/>
<template #actions>
...
<span @click.stop>
<a-tooltip title="评论">
<CommentOutlined @click="comment(personage)"/>
</a-tooltip>
</span>
</template>
</a-card>

细节说明:

  1. 卡片的 loading 效果通过 :loading="personage.loading" 实现,但是我没有做针对单个卡片的懒加载,所以不会出现这个效果,只停留在代码上
  2. 鼠标悬停效果是通过 hoverable 实现的,点击事件通过 @click 实现,点击进入人物详情页面
  3. a-card-meta 中可以设置标题内容、描述内容和头像
  4. a-typography-paragraph 中可以设置卡片的文本内容
  5. 操作按钮通过 actions 插槽实现,其中 @click.stop 是防止同时触发多个点击事件

卡片组件配置项较多,有兴趣的朋友可以浏览 antdv 官网进行查看,如果要对它默认的样式进行修改,除上述代码中的样式外,还能通过 .card :deep(.ant-card-body) 进行修改。效果如下图所示:

卡片 Card 效果展示

分页 Pagination

分页是一种常见的数据分片展示方式,它的实现方式很多,我选择的是 ant-design 标准方式,它的耦合度较低,代码如下:

/src/views/record/personage/index.vue
<!-- 分页 -->
<a-pagination :current="query.pageNum" :page-size="query.pageSize" showSizeChanger style="margin-top: 2vh; text-align: center;" :total="personageTotal" :show-total="(total, range) => `${range[0]}-${range[1]} of ${total} items`" @change="paginationChange"/>
...
// 分页变化
const paginationChange = (page, pageSize) => {
// 更改查询条件
state.query.pageNum = page;
state.query.pageSize = pageSize;
// 重载页面
loadPage();
};

细节说明:

  1. 分页器独立于 table 之外,它可以放置在几乎任何位置,只需要传入数据总量、分页大小与当前页即可
  2. 分页器的页码变化事件由 @change 触发,在相应的 js 方法中改变查询参数后重新加载数据即可

分页组件比较简单,它的样式有很多种,有兴趣的朋友可以浏览 antdv 官网进行查看。效果如下图所示:

分页 Pagination 效果展示

排版 Typography

排版可用于展示标题、段落及列表等内容,也可以完成拷贝、省略与编辑等基本操作,目前我只用到了基本的展示功能,代码如下:

/src/views/record/personage/detail.vue
<a-typography class="typography" style="border: 1px solid #f0f0f0; border-radius: 8px; margin-top: 8px; padding: 0px 0.8em;">
...
<a-typography-title :level="4" id="originalDescription" style="margin-top: 0.8em">原文描述</a-typography-title>
<a-typography-paragraph>
<p></p>
</a-typography-paragraph>
...
</a-typography>

细节说明:

  1. 在 a-typography-title 中使用 level 设置标题级别
  2. 在 a-typography-paragraph 中使用 :ellipsis="{rows: 3, expandable: false}" 设置超长忽略与是否可展开(我没设置)

排版组件配置项较多,目前我使用的功能很少,有兴趣的朋友可以浏览 antdv 官网进行查看。效果如下图所示:

排版 Typography 效果展示

锚点 Anchor

锚点可用于展现当前页面上可供跳转的位置,以及快速在不同位置之间跳转,代码如下:

/src/views/record/personage/detail.vue
<a-typography-title :level="4" id="baseInfo" style="margin-top: 0.8em">基本信息</a-typography-title>
...
<a-col span="4" style="background-color: white;">
<a-anchor :affix="true" :offsetTop="30" style="margin-top: 8px; margin-left: 10px" :items="anchorItems"></a-anchor>
</a-col>
...
const state = reactive({
// 锚点菜单
anchorItems: [],
...
});
...
// 锚点信息
state.anchorItems.push({
href: '#baseInfo',
title: '基本信息'
});

细节说明:

  1. 在每一个可供跳转的位置(html元素)上设置唯一 id 用于标识
  2. 锚点展示形式通过 :affix="true" 设置为固定模式,即锚点菜单不随页面滚动而隐藏
  3. 锚点内容通过 :items 进行设置,其中 href 必须设置为有效的元素 id 才能生效

锚点组件比较简单,官网中配置项虽然多,但多数都是样式相关,有兴趣的朋友可以浏览 antdv 官网进行查看。效果如下图所示:

锚点 Anchor 效果展示

骨架屏 Skeleton

骨架屏主要用于需要长时间等待加载数据的场景下,给用户提供更好的视觉体验,代码如下:

/src/views/record/personage/detail.vue
<a-skeleton :loading="false" :paragraph="{ rows: 4 }">
...
</a-skeleton>

细节说明:

  1. 通过 :loading="false" 设置骨架屏是否处于等待状态,此处应该使用变量,由于我没有实现,所以暂时固定 false 了
  2. 骨架屏的行数是通过 :paragraph="{ rows: 4 }" 进行设置

骨架屏组件比较简单,配置基本都是样式相关,有兴趣的朋友可以浏览 antdv 官网进行查看。效果如下图所示:

骨架屏 Skeleton 效果展示

描述列表 Descriptions

描述列表常见于详情页的信息展示,代码如下:

/src/views/record/personage/detail.vue
<a-descriptions :column="1" :labelStyle="{'margin-bottom': '0.6em'}">
<a-descriptions-item label="">{{personage.lastName}}</a-descriptions-item>
<a-descriptions-item label="">{{personage.firstName}}</a-descriptions-item>
<a-descriptions-item label="">{{personage.courtesyName}}</a-descriptions-item>
...
</a-descriptions>

细节说明:

  1. 描述列表的每行默认是 3 列,可以通过 :column="1" 进行设置
  2. 列表中的项可以通过 :span="1" 设置占几列,由于我设置每行仅有一列,所以不需要设置此值

描述列表组件比较简单,在展示形式上与 table 表格类似,有兴趣的朋友可以浏览 antdv 官网进行查看。效果如下图所示:

描述列表 Descriptions 效果展示


本篇文章到这里就结束了,欢迎关注后续更新。

· 阅读需 10 分钟
tjuzyp

本系列文章适用于前端开发初学者,我将从环境搭建开始,逐步介绍如何开发一个基于 Vue 3.x & Ant Design Vue 4.x 的网站,如果哪位朋友发现其中存在技术错误或其他不当之处,请及时指出,如果有任何疑问或需要帮助,也可以在评论区留言,我将尽我所能一一解答,谢谢。

本篇文章将介绍前后端分离项目中前端是如何请求后端接口数据的:接口方法怎么写、页面怎么集成、后端服务怎么配置?与此同时,我已经开发完成三国演义人物大全页面,大家可以在三国史诗馆网站看到更新,但细节将推迟到下一章中再进行介绍。

本章更新内容:

  1. 安装 axios 插件
  2. 安装 ant-design/icons 图标
  3. 增加接口请求相关内容
  4. 增加人物大全页面并将其设置为首页
  5. 增加后端服务配置

安装 axios 插件

Axios 是一个基于 promise 的网络请求库,作用于 node.js 和浏览器中,通常用于 Vue 项目访问后端接口,在 sanguo-ui 目录下执行下方命令进行安装:

# 安装 axios
npm install axios --save

操作截图如下:

安装 axios

安装 ant-design/icons 图标

Ant Design 图标是语义化的矢量图形,需要安装 @ant-design/icons-vue 图标组件包,在 sanguo-ui 目录下执行下方命令进行安装:

# 安装 ant-design/icons
npm install @ant-design/icons-vue --save

操作截图如下:

安装 ant-design/icons

接口请求工具类

增加 utils/request.js 文件:

/src/utils/request.js
import axios from 'axios';

// 创建axios实例
const request = axios.create({
// API请求的默认前缀
baseURL: '/api',
// 请求超时时间
timeout: 20000,
// 请求头(必须加,否则transformRequest后数据格式不正确)
headers: {'Content-Type': 'application/json;charset=UTF-8'},
// 修改请求数据
transformRequest: [function (data, headers) {
let req = {};
req.data = data;
return JSON.stringify(req);
}],
});

export default request;

我只配置了几个必要的参数,更多参数请参考 Axios 官网,以上参数的简单介绍如下:

  • baseURL:用于标识需要请求后端的 url 地址,比如 /api/login 会请求后端 http://host:ip/login 接口,而 /apix/login 则只在前端路由中查找此地址
  • timeout:接口超时时间,如果系统中存在已知很慢的接口,需要将此值调大
  • headers:与后端协定的请求体格式
  • transformRequest:这里可以对请求体进行二次封装,可以增加一些通用的数据,例如当前登陆用户等

增加接口请求方法

为了便于维护,我将所有的接口文件都放在了 /src/api 目录下,并且目录结构尽量与 /src/views 保持一致。

首先第一个增加的接口请求方法是人物列表,增加 record/personage.js 文件:

/src/api/record/personage.js
import request from '@/utils/request';

// 获取人物列表
export function getPage(query) {
// 设置请求为PageQuery
query.service = 'PageQuery';
// 发送请求
return request({
url: '/record/personage/getPage',
method: 'post',
data: query
});
}

这个方法中向接口 /record/personage/getPage 发送 post 请求并获取响应数据,其中 PageQuery 是与后端约定的请求体数据类型,如果漏掉或写错,后端将无法正常解析请求。

页面中获取数据

页面文件主要放在 /src/components/src/views 两个目录下,前者主要是公共组件,后者是各个具体页面,目录结构一般按照功能划分。

第一个增加的页面是人物大全,增加 record/personage/index.vue 文件:

/src/views/record/personage/index.vue
<template>
<a-layout style="min-height: calc(100vh - 64px - 70px);">
<a-page-header style="padding-left: 0; background-color: transparent" title="人物大全" sub-title="滚滚长江东逝水,浪花淘尽英雄"/>
<a-layout style="background: #fff">
<a-layout-content style="padding: 24px;">
...
<!-- 人物卡片列表 -->
<a-row :gutter="[8,8]">
<a-col v-for="personage in personageList" :xs="12" :sm="8" :lg="4">
<a-card :loading="personage.loading" class="card" @click="detail(personage)" hoverable>
...
</a-card>
</a-col>
</a-row>
...
</a-layout-content>
...
</a-layout>
</a-layout>
</template>

<script>
...
import {getPage} from '@/api/record/personage';
...

export default defineComponent({
setup() {
const state = reactive({
// 遮罩层
loading: true,
// 查询参数
query: {
param: {},
pageNum: 1,
pageSize: 30,
sortField: 'CREATE_TIME',
sortOrder: 'ASC'
},
// 人物列表
personageList: [],
personageTotal: 0
});
...
// 查询列表
const loadPage = () => {
// 打开加载
state.loading = true;
...
// 请求数据
getPage(state.query).then(response => {
if (response.data.code == 200) {
// 获取真正返回结果
let data = response.data.data;
// 更新人物列表数据
state.personageList = data.list;
// 更新人物总数
state.personageTotal = data.total;
// 关闭加载
state.loading = false;
} else {
// 提示失败
requestFailed(response.data);
}
}).catch((error) => {
// 提示失败
requestFailed(error);
});
};
// 重置
const resetQuery = () => {
// 重置查询条件
...
// 查询列表
loadPage();
};
...
// 加载页面
onMounted(() => {
...
resetQuery();
});

return {
...toRefs(state),
...
};
}
});
</script>

<style scoped>
...
</style>

页面的整体布局是标题、副标题、内容、分页,其中前两个较简单,内容与分页将推迟到下一章中再进行介绍,此处主要对页面的数据交互进行介绍:

  1. 使用生命周期中的 onMounted 方法作为入口,即页面加载完成后调用查询数据的方法 resetQuery -> loadPage
  2. 在 loadPage 方法中调用 getPage 方法(上方已经 import 引入),得到数据集 personageList 并放入 reactive 变量中
  3. 使用 v-for 遍历 personageList 拼装元素,展示页面内容

增加页面路由

完成页面后,需为页面增加路由,修改 router/index.js 文件,增加以下内容:

/src/router/index.js
const routes = [
...
{
path: '/personage',
component: Layout,
children: [
{
path: 'list',
meta: {
title: '人物大全',
icon: 'TeamOutlined',
key: 'personage:list'
},
component: () => import('@/views/record/personage/index.vue')
}
]
},
...
];

我要把刚才写的人物大全页面作为网站首页,所以我把首页 views/index.vue 文件修改为如下内容:

/src/views/index.vue
<template>
<personage-list msg="欢迎您的访问"/>
</template>

<script>
import {defineComponent} from 'vue';
import PersonageList from './record/personage/index';

export default defineComponent({
components: {
PersonageList
}
});
</script>

<style scoped></style>

配置后端地址

完成以上内容后,前端工作已经结束,但是 axios 请求的后端地址还没有,这里也是比较难懂的一块内容,我只介绍我熟悉的方式。

开发环境

在开发环境中配置后端地址,需要在 vue.config.js 文件中增加以下内容:

/vue.config.js
...
module.exports = defineConfig({
...
// 开发环境的设置
devServer: {
// 端口
port: 9518,
// 设置代理
proxy: {
// 转发前缀为/api的请求
"/api": {
// 转发目的地址
target: "http://127.0.0.1:9517/",
// 设置同源,解决跨域问题
changeOrigin: true,
// 路径重写
pathRewrite: {
// 去掉前缀
"^/api": ""
}
}
}
}
...
});

这里的配置并非只针对后端地址,其实是对开发服务的配置,每个配置项都加了注释,所以不再赘述。

生产环境

所谓生产环境,是相对于开发环境而言,更准确的说是我在 Nginx 下做的配置,以我的网站为例,可以通过以下配置完成接口请求的转发:

/nginx.conf
...
http {
...
server {
listen 80;
server_name www.sanguo.fun;
location / {
root /root/www.sanguo.fun/www-root;
index index.html;
try_files $uri $uri/ /index.html;
}
location /api/ {
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Methods 'GET,POST,OPTIONS,PUT,DELETE';
add_header Access-Control-Allow-Headers 'Token,DNT,X-Mx-ReqToken,keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization';
proxy_pass http://127.0.0.1:9517/;
}
}
}

更新后的页面

现在通过 npm run serve 命令启动前端项目,现在的页面如下图所示:

展示后端数据的人物大全页面


本篇文章到这里就结束了,欢迎关注后续更新。

· 阅读需 4 分钟
tjuzyp

本系列文章适用于前端开发初学者,我将从环境搭建开始,逐步介绍如何开发一个基于 Vue 3.x & Ant Design Vue 4.x 的网站,如果哪位朋友发现其中存在技术错误或其他不当之处,请及时指出,如果有任何疑问或需要帮助,也可以在评论区留言,我将尽我所能一一解答,谢谢。

本篇文章将介绍如何搭建基于 SpringBoot 的后端项目,由于本系列文章重点在前端功能,所以后续可能很少再提及后端,相关代码可以到我的 Gitee 仓库下载更新。

安装 JDK

我安装的是 openjdk-11 版本(不要求相同,建议不低于 jdk1.8),依次执行下方命令:

# 安装 openjdk-11
sudo apt install openjdk-11-jdk
# 查看 JDK 版本
java -version

操作截图如下:

安装 openjdk-11

查看 JDK 版本

安装 Maven

我安装的是 3.6.3 版本,依次执行下方命令:

# 安装 Maven
sudo apt install maven
# 查看 Maven 版本
mvn -v

操作截图如下:

安装 Maven

查看 Maven 版本

创建项目

依次执行下方命令:

# 创建工作区目录
mkdir -p ./projects/app/ && cd ./projects/app/
# 创建 Maven 项目
mvn archetype:generate -DgroupId=fun.sanguo -DartifactId=sanguo-server -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false
# 查看已创建的项目及项目内文件列表
()

操作截图如下:

创建工作区目录

创建 Maven 项目

创建 Maven 项目成功

查看已创建的项目及项目内文件列表

至此我已经创建好了最简单最原始的 Maven 项目,由于我之前已经开发了一些代码,而且前面我也说过不对后端做重点介绍,所以我直接将我的代码复制过来,目录结构如下:

查看更新后的项目内文件列表

编译打包

直接执行 Maven 打包命令即可:

# Maven 编译打包
mvn clean package

操作截图如下:

Maven 编译打包

Maven 编译打包成功

编译打包成功后,可以看到 target 目录下生成了带版本号的 jar 文件与 tar.gz 文件,这是目标结果,如下图所示:

编译打包结果

此时进入 target 目录执行 java -jar sanguo-server-1.0.0.jar 命令即可启动应用,也可以先修改 sanguo-server/src/main/resources 目录下的各配置文件后再执行上述编译运行操作。


本篇文章到这里就结束了,欢迎关注后续更新。

· 阅读需 7 分钟
tjuzyp

本系列文章适用于前端开发初学者,我将从环境搭建开始,逐步介绍如何开发一个基于 Vue 3.x & Ant Design Vue 4.x 的网站,如果哪位朋友发现其中存在技术错误或其他不当之处,请及时指出,如果有任何疑问或需要帮助,也可以在评论区留言,我将尽我所能一一解答,谢谢。

本篇文章将在上一篇文章的基础上介绍如何将标准初始化项目改造为自定义初始化项目,主要包括以下内容:

  1. 关闭 eslint-loader 检查
  2. 增加 @ 配置
  3. 修改网站标题与关键词等内容
  4. 设置页面整体为上中下布局
  5. 使用 vue-router 实现路由

关闭 eslint-loader 检查

ESLint 是一个代码检查工具,用来检查代码是否符合指定的规范,由于默认的规则太过令人抓狂,所以首先我先把它关掉,高手们请忽略这一步。

在 vue.config.js 文件中增加以下代码即可关闭检查:

/vue.config.js
...
module.exports = defineConfig({
...
// 是否在保存的时候使用eslint-loader进行检查
lintOnSave: false,
...
});

增加 @ 配置

项目中文件间相互引用时会用到相对路径或绝对路径,如果目录结构比较深或目录结构变化,经常会出现问题,我们通常可以将 src 目录通过设置别名为 @ 目录,这样引入文件时候可以一目了然,而且编辑器一般会有提示,能提高开发效率并且尽量避免错误。

在 vue.config.js 文件中增加以下代码即可增加 @ 配置:

/vue.config.js
...
const path = require('path');
...
module.exports = defineConfig({
...
// 接收被解析的配置作为参数
configureWebpack: {
resolve: {
alias: {
'@': path.resolve(__dirname, 'src')
}
}
},
...
});

修改网站标题与关键词等内容

修改标题的方法有很多,我这里选择在 vue.config.js 文件中设置 chainWebpack 来修改。

在 vue.config.js 文件中增加以下代码,设置标题与关键词等内容:

/vue.config.js
...
module.exports = defineConfig({
...
// 修改标题
chainWebpack: config => {
config.plugin('html').tap(
args => {
args[0].title = "三国史诗馆";
args[0].keywords = "三国演义,三国人物,三国文化,历史资料,文化解读,人物传记,战役分析";
args[0].description = "以《三国演义》为蓝本,专注于英雄人物与诗词歌赋,展现那段史诗般的历史长卷,带您领略那段波澜壮阔的历史风云。";
return args;
}
);
},
...
});

在 public/index.html 文件中增加以下代码:

/public/index.html
<head>
...
<meta name="keywords" content="<%= htmlWebpackPlugin.options.keywords %>">
<meta name="description" content="<%= htmlWebpackPlugin.options.description %>">
...
</head>

修改 src/assets/logo.png 与 public/favicon.ico 图片,新增 src/assets/logo-full.png 图片,如下图所示:

修改/新增 logo 图片

设置页面整体为上中下布局

增加 src/layout/*.vue 文件:

  • index.vue
/src/layout/index.vue
<template>
<a-layout style="min-height: 100vh;">
<a-layout-header class="clear-fix" style="background-color: white">
<page-header/>
</a-layout-header>
<a-layout-content style="padding: 0 50px">
<component :is="getRoute"/>
</a-layout-content>
<a-layout-footer style="text-align: center">
<page-footer/>
</a-layout-footer>
</a-layout>
</template>

<script>
import {computed, defineComponent} from 'vue';
import {useRoute} from 'vue-router';
import PageHeader from '@/layout/page-header.vue';
import PageFooter from '@/layout/page-footer.vue';

export default defineComponent({
components: {
PageHeader,
PageFooter
},
setup() {
const route = useRoute();

const getRoute = computed(() => {
return route.matched[route.matched.length - 1]?.components?.default;
});

return {
getRoute
}
}
});
</script>

<style scoped></style>
  • page-header.vue
/src/layout/page-header.vue
<template>
<page-header-logo/>
</template>

<script>
import {defineComponent} from 'vue';
import PageHeaderLogo from './page-header-logo.vue';

export default defineComponent({
components: {
PageHeaderLogo
}
});
</script>

<style scoped></style>
  • page-header-logo.vue
/src/layout/page-header-logo.vue
<template>
<div class="logo">
<a id="logo" href="/index">
<img src="@/assets/logo-full.png"/>
</a>
</div>
</template>

<script></script>

<style scoped>
.logo {
float: left;
height: 64px;
}

#logo img {
position: relative;
top: 6px;
height: 52px;
}
</style>
  • page-footer.vue
/src/layout/page-footer.vue
<template>
<span>Copyright © <a href="http://www.sanguo.fun/" target="_blank">http://www.sanguo.fun/</a> All Rights Reserved. 备案号:<a href="http://beian.miit.gov.cn/" target="_blank">津ICP备2021007159号-2</a></span>
</template>

<script></script>

<style scoped></style>

使用 vue-router 实现路由

执行下方命令,安装 vue-router 组件:

# 安装 vue-router 组件
npm install vue-router --save

操作截图如下:

安装 vue-router 组件

增加 src/views/index.vue 文件:

/src/views/index.vue
<template>
<a-layout style="min-height: calc(100vh - 64px - 70px); padding-top: 36px;">
<p>欢迎您的访问</p>
</a-layout>
</template>

<script></script>

<style scoped></style>

增加 src/router/index.js 文件:

/src/router/index.js
import {createRouter, createWebHistory} from 'vue-router';

import Layout from '@/layout/index.vue';

const routes = [
{
path: '/index',
meta: {
title: '平台首页',
icon: 'HomeOutlined',
key: 'index'
},
component: Layout,
children: [
{
path: '',
component: () => import('@/views/index.vue')
}
]
},
// 首页跳转index
{
path: '/:lang(.*)',
redirect: '/index'
}
];

export default createRouter({
history: createWebHistory(),
fallback: false,
routes,
scrollBehavior: to => {
if (to.hash) {
return {el: to.hash, top: 80, behavior: 'auto'};
}
}
});

在 main.js 文件中增加以下代码引入路由:

/src/main.js
...
// 引入路由
import router from './router';
...
// 使用路由
app.use(router);
...

修改 App.vue 文件为以下代码,使用路由:

/src/App.vue
<template>
<router-view/>
</template>

<script></script>

<style>
body {
margin: 0;
}
</style>

删除 src/components/HelloWorld.vue 文件,如下图所示:

删除 src/components/HelloWorld.vue 文件

此时项目整体目录结构如下图所示:

项目整体目录结构

启动项目

启动项目前先执行下方命令安装依赖:

npm install

如果没有报错,则说明项目已经可以正常启动,执行下方命令启动:

npm run serve

项目启动成功后打开查看,现在的页面如下图所示:

自定义初始化项目页面展示


本篇文章到这里就结束了,欢迎关注后续更新。

· 阅读需 7 分钟
tjuzyp

本系列文章适用于前端开发初学者,我将从环境搭建开始,逐步介绍如何开发一个基于 Vue 3.x & Ant Design Vue 4.x 的网站,如果哪位朋友发现其中存在技术错误或其他不当之处,请及时指出,如果有任何疑问或需要帮助,也可以在评论区留言,我将尽我所能一一解答,谢谢。

本篇文章将介绍如何搭建开发环境及如何通过 Vue 脚手架创建一个最简单的 Vue 前端项目。

基础环境

我使用的操作系统是 ubuntu22.04,命令行与 Windows 或 MacOS 略有不同,所以请不要完全复制粘贴,但基本步骤是相同的。

安装 node 与 npm

依次执行下方命令:

# 更新软件源
sudo apt update
# 安装 nodejs
sudo apt install nodejs
# 安装 npm
sudo apt install npm
# 查看 node & npm 版本
node -v
npm -v

操作截图如下:

更新软件源

安装 nodejs

安装 npm

查看 node &amp; npm 版本

更新默认 node & npm 版本至最新版本

请注意 node & npm 版本不要过低,否则后续将出现一系列未知故障。

依次执行下方命令:

# 切换 npm 镜像源
npm config set registry https://registry.npmmirror.com/
# 查看 npm 镜像源
npm config get registry
# 清除缓存
npm cache clean --force
# 安装 n
sudo npm install -g n
# 升级 node 至最新版本
sudo n stable

操作截图如下:

切换 npm 镜像源并清除缓存

安装 n

升级 node 至最新版本

至此,可能出现 node -v 还是老版本的问题,这是因为 nodejs 的安装目录与更新目录不同(在上图中可以看到旧版本路径是 /usr/bin/node 新版本路径是 /usr/local/bin/node ),需要继续执行下方命令:

# 删除旧版本
sudo rm /usr/bin/node
sudo rm /usr/bin/npm
sudo rm /usr/bin/npx
# 创建新版本的软连接
sudo ln -s /usr/local/bin/node /usr/bin/node
sudo ln -s /usr/local/bin/npm /usr/bin/npm
sudo ln -s /usr/local/bin/npx/usr/bin/npx
# 查看 node & npm & npx 版本
node -v
npm -v
npx -v

操作截图如下:

删除旧版本、创建新版本的软连接、查看 node &amp; npm &amp; npx 版本

安装 Vue 脚手架

依次执行下方命令:

# 安装 Vue 脚手架
sudo npm install -g @vue/cli
# 查看 Vue 脚手架版本
vue -V

操作截图如下:

安装 Vue 脚手架

查看 Vue 脚手架版本

创建项目

依次执行下方命令:

# 创建工作区目录
mkdir -p ./projects/ui/ && cd ./projects/ui/
# 创建 Vue 3.x 项目
vue create sanguo-ui
# 查看已创建的项目及项目内文件列表
()

操作截图如下:

创建工作区目录

创建 Vue 3.x 项目

查看已创建的项目及项目内文件列表

至此我已经创建好了最简单最原始的 Vue 3.x 前端项目,它现在已经可以直接运行了,不过我还是要再补充一步操作,将它与 Ant Design Vue 4.x 结合起来,虽然在本篇文章中不能看出变化,但是那才是我认为的基础版。

引入 Ant Design Vue 4.x

在项目目录下执行下方命令,安装 Ant Design Vue:

# 安装 Ant Design Vue
npm install ant-design-vue --save

操作截图如下:

安装 Ant Design Vue

使用文本编辑工具打开 src/main.js 文件,修改为如下内容:

/src/main.js
// 创建应用
import { createApp } from 'vue';
// 引入ant-design-vue
import Antd from 'ant-design-vue';
// 引入ant-design-vue样式
import 'ant-design-vue/dist/reset.css';
// 引入主程序
import App from './App.vue';

// 创建应用
const app = createApp(App);
// 使用ant-design-vue
app.use(Antd);

// 启动
app.mount('#app');

启动项目

启动项目前先执行下方命令安装依赖:

npm install

如果没有报错,则说明项目已经可以正常启动,执行下方命令启动:

npm run serve

等到出现如下图所示结果时,说明项目已经启动成功(我这里的访问地址是 http://localhost:8008/):

项目启动成功

现在的页面如下图所示:

标准初始化项目页面展示


本篇文章到这里就结束了,欢迎关注后续更新。