【家政平台开发(14)】家政平台PC前端(Element plus)开发指南:从集成到组件应用
本【家政平台开发】专栏聚焦家政平台从 0 到 1 的全流程打造。从前期需求分析,剖析家政行业现状、挖掘用户需求与梳理功能要点,到系统设计阶段的架构选型、数据库构建,再到开发阶段各模块逐一实现。涵盖移动与 PC 端设计、接口开发及性能优化,测试阶段多维度保障平台质量,部署发布阶段确保平稳上线。还深入探讨运营策略、技术创新应用及未来发展方向,为家政平台开发提供全面且实用的知识体系与实践指南。
目录
- 一、Element plus 项目集成与配置
- 1.1 Element plus 引入与初始化
- 1.2 主题定制与样式调整
- 1.2.1 定制原理
- 1.2.2 实现步骤
- 1.3 常见问题与解决
- 二、页面架构与布局设计
- 2.1 后台管理系统页面架构
- 2.1.1 整体架构规划
- 2.1.2 导航与菜单设计
- 2.2 数据展示页面布局设计
- 2.2.1 表格布局
- 2.2.2 卡片布局
- 2.3 响应式布局设计
- 三、组件库应用与二次开发
- 3.1 Element plus 组件的使用技巧
- 3.1.1 表单组件
- 3.1.2 弹窗组件
- 3.2 自定义组件开发与封装
- 3.2.1 组件封装思路
- 3.2.2 代码实现
- 3.3 组件间通信与协同
一、Element plus 项目集成与配置
1.1 Element plus 引入与初始化
在基于 Vue 的家政平台 PC 前端项目中,使用 Element plus 可极大提升开发效率。首先,通过包管理工具进行安装,若使用 npm,在项目根目录下执行命令:
npm install element-plus --save
若使用 yarn ,则执行:
yarn add element-plus
这一步将 Element plus 及其相关依赖安装到项目中。
安装完成后,在项目入口文件main.js(对于 Vue3+Vite 项目是main.ts)中进行初始化配置。以 Vue3 项目为例,代码如下:
import { createApp } from 'vue'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import App from './App.vue'
const app = createApp(App)
app.use(ElementPlus)
app.mount('#app')
上述代码中,先引入createApp函数用于创建 Vue 应用实例,接着引入ElementPlus组件库和其默认样式文件element-plus/dist/index.css 。通过app.use(ElementPlus)将 Element plus 安装到 Vue 应用中,使其全局可用。
此外,Element plus 还支持按需引入,以减少项目打包体积。可借助unplugin-vue-components和unplugin-auto-import插件实现。首先安装这两个插件:
npm install -D unplugin-vue-components unplugin-auto-import
然后在vite.config.js(或vue.config.js,根据项目构建工具而定)中进行配置:
import { defineConfig } from 'vite'
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
export default defineConfig({
plugins: [
AutoImport({
resolvers: [ElementPlusResolver()],
}),
Components({
resolvers: [ElementPlusResolver()],
}),
],
})
这样配置后,在使用 Element plus 组件时无需手动导入,插件会自动按需引入。比如在某个组件中使用ElButton:
<template>
<el-button type="primary">点击我</el-button>
</template>
虽然代码中未显式导入ElButton,但插件会自动处理导入逻辑。
1.2 主题定制与样式调整
1.2.1 定制原理
Element plus 基于 CSS 变量实现主题定制。在 CSS 中,通过:root选择器定义全局 CSS 变量,Element plus 预定义了一系列 CSS 变量来控制组件的样式,如颜色、字体大小、间距等。例如,–el-color-primary用于定义主色调,–el-font-size-base用于定义基础字体大小 。这些变量的动态性和可重用性是主题定制的关键。当修改某个 CSS 变量的值时,所有使用该变量的样式都会自动更新,无需逐一修改每个组件的样式代码,从而实现全局样式的统一调整。
1.2.2 实现步骤
实际操作中,首先需要安装sass和sass-loader依赖,用于处理 SCSS 文件。
npm install sass sass-loader -D
接着,在项目中创建一个 SCSS 文件,比如src/styles/element/theme.scss ,用于定义和修改 CSS 变量。假设要将 Element plus 的主色调修改为自定义的橙色:
// 覆盖主色
$--color-primary: #FF4500;
// 导入Element plus所有样式源文件
@forward "element-plus/theme-chalk/src/index" as element-*;
然后,在vite.config.js中配置scss的additionalData选项,让所有 SCSS 文件都能自动导入上述主题文件:
import { defineConfig } from 'vite'
export default defineConfig({
css: {
preprocessorOptions: {
scss: {
additionalData: `@use "@/styles/element/theme.scss" as *;`,
},
},
},
})
如果要实现主题切换效果,可以利用 JavaScript 动态修改 CSS 变量的值。例如,在页面中添加一个切换主题的按钮,点击按钮时修改–el-color-primary变量的值:
<template>
<div>
<el-button @click="toggleTheme">切换主题</el-button>
</div>
</template>
<script setup>
const toggleTheme = () => {
const root = document.documentElement;
if (root.style.getPropertyValue('--el-color-primary') === '#FF4500') {
root.style.setProperty('--el-color-primary', '#1890FF'); // 切换回默认蓝色
} else {
root.style.setProperty('--el-color-primary', '#FF4500'); // 设置为橙色
}
};
</script>
1.3 常见问题与解决
在 Element plus 项目集成与配置过程中,可能会遇到一些问题。例如,样式不生效的情况,可能是因为样式文件的引入顺序不正确。如果自定义的样式覆盖不了 Element plus 的默认样式,需要检查自定义样式是否在默认样式之后引入。在使用vite构建项目时,如果自定义 SCSS 变量不生效,可能是vite.config.js中scss配置有误,要确保additionalData的路径正确指向自定义主题文件。
当组件无法正常引入时,可能是按需引入插件配置问题。比如unplugin-vue-components和unplugin-auto-import的配置参数错误,需要检查ElementPlusResolver是否正确配置,以及插件是否在vite.config.js中正确注册。此外,还需确认项目的依赖版本是否兼容,若 Element plus 版本与插件版本不匹配,也可能导致组件引入失败或功能异常。
二、页面架构与布局设计
2.1 后台管理系统页面架构
2.1.1 整体架构规划
家政平台 PC 端后台管理系统采用典型的分层架构设计,以实现高效的业务逻辑处理和清晰的页面展示。从功能模块划分来看,主要包含用户管理、订单管理、服务管理、财务管理、数据分析等核心模块。
- 用户管理模块负责对家政服务人员和客户的信息进行全面管理,涵盖信息录入、编辑、查询以及权限控制等功能。在页面层级关系上,它处于基础层级,为其他模块提供用户数据支持。例如,订单管理模块在处理订单时,需要调用用户管理模块中的用户信息来关联订单与用户。
- 订单管理模块则是整个系统的关键模块之一,负责订单的全生命周期管理,包括订单创建、分配、跟踪、完成以及售后处理等。该模块页面与其他多个模块页面存在紧密的数据交互,如服务管理模块会根据订单信息安排家政服务人员,财务管理模块会依据订单数据进行费用结算。
- 服务管理模块聚焦于家政服务项目的管理,包括服务项目的添加、编辑、下架,以及服务人员的任务分配和调度。它与订单管理模块相互关联,根据订单需求调配服务资源,同时向数据分析模块提供服务相关数据,以便进行服务质量和效率分析。
- 财务管理模块主要处理家政服务的费用相关事务,如订单费用计算、支付处理、收入统计以及财务报表生成。它依赖于订单管理模块的订单数据和用户管理模块的用户信息,确保费用计算和支付的准确性。
- 数据分析模块通过对各个业务模块产生的数据进行收集、整理和分析,为平台运营决策提供数据支持。它从多个模块获取数据,如订单数据、用户数据、服务数据等,通过数据可视化的方式展示关键指标,如订单量趋势、用户增长趋势、服务满意度等。
在页面布局上,采用了常见的左侧导航栏加右侧内容区域的布局方式。左侧导航栏以树形结构展示各个功能模块,方便用户快速定位和切换不同的管理功能。右侧内容区域则根据用户选择的功能模块,加载相应的页面内容,实现具体的业务操作和数据展示。这种布局方式不仅符合用户操作习惯,还能提高页面空间利用率,使系统的操作流程更加直观和便捷。
2.1.2 导航与菜单设计
在 Element plus 框架下,借助 el-menu 组件可以轻松实现清晰易用的导航栏和菜单设计。el-menu 组件提供了丰富的属性和事件,能够满足各种复杂的导航需求。
首先,通过设置mode属性来确定菜单的模式,如mode="horizontal"可实现水平导航栏,适用于页面顶部的全局导航;mode="vertical"则创建垂直导航栏,常用于左侧边栏导航,这在后台管理系统中较为常见。例如:
<el-menu mode="vertical" :default-active="activeIndex" class="el-menu-vertical-demo">
<el-menu-item index="1">
<i class="el-icon-menu"></i>
<span slot="title">用户管理</span>
</el-menu-item>
<el-sub-menu index="2">
<template slot="title">
<i class="el-icon-location"></i>
<span>订单管理</span>
</template>
<el-menu-item index="2-1">待处理订单</el-menu-item>
<el-menu-item index="2-2">已完成订单</el-menu-item>
</el-sub-menu>
<!-- 其他菜单项 -->
</el-menu>
上述代码中,default-active属性用于设置当前默认激活的菜单项,通过绑定一个数据变量activeIndex,可以根据用户的操作动态更新激活状态。el-menu-item表示普通菜单项,el-sub-menu用于创建带有子菜单的菜单项,通过template插槽可以自定义菜单项的标题和图标,增强菜单的可视化效果和可识别性。
为了实现不同页面之间的快速切换和操作引导,利用 el-menu 的@select事件来捕获用户的菜单选择操作。当用户点击菜单项时,@select事件会触发,并传递当前点击菜单项的index值,通过这个index值可以在 Vue 组件的方法中进行相应的页面路由跳转或内容更新操作。例如:
export default {
data() {
return {
activeIndex: '1'
};
},
methods: {
handleSelect(key, keyPath) {
// 根据key进行页面跳转或内容更新逻辑
if (key === '1') {
this.$router.push('/user-management');
} else if (key === '2-1') {
this.$router.push('/order-management/pending');
}
}
}
};
此外,还可以通过设置router属性为true,使 el-menu 与 Vue Router 深度集成,实现基于路由的菜单导航。在这种模式下,菜单项的index值会被视为路由路径,点击菜单项时会自动进行路由跳转,无需手动编写跳转逻辑,进一步简化了导航实现过程。
2.2 数据展示页面布局设计
2.2.1 表格布局
在展示家政服务相关数据时,Element plus 的 el-table 组件是一个强大的工具。以订单列表展示为例,首先需要定义数据源,假设从后端获取的订单数据存储在orderList数组中,每个订单对象包含订单编号、客户姓名、服务类型、订单金额、订单状态等属性。
<el-table :data="orderList" style="width: 100%" stripe border highlight-current-row>
<el-table-column type="selection" width="55"></el-table-column>
<el-table-column prop="orderId" label="订单编号" width="180"></el-table-column>
<el-table-column prop="customerName" label="客户姓名" width="180"></el-table-column>
<el-table-column prop="serviceType" label="服务类型" width="180"></el-table-column>
<el-table-column prop="orderAmount" label="订单金额" width="120" align="right">
<template #default="scope">
{{ scope.row.orderAmount | currencyFilter }}
</template>
</el-table-column>
<el-table-column prop="orderStatus" label="订单状态" width="120">
<template #default="scope">
<el-tag :type="getStatusType(scope.row.orderStatus)">{{ getStatusText(scope.row.orderStatus) }}</el-tag>
</template>
</el-table-column>
</el-table>
在上述代码中,el-table的data属性绑定了订单数据数组orderList,stripe属性使表格呈现斑马条纹效果,border属性添加纵向边框,highlight-current-row属性用于高亮当前行。el-table-column组件用于定义表格的每一列,prop属性指定绑定的数据源属性,label属性设置列标题。
对于需要特殊处理的数据列,如订单金额列,通过插槽#default自定义单元格内容,使用过滤器currencyFilter将金额格式化为货币形式;订单状态列则根据订单状态值动态显示不同颜色的标签,通过getStatusType和getStatusText方法获取标签类型和文本内容。
为了实现表格数据的排序和筛选功能,el-table 提供了相应的属性和事件。在列定义中设置sortable属性为true或custom,可开启列排序功能。例如:
<el-table-column prop="orderAmount" label="订单金额" width="120" align="right" sortable>
<!-- 列内容定义 -->
</el-table-column>
对于自定义排序逻辑,可以在sortable设置为custom后,通过@sort-change事件监听排序变化,在事件处理函数中编写自定义排序算法。筛选功能则可以通过el-table的filter-method属性结合自定义筛选函数实现,用户在表头输入筛选条件时,触发筛选函数对数据进行过滤。
2.2.2 卡片布局
卡片式布局(el-card)在展示家政服务详情、用户评价等内容时具有独特的优势,能够有效提高信息展示的可读性和美观度。以展示家政服务人员的详情为例:
<el-card class="service-worker-card" :body-style="{ padding: '20px' }">
<template #header>
<div class="card-header">
<span>{{ serviceWorker.name }}</span>
<el-button type="text" class="edit-button" @click="editWorker(serviceWorker)">编辑</el-button>
</div>
</template>
<div class="card-content">
<p><span class="label">服务年限:</span>{{ serviceWorker.workingYears }}</p>
<p><span class="label">擅长技能:</span>{{ serviceWorker.skills }}</p>
<p><span class="label">客户评价:</span>
<el-rate :value="serviceWorker.rating" disabled></el-rate>
<span class="rating-text">{{ serviceWorker.ratingText }}</span>
</p>
</div>
</el-card>
在这个示例中,el-card 组件包裹了家政服务人员的相关信息。通过template #header插槽定义卡片的头部,包含服务人员姓名和一个编辑按钮,点击编辑按钮可触发editWorker方法进行服务人员信息编辑操作。卡片主体部分通过自定义样式展示服务年限、擅长技能和客户评价等内容。其中,客户评价使用了 el-rate 组件展示评分,并结合文本展示具体的评价内容。
对于用户评价的展示,也可以采用列表形式的卡片布局。假设获取到的用户评价数据存储在reviews数组中,每个评价对象包含评价内容、评价时间、评分等属性:
<el-card v-for="(review, index) in reviews" :key="index" class="review-card" :body-style="{ padding: '15px' }">
<template #header>
<div class="review-header">
<span class="customer-name">{{ review.customerName }}</span>
<time class="review-time">{{ review.reviewTime }}</time>
</div>
</template>
<div class="review-content">{{ review.content }}</div>
<el-rate :value="review.rating" disabled class="review-rating"></el-rate>
</el-card>
这样,每个用户评价都以卡片的形式展示,头部显示评价客户姓名和评价时间,主体展示评价内容和评分,使评价信息的展示更加清晰和有条理,提升用户体验。
2.3 响应式布局设计
为了使家政平台 PC 前端页面在不同屏幕尺寸下都能保持良好的显示效果,Element plus 的响应式特性,特别是栅格系统(el-row 和 el-col)发挥着关键作用。栅格系统基于 Flex 布局,将页面划分为 24 列,通过设置el-col的span属性来控制列的宽度占比,从而实现灵活的布局。
例如,在展示家政服务项目列表时,希望在大屏幕上每行展示 4 个服务项目,中等屏幕展示 3 个,小屏幕展示 2 个。可以如下实现:
<el-row :gutter="20">
<el-col :span="6" md="8" sm="12" v-for="(service, index) in serviceList" :key="index">
<el-card class="service-card">
<img :src="service.imageUrl" alt="Service Image" class="service-image">
<div class="service-info">
<h3 class="service-title">{{ service.title }}</h3>
<p class="service-description">{{ service.description }}</p>
<el-button type="primary" class="book-button">预约服务</el-button>
</div>
</el-card>
</el-col>
</el-row>
在上述代码中,el-row的gutter属性设置了列与列之间的间距为 20 像素。el-col的span属性设置在超大屏幕(大于 1920px)上每列占 6 份,即每行展示 4 个服务项目;md属性(适用于中等屏幕,992px - 1919px)设置为 8,即每行展示 3 个;sm属性(适用于小屏幕,768px - 991px)设置为 12,即每行展示 2 个。这样,随着屏幕尺寸的变化,服务项目列表能够自动调整布局,以适应不同的屏幕环境。
除了栅格系统,还可以结合 CSS 媒体查询和 Element plus 的其他响应式类名来进一步优化页面在不同设备上的显示效果。例如,对于一些在小屏幕上不需要显示的元素,可以通过媒体查询设置其display:none;对于某些需要在不同屏幕尺寸下调整样式的组件,可以使用 Element plus 提供的响应式类名,如is-hidden-mobile(在移动设备上隐藏)、is-visible-desktop(在桌面设备上显示)等,确保用户在 PC、平板等设备上都能获得一致且友好的使用体验。
三、组件库应用与二次开发
3.1 Element plus 组件的使用技巧
3.1.1 表单组件
在开发家政平台 PC 前端时,表单组件是收集用户信息的关键部分。以家政服务预订表单为例,el-input组件用于输入客户姓名、联系电话等文本信息。例如:
<el-form-item label="客户姓名">
<el-input v-model="formData.customerName" placeholder="请输入姓名" clearable></el-input>
</el-form-item>
这里通过v-model指令实现数据双向绑定,将用户输入的值绑定到formData.customerName变量上,clearable属性为输入框添加清空按钮,方便用户清除已输入内容。
对于选择家政服务类型、服务人员等场景,el-select组件发挥重要作用。假设从后端获取的家政服务类型数据存储在serviceTypeList数组中,每个元素包含value和label属性:
<el-form-item label="服务类型">
<el-select v-model="formData.serviceType" placeholder="请选择服务类型">
<el-option v-for="item in serviceTypeList" :key="item.value" :label="item.label" :value="item.value"></el-option>
</el-select>
</el-form-item>
上述代码中,v-for指令遍历serviceTypeList数组,为每个服务类型生成一个el-option选项,用户选择的服务类型值会同步更新到formData.serviceType中。
在预订家政服务时,选择服务时间是常见需求,el-date-picker组件可轻松实现。若要选择服务预约日期:
<el-form-item label="预约日期">
<el-date-picker v-model="formData.appointmentDate" type="date" placeholder="选择预约日期"></el-date-picker>
</el-form-item>
type="date"指定选择日期类型,还可根据需求设置type为datetimerange选择日期时间范围等。
表单验证是确保用户输入数据准确性和有效性的重要环节。Element plus 提供了强大的表单验证功能,通过el-form的rules属性定义验证规则。例如,对客户联系电话进行验证:
export default {
data() {
return {
formData: {
customerPhone: ''
},
rules: {
customerPhone: [
{ required: true, message: '联系电话不能为空', trigger: 'blur' },
{ pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号码', trigger: 'blur' }
]
}
};
}
};
在模板中,将rules属性绑定到el-form上:
<el-form :model="formData" :rules="rules">
<el-form-item label="联系电话" prop="customerPhone">
<el-input v-model="formData.customerPhone" placeholder="请输入联系电话"></el-input>
</el-form-item>
</el-form>
这里prop属性指定要验证的字段,required规则表示字段必填,pattern规则用于正则表达式验证,trigger指定验证触发时机,如blur表示失去焦点时触发验证。
3.1.2 弹窗组件
弹窗组件在提升用户交互体验方面起着重要作用。以el-dialog为例,当用户点击家政服务详情页面的 “预约服务” 按钮时,弹出预约服务表单弹窗:
<template>
<div>
<el-button type="primary" @click="openDialog">预约服务</el-button>
<el-dialog title="预约服务" :visible.sync="dialogVisible">
<el-form :model="formData" :rules="rules">
<!-- 表单内容 -->
</el-form>
<template #footer>
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="submitForm">提交</el-button>
</template>
</el-dialog>
</div>
</template>
<script setup>
import { ref } from 'vue';
const dialogVisible = ref(false);
const formData = ref({});
const rules = ref({});
const openDialog = () => {
dialogVisible.value = true;
};
const submitForm = () => {
// 表单提交逻辑
};
</script>
通过visible.sync双向绑定控制弹窗的显示与隐藏,title属性设置弹窗标题,#footer插槽自定义弹窗底部按钮。
el-message组件常用于提示用户操作结果。当用户成功提交家政服务订单时,显示成功提示:
import { ElMessage } from 'element-plus';
const submitOrder = () => {
// 订单提交逻辑
ElMessage.success('订单提交成功');
};
ElMessage.success方法显示绿色背景的成功提示信息,还有ElMessage.error(显示红色错误提示)、ElMessage.warning(显示黄色警告提示)等方法。
el-tooltip组件用于在用户鼠标悬停在元素上时显示额外信息。在家政服务人员列表页面,鼠标悬停在服务人员头像上显示其详细信息:
<el-tooltip content="姓名:张三\n服务年限:5年\n擅长技能:家居清洁、家电维修" placement="top">
<img src="serviceWorkerAvatar.jpg" alt="服务人员头像">
</el-tooltip>
content属性设置提示内容,placement属性指定提示框的位置,如top表示在元素上方显示。还可通过插槽自定义提示内容,以满足更复杂的展示需求。
3.2 自定义组件开发与封装
3.2.1 组件封装思路
在开发家政平台 PC 前端时,为提高代码的复用性和可维护性,需要将一些常用的功能封装成自定义组件。以家政服务预订组件为例,在多个页面(如服务详情页、用户个人中心的预订页面等)都会涉及家政服务预订操作,将这部分功能封装成组件,可以避免重复编写代码。
该组件需要接收一些外部传递的参数,如家政服务的详细信息(服务类型、服务价格、服务时长等)、用户的基本信息(姓名、联系方式等)。封装的思路是将这些参数通过props属性传递给组件,组件内部根据这些参数构建预订表单,并处理表单的提交逻辑。这样,在不同页面使用该组件时,只需传入相应的参数,即可快速实现家政服务预订功能。
再比如员工排班组件,家政公司需要对员工的工作时间进行合理安排。这个组件可以接收员工列表、排班规则(如每周工作天数、每天工作时长、休息时间等)等参数。通过将这些参数作为props传递给组件,组件内部利用 Element plus 的表格组件(el-table)展示排班信息,并提供编辑、保存排班的功能。封装该组件后,在员工管理模块的排班页面,以及调度中心的资源分配页面等都可以方便地复用,提高开发效率。
3.2.2 代码实现
以家政服务预订组件为例,创建一个Reservation.vue文件。
模板部分(template):
<template>
<el-dialog title="家政服务预订" :visible.sync="dialogVisible">
<el-form :model="reservationData" :rules="rules" ref="reservationForm">
<el-form-item label="服务类型" prop="serviceType">
<el-select v-model="reservationData.serviceType" placeholder="请选择服务类型">
<el-option v-for="type in serviceTypeList" :key="type.value" :label="type.label" :value="type.value"></el-option>
</el-select>
</el-form-item>
<el-form-item label="服务日期" prop="serviceDate">
<el-date-picker v-model="reservationData.serviceDate" type="date" placeholder="选择服务日期"></el-date-picker>
</el-form-item>
<el-form-item label="客户姓名" prop="customerName">
<el-input v-model="reservationData.customerName" placeholder="请输入姓名"></el-input>
</el-form-item>
<el-form-item label="联系电话" prop="customerPhone">
<el-input v-model="reservationData.customerPhone" placeholder="请输入联系电话"></el-input>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="submitReservation">提交</el-button>
</template>
</el-dialog>
</template>
在上述模板中,使用el-dialog组件创建预订弹窗,通过visible.sync双向绑定控制弹窗的显示和隐藏。el-form组件用于构建预订表单,包含服务类型选择、服务日期选择、客户姓名和联系电话输入等表单项,每个表单项都通过prop属性与rules中的验证规则关联。
脚本部分(script):
import { ref } from 'vue';
export default {
name: 'Reservation',
props: {
serviceTypeList: {
type: Array,
required: true
}
},
setup(props) {
const dialogVisible = ref(false);
const reservationData = ref({
serviceType: '',
serviceDate: '',
customerName: '',
customerPhone: ''
});
const rules = ref({
serviceType: [{ required: true, message: '请选择服务类型', trigger: 'change' }],
serviceDate: [{ required: true, message: '请选择服务日期', trigger: 'change' }],
customerName: [{ required: true, message: '请输入客户姓名', trigger: 'blur' }],
customerPhone: [{ required: true, message: '请输入联系电话', trigger: 'blur' },
{ pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号码', trigger: 'blur' }]
]
});
const openDialog = () => {
dialogVisible.value = true;
};
const submitReservation = () => {
// 表单验证
this.$refs.reservationForm.validate((valid) => {
if (valid) {
// 提交预订逻辑,例如发送HTTP请求到后端
console.log('预订信息提交成功', reservationData.value);
dialogVisible.value = false;
} else {
console.log('表单验证失败');
return false;
}
});
};
return {
dialogVisible,
reservationData,
rules,
openDialog,
submitReservation
};
}
};
在脚本部分,定义了props接收外部传递的serviceTypeList(服务类型列表)。使用ref定义了弹窗的显示状态dialogVisible、预订数据reservationData和验证规则rules。openDialog方法用于打开弹窗,submitReservation方法用于处理表单提交,先进行表单验证,验证通过后执行提交预订逻辑(这里只是简单打印预订信息,实际应用中会发送请求到后端)。
样式部分(style):
<style scoped>
/* 自定义组件样式,例如调整表单元素间距、字体大小等 */
.el-form-item {
margin-bottom: 15px;
}
</style>
样式部分使用scoped属性确保样式只作用于当前组件,通过调整el-form-item的margin-bottom属性,设置表单项之间的间距。
在父组件中使用该自定义组件时,先引入组件并注册:
<template>
<div>
<h2>家政服务页面</h2>
<reservation :serviceTypeList="serviceTypeList" />
</div>
</template>
<script setup>
import Reservation from './Reservation.vue';
const serviceTypeList = [
{ value:'service1', label: '家居清洁' },
{ value:'service2', label: '家电维修' },
{ value:'service3', label: '月嫂服务' }
];
</script>
在上述父组件代码中,引入Reservation.vue组件并注册,将serviceTypeList传递给reservation组件,这样在父组件中就可以使用该预订组件实现家政服务预订功能。
3.3 组件间通信与协同
在构建家政平台 PC 前端时,不同组件之间需要进行通信和协同工作,以实现完整的业务功能。例如,在订单管理模块中,订单列表组件和订单详情组件之间存在紧密的交互。
父子组件通信是常见的通信方式之一。假设订单列表组件(父组件)展示了一系列订单,当用户点击某个订单时,需要在订单详情组件(子组件)中展示该订单的详细信息。在订单列表组件中,可以通过props向订单详情组件传递订单的唯一标识(如订单编号)。
订单列表组件模板(OrderList.vue):
<template>
<el-table :data="orderList" style="width: 100%">
<el-table-column prop="orderId" label="订单编号"></el-table-column>
<el-table-column prop="customerName" label="客户姓名"></el-table-column>
<el-table-column label="操作">
<template #default="scope">
<el-button type="text" @click="showOrderDetail(scope.row.orderId)">查看详情</el-button>
</template>
</el-table-column>
</el-table>
<order-detail :orderId="selectedOrderId" v-if="selectedOrderId" />
</template>
<script setup>
import { ref } from 'vue';
import OrderDetail from './OrderDetail.vue';
const orderList = ref([
{ orderId: 1, customerName: '张三' },
{ orderId: 2, customerName: '李四' }
]);
const selectedOrderId = ref(null);
const showOrderDetail = (orderId) => {
selectedOrderId.value = orderId;
};
</script>
在上述代码中,orderList是订单数据列表,当用户点击 “查看详情” 按钮时,调用showOrderDetail方法,将当前订单的orderId赋值给selectedOrderId,并通过props将selectedOrderId传递给order-detail组件。
订单详情组件模板(OrderDetail.vue):
<template>
<el-card>
<h3>订单详情</h3>
<p>订单编号:{{ orderId }}</p>
<!-- 根据orderId从后端获取并展示订单详细信息 -->
</el-card>
</template>
<script setup>
import { ref } from 'vue';
const props = defineProps({
orderId: {
type: Number,
required: true
}
});
</script>
订单详情组件通过defineProps接收父组件传递的orderId,并在模板中展示订单编号,还可以根据orderId从后端获取订单的详细数据进行展示。
对于兄弟组件通信,可以借助事件总线或 Vuex 来实现。以家政服务管理模块中的服务列表组件和服务编辑组件为例,这两个组件是兄弟关系。当在服务列表组件中点击 “编辑” 按钮时,需要在服务编辑组件中展示对应服务的详细信息。
使用事件总线实现时,首先在创建 Vue 应用实例时创建一个事件总线对象:
// main.js
import { createApp } from 'vue';
import App from './App.vue';
const app = createApp(App);
app.config.globalProperties.$eventBus = new Vue();
app.mount('#app');
在服务列表组件(ServiceList.vue)中:
<template>
<el-table :data="serviceList" style="width: 100%">
<el-table-column prop="serviceId" label="服务ID"></el-table-column>
<el-table-column prop="serviceName" label="服务名称"></el-table-column>
<el-table-column label="操作">
<template #default="scope">
<el-button type="text" @click="editService(scope.row.serviceId)">编辑</el-button>
</template>
</el-table-column>
</el-table>
</template>
<script setup>
import { ref } from 'vue';
const serviceList = ref([
{ serviceId: 1, serviceName: '家居清洁' },
{ serviceId: 2, serviceName: '家电维修' }
]);
const editService = (serviceId) => {
// 通过事件总线触发事件,传递serviceId
this.$eventBus.$emit('edit-service', serviceId);
};
</script>
在服务编辑组件(ServiceEdit.vue)中:
<template>
<el-card>
<h3>服务编辑</h3>
<p v-if="editedServiceId">编辑服务ID:{{ editedServiceId }}</p>
<!-- 根据editedServiceId从后端获取并展示服务详细信息,进行编辑操作 -->
</el-card>
</template>
<script setup>
import { ref, onMounted } from 'vue';
const editedServiceId = ref(null);
onMounted(() => {
// 通过事件总线监听事件
this.$eventBus.$on('edit-service', (serviceId) => {
editedServiceId.value = serviceId;
});