用uniapp实现打印预约界面一😶‍🌫️

275 阅读6分钟

前言

前段时间在公司做了一个uiapp的小程序,我负责打印预约界面的制作。为了能在下次面试中有料可讲我决定好好写篇文章来梳理梳理自负责的部分!!

需求

要求还是挺易懂的,但实现起来还是挺吃力的🥲要求如下:

894ae30654e1b0c95ecc4fbd55775b5.jpg

uview-plus

之前都是用的vant4和ElementPlus,但是uniapp有它独属的组件库-uview-plus。其实组件库都大差不差,uview-plus比前两者要简洁好多。它是一个专为 uni-app 生态设计的 UI 框架,旨在帮助开发者快速构建多平台应用。它全面兼容 nvue(原生渲染组件)、鸿蒙系统、以及 uni-app-x 版本,使得基于 Vue 3 的项目可以在多个平台上运行,包括 iOS、Android、H5 以及各种小程序(如微信、支付宝、百度等)。话不多说我来进入正题!!

组件的选择

产品经理并没有给我UI图只是给我需求,于是自己便看着组件库慢慢构思,后边呢也是边做边改。下边是效果的实现!!

image.png 我这个部分是pc端的,用户可以通过信息筛选出符合条件的乘客,并把乘客的信息打印出来交给司机。 如图我们可以单独勾选去程、单独勾选返程、同时勾选去程和返程。然后选择上下车地点,选择去程日期、或选择返程日期、或同时选择。然后我就可以点击提交按钮就能得出所有的乘客信息。 值得注意的是同时勾选去程和返程的时候,会检索出来两批乘客,一批乘客是在这里上车,一批刚好在这里下车。

image.png 然后点击打印订单。

image.png

为了实现效果我引入了check-box复选框组件,并对该组件进行了修改,下边是修改后的代码。

<view class="checkbox-container">  
<up-checkbox-group  
v-model="selectedValues"  
placement="column"  
@change="checkboxChange"  
>  
<view class="checkbox-row">  
<!-- 遍历生成的复选框,包括上下车地点 -->  
<div  
v-for="(item, index) in checkboxList1"  
:key="index"  
class="checkbox-item"  
>  
<up-checkbox :label="item.name.label" :name="item.name"></up-checkbox>  
</div>  
<Place @update-selected-id="handleSelectedId" />  
</view>  
</up-checkbox-group>  
<div><Reservation_date @updateDates="handleUpdateDates" /></div>  
</view>

我先设置一个响应式的数组checkboxList用来存储我需要的复选框数据(比如复选框的lable之类的)。然后通过v-for指令来遍历这个数组生成了复选框。

image.png 这个部分我封装了一个

<Reservation_date @updateDates="handleUpdateDates" />
组件,下边是该组件的代码。

<template>  
<view class="container">  
<!-- 去程日期文本 -->  
<div class="date-label">去程日期:</div>  
<!-- 表单区域 -->  
<div class="form-field" @click="show = true" style="margin-left: -13px">  
<div class="date-value">{{ selectedDate || "请选择日期" }}</div>  
</div>  
<!-- 日历组件 -->  
<!-- <up-calendar :show="show" @select="selectDate"></up-calendar>-->  
<DatetimePicker  
:show="show"  
@confirm="handleDateConfirm"  
@update:show="onUpdateShow"  
/>  
<div style="margin-left: 50px; display: flex">  
<div class="date-label" style="margin-top: 17px">返程日期:</div>  
<div class="form-field" @click="show2 = true">  
<div class="date-value">{{ selectedDate2 || "请选择日期" }}</div>  
</div>  
<Datetime  
:show2="show2"  
@confirm2="handleDateConfirm2"  
@update:show2="onUpdateShow2"  
/>  
</div>  
</view>  
</template>  
<script setup>  
import { ref } from "vue";  
import DatetimePicker from "@/components/DatetimerPicker.vue";  
import Datetime from "@/components/DatetimerPicker2.vue";  
import { useFormatDate } from "@/hooks/useFormaDate";  
// 控制日历组件的显示状态  
const show = ref(false);  
const show2 = ref(false);  
// 存储选中的日期  
const selectedDate = ref("");  
const selectedDate2 = ref("");  
const emit = defineEmits(["updateDates"]);  
// 处理日期选择的函数  
const onUpdateShow = (newShowValue) => {  
show.value = newShowValue;  
};  
const onUpdateShow2 = (newShowValue) => {  
show2.value = newShowValue;  
};  
let formatDate = ref("");  
let formDate2 = ref("");  
const handleDateConfirm = (timestamp) => {  
formatDate = useFormatDate(timestamp);  
selectedDate.value = formatDate;  
emit("updateDates", selectedDate, selectedDate2);  
  
console.log(selectedDate.value);  
};  
const handleDateConfirm2 = (timestamp) => {  
formDate2 = useFormatDate(timestamp);  
selectedDate2.value = formDate2;  
emit("updateDates", selectedDate, selectedDate2);  
console.log("返程日期" + selectedDate2.value);  
};  
const sendDatesToParent = () => {  
emit("updateDates", selectedDate.value, selectedDate2.value);  
};  
</script>

