《Nuxt.js 实战:从放弃到入门》二、Element Plus 集成
在项目开发过程中,引入公共头部和脚部组件,有助于实现页面布局的统一,提升用户体验。以下是具体的实施步骤和相关代码。
首先,需要创建一个目录,用于存放相关组件文件,为项目文件结构提供清晰的组织方式。
创建 Header.vue 组件
Header.vue 组件定义了页面的公共头部部分,包含导航链接等内容。其代码如下:
<template>
<header class="bg-white shadow-sm">
<nav class="container mx-auto px-4 py-3">
<div class="flex items-center justify-between">
<div class="flex items-center space-x-4">
<NuxtLink to="/" class="text-xl font-bold text-gray-800">OutEasy</NuxtLink>
<div class="hidden md:flex space-x-4">
<NuxtLink to="/resize" class="text-gray-600 hover:text-gray-900">图片调整</NuxtLink>
</div>
</div>
</div>
</nav>
</header>
</template>
创建 Footer.vue 组件
Footer.vue 组件定义了页面的公共脚部部分,包含版权信息和相关链接。其代码如下:
<template>
<footer class="bg-gray-50 border-t">
<div class="container mx-auto px-4 py-6">
<div class="flex flex-col items-center justify-center space-y-2">
<p class="text-gray-600 text-sm">© 2024 OutEasy. All rights reserved.</p>
<div class="flex space-x-4">
<a href="#" class="text-gray-500 hover:text-gray-700 text-sm">关于我们</a>
<a href="#" class="text-gray-500 hover:text-gray-700 text-sm">使用条款</a>
<a href="#" class="text-gray-500 hover:text-gray-700 text-sm">隐私政策</a>
</div>
</div>
</div>
</footer>
</template>
在 app.vue 中引入组件
在 app.vue 中,将 Header.vue 和 Footer.vue 组件引入,以构建完整的页面结构。代码如下:
<template>
<div class="min-h-screen flex flex-col">
<Header />
<main class="flex-grow">
<NuxtPage />
</main>
<Footer />
</div>
</template>
在 app.vue 中引入组件
在 app.vue 中,将 Header.vue 和 Footer.vue 组件引入,以构建完整的页面结构。代码如下:
<script setup>
const router = useRouter()
onMounted(() => {
router.push('/resize')
})
</script>
<template>
<div></div>
</template>
接入 Element - Plus UI 组件框架
为优化页面样式和交互,接入 Element - Plus UI 组件框架。
接入后需对 nuxt.config.ts 和 package.json 进行相应配置修改。
nuxt.config.ts,修改后的内容
// https://nuxt.com/docs/api/configuration/nuxt-config
export default defineNuxtConfig({
compatibilityDate: '2024-11-01',
devtools: { enabled: true },
modules: ['element-plus/nuxt'],
css: ['element-plus/dist/index.css']
})
package.json修改后的内容
{
"name": "nuxt-app",
"private": true,
"type": "module",
"scripts": {
"build": "nuxt build",
"dev": "nuxt dev",
"generate": "nuxt generate",
"preview": "nuxt preview",
"postinstall": "nuxt prepare"
},
"dependencies": {
"nuxt": "^3.15.4",
"vue": "latest",
"vue-router": "latest",
"element-plus": "^2.6.1",
"@element-plus/icons-vue": "^2.3.1"
}
}
修改相关组件代码以适配新框架
接入 Element - Plus UI 组件框架后,Header.vue、Footer.vue、resize.vue 的代码需相应修改,以使用新框架的组件和样式。
Header.vue 新代码
<template>
<header>
<div class="container mx-auto px-4">
<el-row :gutter="20" >
<!-- el-row 用于创建行布局,justify="space-between" 使内容两端对齐,align="middle" 使内容垂直居中 -->
<el-col :span="2">
<!-- el-col 用于创建列布局,span="6" 可以调整宽度,这里是占据六分之一的宽度 -->
<el-menu mode="horizontal" router :ellipsis="false">
<el-menu-item index="/">
<span>OutEasy</span>
</el-menu-item>
</el-menu>
</el-col>
<el-col :span="2">
<!-- 这里给中间部分设置了 span="12",让它占据剩余空间 -->
<el-menu mode="horizontal" router :ellipsis="false">
<el-menu-item index="/resize">
<span>图片调整</span>
</el-menu-item>
</el-menu>
</el-col>
<el-col :offset="20">
</el-col>
</el-row>
</div>
</header>
</template>
<script setup>
</script>
Footer.vue代码如下:
<template>
<footer class="bg-gray-50 border-t">
<el-row justify="center" align="middle" class="w-full min-h-[80px]">
<!-- el-row 用于创建行布局,justify="center" 使内容水平居中,align="middle" 使内容垂直居中 -->
<el-col :span="24">
<!-- el-col 用于创建列布局,span="24" 表示占据整行 -->
<div class="text-center">
<p class="text-gray-600 text-sm">© 2024 OutEasy</p>
</div>
</el-col>
</el-row>
</footer>
</template>
<style scoped>
footer {
border-top: 1px solid #ffffff; /* 对应 border-t 的样式 */
}
.text-center {
text-align: center;
}
.text-gray-600 {
color: #4b5563; /* 对应 text-gray-600 的颜色 */
}
.text-sm {
font-size: 0.875rem; /* 对应 text-sm 的样式 */
}
</style>
resize.vue代码如下:
<template>
<div class="container mx-auto p-4 max-w-2xl">
<el-card>
<template #header>
<div class="text-center">
<span class="text-xl font-medium">图片尺寸调整</span>
</div>
</template>
<!-- 图片上传区域 -->
<el-upload
class="upload-area mb-6"
drag
:auto-upload="false"
:show-file-list="false"
accept="image/*"
:on-change="handleFileSelect"
>
<template #trigger>
<div v-if="!imageUrl && !isLoading && !errorMessage" class="text-center">
<el-icon class="el-icon--upload mx-auto block"><upload-filled /></el-icon>
<div class="el-upload__text">
拖拽图片到此处或 <em>点击上传</em>
</div>
<div class="text-sm text-gray-400 mt-2">支持 JPG、PNG 格式</div>
</div>
</template>
</el-upload>
<div v-if="isLoading" class="text-center py-8">
<el-icon class="is-loading mb-2" :size="24"><loading /></el-icon>
<div class="text-gray-600">正在处理图片...</div>
</div>
<div v-else-if="errorMessage" class="text-center py-8">
<el-alert
:title="errorMessage"
type="error"
:closable="false"
class="mb-4"
/>
<el-button type="primary" @click="errorMessage = ''">
重试
</el-button>
</div>
<el-image
v-else-if="imageUrl"
:src="imageUrl"
fit="contain"
class="mx-auto block max-h-[300px] mt-4"
/>
<!-- 尺寸调整控制面板 -->
<el-form v-if="imageUrl" class="mt-6 w-full max-w-3xl mx-auto">
<el-form-item label="缩放比例" class="mb-6">
<el-row class="items-center justify-center">
<el-col :span="16">
<el-slider
v-model="scale"
:min="1"
:max="100"
:format-tooltip="value => `${value}%`"
@input="handleScaleChange"
/>
</el-col>
<el-col :span="8" class="text-right">
<span class="text-gray-600">{{ scale }}%</span>
</el-col>
</el-row>
</el-form-item>
<el-button
type="primary"
@click="downloadImage"
class="w-full mx-auto block"
>
下载调整后的图片
</el-button>
</el-form>
</el-card>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import { UploadFilled, Loading } from '@element-plus/icons-vue'
const fileInput = ref(null)
const imageUrl = ref('')
const originalImageUrl = ref('')
const scale = ref(100)
const width = ref(0)
const height = ref(0)
const originalWidth = ref(0)
const originalHeight = ref(0)
const isLoading = ref(false)
const errorMessage = ref('')
// 文件大小限制(10MB)
const MAX_FILE_SIZE = 10 * 1024 * 1024
// 支持的图片格式
const SUPPORTED_FORMATS = ['image/jpeg', 'image/png', 'image/webp']
// 处理文件选择
const handleFileSelect = (uploadFile) => {
const file = uploadFile.raw
if (file) {
processImage(file)
}
}
// 处理拖拽上传
const handleDrop = (event) => {
const file = event.dataTransfer.files[0]
if (file && file.type.startsWith('image/')) {
processImage(file)
}
}
// 处理图片
const validateFile = (file) => {
if (!SUPPORTED_FORMATS.includes(file.type)) {
errorMessage.value = '请上传 JPG、PNG 或 WebP 格式的图片'
return false
}
if (file.size > MAX_FILE_SIZE) {
errorMessage.value = '图片大小不能超过 10MB'
return false
}
errorMessage.value = ''
return true
}
const processImage = (file) => {
if (!validateFile(file)) return
isLoading.value = true
errorMessage.value = ''
const reader = new FileReader()
reader.onload = (e) => {
const img = new Image()
img.onload = () => {
originalWidth.value = img.width
originalHeight.value = img.height
width.value = img.width
height.value = img.height
scale.value = 100
isLoading.value = false
}
img.onerror = () => {
errorMessage.value = '图片加载失败,请重试'
isLoading.value = false
}
img.src = e.target.result
imageUrl.value = e.target.result
originalImageUrl.value = e.target.result
}
reader.onerror = () => {
errorMessage.value = '图片读取失败,请重试'
isLoading.value = false
}
reader.readAsDataURL(file)
}
// 处理缩放比例变化
const handleScaleChange = () => {
width.value = Math.round(originalWidth.value * scale.value / 100)
height.value = Math.round(originalHeight.value * scale.value / 100)
// 更新预览图片
const canvas = document.createElement('canvas')
canvas.width = width.value
canvas.height = height.value
const ctx = canvas.getContext('2d')
const img = new Image()
img.onload = () => {
ctx.drawImage(img, 0, 0, width.value, height.value)
imageUrl.value = canvas.toDataURL('image/png')
}
img.src = originalImageUrl.value
}
// 下载调整后的图片
const downloadImage = () => {
const canvas = document.createElement('canvas')
canvas.width = width.value
canvas.height = height.value
const ctx = canvas.getContext('2d')
const img = new Image()
img.onload = () => {
ctx.drawImage(img, 0, 0, width.value, height.value)
const link = document.createElement('a')
link.download = 'resized-image.png'
link.href = canvas.toDataURL('image/png')
link.click()
}
img.src = imageUrl.value
}
</script>
安装插件与运行服务
在运行服务前,需安装 Element - Plus 相关插件,使用以下命令进行安装:
npm install @element-plus/nuxt
安装完成后,使用以下命令运行服务:
npm run dev
托管地址:https://github.com/outeasy/outeasy/releases/tag/v0.2