<template>
	<view class="add-program-container">
	  <!-- Hidden canvas for text measurement -->
	  <canvas canvas-id="__temp_prepare_canvas"
		style="width: 0; height: 0; position: absolute; left: -9999px; top: -9999px;"></canvas>
	  <!-- 画面预览 -->
	  <view class="section preview-section">
		<view class="section-title blue">画面预览</view>
		<canvas class="preview-canvas" canvas-id="previewCanvas"></canvas>
	  </view>
	  <!-- 节目参数（常显卡片） -->
	  <view class="section">
		<view class="section-title blue">节目参数</view>
		<view class="form-row">
		  <view class="mini-label">模式</view>
		  <view class="picker" style="margin-left: 90px;" @click="showModePicker = true">{{ modes[form.mode] }}</view>
		  <u-picker :show="showModePicker" :columns="[modes]" @confirm="onModeConfirm"
			@cancel="showModePicker = false"></u-picker>
		</view>
		<view class="form-row">
		  <view class="mini-label">时长</view>
		  <u-input v-model="form.duration" type="number" placeholder="请输入节目时长"
			style="max-width:110px; margin-left: 60px;" />
		  <view style="line-height: 36px; margin-right: 14px; color: #666;">秒</view>
		</view>
        <view class="form-row">
          <view class="mini-label">节目备注</view>
          <u-input v-model="form.remark" placeholder="请输入节目备注" style="margin-left: 60px;max-width:220px;" />
        </view>
	  </view>
	  <!-- 分区配置（tab切换） -->
	  <view class="section">
		<view class="section-title blue">分区配置</view>
		<u-tabs :list="zoneTabs" :current="currentZoneTab" :scrollable="false"
		  :activeStyle="{color:'#2979ff',fontWeight:'bold'}" :lineColor="'#2979ff'" @click="onTabClick" />
		<view v-for="(zone, index) in form.zones" :key="index" v-show="currentZoneTab === index">
		  <view class="group-card">
			<view class="group-title">项目选择</view>
			<view class="form-row">
			  <view class="form-label">播放类型</view>
			  <view class="picker" @click="openPicker('showPlayTypePicker')">
				{{ playTypes[zone.playType] }}
			  </view>
			  <u-picker :show="showPlayTypePicker" :columns="[playTypes]" @confirm="onPlayTypeConfirm"
				@cancel="showPlayTypePicker = false"></u-picker>
			</view>
		  </view>
		  <!-- 文字参数 -->
		  <view class="group-card" v-if="zone.playType == 0">
			<view class="group-title">文字参数</view>
			<view class="form-row">
			  <view class="form-label">常用语句</view>
			  <view class="picker" @click="openPicker('showPhrasePicker')">
				{{ commonPhrases[zone.commonPhrase] || '常用语句' }}
			  </view>
			  <u-picker :show="showPhrasePicker" :columns="[commonPhrases]" @confirm="onPhraseConfirm"
				@cancel="showPhrasePicker = false"></u-picker>
			</view>
			<view class="form-row">
			  <u-input v-model="zone.displayText" type="textarea" placeholder="请输入展示信息" class="input-area" />
			</view>
			<view class="form-row">
			  <view class="mini-label">字体</view>
			  <view class="picker" @click="openPicker('showFontPicker')">{{ fonts[zone.font] }}</view>
			  <u-picker :show="showFontPicker" :columns="[fonts]" @confirm="onFontConfirm"
				@cancel="showFontPicker = false"></u-picker>
			</view>
			<view class="form-row">
			  <view class="mini-label">字号</view>
			  <view class="picker" @click="openPicker('showFontSizePicker')">{{ fontSizes[zone.fontSize] }}</view>
			  <u-picker :show="showFontSizePicker" :columns="[fontSizes]" @confirm="onFontSizeConfirm"
				@cancel="showFontSizePicker = false"></u-picker>
			</view>
			<view class="form-row">
			  <view class="mini-label">颜色</view>
			  <view class="picker" @click="openPicker('showFontColorPicker')">{{ fontColors[zone.fontColor] }}</view>
			  <u-picker :show="showFontColorPicker" :columns="[fontColors]" @confirm="onFontColorConfirm"
				@cancel="showFontColorPicker = false"></u-picker>
			</view>
		  </view>
		  <!-- 图片参数 -->
		  <view class="group-card" v-else>
			<view class="group-title">图片参数</view>
			<view class="form-row">
			  <view class="picker" @click="openPicker('showImagePicker')">
				{{ zone.image ? zone.image : '请选择图片' }}
			  </view>
			  <u-picker :show="showImagePicker" :columns="[imageFiles]" @confirm="onImageConfirm"
				@cancel="showImagePicker = false" />
			</view>
			<view class="form-row">
			  <view class="mini-label">尺寸</view>
			  <view class="picker" @click="openPicker('showImageSizePicker')">
				{{ imageSizes[zone.imageSize] }}
			  </view>
			  <u-picker :show="showImageSizePicker" :columns="[imageSizes]" @confirm="onImageSizeConfirm"
				@cancel="showImageSizePicker = false" />
			</view>
			<view class="form-row">
			  <view class="mini-label">颜色</view>
			  <view class="picker" @click="openPicker('showImageColorPicker')">
				{{ fontColors[zone.imageColor] }}
			  </view>
			  <u-picker :show="showImageColorPicker" :columns="[fontColors]" @confirm="onImageColorConfirm"
				@cancel="showImageColorPicker = false" />
			</view>
		  </view>
		  <!-- 显示参数 -->
		  <view class="group-card">
			<view class="group-title">显示参数</view>
			<view class="form-row">
			  <view class="form-label">动画特效</view>
			  <view class="picker" @click="openPicker('showEffectPicker')">
				{{ effects[zone.effect] || '动画特效' }}
			  </view>
			  <u-picker :show="showEffectPicker" :columns="[effects]" @confirm="onEffectConfirm"
				@cancel="showEffectPicker = false"></u-picker>
			</view>
			<view class="form-row">
			  <view class="form-label">速度</view>
			  <view class="picker" @click="openPicker('showSpeedPicker')">{{ speeds[zone.speed] || '速度' }}</view>
			  <u-picker :show="showSpeedPicker" :columns="[speeds]" @confirm="onSpeedConfirm"
				@cancel="showSpeedPicker = false"></u-picker>
			</view>
			<view class="form-row">
			  <view class="form-label">停留时间</view>
			  <view class="picker" @click="openPicker('showStayTimePicker')">
				{{ stayTimes[zone.stayTime] || '停留时间' }}
			  </view>
			  <u-picker :show="showStayTimePicker" :columns="[stayTimes]" @confirm="onStayTimeConfirm"
				@cancel="showStayTimePicker = false"></u-picker>
			</view>
			<view class="form-row">
			  <view class="form-label">水平对齐</view>
			  <view class="picker" @click="openPicker('showHAlignPicker')">
				{{ horizontalAlign[zone.hAlign] || '水平对齐' }}
			  </view>
			  <u-picker :show="showHAlignPicker" :columns="[horizontalAlign]" @confirm="onHAlignConfirm"
				@cancel="showHAlignPicker = false"></u-picker>
			</view>
			<view class="form-row">
			  <view class="form-label">垂直对齐</view>
			  <view class="picker" @click="openPicker('showVAlignPicker')">
				{{ verticalAlign[zone.vAlign] || '垂直对齐' }}
			  </view>
			  <u-picker :show="showVAlignPicker" :columns="[verticalAlign]" @confirm="onVAlignConfirm"
				@cancel="showVAlignPicker = false"></u-picker>
			</view>
		  </view>
		  <!-- 高级参数 -->
		  <view class="group-card">
			<view class="group-title">高级参数</view>
			<view class="form-row">
			  <view class="mini-label">X</view>
			  <u-input v-model="zone.x" placeholder="0" />
			  <view class="unit-label">PX</view>
			</view>
			<view class="form-row">
			  <view class="mini-label">宽</view>
			  <u-input v-model="zone.width" placeholder="32" />
			  <view class="unit-label">PX</view>
			</view>
			<view class="form-row">
			  <view class="mini-label">Y</view>
			  <u-input v-model="zone.y" placeholder="0" />
			  <view class="unit-label">PX</view>
			</view>
			<view class="form-row">
			  <view class="mini-label">高</view>
			  <u-input v-model="zone.height" placeholder="64" />
			  <view class="unit-label">PX</view>
			</view>
		  </view>
		</view>
	  </view>
	  <!-- 操作（常显卡片） -->
	  <view class="section">
		<view class="section-title blue">操作</view>
		<view class="button-row">
		  <u-button type="default" class="btn" @click="onRead">读取</u-button>
		  <u-button type="primary" class="btn" @click="onSet">设置</u-button>
		</view>
	  </view>
	</view>
  </template>
  
  <script>
  export default {
	name: 'AddProgram',
	onLoad() {
	  const device = uni.getStorageSync('currentDevice');
	  const deviceInfo = uni.getStorageSync('currentDeviceInfo');
	  if (device) this.device = device;
	  if (deviceInfo) this.deviceInfo = deviceInfo;
	},
	onReady() {
	  this.$nextTick(() => {
		this.initializeAnimations();
	  });
	},
	onUnload() {
	  if (this.animationFrameId) cancelAnimationFrame(this.animationFrameId);
	  this.animationStates.forEach(state => {
		if (state.standTimer) clearTimeout(state.standTimer);
		if (state.pageTimer) clearTimeout(state.pageTimer);
	  });
	},
	data() {
	  return {
		device: null,
		deviceInfo: null,
		screenWidth: 32,
		screenHeight: 64,
		// picker弹窗控制
		showModePicker: false,
		showPlayTypePicker: false,
		showPhrasePicker: false,
		showFontPicker: false,
		showFontShapePicker: false,
		showFontSizePicker: false,
		showFontColorPicker: false,
		showFontBoldPicker: false,
		showFontStretchPicker: false,
		showEffectPicker: false,
		showHAlignPicker: false,
		showVAlignPicker: false,
		showSpeedPicker: false,
		showStayTimePicker: false,
		showImagePicker: false,
		showImageColorPicker: false,
		showImageSizePicker: false,
		// 选项数组
		playTypes: ['文字', '图片'],
		modes: ['模式1', '模式2(上下)', '模式3(左右)', '模式1114(上中下)', '模式5', '模式6'],
		commonPhrases: [
		  '自定义',
		  "公安交警正在巡逻",
		  "公安交警停车检查",
		  "前方事故减速慢行",
		  "警察临检请您配合",
		  "交警临检请您配合",
		  "《《《《《",
		  "》》》》》",
		  "禁止停车",
		  "交通管制禁止通行",
		  "公安检查靠边停车",
		  "前方事故道路封闭",
		  "禁止掉头",
		  "雨雪天气注意安全",
		  "大雾天气减速慢行",
		  "靠右停车接受检查",
		  "警察临检靠边停车"
		],
		fonts: ['宋体(中)', '黑体(中)', '楷体(中)'],
		fontShapes: ['圆角(英)', '直角(英)'],
		fontSizes: ['16px', '24px', '32px', '48px', '64px'],
		fontColors: ['红色', '绿色', '蓝色'],
		fontBold: ['不加粗', '加粗'],
		fontStretch: ['不拉伸', '横向拉伸', '纵向拉伸'],
		effects: ['立即显示', '左移', '右移', '上移', '下移', '连续左移', '闪烁换页'],
		speeds: ['1X', '2X', '3X', '4X', '5X'],
		stayTimes: ['不停顿', '100毫秒', '500毫秒', '1秒', '2秒', '3秒', '4秒', '5秒', '6秒', '8秒', '10秒'],
		horizontalAlign: ['居左对齐', '居中对齐', '居右对齐'],
		verticalAlign: ['顶部对齐', '居中对齐', '底部对齐'],
		imageFiles: [
		  '上箭头',
		  '两侧道路变窄',
		  '停车',
		  '减速慢行',
		  '减速让行',
		  '前方事故',
		  '前方施工',
		  '右下箭头',
		  '右侧道路变窄',
		  '右箭头',
		  '向右',
		  '向右转弯',
		  '向左',
		  '向左向右转弯',
		  '向左转弯',
		  '左下箭头',
		  '左侧道路变窄',
		  '左右两侧绕行',
		  '左箭头',
		  '掉头',
		  '检查',
		  '注意安全',
		  '禁止临时停车',
		  '禁止停车',
		  '禁止停车停',
		  '禁止右转',
		  '禁止左转',
		  '禁止掉头',
		  '禁止驶入',
		  '禁止鸣笛',
		  '箭头向上',
		  '箭头向下',
		  '解除限速',
		  '限速',
		  '靠右行驶',
		  '靠左行驶'
		],
		imageSizes: ['16x16', '32x32', '64x64'],
		form: {
		  mode: 0,
		  duration: '',
		  zones: [{
			playType: 0,
			commonPhrase: 0,
			displayText: '',
			font: 0,
			fontShape: 0,
			fontSize: 0,
			fontColor: 0,
			fontBold: 0,
			fontStretch: 0,
			image: '',
			effect: 0,
			speed: 4,
			stayTime: 5,
			hAlign: 1,
			vAlign: 1,
			x: 0,
			y: 0,
			width: 32,
			height: 64,
			imageColor: 0,
			imageSize: 1
		  }]
		},
		animationFrameId: null,
		animationStates: [],
		imageCache: {},
		currentZoneTab: 0,
	  };
	},
	computed: {
	  zoneTabs() {
		return this.form.zones.map((z, i) => ({ name: `分区${i + 1}` }));
	  }
	},
	watch: {
	  'form.zones': {
		handler() {
		  this.$nextTick(() => {
			this.initializeAnimations();
		  });
		},
		deep: true
	  }
	},
	methods: {
	  deepClone(obj) {
		if (obj === null || typeof obj !== 'object') return obj;
		return Array.isArray(obj)
		  ? obj.map(item => this.deepClone(item))
		  : Object.keys(obj).reduce((acc, key) => {
			acc[key] = this.deepClone(obj[key]);
			return acc;
		  }, {});
	  },
	  getIndex(e) {
		if (e && e.indexs && e.indexs[0] !== undefined) return e.indexs[0];
		if (e && e.index && e.index[0] !== undefined) return e.index[0];
		if (Array.isArray(e)) return e[0];
		return 0;
	  },
	  updateZoneField(field, value) {
		this.form.zones[this.currentZoneTab][field] = value;
		this.drawCanvas();
	  },
	  onModeConfirm(e) {
		this.form.mode = this.getIndex(e);
		this.updateZones();
		this.drawCanvas();
		this.showModePicker = false;
	  },
	  updateZones() {
		const zoneCounts = [1, 2, 2, 3, 1, 1];
		const count = zoneCounts[this.form.mode] || 1;
		const newZones = [];
		const screenW = this.screenWidth;
		const screenH = this.screenHeight;
  
		for (let i = 0; i < count; i++) {
		  const templateZone = this.deepClone({
			playType: 0,
			commonPhrase: 0,
			displayText: '',
			font: 0,
			fontShape: 0,
			fontSize: 0,
			fontColor: 0,
			fontBold: 0,
			fontStretch: 0,
			image: '',
			effect: 0,
			speed: 4,
			stayTime: 5,
			hAlign: 1,
			vAlign: 1,
			x: 0,
			y: 0,
			width: screenW,
			height: screenH,
			imageColor: 0,
			imageSize: 1
		  });
  
		  switch (this.form.mode) {
			case 1:
			  templateZone.height = Math.floor(screenH / count);
			  templateZone.y = i * templateZone.height;
			  if (i === count - 1) {
				templateZone.height = screenH - templateZone.y;
			  }
			  break;
			case 2:
			  templateZone.width = Math.floor(screenW / count);
			  templateZone.x = i * templateZone.width;
			  if (i === count - 1) {
				templateZone.width = screenW - templateZone.x;
			  }
			  break;
			case 3:
			  templateZone.height = Math.floor(screenH / count);
			  templateZone.y = i * templateZone.height;
			  if (i === count - 1) {
				templateZone.height = screenH - templateZone.y;
			  }
			  break;
		  }
  
		  if (this.form.zones[i]) {
			newZones.push({
			  ...this.deepClone(this.form.zones[i]),
			  x: templateZone.x,
			  y: templateZone.y,
			  width: templateZone.width,
			  height: templateZone.height
			});
		  } else {
			newZones.push(templateZone);
		  }
		}
  
		this.form.zones = newZones;
		if (this.currentZoneTab >= newZones.length) {
		  this.currentZoneTab = newZones.length - 1;
		}
	  },
	  async initializeAnimations() {
		if (this.animationFrameId) {
		  cancelAnimationFrame(this.animationFrameId);
		}
		this.animationStates.forEach(state => {
		  if (state.standTimer) clearTimeout(state.standTimer);
		  if (state.pageTimer) clearTimeout(state.pageTimer);
		});
  
		this.animationStates = this.form.zones.map(zone => {
		  const assetInfo = this.prepareZoneAssets(zone);
		  return {
			...assetInfo,
			currentPage: 0,
			currentX: 0,
			currentY: 0,
			standTimer: null,
			pageTimer: null,
			...this.getInitialPosition(zone, assetInfo)
		  };
		});
  
		this.animationLoop();
	  },
	  getInitialPosition(zone, assetInfo) {
		const pos = {
		  currentX: 0,
		  currentY: 0
		};
		const effect = zone.effect;
		if (effect === 1) pos.currentX = zone.width;
		if (effect === 2) pos.currentX = -assetInfo.assetWidth;
		if (effect === 3) pos.currentY = zone.height;
		if (effect === 4) pos.currentY = -assetInfo.assetHeight;
		if (effect === 5) pos.currentX = 0;
		return pos;
	  },
	  animationLoop() {
		this.drawCanvas();
		this.animationFrameId = requestAnimationFrame(this.animationLoop.bind(this));
	  },
	  drawCanvas() {
		const ctx = uni.createCanvasContext('previewCanvas', this);
		const query = uni.createSelectorQuery().in(this);
		query.select('.preview-canvas').boundingClientRect(rect => {
		  if (!rect) return;
		  const canvasWidth = rect.width;
		  const canvasHeight = rect.height;
		  ctx.clearRect(0, 0, canvasWidth, canvasHeight);
		  const scale = Math.min(canvasWidth / this.screenWidth, canvasHeight / this.screenHeight);
		  const screenDrawWidth = this.screenWidth * scale;
		  const screenDrawHeight = this.screenHeight * scale;
		  const offsetX = (canvasWidth - screenDrawWidth) / 2;
		  const offsetY = (canvasHeight - screenDrawHeight) / 2;
		  ctx.fillStyle = '#000000';
		  ctx.fillRect(offsetX, offsetY, screenDrawWidth, screenDrawHeight);
  
		  this.form.zones.forEach((zone, index) => {
			const state = this.animationStates[index];
			if (!state) return;
  
			if (zone.playType === 1 && state.isImage && state.imageUrl) {
			  const zoneDrawX = offsetX + zone.x * scale;
			  const zoneDrawY = offsetY + zone.y * scale;
			  const zoneDrawWidth = zone.width * scale;
			  const zoneDrawHeight = zone.height * scale;
  
			  ctx.save();
			  ctx.beginPath();
			  ctx.rect(zoneDrawX, zoneDrawY, zoneDrawWidth, zoneDrawHeight);
			  ctx.clip();
  
			  const cachePath = this.imageCache[state.imageUrl];
			  if (cachePath) {
				ctx.drawImage(cachePath, zoneDrawX, zoneDrawY, zoneDrawWidth, zoneDrawHeight);
				if (zone.imageColor !== undefined) {
				  ctx.globalAlpha = 0.4;
				  ctx.setFillStyle(this.mapColor(zone.imageColor));
				  ctx.fillRect(zoneDrawX, zoneDrawY, zoneDrawWidth, zoneDrawHeight);
				  ctx.globalAlpha = 1;
				}
				ctx.restore();
				return;
			  }
  
			  uni.getImageInfo({
				src: state.imageUrl,
				success: (res) => {
				  this.imageCache[state.imageUrl] = res.path;
				  this.drawCanvas();
				}
			  });
			  ctx.restore();
			  return;
			}
  
			ctx.save();
			const zoneDrawX = offsetX + zone.x * scale;
			const zoneDrawY = offsetY + zone.y * scale;
			const zoneDrawWidth = zone.width * scale;
			const zoneDrawHeight = zone.height * scale;
  
			ctx.beginPath();
			ctx.rect(zoneDrawX, zoneDrawY, zoneDrawWidth, zoneDrawHeight);
			ctx.clip();
  
			const fontSize = parseInt(this.fontSizes[zone.fontSize] || '16px');
			const scaledFontSize = Math.max(1, Math.round(fontSize * scale));
			ctx.font = `${this.mapBold(zone.fontBold)} ${scaledFontSize}px ${this.mapFont(zone.font)}`;
			ctx.fillStyle = this.mapColor(zone.fontColor);
  
			const pageLines = state.pages[state.currentPage] || [];
			const blockHeight = pageLines.length * scaledFontSize;
			let blockStartY = zoneDrawY + state.currentY * scale;
  
			const verticalAlign = this.mapVAlign(zone.vAlign);
			if (verticalAlign === 'middle') {
			  blockStartY = zoneDrawY + state.currentY * scale + (zoneDrawHeight - blockHeight) / 2;
			} else if (verticalAlign === 'bottom') {
			  blockStartY = zoneDrawY + state.currentY * scale + zoneDrawHeight - blockHeight;
			}
  
			ctx.textBaseline = 'top';
  
			pageLines.forEach((line, lineIndex) => {
			  let xPos = zoneDrawX + state.currentX * scale;
			  const horizontalAlign = this.mapHAlign(zone.hAlign);
			  if (horizontalAlign === 'center') {
				const lineWidth = this.calculateTextWidth(line, scaledFontSize);
				xPos = zoneDrawX + state.currentX * scale + (zoneDrawWidth - lineWidth) / 2;
			  } else if (horizontalAlign === 'right') {
				const lineWidth = this.calculateTextWidth(line, scaledFontSize);
				xPos = zoneDrawX + state.currentX * scale + zoneDrawWidth - lineWidth;
			  }
  
			  const yPos = blockStartY + (lineIndex * scaledFontSize);
			  ctx.fillText(line, xPos, yPos);
			});
  
			if (zone.effect === 5) {
			  const secondCopyX = zoneDrawX + state.currentX * scale + state.assetWidth * scale;
			  pageLines.forEach((line, lineIndex) => {
				const yPos = blockStartY + (lineIndex * scaledFontSize);
				ctx.fillText(line, secondCopyX, yPos);
			  });
			}
  
			ctx.restore();
			this.updateAnimationState(zone, state, index);
		  });
  
		  ctx.strokeStyle = '#FFFFFF';
		  ctx.lineWidth = 1;
		  ctx.save();
		  ctx.translate(offsetX, offsetY);
		  const mode = this.form.mode;
		  if (mode === 1) {
			ctx.beginPath();
			ctx.moveTo(0, screenDrawHeight / 2);
			ctx.lineTo(screenDrawWidth, screenDrawHeight / 2);
			ctx.stroke();
		  } else if (mode === 2) {
			ctx.beginPath();
			ctx.moveTo(screenDrawWidth / 2, 0);
			ctx.lineTo(screenDrawWidth / 2, screenDrawHeight);
			ctx.stroke();
		  } else if (mode === 3) {
			ctx.beginPath();
			ctx.moveTo(0, screenDrawHeight / 3);
			ctx.lineTo(screenDrawWidth, screenDrawHeight / 3);
			ctx.moveTo(0, (screenDrawHeight / 3) * 2);
			ctx.lineTo(screenDrawWidth, (screenDrawHeight / 3) * 2);
			ctx.stroke();
		  }
		  ctx.restore();
		  ctx.draw();
		}).exec();
	  },
	  updateAnimationState(zone, state) {
		if (state.standTimer) return;
		const speedMap = [1, 2, 3, 4, 5];
		const animationSpeed = (speedMap[zone.speed] || 5) / 5;
		const effect = zone.effect;
  
		if (effect !== 5 && state.pages.length > 1) {
		  if (!state.pageTimer) {
			const stayTimeMap = [0, 100, 500, 1000, 2000, 3000, 4000, 5000, 6000, 8000, 10000];
			const stayTime = stayTimeMap[zone.stayTime] || 3000;
			state.pageTimer = setTimeout(() => {
			  state.currentPage = (state.currentPage + 1) % state.pages.length;
			  state.pageTimer = null;
			}, stayTime);
		  }
		}
  
		switch (effect) {
		  case 0:
			break;
		  case 1:
			if (state.currentX > 0) state.currentX = Math.max(0, state.currentX - animationSpeed);
			break;
		  case 2:
			if (state.currentX < 0) state.currentX = Math.min(0, state.currentX + animationSpeed);
			break;
		  case 3:
			if (state.currentY > 0) state.currentY = Math.max(0, state.currentY - animationSpeed);
			break;
		  case 4:
			if (state.currentY < 0) state.currentY = Math.min(0, state.currentY + animationSpeed);
			break;
		  case 5:
			state.currentX -= animationSpeed;
			if (state.currentX <= -state.assetWidth) {
			  state.currentX = 0;
			}
			break;
		  case 6:
			if (!state.pageTimer) {
			  const stayTimeMap = [0, 100, 500, 1000, 2000, 3000, 4000, 5000, 6000, 8000, 10000];
			  const stayTime = stayTimeMap[zone.stayTime] || 3000;
			  state.pageTimer = setTimeout(() => {
				state.currentPage = (state.currentPage + 1) % state.pages.length;
				state.pageTimer = null;
			  }, stayTime);
			}
			break;
		}
	  },
	  prepareZoneAssets(zone) {
		if (zone.playType === 1 && zone.image) {
		  return {
			isImage: true,
			imageUrl: zone.image,
			assetWidth: zone.width || 32,
			assetHeight: zone.height || 32,
			pages: [[]],
			currentPage: 0
		  };
		}
		if (zone.playType !== 0 || !zone.displayText) {
		  return {
			pages: [],
			assetWidth: 0,
			assetHeight: 0,
			isText: false
		  };
		}
		const ctx = uni.createCanvasContext('__temp_prepare_canvas', this);
		const fontSize = parseInt(this.fontSizes[zone.fontSize] || '16px');
		ctx.font = `${this.mapBold(zone.fontBold)} ${fontSize}px ${this.mapFont(zone.font)}`;
  
		if (zone.effect === 5) {
		  const textWidth = this.calculateTextWidth(zone.displayText, fontSize);
		  return {
			pages: [[zone.displayText]],
			assetWidth: textWidth,
			assetHeight: fontSize,
			isText: true
		  };
		}
  
		const lines = [];
		let currentLine = '';
		const text = zone.displayText;
		for (let i = 0; i < text.length; i++) {
		  const char = text[i];
		  const testLine = currentLine + char;
		  const lineWidth = this.calculateTextWidth(testLine, fontSize);
		  if (lineWidth > zone.width && currentLine !== '') {
			lines.push(currentLine);
			currentLine = char;
		  } else {
			currentLine = testLine;
		  }
		}
		if (currentLine) lines.push(currentLine);
  
		const lineHeight = fontSize;
		const maxLinesPerPage = zone.height > 0 ? Math.floor(zone.height / lineHeight) : 1;
		const pages = [];
  
		if (maxLinesPerPage > 0) {
		  for (let i = 0; i < lines.length; i += maxLinesPerPage) {
			pages.push(lines.slice(i, i + maxLinesPerPage));
		  }
		} else {
		  pages.push(lines);
		}
  
		if (pages.length === 0) {
		  pages.push([]);
		}
  
		return {
		  pages: pages,
		  assetWidth: zone.width,
		  assetHeight: zone.height,
		  isText: true
		};
	  },
	  calculateTextWidth(text, fontSize) {
		let width = 0;
		for (let i = 0; i < text.length; i++) {
		  if (text.charCodeAt(i) > 127) {
			width += fontSize;
		  } else {
			width += fontSize / 2;
		  }
		}
		return width;
	  },
	  openPicker(pickerFlag) {
		this[pickerFlag] = true;
	  },
	  onPlayTypeConfirm(e) {
		this.updateZoneField('playType', this.getIndex(e));
		this.showPlayTypePicker = false;
	  },
	  onPhraseConfirm(e) {
		const index = this.getIndex(e);
		const phrase = this.commonPhrases[index];
		this.updateZoneField('commonPhrase', index);
		if (index > 0) {
		  this.updateZoneField('displayText', phrase);
		}
		this.showPhrasePicker = false;
	  },
	  onFontConfirm(e) {
		this.updateZoneField('font', this.getIndex(e));
		this.showFontPicker = false;
	  },
	  onFontShapeConfirm(e) {
		this.updateZoneField('fontShape', this.getIndex(e));
		this.showFontShapePicker = false;
	  },
	  onFontSizeConfirm(e) {
		this.updateZoneField('fontSize', this.getIndex(e));
		this.showFontSizePicker = false;
	  },
	  onFontColorConfirm(e) {
		this.updateZoneField('fontColor', this.getIndex(e));
		this.showFontColorPicker = false;
	  },
	  onFontBoldConfirm(e) {
		this.updateZoneField('fontBold', this.getIndex(e));
		this.showFontBoldPicker = false;
	  },
	  onFontStretchConfirm(e) {
		this.updateZoneField('fontStretch', this.getIndex(e));
		this.showFontStretchPicker = false;
	  },
	  onEffectConfirm(e) {
		this.updateZoneField('effect', this.getIndex(e));
		this.showEffectPicker = false;
	  },
	  onHAlignConfirm(e) {
		this.updateZoneField('hAlign', this.getIndex(e));
		this.showHAlignPicker = false;
	  },
	  onVAlignConfirm(e) {
		this.updateZoneField('vAlign', this.getIndex(e));
		this.showVAlignPicker = false;
	  },
	  onSpeedConfirm(e) {
		this.updateZoneField('speed', this.getIndex(e));
		this.showSpeedPicker = false;
	  },
	  onStayTimeConfirm(e) {
		this.updateZoneField('stayTime', this.getIndex(e));
		this.showStayTimePicker = false;
	  },
	  onRead() {
		this.$u.toast('读取功能待实现');
	  },
	  onSet() {
		const programData = {
		  form: this.form,
		  device: this.device,
		  deviceInfo: this.deviceInfo
		};
		uni.$emit('programDataReady', programData);
		this.$u.toast('设置成功');
		uni.navigateBack();
	  },
	  onImageConfirm(e) {
		const index = this.getIndex(e);
		const fileName = this.imageFiles[index];
		this.updateZoneField('image', `https://xaznkj.cn/doc/traffic/ ${fileName}.png`);
		this.showImagePicker = false;
	  },
	  onImageSizeConfirm(e) {
		const idx = this.getIndex(e);
		const sizeMap = [
		  { w: 16, h: 16 },
		  { w: 32, h: 32 },
		  { w: 64, h: 64 }
		];
		this.updateZoneField('imageSize', idx);
		this.updateZoneField('width', sizeMap[idx].w);
		this.updateZoneField('height', sizeMap[idx].h);
		this.showImageSizePicker = false;
	  },
	  onImageColorConfirm(e) {
		this.updateZoneField('imageColor', this.getIndex(e));
		this.showImageColorPicker = false;
	  },
	  onTabClick(item) {
		this.currentZoneTab = item.index;
		console.log(this.currentZoneTab);
	  },
	  mapFont(index) {
		return ['SimSun', 'SimHei', 'KaiTi'][index] || 'SimSun';
	  },
	  mapColor(index) {
		return ['#FF0000', '#00FF00', '#0000FF'][index] || '#FF0000';
	  },
	  mapBold(index) {
		return ['normal', 'bold'][index] || 'normal';
	  },
	  mapHAlign(index) {
		return ['left', 'center', 'right'][index] || 'left';
	  },
	  mapVAlign(index) {
		return ['top', 'middle', 'bottom'][index] || 'top';
	  }
	}
  };
  </script>
  