我可以点击选择日期然后就会弹出日期框,日期框是调用 DatetimePicker组件下边是效果!!

image.png 弹出日期框的效果是由于DatetimePicker组件上绑定了一个show属性(响应式变量),当我们点击选择日期后就会把show设置为true。

image.png 日期弹出框出来后,我们要获得这个时间戳,并将其格式化。 于是我封装了一个useFormatDatehook函数用来将时间戳格式化为 "YYYY-MM-DD HH:MM:SS" 格式的字符串。具体代码如下:

<DatetimePicker  
:show="show"  
@confirm="handleDateConfirm"  
@update:show="onUpdateShow"  
/>

@confirm 事件是用于处理用户在选择日期和时间后确认选择的操作。当用户在日期时间选择器中挑选了他们想要的日期和时间,并点击了确认按钮(或执行了确认动作),这个事件就会被触发。这个事件就是 handleDateConfirm:

const handleDateConfirm = (timestamp) => {  
formatDate = useFormatDate(timestamp);  
selectedDate.value = formatDate;  
emit("updateDates", selectedDate, selectedDate2);  
  
console.log(selectedDate.value);  
};

我们获取到了时间后便要将其传输给父组件,那么这里用到的数据流管理就是组件通信中的父子组件通信,子组件向父组件传值,过程如下: 子组件通过defineEmits发布一个updateDates事件。

首先先定义发布const emit = defineEmits(["updateDates"]); 然后在handleDateConfirm函数里边发布emit("updateDates", selectedDate, selectedDate2); 发布updateDates。之后呢在父组件里边订阅, <Reservation_date @updateDates="handleUpdateDates" />通过v-on来绑定发布出来的事件。(数据更新事件触发后会执行数据更新函数)具体执行逻辑为const handleUpdateDates = (date1, date2) => {
selectedDate.value = date1;
selectedDate2.value = date2;
}; 之前emit发布出来的两个值emit("updateDates", selectedDate, selectedDate2);都会作为参数传给父组件的数据更新函数。这样父组件就拿到了时间。

向后端发送请求

uni.request({  
url: baseUrl + "/order/reserve/list", // 你的请求地址  
method: "POST", // 请求方法,支持 GET、POST 等  
data: {  
go_date: selectedDate.value.value,  
return_date: selectedDate2.value.value,  
reservation_type: reservation_type.value,  
pickup_location_id: selectedId.value,  
},  
header: {  
"Content-Type": "application/json", // 请求头,可以根据需要设置  
},  
success: (res) => {  
data.value = res.data.data;  
// 请求成功的回调  
console.log("请求成功", data.value);  
},  
fail: (err) => {  
// 请求失败的回调  
console.error("请求失败", err);  
},  
});

