更新记录
1.0.2(2026-09-03) 下载此版本
优化包大小
1.0.1(2026-09-03) 下载此版本
优化
平台兼容性
uni-app
| Vue2 | Vue3 | Chrome | Safari | app-vue | app-nvue | Android | iOS | 鸿蒙 |
|---|---|---|---|---|---|---|---|---|
| √ | √ | √ | √ | × | × | - | - | - |
| 微信小程序 | 支付宝小程序 | 抖音小程序 | 百度小程序 | 快手小程序 | 京东小程序 | 鸿蒙元服务 | QQ小程序 | 飞书小程序 | 小红书小程序 | 快应用-华为 | 快应用-联盟 |
|---|---|---|---|---|---|---|---|---|---|---|---|
| √ | √ | √ | × | × | × | - | × | × | - | - | - |
其他
| 多语言 | 暗黑模式 | 宽屏模式 |
|---|---|---|
| × | × | √ |
canvas-seatmap-pro
适用范围
- 影院、剧院和规则行列排列的选座场景
- 普通座、情侣座、分区价格
- H5、微信小程序、支付宝小程序、抖音小程序(均已真机验证)
- Vue 2、Vue 3
- App 端(app-vue / nvue)暂不支持
本组件不处理座位设计器、任意旋转座位、自由多边形分区或大型场馆设计图格式。
Vue2 项目注意:监听 error 事件时处理方法不要命名为 onError(uni-app 应用级生命周期保留名,Vue2 下会被框架劫持导致绑定失效),改用其他名字如 onSeatmapError。
抖音小程序接入注意:抖音不支持 canvas 的 disable-scroll 属性,宿主页面需在 pages.json 配置 "disableScroll": true,否则拖动画布会带动页面滚动。
基础用法
组件符合 easycom 规范,无需 import 和注册,直接在页面中使用 <canvas-seatmap-pro> 标签即可。
<template>
<canvas-seatmap-pro
ref="seatmap"
@ready="onReady"
@change="onChange"
@seat-tap="onSeatTap"
@error="onSeatmapError"
/>
</template>
<script>
export default {
data() {
return { seatList: [], selectedSeats: [] }
},
async onReady() {
try {
await this.$refs.seatmap.init({
seatList: this.seatList,
options: {
title: '淘淘票',
hallName: '1号厅',
footerHeight: 270,
maxSelectNum: 4,
isolateSeats: true,
areaList: []
}
})
} catch (error) {
// 初始化错误也会通过 error 事件抛出。
}
},
methods: {
onReady(event) {
console.log('初始化完成', event.seatCount)
},
onChange(event) {
this.selectedSeats = event.selectedSeats
},
onSeatTap(event) {
console.log(event.seat, event.changed)
},
onSeatmapError(error) {
if (['MAX_SELECT_EXCEEDED', 'ISOLATED_SEAT'].includes(error.code) && error.message) {
uni.showToast({ title: error.message, icon: 'none' })
return
}
// 其他技术错误用于开发排查,不建议直接向终端用户展示。
console.error('[canvas-seatmap-pro]', error)
}
}
}
</script>
组件采用 ref.init() 初始化,不使用 seatList 和 options Props。
座位数据
[
{
seatId: 'seat-6-13',
rowId: 6,
columnId: 13,
rowName: 'F排',
seatName: 'F排13座',
status: 1,
seatType: 0,
areaId: '372',
marketPrice: 8000
}
]
| 字段 | 说明 |
|---|---|
seatId |
座位唯一标识,推荐所有座位都提供 |
rowId |
Canvas布局行坐标,必须是大于等于0的数字或数字字符串 |
columnId |
Canvas布局列坐标,必须是大于等于0的数字或数字字符串 |
rowName |
接口返回的实际排号;未传时不显示排号栏 |
seatName |
业务展示名称,组件原样返回 |
status |
座位状态 |
seatType |
座位类型 |
areaId |
价格或颜色区域标识 |
行列坐标只决定座位画在哪里。接口缺少某个坐标时,该位置不绘制座位;组件不会将空缺自行解释为过道。
字段映射
接口字段名不同时,通过 SEAT_FIELDS 映射:
{
SEAT_FIELDS: {
ROW_ID: 'rowId',
COLUMN_ID: 'columnId',
ROW_NAME: 'rowName',
SEAT_ID: 'seatId',
STATUS_NAME: 'status',
TYPE_NAME: 'seatType',
AREA_ID: 'areaId'
}
}
字段映射只指定接口字段位置,不生成排号、座号或座位名称。
状态与类型
{
SEAT_STATUS: {
DISABLED: -2,
LOCKED: -1,
SOLD: 0,
AVAILABLE: 1,
SELECTED: 10
},
SEAT_TYPE: {
SINGLE: 0,
COUPLE_LEFT: 1,
COUPLE_MIDDLE: 2,
COUPLE_RIGHT: 3
}
}
情侣左座必须与右侧的 COUPLE_MIDDLE 或 COUPLE_RIGHT 配对。情侣中/右座的左侧必须是 COUPLE_LEFT。异常接口数据会返回 INVALID_COUPLE_PAIR。
配置
| 配置 | 类型 | 默认值 | 说明 |
|---|---|---|---|
title |
String | '' |
座位区域底部标题 |
hallName |
String | '屏幕' |
银幕名称 |
hallHeight |
Number | 40 |
银幕区域高度 |
footerHeight |
Number | 0 |
底部业务区域遮挡高度 |
maxSelectNum |
Number | 0 |
最大选座数,0表示不限制 |
seatMaxWidth |
Number | 35 |
最大座位宽度 |
seatMinWidth |
Number | 5 |
最小座位宽度 |
isolateSeats |
Boolean | true |
是否启用孤座校验 |
autoResize |
Boolean | true |
是否监听窗口尺寸变化 |
canvasWidth |
Number | 自动读取宿主容器宽度 | 宿主容器宽度。未传入时会自动跟随容器宽度;传入后保持固定 |
canvasHeight |
Number | 自动填满窗口剩余高度 | 宿主容器高度。传入后不再被窗口高度覆盖 |
messages |
Object | 见下文 | 最大选座数和孤座提示文案 |
areaList |
Array | [] |
分区颜色配置 |
SEAT_FIELDS |
Object | 见上文 | 接口字段映射 |
SEAT_STATUS |
Object | 见上文 | 状态值映射 |
SEAT_TYPE |
Object | 见上文 | 类型值映射 |
SEAT_STYLE |
Object | 见下文 | 状态样式 |
画布默认自动读取宿主容器宽度,并在 H5 容器宽度变化时自动重绘;画布高度和像素比由组件读取。嵌入固定高度的非全屏容器时,传入 canvasHeight;组件会按该尺寸计算初始全景、缩放边界和点击坐标。
await seatmap.init({
seatList,
options: {
canvasWidth: 320,
canvasHeight: 420,
footerHeight: 80
}
})
提示文案
组件只内置两类需要用户处理的规则提示,支持覆盖默认文案。MAX_SELECT_EXCEEDED 中可使用 {maxSelectNum} 占位符:
{
messages: {
MAX_SELECT_EXCEEDED: '一次最多购买{maxSelectNum}张票',
ISOLATED_SEAT: '请不要留下单个空座'
}
}
点击空白、不可用座位、选座成功和状态更新不会产生提示文案。程序化选座失败通过 code 和相关字段返回,由接入方决定是否展示。
分区颜色
{
areaList: [
{
areaId: '372',
areaName: 'VIP区',
strokeStyle: '#97cafc',
marketPrice: 8000
}
]
}
areaList 中区域标识字段必须与 SEAT_FIELDS.AREA_ID 指向的字段一致。
状态样式
{
SEAT_STYLE: {
DISABLED: { fillStyle: '#f7f9fc', strokeStyle: '#eeeff1' },
LOCKED: { fillStyle: '#f7f9fc', strokeStyle: '#eeeff1' },
SOLD: { fillStyle: 'red', strokeStyle: 'transparent' },
AVAILABLE: { fillStyle: 'transparent', strokeStyle: '#97cafc' },
SELECTED: { fillStyle: '#0ed7b8', strokeStyle: 'transparent' }
}
}
方法
初始化与尺寸
await seatmap.init({ seatList, options })
seatmap.destroy()
await seatmap.resize()
seatmap.resetView()
主动调整尺寸:
await seatmap.resize({
canvasWidth: 375,
canvasHeight: 600,
footerHeight: 270
})
查询和选择
seatmap.getSelectedSeats()
seatmap.getSeat(seatId)
seatmap.selectSeats([seatId1, seatId2])
seatmap.cancelSeat(seatId)
seatmap.clearSelectedSeats()
cancelSeat() 也兼容传入组件事件返回的座位对象。
selectSeats() 不能执行时只返回结构化结果:不存在的座位使用 SEAT_NOT_FOUND,不可选座位使用 SEAT_UNAVAILABLE;这两种结果不包含用户提示文案。
更新接口数据
seatmap.updateSeatStatus('seat-101', 0)
seatmap.updateSeatStatuses([
{ seatId: 'seat-101', status: 0 },
{ seatId: 'seat-102', status: -1 }
])
seatmap.replaceSeatList(nextSeatList)
整体替换会重建索引并复位视图。组件内部使用座位数据副本,不修改调用方传入的原始对象。
规则校验
const result = seatmap.validateSelection()
{
valid: false,
violations: [
{
code: 'ISOLATED_SEAT',
message: '请不要留下单个空座',
seats: ['seat-101']
}
]
}
| 规则码 | 说明 |
|---|---|
MAX_SELECT_EXCEEDED |
超过最大选座数 |
COUPLE_SEAT_INCOMPLETE |
情侣座没有成对选择 |
ISOLATED_SEAT |
产生孤座 |
孤座只在普通座中计算。选择后若新产生一个紧邻已选普通座、且可以通过其他选法避免的单个空位,组件保留本次选择并返回 ISOLATED_SEAT 提示,不会自动回滚。若按当前选座数量无论怎么选择都必然只剩1座,则视为正常情况,不提示。情侣座不参与孤座计算;接口原本已经存在且与本次选择无关的单个空位也不会提示。
旧版 validateIsolates() 保留,返回孤座错误数量。
事件
| 事件 | 触发时机 |
|---|---|
ready |
图片、布局和引擎初始化完成 |
seat-tap |
点击Canvas后完成座位命中处理 |
change |
已选座位或库存状态实际发生变化 |
resize |
完成尺寸重算 |
error |
初始化、交互或数据错误 |
onSeatTap |
旧版兼容事件,建议新项目使用 change |
change 和 seat-tap 事件结构:
{
selectedSeats: [],
chooseSeatList: [],
seat: null,
trigger: 'tap',
ok: true,
changed: true
}
点击空白位置会触发 seat-tap,但不会触发 change。
只有 MAX_SELECT_EXCEEDED 和 ISOLATED_SEAT 结果会包含可配置的 message。
错误事件
{
code: 'INVALID_COUPLE_PAIR',
message: '情侣座配对不正确',
details: {}
}
| 错误码 | 说明 |
|---|---|
CANVAS_NOT_READY |
无法获取Canvas |
INVALID_SEAT_LIST |
座位列表不是数组 |
INVALID_SEAT_COORDINATE |
行列坐标无效 |
DUPLICATE_SEAT_COORDINATE |
行列坐标重复 |
DUPLICATE_SEAT_ID |
座位ID重复 |
INVALID_SEAT_STATUS |
状态值不在配置中 |
INVALID_SEAT_TYPE |
座位类型不在配置中 |
INVALID_COUPLE_PAIR |
情侣座数据无法正确配对 |
IMAGE_LOAD_FAILED |
绘制图片加载失败 |
INVALID_CANVAS_SIZE |
Canvas尺寸无效 |
INVALID_FOOTER_HEIGHT |
底部预留高度无效 |
EVENT_COORDINATE_MISSING |
点击事件没有返回坐标 |
CANVAS_NOT_READY |
无法获取 Canvas 布局信息 |
CANVAS_QUERY_TIMEOUT |
获取 Canvas 节点超时(3s),canvas 未渲染或被嵌套层级影响 |
验证
npm test
发布前建议在目标 HBuilderX、H5 浏览器及对应小程序真机上验证渲染与交互。H5、微信、支付宝、抖音四端已完成真机验证。
源码授权与定制开发
插件包内即为完整源码,可自由学习和二次开发。如需功能定制(如 App 端适配、特殊座位形态、场馆设计图格式支持)、商业授权或技术支持,请联系作者:
-
- 邮箱:bugover@qq.com

收藏人数:
下载插件并导入HBuilderX
下载示例项目ZIP
赞赏(1)
下载 227
赞赏 1
下载 12558371
赞赏 1948
赞赏
京公网安备:11010802035340号