SelectSeat 选座
平台兼容性
| App | Web | 微信小程序 |
|---|---|---|
| √ | √ | √ |
说明
影院 / 剧场 / 巴士等场景的图形化选座:座位平面以画布渲染,支持单指拖动平移、双指缩放、点按选中或取消座位;座位可声明禁用(已售)、空位(走道缺口)、图片座位、逐座位配色与图标。选中集合经 v-model 绑定({row, col} 数组,行列从 0 起),左侧行号条随视图平移缩放联动。
- 无
seats数据时按rows × cols自动生成满布座位 - 双指缩放以触点中心为基准,范围
minScale ~ maxScale;平移范围受约束(内容边缘最多进入视图 20% 空白) - 拖动超过 10px 判定为视图调整,松手不再触发选座(防滚动浏览误选)
- 手势轴向锁定:首段位移主方向判定(阈值 6px)——横向为主才平移座位平面,纵向为主判为页面滚动意图(本轮手势不平移、不选座)。页面内嵌画布时纵向滑动交还页面滚动(Web/App 端动态不阻断;微信端 catch 静态阻断页面滚动为平台限制,座位不再跟随跑动)
- 禁用与空位不响应点选;选中态绘制顺序:座位图片 > 选中背景 + 图标
基本用法
html
<template>
<view>
<n-select-seat
v-model="picked"
:rows="8"
:cols="12"
:seats="seatList"
:width="343"
:height="300"
@seat-click="onSeatClick"
@move="onMove"
/>
</view>
</template>
<script setup lang="uts">
import { ref } from "vue";
import { type NSelectSeatItem, type NSelectSeatValue, type NSelectSeatMoveDetail } from "@/uni_modules/new-ui/components/n-select-seat/props";
const picked = ref<NSelectSeatValue[]>([{ row: 0, col: 3 } as NSelectSeatValue]);
// 座位数据(示例:禁用 + 空位 + 情侣座图标)
const seatList: NSelectSeatItem[] = [
{ row: 1, col: 2, disabled: true } as NSelectSeatItem,
{ row: 1, col: 3, empty: true } as NSelectSeatItem,
{ row: 7, col: 5, icon: "heart-fill", color: "#f472b6" } as NSelectSeatItem
];
function onSeatClick(seat: NSelectSeatItem): void {
console.log(`${seat.row + 1}排${seat.col + 1}座`);
}
function onMove(detail: NSelectSeatMoveDetail): void {
// detail.screenTranslateX 可驱动银幕图等装饰层同步平移
console.log(detail.scale);
}
</script>
API
Properties
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| modelValue | NSelectSeatValue[] | [] | 选中座位集合({row, col} 数组,行列从 0 起);外部变化后画布按值重绘,组件点选后经 update:model-value 回写 |
| rows | number | 0 | 行数(无 seats 数据时按 rows × cols 自动生成满布座位) |
| cols | number | 0 | 列数(无 seats 数据时按 rows × cols 自动生成满布座位) |
| seats | NSelectSeatItem[] | [] | 座位数据(空数组时按 rows/cols 自动生成;每项含坐标与外观状态) |
| seat-gap | number | 8 | 座位间距 px |
| border-radius | number | 8 | 座位圆角 px |
| border-width | number | 1 | 座位描边宽度 px(0 = 无描边) |
| min-scale | number | 1 | 最小缩放倍数(双指缩放下限) |
| max-scale | number | 3 | 最大缩放倍数(双指缩放上限) |
| width | number | string | 300 | 画布宽度(数字 = px;字符串支持 px / rpx) |
| height | number | string | 300 | 画布高度(数字 = px;字符串支持 px / rpx) |
| color | string | #d4d4d4 | 未选中图标色(浅色主题);座位级 color 优先 |
| dark-color | string | #737373 | 未选中图标色(深色主题);座位级 color 优先 |
| bg-color | string | #ffffff | 未选中背景色(浅色主题);座位级 bgColor 优先 |
| dark-bg-color | string | #262626 | 未选中背景色(深色主题);座位级 bgColor 优先 |
| border-color | string | #e5e6eb | 未选中描边色(浅色主题);座位级 borderColor 优先 |
| dark-border-color | string | #333333 | 未选中描边色(深色主题);座位级 borderColor 优先 |
| selected-bg-color | string | #10b981 | 选中背景色;座位级 selectedBgColor 优先 |
| selected-color | string | #ffffff | 选中图标色 |
| selected-icon | string | check | 选中图标名(内置图标表名称;座位级 selectedIcon 优先;空串不绘制图标) |
| selected-image | string | "" | 选中图片地址(优先于选中背景 + 图标绘制,按座位尺寸圆角裁剪) |
| image | string | "" | 未选中图片地址(优先于背景 + 描边绘制,按座位尺寸圆角裁剪) |
| custom-class | string | "" | 根节点类名(须为本组件样式表内定义的类) |
| custom-style | string | "" | 根节点行内样式,尾拼可覆盖内部值(含尺寸) |
座位数据项 NSelectSeatItem:{ row, col, disabled?, empty?, color?, bgColor?, borderColor?, selectedBgColor?, selectedColor?, selectedIcon?, selectedImage?, icon?, image? }(行列从 0 起;disabled 禁用不响应点选、empty 空位不绘制但保留位置;外观字段缺省回落组件级同名属性与主题自动色;icon / selectedIcon 为内置图标表名称)。
NSelectSeatStyle 为不含坐标的字段子集,用于 setSeat() 局部更新。
Event
| 事件名 | 参数 | 说明 |
|---|---|---|
| update:modelValue | NSelectSeatValue[] | 选中集合变化(v-model),点选翻转时触发 |
| seat-click | NSelectSeatItem | 座位被点击且选中状态翻转时触发,参数为座位数据项(禁用与空位不触发) |
| move | NSelectSeatMoveDetail | 拖动或缩放过程中触发,参数 {translateX, translateY, scale, screenTranslateX}(screenTranslateX 为内容未缩放坐标系下的横向视口偏移,可驱动银幕图等装饰层同步平移) |
| error | string | 画布通道失败 / 绘制异常时触发,参数为错误说明 |
Method
| 方法名 | 参数 | 说明 |
|---|---|---|
| setSeat | (row: number, col: number, patch: NSelectSeatStyle) | 局部更新座位数据(合并字段)并重绘;坐标不存在时忽略 |
| getSeats | () | 获取内部座位表(含自动生成的座位;引用即内部状态,修改后调 draw() 生效) |
| draw | () | 重新绘制(手动修改座位数据后调用) |
Slot
无插槽。
实现说明(与参考实现的机制对照)
| 机制 | 参考实现 | 本库实现 | 理由 |
|---|---|---|---|
| 触摸绑定 | 手势三事件绑在 canvas 元素上 | 绑在组件根 view(画布铺满根,命中区一致) | 各端 canvas 元素的事件转发行为不一致(点选与手势事件在部分端不回传);根 view 绑定为画布族验证过的可靠通道 |
| 图标绘制 | 组件图标字体运行时查表(名称 → 码点 + 字体名)后 fillText | 同机制,查库内置图标表 + 库图标字体 fillText | 图标资产与名称表随库发行;check-line 等参考侧图标名换成库内置表名称(勾 = check) |
| width / height | 仅 number(页面量宽后传入) | number 或 px / rpx 字符串(rpx 随窗口宽度换算) | 与库内画布族(chart / qrcode / barcode)尺寸语义一致 |
| error 事件 | 无(画布失败静默) | 画布通道失败 / 绘制异常显式发 error | 库内画布族一致约定:错误显式暴露,不静默吞错 |
| toPng 导出 | 无 | 不收录 | 选座为交互组件,画布内容非保存 / 分享产物,导出无业务价值 |
| setSeat 参数 | UTSJSONObject + 运行时合并 | 类型化 NSelectSeatStyle(不含坐标的字段子集) | 编译期字段校验,避免运行时字段名拼写静默失效 |
| 行号条触摸 | 无处理(条覆盖画布左缘可拦截触摸) | 行号条 pointer-events: none |
装饰条不应吃掉覆盖区域的座位点选 |
| 图片加载失败 | 静默跳过(缓存未命中回退背景形态) | 同语义:失败不入缓存,绘制时回退背景 + 图标形态 | 座位图片是可选装饰,失败回退不阻断选座主功能 |
| 深浅主题 | isDark 监听重绘(未选中三色换 dark 系) | 同机制(useTheme 主题单例 isDark 重绘) | 一致 |
| 导出图片缓存 | 模块级 Map 缓存(src → Image) | 组件实例 Map,卸载时清空 | 避免跨实例残留与内存滞留 |