如上段代码所见我向后端发送POST请求。请求是一个对象的形式。里边有url(请求地址),method(请求方法),data(发送给后端的参数数据),header(请求头),success(成功时候返回来数据),fail(失败时候执行的逻辑)

v-print

全局定义v-print指令,当时要做出打印订单的要求便通过github上找到了vue3-print-nb这个包并通过阅读readme.md文件来使用这个包。先是 npm install vue3-print-nb,然后在main.ts文件里边 import Vue3PrintNb from 'vue3-print-nb';然后再 app.use(Vue3PrintNb);这样便全局挂载了,项目中的所有组件都能使用到。执行打印逻辑的部分主要是通过 v-print 指令和 printSetting 配置对象来实现的。v-print指令是插件自带的

image.png

  • v-print 是一个自定义指令,通常由第三方库(如 vue3-print-nb)提供。它会在绑定的元素(在这个例子中是 up-button 按钮)上监听点击事件,并根据传递的配置对象(printSetting)来执行打印操作。printSetting配置对象如下:

image.png

printSetting 配置对象定义了要打印的内容和样式。它指定了要打印的 DOM 元素的 ID (id: "printMe") 和自定义的打印样式 (style 属性)

<section v-if="true" ref="orderBody" class="order-body" id="printMe">  
<div class="tables-container">  
<div class="table-section" v-if="goOrders.length > 0">  
<div style="justify-content: space-evenly; font-weight: bold">  
<span> {{ dateLabel }}: {{ formattedDate }} ({{ weekday }}) </span>  
<span>一共 {{ goOrders.length }} 批客人</span>  
<span>总人数: {{ totalPassengerCount }}</span>  
<p>上下车地点: {{ goOrders[0]?.pickup_location }}</p>  
</div>  
<table class="order-table">  
<thead>  
<tr>  
<th style="width: 195px">序号</th>  
<th style="width: 195px">行程类型</th>  
<th style="width: 195px">姓名</th>  
<th style="width: 195px">人数</th>  
<th style="width: 195px">手机号</th>  
<th style="width: 195px">实到人数</th>  
</tr>  
</thead>  
<tbody>  
<tr v-for="(item, index) in goOrders" :key="index">  
<td>{{ index + 1 }}</td>  
<td>{{ tripTypeText(item.reservation_type) }}</td>  
<td>{{ item.passenger_name }}</td>  
<td>{{ item.passenger_count }}</td>  
<td>{{ item.phone_number }}</td>  
<td>{{ item.actual_passenger_count || "" }}</td>  
</tr>  
</tbody>  
</table>  
</div>  
<div class="table-section" v-if="returnOrders.length > 0">  
<div style="justify-content: space-evenly; font-weight: bold">  
<span> {{ dateLabel }}: {{ formattedDate }} ({{ weekday }}) </span>  
<span>一共 {{ returnOrders.length }} 批客人</span>  
<span>总人数: {{ returnTotalPassengerCount }}</span>  
<p>上下车地点: {{ returnOrders[0]?.pickup_location }}</p>  
</div>  
<table class="order-table">  
<thead>  
<tr>  
<th style="width: 195px">序号</th>  
<th style="width: 195px">行程类型</th>  
<th style="width: 195px">姓名</th>  
<th style="width: 195px">人数</th>  
<th style="width: 195px">手机号</th>  
<th style="width: 195px">实到人数</th>  
</tr>  
</thead>  
<tbody>  
<tr v-for="(item, index) in returnOrders" :key="index">  
<td>{{ index + 1 }}</td>  
<td>{{ tripTypeText(item.reservation_type) }}</td>  
<td>{{ item.passenger_name }}</td>  
<td>{{ item.passenger_count }}</td>  
<td>{{ item.phone_number }}</td>  
<td>{{ item.actual_passenger_count || "" }}</td>  
</tr>  
</tbody>  
</table>  
</div>  
</div>  
</section>

section便是我要打印的内容,给它绑定了一个id为printMe。这样便可以打印出来啦!!好啦今天的分享到这里了请多多支持哈~点赞收藏