影院购票系统(二)——uni-app移动应用开发
这一篇讲解系统的逻辑代码部分,下面是ai的讲解,也可以直接跳到代码部分进行浏览。
一、整体功能概述
这个Vue组件构建了一个完整的影院座位选择系统,涵盖从座位数据初始化、视图渲染到交互处理以及业务逻辑的整个流程。它遵循响应式编程模式,数据的变化能够及时反映在视图上,反之亦然。
二、核心数据结构剖析
- seatData二维数组
- 组件利用Vue的响应式数据模型,定义了
seatData这个二维数组,用来表示9x14的影院座位布局。每个元素代表一个座位对象,包含seatState(座位状态,取值为不可选、可选、被选中、空白)、rowIndex(排号 + 1)和colIndex(列号 + 1)属性。通过行列索引,能够以O(1)的复杂度查询座位状态,这与影院座位的物理布局特征相匹配。
- 组件利用Vue的响应式数据模型,定义了
- selectedSeats数组
- 用于记录用户选择的座位坐标。在用户操作过程中,这个数组会不断更新,以反映用户的选择状态。
- seatPrice和totalPrice
seatPrice存储座位的单价,并且保持两位小数精度。totalPrice则实时根据selectedSeats数组的长度和seatPrice计算订单总额。
三、生命周期初始化 - onLoad函数
- 座位矩阵构建
- 在
onLoad生命周期钩子函数中,通过双重循环构建9行14列的座位矩阵。这种方式能够清晰地初始化座位数据结构。
- 在
- 状态判定 - decideState方法
- 采用坐标匹配算法对座位状态进行初始化。例如,对于过道等特殊区域(如
i = 7且j∈[2,13])标记为空白,模拟已售座位(如i = 6的特定列)标记为不可选,其余位置默认为可选状态。不过,目前这种硬编码的方式虽然直观,但可维护性较差。建议将特殊坐标等配置参数化,以便在需要调整布局或座位状态时能够更轻松地进行修改。
- 采用坐标匹配算法对座位状态进行初始化。例如,对于过道等特殊区域(如
四、视图交互方法解析
- 视图渲染 - getSeatImage方法
- 此方法遵循状态驱动视图模式,通过座位的
seatState属性映射到相应的静态资源路径。例如,不可选状态返回不可选座位图标,可选状态返回可选座位图标,被选中状态返回选中状态图标,空白状态返回透明占位图。这种方式有效地实现了视图层与数据层的解耦,符合MVVM设计原则,并且使用uni - app的图片组件能够实现跨平台渲染。
- 此方法遵循状态驱动视图模式,通过座位的
- 选择逻辑 - selectSeat方法
- 该方法实现了一个状态切换的三态机逻辑。
- 当座位处于不可选状态时,触发
uni.showToast提示用户,这是一种很好的异常反馈封装方式。 - 对于空白区域,建立了防误触机制,避免不必要的操作。
- 可选与被选中状态之间通过状态切换算法完成状态变更。当座位从被选中变为可选时,从
selectedSeats数组中移除相应记录,并且总价减42;当座位从可选变为被选中时,添加选中记录到selectedSeats数组,总价加42。然而,在定位已选座位索引时,当前采用线性查找(O(n)复杂度),这在性能上存在一定的瓶颈。建议改用哈希表或者Map结构存储行列组合键(如${row}-${col})来实现O(1)的查找效率。
- 当座位处于不可选状态时,触发
- 该方法实现了一个状态切换的三态机逻辑。
五、业务逻辑封装解析
- 下单验证 - clickOrder方法
- 该方法通过
uni.showModal模态对话框实现二次确认,这符合金融交易安全规范。在成功下单后,执行一系列状态持久化操作,如将选中座位标记为不可选,并清空临时数据。不过,目前在清空selectedSeats数组和重置totalPrice时存在一些问题。- 清空
selectedSeats数组时,不应采用循环splice的方式,直接赋值为空数组更为简洁高效。 - 重置
totalPrice时,应该使用this.totalPrice = 0,以避免变量作用域错误。
- 清空
- 该方法通过
- 异常处理
- 在用户没有选择座位就下单时,采用防御性编程策略,通过
uni.showToast组件阻断无效操作,并且通过image参数实现图形化的错误提示信息反馈,提供了较好的用户体验。
- 在用户没有选择座位就下单时,采用防御性编程策略,通过
六、性能优化建议
- 计算属性优化
- 目前
totalPrice采用显式加减运算,这可能会导致金额累加误差。建议改用computed属性,基于selectedSeats.length动态计算totalPrice,以确保金额计算的准确性。
- 目前
- 响应式更新
- 直接通过索引修改
seatData数组元素可能会丢失响应性。建议使用Vue.set或数组splice方法触发视图更新,以确保视图能够正确反映数据的变化。
- 直接通过索引修改
- 状态管理优化
- 整个组件存在状态管理颗粒度过粗的问题。建议引入
Vuex进行全局状态管理,解耦座位数据与视图逻辑。同时,对于大数据量下的渲染性能,可以考虑采用Immutable.js进行优化,提高应用的整体性能。
- 整个组件存在状态管理颗粒度过粗的问题。建议引入
代码
最终完整代码如下:
<!-- 小程序的标题在 pages.json中navigationBarTitleText定义,其中page的是启动页标题,而globalStyle是全局的标题-->
<template><view class="content"><!-- <image src="../../static/可选座位.png" --><text class="title">屏幕</text><!-- 用v-for去渲染数据,这里得对座位对象的属性进行设计1. 状态:未选、不可选、选中2. 根据状态去更换图片路径并且由于是二维座位数组,所以需要用两个v-for进行渲染,其中row为行索引,col为列索引--><view class="container"><view v-for="(row, rowIndex) in seatData" :key="rowIndex" class="seat-row"><view v-for="(col, colIndex) in row" :key="colIndex" class="" @click="selectSeat(rowIndex, colIndex)"><!-- 在methods设定一个函数来获取座位状态,来显示对应的图标,参数应当是col(座位)对象的seatState属性 --><image :src="getSeatImage(col.seatState)"></image></view></view></view><view class="selected-seats-info"><view class="seatAddress"><view v-for="(seat, index) in selectedSeats" :key="index" class="seatItem">{{seat.selectedRow}}排{{seat.selectedCol}}列</view></view><view class="seatMoney"><view class="text">购票:{{selectedSeats.length}} 张</view><!-- 这里使用toFixed(2)函数让数字显示出小数点后两位 --><view class="text">单价:¥{{seatPrice.toFixed(2)}}</view></view><!-- 设置点击下单函数 --><button @click="clickOrder" class="btn">¥{{totalPrice.toFixed(2)}} 确认下单</button></view><view class="image"><image src='../../static/photo.jpg' mode="widthFix" style="width:230px"></image></view></view></template><script>export default {data() {return {// 在data中定义seatData属性seatData: [],selectedSeats: [],seatPrice: 42.00, // 假设单价为 50 元totalPrice: 0}},onLoad() {// 定义座位行数和列数const rows = 9;const cols = 14;// 创建座位数组const seatData = [];// 创建被选中座位for (let i = 0; i < rows; i++) {// 创建单行数组const row = [];for (let j = 0; j < cols; j++) {// 往单行数组里添加一个对象体// 先判断现实位置是否是空白,即无座位// 在这里设定一个状态枚举变量,来对应座位的状态if (this.decideState(i, j) == '空白') {row.push({seatState: '空白',rowIndex: i + 1,colIndex: j + 1})} else if (this.decideState(i, j) == '不可选') {row.push({seatState: '不可选',rowIndex: i + 1,colIndex: j + 1})} else {row.push({seatState: '可选',rowIndex: i + 1,colIndex: j + 1})}}// 最后往座位数组添加单行数组seatData.push(row);}// 将seatData数组赋值到当前vue实例的seatData属性this.seatData = seatData;},methods: {// 返回图片路径函数getSeatImage(seatState) {if (seatState == '不可选') {return '../../static/不可选座位.png';} else if (seatState == '可选') {return '../../static/可选座位.png';} else if (seatState == '被选中') {return '../../static/被选中座位.png';} else return '../../static/空白.png';},// 座位点击函数selectSeat(rowIndex, colIndex) {if (this.seatData[rowIndex][colIndex].seatState == '不可选') {uni.showToast({title: '该座位已被选用',duration: 1500,icon: 'none', // 不显示默认图标image: '../../static/错误.png', // 使用自定义图标mask: true // 显示透明蒙层,防止触摸穿透})return;}if (this.seatData[rowIndex][colIndex].seatState == '空白') {return;}if (this.seatData[rowIndex][colIndex].seatState == '被选中') {this.seatData[rowIndex][colIndex].seatState = '可选';// 获取该位置在数组中的索引let index = -1;for(index =0; index < this.selectedSeats.length; index++){if(this.selectedSeats[index].selectedRow == rowIndex&&this.selectedSeats[index].selectedCol == colIndex){break;}}// 所成功找到索引,则溢出该座位if (index !== -1) {this.selectedSeats.splice(index, 1);}this.totalPrice -= 42;} else {this.seatData[rowIndex][colIndex].seatState = '被选中';this.selectedSeats.push({selectedRow: rowIndex,selectedCol: colIndex});this.totalPrice += 42;}},// 分辨座位类型函数decideState(i, j) {if ((i == 7 && j == 2) || (i == 7 && j == 11) || (i == 7 && j == 12) || (i == 7 && j == 13) || (i == 8 &&j == 0) || (i == 8 && j == 1) || (i == 8 && j == 2) || (i == 8 && j == 9) || (i == 8 && j == 10) ||(i == 8 && j == 11) || (i == 8 && j == 12) || (i == 8 && j == 13)) {return '空白';} else if ((i == 6 && j == 0) || (i == 6 && j == 1) || (i == 6 && j == 9) || (i == 6 && j == 10) || (i ==6 && j == 12) || (i == 6 && j == 13)) {return '不可选';} else return '';},// 点击下单函数clickOrder() {// 判断用户是否有选中座位if (this.selectedSeats.length == 0) {uni.showToast({title: '请先选择座位',duration: 1500,icon: 'none', // 不显示默认图标image: '../../static/错误.png', // 使用自定义图标mask: true // 显示透明蒙层,防止触摸穿透})return;}uni.showModal({title: '操作提示',content: '确认购买?',showCancel: true, // 是否显示取消按钮(默认true)cancelText: '取消', // 取消按钮文字(默认"取消")cancelColor: '#999', // 取消按钮文字颜色(默认#000)confirmText: '确定', // 确认按钮文字(默认"确定")confirmColor: '#007AFF', // 确认按钮颜色(默认#3CC51F)success: res => {if (res.confirm) {console.log('用户确认操作');// 执行确认逻辑uni.showToast({title: '下单成功',duration: 1500});// 将被选座位同步到总座位数组里this.selectedSeats.forEach(seat=>{let row = seat.selectedRow;let col = seat.selectedCol;this.seatData[row][col].seatState = '不可选'; });// 清空被选中座位let leng = this.selectedSeats.length;for(let i = 0; i < leng; i++) {this.selectedSeats.splice(i, 1);}// 以及对总价重新赋值totalPrice = 0;} else if (res.cancel) {console.log('用户取消操作');// 执行取消逻辑return;}}});}}}
</script><style>.content {padding: 10px;text-align: center;}/* text-align属性需要作用于父容器,因此要实现屏幕居中,要对它的父容器进行设置。 */.title {display: inline-block;/* 确保行内元素特性 */font-size: 20px;margin-bottom: 10px;}.container {padding: 0 10px;}.seat-row {display: flex;/* 修改为 flex-start 或 space-between 以减少行内元素间的间距 */justify-content: flex-start;/* 可以根据需要调整行与行之间的间距 */margin-bottom: 5px;/* 添加负边距来减少行内元素的间距 */margin-left: -5px;}.seat-row view {width: 30px;height: 30px;}.seat-row view image {width: 100%;height: 100%;}.selected-seats-info {border: 2px solid #ccc;border-radius: 10px;;}.seatAddress {margin: 10px 20px;display: flex;/* 子元素按横轴方向顺序排列 */flex-direction: row;/* 设置从开始方向对齐 */justify-content: flex-start;/* 设置子元素可放置成多行 */flex-wrap: wrap;}.seatItem {font-size: 15px;background-color: antiquewhite;border: 1px solid antiquewhite;border-radius: 3px;margin: 2px;padding: 2px;}.seatMoney {display: flex;justify-content: space-evenly;padding: 8px;font-size: 18px;}.btn {width:90%;background-color: antiquewhite;border:1px solid antiquewhite;border-radius: 4px;margin-bottom:10px;}
</style>
相关文章:
影院购票系统(二)——uni-app移动应用开发
这一篇讲解系统的逻辑代码部分,下面是ai的讲解,也可以直接跳到代码部分进行浏览。 一、整体功能概述 这个Vue组件构建了一个完整的影院座位选择系统,涵盖从座位数据初始化、视图渲染到交互处理以及业务逻辑的整个流程。它遵循响应式编程模式…...
DeepSeek×博云AIOS:突破算力桎梏,开启AI普惠新纪元
背景 在全球人工智能技术高速迭代的背景下,算力成本高企、异构资源适配复杂、模型部署效率低下等问题,始终是制约企业AI规模化应用的关键。 DeepSeek以创新技术直击产业痛点,而博云先进算力管理平台AIOS的全面适配,则为这一技术…...
DeepSeek能画流程图吗?分享一种我正在使用的DeepSeek画流程图教程
…...
网络安全试题填空题
🍅 点击文末小卡片 ,免费获取网络安全全套资料,资料在手,涨薪更快 2018年期末题 1. 分布式防火墙系统组成不包括(D) A.网络防火墙 B.主机防火墙 C.中心管理防火墙 D.传统防火墙 2.下列不是入侵者主要行为模…...
MySQL中查看表结构
1. 使用 DESCRIBE 或 DESC 命令 DESCRIBE(或其简写 DESC)是最简单和最直接的方法,可以显示表的列信息。 语法: DESCRIBE table_name; -- 或者 DESC table_name;示例: 假设有一个名为 employees 的表,可以…...
个推助力小米米家全场景智能生活体验再升级
当AI如同水电煤一般融入日常,万物互联的图景正从想象照进现实。作为智能家居领域的领跑者,小米米家凭借开放的生态战略,已连接了超8.6亿台设备,构建起全球领先的消费级AIoT平台。如今,小米米家携手个推,通过…...
linux服务器根据内核架构下载各种软件依赖插件(例子:Anolis服务器ARM64架构内核Nginx依赖插件下载)
Anolis服务器ARM64架构内核Nginx依赖插件下载 Nginxy依赖包:阿里云镜像站搜索自己的系统如下点击系统,进入详情页面点击下载地址点击对应版本号选择Os继续点击OS点击Packagesctrf搜索资源,依次下载资源,版本建议选最新把下载好的资…...
[css] line-height如何继承
line-height继承,一共有以下3种情况: <body><p>这是一行文字</p> </body>写具体数值,则直接继承该值。 body {font-size: 20px;line-height: 50px; /* 数值 */ } p {font-size: 10px; }<p> 元素 line-height…...
GaussianCity:实时生成城市级数字孪生基底的技术突破
在空间智能领域,如何高效、大规模地生成高质量的3D城市模型一直是一个重大挑战。传统方法如NeRF和3D高斯溅射技术(3D-GS)在效率和规模上存在显著瓶颈。GaussianCity通过创新性的技术方案,成功突破了这些限制,为城市级数字孪生的构建提供了全新路径。 一、核心创新:突破传…...
华为配置篇-OSPF基础实验
OSPF 一、简述二、常用命令总结三、实验3.1 OSPF单区域 一、简述 OSPF(开放式最短路径优先协议) 基本定义 全称:Open Shortest Path First 类型:链路状态路由协议(IGP),用于自治系统ÿ…...
获取哔站评论
一、文章立论 哔哩哔哩(B站)是当前年轻人十分喜爱的视频分享平台,以其丰富多样的内容、互动性强的社区氛围以及独特的弹幕文化深受用户喜爱。在该平台上,用户不仅可以观看各种类型的视频,如动画、游戏、科技、生活、影…...
《当AI生成内容遭遇审核:需求与困境的深度剖析》:此文为AI自动生成
AI 内容审核:数字时代的守门人 在当今数字技术迅猛发展的浪潮中,AI 在内容生成领域取得了令人瞩目的成就,成为了推动创新与变革的核心力量。以 AI 绘画为例,从早期简单粗糙的图像生成,到如今能够创作出细节丰富、风格多…...
jenkins流程概述
1. 需求场景 代码托管在 GitHub/GitLab需要 自动化构建 Docker 镜像,并部署到 Docker 服务器生产环境使用 Docker Compose 或 Kubernetes(K8s) 2. Jenkins 流程 拉取代码使用 Maven 构建 JAR使用 Docker 构建镜像推送 Docker 镜像到仓库在…...
深入 Vue.js 组件开发:从基础到实践
深入 Vue.js 组件开发:从基础到实践 Vue.js 作为一款卓越的前端框架,其组件化开发模式为构建高效、可维护的用户界面提供了强大支持。在这篇博客中,我们将深入探讨 Vue.js 组件开发的各个方面,从基础概念到高级技巧,助…...
【FAQ】HarmonyOS SDK 闭源开放能力 —Map Kit(5)
1.问题描述: 提供两套标准方案,可根据体验需求选择: 1.地图Picker(地点详情) 用户体验:①展示地图 ②标记地点 ③用户选择已安装地图应用 接入文档:https://developer.huawei.com/consumer/cn/doc/harmonyos-guide…...
C#基础及标准控件的使用,附登录案例
C#基础及标准控件的使用,附登录案例 一、项目整体结构1. 项目结构2. 程序结构二、项目的基础操作三、常用的windows标准控件1. 按钮控件的使用2. 项目资源的配置(如图标)3. 文本控件的使用四、WinForm程序生成及运行调试1. Debug调试模式下生成2. Release发布模式下生成3. 程…...
61. Three.js案例-彩色旋转立方体创建与材质应用
61. Three.js案例-彩色旋转立方体创建与材质应用 实现效果 知识点 WebGLRenderer(WebGL渲染器) 构造器 WebGLRenderer( parameters : Object ) 参数类型描述antialiasBoolean是否执行抗锯齿(默认false)alphaBoolean是否包含alpha通道(默认false)方法 setSize( width…...
CSRF 攻击详解:原理、案例与防御
跨站请求伪造(Cross-Site Request Forgery,简称 CSRF)是一种针对 Web 应用程序的攻击方式。通过 CSRF,攻击者诱导受害者在不知情的情况下,以受害者的身份执行非本意的操作。本文将详细介绍 CSRF 的基本原理、常见攻击方…...
爬虫逆向实战小记——解决captcha滑动验证码
注意!!!!某XX网站实例仅作为学习案例,禁止其他个人以及团体做谋利用途!!! IGh0dHBzOi8vY2FwdGNoYS5ydWlqaWUuY29tLmNuLw 第一步: 分析请求网址和响应内容 (1)通过观察,滑…...
Spring Boot3+Vue3极速整合: 10分钟搭建DeepSeek AI对话系统(进阶)
Spring Boot3Vue3极速整合: 10分钟搭建DeepSeek AI对话系统(进阶) 前言 在上次实战指南《Spring Boot3Vue2极速整合: 10分钟搭建DeepSeek AI对话系统》引发读者热议后,我通过200真实用户反馈锁定了几个问题进行优化进阶处理: 每次对话都需重复上下文背…...
Python 图像处理之 Pillow 库:玩转图片
哈喽,大家好,我是木头左! Pillow 库作为 Python 图像处理的重要工具之一,为提供了便捷且功能丰富的接口,让能够轻松地对图像进行各种操作,从简单的裁剪、旋转到复杂的滤镜应用、图像合成等,几乎无所不能。接下来,就让一起深入探索如何使用 Pillow 库来处理图片,开启一…...
Node.js学习分享(上)
Node.js fs文件系统模块fs.readFile()fs.writeFile() path路径模块路径拼接path.join()获取路径中的文件名path.basename()的语法格式 获取路径中的文件扩展名path.extname()的语法格式 http模块服务器相关概念IP地址域名和域名服务器端口号 创建最基本的web服务器创建web服务器…...
Java 第十一章 GUI编程(2)
目录 GUI 事件处理 基本思路 添加事件监听器 对话框 实例 GUI 事件处理 对于采用了图形用户界面的程序来说,事件控制是非常重要的;到目前为止, 我们编写的图形用户界面程序都仅仅只是完成了界面,而没有任何实际的功能&…...
anaconda 安装geemap配置详细教程
本章教程,主要介绍如何通过anaconda 安装ee 和geemap模块 一、示例代码 创建一个测试文件:geemapTets.ipynb # 导入 Google Earth Engine (GEE) 库,用于处理地理空间数据 import ee # 导入 geemap 库,用于可视化和交互式处理 GEE 数据 import geemap # 创建一个 geemap.M…...
4G工业路由器在公交充电桩中的应用与优势
随着电动公交车的普及,公交充电桩的稳定运行和高效管理是交通营运部门最关心的问题。4G工业路由器凭借其卓越的数据采集和通讯能力,成为实现充电桩智能化管理的关键。 公交充电桩运维管理需求概述: 1.实时性:实时监控充电状态、剩…...
【设计模式】单例模式|饿汉模式|懒汉模式|指令重排序
目录 1.什么是单例模式? 2.如何保证单例? 3.两种写法 (1)饿汉模式(早创建) (2)懒汉模式(缓执行,可能不执行) 4.应用场景 🔥5.多…...
Microsoft.Office.Interop.Excel 的简单操作
Microsoft.Office.Interop.Excel 的简单操作 1、安装 Microsoft.Office.Interop.Excel2、声明引用 Microsoft.Office.Interop.Excel3、简单的新建 EXCEL 操作代码4、将 DataGridView 表数据写到 EXCEL 操作代码5、将 EXCEL 表数据读取到 C# 数据表 DataTable 操作代码 1、安装 …...
说一下redis事务底层原理
Redis事务 1. 事务的基本流程 Redis 事务通过 MULTI、EXEC、WATCH 等命令实现,底层原理可以分为以下几个步骤: (1) MULTI 命令 当客户端发送 MULTI 命令时,Redis 会将客户端标记为“事务模式”。在事务模式下,客户端发送的所有…...
【powerjob】 powerjobserver注册服务IP错误
1、问题:powerjobserver 4.3.6 的服务器上有多个网卡对应多个ip,示例 eth0 :IP1 ,docker0:IP2 和worker 进行通信时 正确的应该时IP1 但是注册显示获取的确实IP2,导致 worker 通过ip2和server通信,网络不通,注册不上 2、解决方案 …...
01. HarmonyOS应用开发实践与技术解析
文章目录 前言项目概述HarmonyOS应用架构项目结构Ability生命周期 ArkTS语言特性装饰器状态管理 UI组件与布局基础组件响应式布局样式与主题 页面路由与参数传递页面跳转参数接收 数据绑定与循环渲染数据接口定义循环渲染 条件渲染组件生命周期最佳实践与性能优化组件复用响应式…...