<style scoped>
	.preview-section {
		position: sticky;
		top: 0;
		z-index: 99;
	}

	.add-program-container {
		background: #f5f7fa;
		padding: 16px;
		/* margin-top: 160px; 增加顶部内边距，避免被标题栏遮挡 */
	}

	.section {
		background: #fff;
		border-radius: 10px;
		margin-bottom: 16px;
		box-shadow: 0 2px 8px #e0e0e0;
		padding: 12px 16px 16px 16px;
	}

	.section-title {
		font-weight: bold;
		font-size: 16px;
		margin-top: 10px;
		margin-bottom: 8px;
		color: #409eff;
	}

	.section-title.blue {
		background: #409eff;
		color: #fff;
		padding: 6px 12px;
		border-radius: 6px 6px 0 0;
		margin: -12px -16px 8px -16px;
	}

	.section-title.preview-fixed {
		position: fixed;
		top: 0;
		left: 0;
		width: 100vw;
		z-index: 999;
		box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08);
		border-radius: 0 0 12px 12px;
		padding-top: env(safe-area-inset-top, 0);
		background: linear-gradient(to bottom, #409eff, #5b9bd5);
		height: auto;
		/* 改为自动高度 */
		min-height: 160px;
		/* 设置最小高度 */
	}

	.preview-canvas {
		width: 100%;
		height: 120px;
		border: 2px solid #e0e0e0;
		border-radius: 8px;
		margin-bottom: 8px;
		box-shadow: inset 0 0 8px rgba(0, 0, 0, 0.2);
		display: block;
	}

	.zone-card {
		background: #fff;
		border-radius: 12px;
		box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08);
		margin-bottom: 20px;
		padding: 0 0 16px 0;
		border: 1px solid #eaeaea;
		overflow: hidden;
	}

	.zone-title {
		background: #409eff;
		color: #fff;
		font-size: 20px;
		font-weight: bold;
		border-radius: 12px 12px 0 0;
		padding: 10px 20px;
		margin-bottom: 8px;
		letter-spacing: 2px;
		box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
	}

	.group-card {
		background: #fafdff;
		border-radius: 12px;
		box-shadow: 0 2px 8px rgba(64, 158, 255, 0.06);
		margin: 18px 0 0 0;
		padding: 18px 20px 12px 20px;
		border: 1.5px solid #e3eaf5;
		position: relative;
		transition: box-shadow 0.2s;
	}
	.group-card:not(:last-child) {
		margin-bottom: 18px;
	}
	.group-title {
		display: flex;
		align-items: center;
		font-size: 16px;
		font-weight: bold;
		color: #2979ff;
		margin-bottom: 14px;
		letter-spacing: 1px;
	}
	.group-title::before {
		content: '';
		display: inline-block;
		width: 4px;
		height: 18px;
		background: #409eff;
		border-radius: 2px;
		margin-right: 10px;
	}
	.form-row {
		display: flex;
		align-items: center;
		justify-content: flex-start;
		margin-bottom: 14px;
		padding: 0;
		border-bottom: 1px dashed #e3eaf5;
		padding-bottom: 10px;
	}
	.form-row:last-child {
		border-bottom: none;
	}
	.form-label, .mini-label {
		color: #333;
		font-size: 15px;
		width: 80px;
		min-width: 80px;
		text-align: left;
		margin-right: 10px;
		font-weight: 500;
	}
	.picker, .u-input, u-input {
		flex: 1;
		min-width: 100px;
		margin-left: 0;
	}
	.input-area {
		width: 100%;
		min-height: 48px;
		border-radius: 6px;
		font-size: 15px;
		margin-top: 4px;
		border: 1px solid #eaeaea;
		background: #f5f7fa;
	}

	.button-row {
		display: flex;
		justify-content: space-around;
		margin-top: 24px;
		padding-top: 16px;
		border-top: 1px dashed #eaeaea;
		background: #fafdff;
	}
	.btn {
		width: 44%;
		border-radius: 24px;
		font-size: 17px;
		font-weight: bold;
		box-shadow: 0 2px 8px rgba(64, 158, 255, 0.10);
		transition: all 0.2s;
	}
	.btn:active {
		transform: scale(0.98);
	}
</style>