<template>
  <div class="task-page-content">
    <input
      ref="bgInput"
      type="file"
      accept="image/*"
      style="display: none"
      @change="handleBackgroundSelect"
    />
    <input
      ref="iconImageInput"
      type="file"
      accept="image/*"
      style="display: none"
      @change="handleIconImageSelect"
    />
    <!-- 可编辑画布区域 -->
    <div
      ref="canvas"
      class="task-canvas"
      :class="{ 'insert-icon': insertMode === 'icon', 'insert-textbox': insertMode === 'textbox' }"
      :style="canvasStyle"
      @click="onCanvasClick"
      @mousedown="onCanvasMouseDown"
      @mousemove="onCanvasMouseMove"
      @mouseup="onCanvasMouseUp"
      @mouseleave="onCanvasMouseUp"
    >
      <!-- 图标元素（插入的图片） -->
      <div
        v-for="icon in icons"
        :key="icon.id"
        class="canvas-icon"
        :class="{ selected: selectedId === icon.id }"
        :style="getIconStyle(icon)"
        @mousedown.stop="selectElement(icon.id, $event)"
      >
        <div class="icon-body" :style="{ transform: `rotate(${icon.rotation || 0}deg)` }">
          <img v-if="icon.src" :src="icon.src" class="icon-image" />
          <i v-else class="el-icon-picture-outline icon-placeholder"></i>
        </div>
        <div class="icon-resize-handle" v-if="selectedId === icon.id">
          <div
            v-for="pos in resizeHandles"
            :key="pos"
            class="handle"
            :class="pos"
            @mousedown.stop="startResize($event, icon, pos)"
          ></div>
          <div class="rotate-handle" @mousedown.stop="startRotate($event, icon)"></div>
          <div class="delete-handle" @mousedown.stop="deleteIcon(icon.id)" title="删除">
            <i class="el-icon-delete"></i>
          </div>
        </div>
      </div>

      <!-- 文本框元素：Office 风格，全透明，可随时拖拽和调整大小 -->
      <div
        v-for="box in textBoxes"
        :key="box.id"
        class="canvas-textbox"
        :class="{ selected: selectedId === box.id }"
        :style="getTextBoxStyle(box)"
        @mousedown="onTextBoxMouseDown(box, $event)"
      >
        <div class="textbox-drag-bar" @mousedown.stop="selectElement(box.id, $event)"></div>
        <div class="textbox-input-wrapper">
          <div
            v-if="box.placeholder && !box.text && focusedBoxId !== box.id"
            class="textbox-placeholder"
            :style="getTextBoxPlaceholderStyle(box)"
            @mousedown="focusTextBox(box, $event)"
          >{{ box.placeholder }}</div>
          <div
            class="textbox-input"
            contenteditable="true"
            :style="getTextBoxInputStyle(box)"
            @focus="onTextBoxFocus(box)"
            @blur="onTextBoxBlur(box, $event)"
            @mousedown.stop="selectedId = box.id"
            @contextmenu="onTextBoxContextMenu(box, $event)"
          ></div>
        </div>
        <div class="textbox-resize-handle" v-if="selectedId === box.id">
          <div
            v-for="pos in resizeHandles"
            :key="pos"
            class="handle"
            :class="pos"
            @mousedown.stop="startResizeTextBox($event, box, pos)"
          ></div>
          <div class="textbox-rotate-handle" @mousedown.stop="startRotateTextBox($event, box)"></div>
          <div class="delete-handle textbox-delete" @mousedown.stop="deleteTextBox(box.id)" title="删除">
            <i class="el-icon-delete"></i>
          </div>
        </div>
      </div>

      <!-- 绘制中的文本框 -->
      <div
        v-if="drawingTextBox"
        class="drawing-textbox"
        :style="drawingTextBoxStyle"
      ></div>
    </div>
    <!-- 格式工具栏：仅选中文字后右键时显示，固定定位在右键位置 -->
    <div
      v-if="formatToolbarBox"
      class="textbox-format-toolbar-fixed"
      :style="{ left: formatToolbarPosition.x + 'px', top: formatToolbarPosition.y + 'px' }"
      @mousedown.stop
    >
      <el-select v-model="formatToolbarBox.fontFamily" size="mini" placeholder="字体" class="format-font" @change="debouncedSave">
        <el-option v-for="f in fontOptions" :key="f" :label="f" :value="f" />
      </el-select>
      <el-select v-model="formatToolbarBox.fontSize" size="mini" placeholder="字号" class="format-size" @change="debouncedSave">
        <el-option v-for="s in fontSizeOptions" :key="s" :label="String(s)" :value="s" />
      </el-select>
      <el-color-picker v-model="formatToolbarBox.color" class="format-color" @change="debouncedSave" />
    </div>
  </div>
</template>

<script>
import request from '@/utils/request'
import { resolveImageUrl } from '@/utils/imageUrl'

let idCounter = 0
function genId() {
  return 'el_' + (++idCounter) + '_' + Date.now()
}

const FONT_OPTIONS = ['宋体', '黑体', '微软雅黑', '楷体', '仿宋', 'Arial', 'Times New Roman', 'Verdana', 'Georgia']
const FONT_SIZE_OPTIONS = [8, 9, 10, 11, 12, 14, 16, 18, 20, 22, 24, 26, 28, 36, 48, 72]
const DEFAULT_FONT = { fontSize: 14, fontFamily: '微软雅黑', color: '#333333' }
const MAIN_TITLE_ID = 'task_default_main_title'
const SUB_TITLE_ID = 'task_default_sub_title'

export default {
  name: 'TaskPageContent',
  props: {
    roomId: {
      type: [Number, String],
      default: null
    },
    backgroundImage: {
      type: String,
      default: null
    },
    taskSubTitles: {
      type: Array,
      default: () => []
    }
  },
  data() {
    return {
      fontOptions: FONT_OPTIONS,
      fontSizeOptions: FONT_SIZE_OPTIONS,
      insertMode: null,
      pendingIconImage: null,
      icons: [],
      textBoxes: [],
      selectedId: null,
      resizeHandles: ['nw', 'n', 'ne', 'e', 'se', 's', 'sw', 'w'],
      // 拖拽/调整/旋转
      dragState: null,
      // 绘制文本框
      drawingTextBox: false,
      drawStartX: 0,
      drawStartY: 0,
      drawCurrentX: 0,
      drawCurrentY: 0,
      formatToolbarBoxId: null,
      formatToolbarPosition: { x: 0, y: 0 },
      focusedBoxId: null
    }
  },
  watch: {
    backgroundImage: { handler() { this.debouncedSave() }, immediate: false },
    icons: { handler() { this.debouncedSave() }, deep: true },
    textBoxes: { handler() { this.debouncedSave() }, deep: true },
    taskSubTitles: { handler() { this.debouncedSave() }, deep: true }
  },
  mounted() {
    this._keydownHandler = (e) => this.onKeydown(e)
    this._clickHandler = (e) => {
      if (this.formatToolbarBoxId && !e.target.closest('.textbox-format-toolbar-fixed')) {
        this.formatToolbarBoxId = null
      }
    }
    document.addEventListener('keydown', this._keydownHandler)
    document.addEventListener('click', this._clickHandler)
    this.$nextTick(() => {
      this.ensureDefaultTitleBoxes()
      this.syncTextBoxContent()
    })
  },
  updated() {
    this.$nextTick(() => this.syncTextBoxContent())
  },
  beforeDestroy() {
    this.syncFromDomToData()
    this.$emit('task-page-data', this.getDataForSave())
    document.removeEventListener('keydown', this._keydownHandler)
    document.removeEventListener('click', this._clickHandler)
    if (this._saveTimer) {
      clearTimeout(this._saveTimer)
      this._saveTimer = null
    }
    this.$emit('save-request')
  },
  computed: {
    formatToolbarBox() {
      if (!this.formatToolbarBoxId) return null
      return this.textBoxes.find(t => t.id === this.formatToolbarBoxId) || null
    },
    canvasStyle() {
      const style = {}
      if (this.backgroundImage) {
        style.backgroundImage = `url(${resolveImageUrl(this.backgroundImage)})`
        style.backgroundSize = '100% 100%'
        style.backgroundPosition = 'center'
        style.backgroundRepeat = 'no-repeat'
      }
      return style
    },
    drawingTextBoxStyle() {
      const style = {}
      const left = Math.min(this.drawStartX, this.drawCurrentX)
      const top = Math.min(this.drawStartY, this.drawCurrentY)
      const width = Math.abs(this.drawCurrentX - this.drawStartX)
      const height = Math.abs(this.drawCurrentY - this.drawStartY)
      style.left = left + 'px'
      style.top = top + 'px'
      style.width = Math.max(width, 20) + 'px'
      style.height = Math.max(height, 20) + 'px'
      return style
    }
  },
  methods: {
    handleInsertCommand(cmd) {
      if (cmd === 'removeBackground') {
        this.$emit('background-change', null)
        return
      }
      this.insertMode = cmd
      if (cmd === 'background') {
        this.$refs.bgInput.value = ''
        this.$refs.bgInput.click()
      } else if (cmd === 'icon') {
        this.$refs.iconImageInput.value = ''
        this.$refs.iconImageInput.click()
      }
    },
    async handleBackgroundSelect(e) {
      const file = e.target.files?.[0]
      if (!file) return
      this.insertMode = null
      e.target.value = ''
      try {
        const formData = new FormData()
        formData.append('file', file)
        const res = await request.post('/common/upload', formData)
        if (res && (res.code === 200 || res.fileName)) {
          const path = res.fileName || res.url
          if (path) this.$emit('background-change', path)
        } else {
          this.$message.error(res?.msg || '背景图上传失败')
        }
      } catch (err) {
        this.$message.error(err?.response?.data?.msg || err?.message || '背景图上传失败')
      }
    },
    handleIconImageSelect(e) {
      const file = e.target.files?.[0]
      if (!file) return
      const reader = new FileReader()
      reader.onload = (ev) => {
        this.pendingIconImage = ev.target.result
        this.insertMode = 'icon'
      }
      reader.readAsDataURL(file)
      e.target.value = ''
    },
    onCanvasClick(e) {
      if (this.insertMode === 'icon' && this.pendingIconImage) {
        const rect = this.$refs.canvas.getBoundingClientRect()
        const x = e.clientX - rect.left
        const y = e.clientY - rect.top
        this.icons.push({
          id: genId(),
          x,
          y,
          width: 60,
          height: 60,
          rotation: 0,
          src: this.pendingIconImage
        })
        this.pendingIconImage = null
        this.insertMode = null
      }
      if (this.insertMode !== 'textbox' && !this.drawingTextBox) {
        if (!e.target.closest('.canvas-textbox') && !e.target.closest('.canvas-icon')) {
          this.selectedId = null
        }
      }
    },
    onCanvasMouseDown(e) {
      if (this.insertMode === 'textbox' && !this.drawingTextBox) {
        const rect = this.$refs.canvas.getBoundingClientRect()
        this.drawingTextBox = true
        this.drawStartX = e.clientX - rect.left
        this.drawStartY = e.clientY - rect.top
        this.drawCurrentX = this.drawStartX
        this.drawCurrentY = this.drawStartY
      }
    },
    onCanvasMouseMove(e) {
      if (this.drawingTextBox) {
        const rect = this.$refs.canvas.getBoundingClientRect()
        this.drawCurrentX = e.clientX - rect.left
        this.drawCurrentY = e.clientY - rect.top
      }
      if (this.dragState) {
        this.$refs.canvas.style.cursor = this.dragState.cursor || 'move'
      }
    },
    onCanvasMouseUp(e) {
      if (this.drawingTextBox) {
        const rect = this.$refs.canvas.getBoundingClientRect()
        const x = Math.min(this.drawStartX, this.drawCurrentX)
        const y = Math.min(this.drawStartY, this.drawCurrentY)
        const w = Math.max(Math.abs(this.drawCurrentX - this.drawStartX), 20)
        const h = Math.max(Math.abs(this.drawCurrentY - this.drawStartY), 20)
        this.textBoxes.push({
          id: genId(),
          x,
          y,
          width: w,
          height: h,
          text: '',
          rotation: 0,
          fontSize: DEFAULT_FONT.fontSize,
          fontFamily: DEFAULT_FONT.fontFamily,
          color: DEFAULT_FONT.color
        })
        this.drawingTextBox = false
        this.insertMode = null
      }
      this.dragState = null
      this.$refs.canvas && (this.$refs.canvas.style.cursor = '')
    },
    onTextBoxMouseDown(box, e) {
      if (e.target.closest('.textbox-input')) return
      this.selectElement(box.id, e)
    },
    focusTextBox(box, e) {
      e.preventDefault()
      e.stopPropagation()
      this.focusedBoxId = box.id
      this.selectedId = box.id
      this.$nextTick(() => {
        const wrapper = e.target.closest('.textbox-input-wrapper')
        const input = wrapper && wrapper.querySelector('.textbox-input')
        if (input) input.focus()
      })
    },
    onTextBoxFocus(box) {
      this.focusedBoxId = box.id
    },
    onTextBoxBlur(box, e) {
      box.text = (e.target.innerText || '').trim()
      this.focusedBoxId = null
    },
    onTextBoxContextMenu(box, e) {
      const sel = window.getSelection()
      const hasSelection = sel && sel.toString().trim().length > 0
      if (!hasSelection) return
      e.preventDefault()
      this.formatToolbarBoxId = box.id
      this.formatToolbarPosition = { x: e.clientX, y: e.clientY }
    },
    selectElement(id, e) {
      this.selectedId = id
      const icon = this.icons.find(i => i.id === id)
      const textbox = this.textBoxes.find(t => t.id === id)
      const el = icon || textbox
      if (!el) return
      const rect = this.$refs.canvas.getBoundingClientRect()
      const offsetX = e.clientX - rect.left - el.x
      const offsetY = e.clientY - rect.top - el.y
      this.dragState = {
        type: 'drag',
        id,
        isIcon: !!icon,
        startX: e.clientX,
        startY: e.clientY,
        origX: el.x,
        origY: el.y,
        offsetX,
        offsetY
      }
      const onMove = (ev) => {
        if (this.dragState?.type !== 'drag') return
        if (this.dragState.isIcon) return
        const dx = ev.clientX - this.dragState.startX
        const dy = ev.clientY - this.dragState.startY
        this.dragState.origX += dx
        this.dragState.origY += dy
        el.x = this.dragState.origX
        el.y = this.dragState.origY
        this.dragState.startX = ev.clientX
        this.dragState.startY = ev.clientY
      }
      const onUp = (ev) => {
        document.removeEventListener('mousemove', onMove)
        document.removeEventListener('mouseup', onUp)
        if (this.dragState?.type === 'drag' && this.dragState.isIcon) {
          const r = this.$refs.canvas.getBoundingClientRect()
          el.x = ev.clientX - r.left - this.dragState.offsetX
          el.y = ev.clientY - r.top - this.dragState.offsetY
        }
        this.dragState = null
      }
      document.addEventListener('mousemove', onMove)
      document.addEventListener('mouseup', onUp)
    },
    startResize(e, icon, pos) {
      e.stopPropagation()
      this.dragState = {
        type: 'resize',
        id: icon.id,
        pos,
        startX: e.clientX,
        startY: e.clientY,
        origW: icon.width,
        origH: icon.height,
        origX: icon.x,
        origY: icon.y
      }
      const onMove = (ev) => {
        if (this.dragState?.type !== 'resize') return
        const dx = ev.clientX - this.dragState.startX
        const dy = ev.clientY - this.dragState.startY
        this.dragState.startX = ev.clientX
        this.dragState.startY = ev.clientY
        const { pos, origW, origH, origX, origY } = this.dragState
        let w = icon.width
        let h = icon.height
        let x = icon.x
        let y = icon.y
        if (pos.includes('e')) { w = Math.max(20, w + dx) }
        if (pos.includes('w')) { w = Math.max(20, w - dx); x = icon.x + dx }
        if (pos.includes('s')) { h = Math.max(20, h + dy) }
        if (pos.includes('n')) { h = Math.max(20, h - dy); y = icon.y + dy }
        icon.width = w
        icon.height = h
        icon.x = x
        icon.y = y
      }
      const onUp = () => {
        document.removeEventListener('mousemove', onMove)
        document.removeEventListener('mouseup', onUp)
        this.dragState = null
      }
      document.addEventListener('mousemove', onMove)
      document.addEventListener('mouseup', onUp)
    },
    startRotate(e, icon) {
      e.stopPropagation()
      const rect = this.$refs.canvas.getBoundingClientRect()
      const cx = icon.x + icon.width / 2
      const cy = icon.y + icon.height / 2
      const startAngle = Math.atan2(e.clientY - rect.top - cy, e.clientX - rect.left - cx)
      const startRot = icon.rotation || 0
      const onMove = (ev) => {
        const angle = Math.atan2(ev.clientY - rect.top - cy, ev.clientX - rect.left - cx)
        const delta = ((angle - startAngle) * 180 / Math.PI)
        icon.rotation = (startRot + delta + 360) % 360
      }
      const onUp = () => {
        document.removeEventListener('mousemove', onMove)
        document.removeEventListener('mouseup', onUp)
      }
      document.addEventListener('mousemove', onMove)
      document.addEventListener('mouseup', onUp)
    },
    startResizeTextBox(e, box, pos) {
      e.stopPropagation()
      this.dragState = {
        type: 'resize',
        id: box.id,
        pos,
        startX: e.clientX,
        startY: e.clientY
      }
      const onMove = (ev) => {
        if (this.dragState?.type !== 'resize') return
        const dx = ev.clientX - this.dragState.startX
        const dy = ev.clientY - this.dragState.startY
        this.dragState.startX = ev.clientX
        this.dragState.startY = ev.clientY
        const { pos } = this.dragState
        let w = box.width
        let h = box.height
        let x = box.x
        let y = box.y
        if (pos.includes('e')) { w = Math.max(20, w + dx) }
        if (pos.includes('w')) { w = Math.max(20, w - dx); x = box.x + dx }
        if (pos.includes('s')) { h = Math.max(20, h + dy) }
        if (pos.includes('n')) { h = Math.max(20, h - dy); y = box.y + dy }
        box.width = w
        box.height = h
        box.x = x
        box.y = y
      }
      const onUp = () => {
        document.removeEventListener('mousemove', onMove)
        document.removeEventListener('mouseup', onUp)
        this.dragState = null
      }
      document.addEventListener('mousemove', onMove)
      document.addEventListener('mouseup', onUp)
    },
    getIconStyle(icon) {
      return {
        left: icon.x + 'px',
        top: icon.y + 'px',
        width: icon.width + 'px',
        height: icon.height + 'px'
      }
    },
    getTextBoxStyle(box) {
      return {
        left: box.x + 'px',
        top: box.y + 'px',
        width: box.width + 'px',
        height: box.height + 'px',
        transform: `rotate(${box.rotation || 0}deg)`
      }
    },
    getTextBoxInputStyle(box) {
      return {
        fontSize: (box.fontSize || DEFAULT_FONT.fontSize) + 'px',
        fontFamily: box.fontFamily || DEFAULT_FONT.fontFamily,
        color: box.color || DEFAULT_FONT.color
      }
    },
    getTextBoxPlaceholderStyle(box) {
      return {
        fontSize: (box.fontSize || DEFAULT_FONT.fontSize) + 'px',
        fontFamily: box.fontFamily || DEFAULT_FONT.fontFamily,
        color: '#999'
      }
    },
    deleteIcon(id) {
      const idx = this.icons.findIndex(i => i.id === id)
      if (idx >= 0) this.icons.splice(idx, 1)
      if (this.selectedId === id) this.selectedId = null
    },
    syncTextBoxContent() {
      if (!this.$refs.canvas) return
      const inputs = this.$refs.canvas.querySelectorAll('.textbox-input')
      this.textBoxes.forEach((box, idx) => {
        const el = inputs[idx]
        if (el && document.activeElement !== el && el.innerText !== box.text) {
          el.innerText = box.text
        }
      })
    },
    syncFromDomToData() {
      if (!this.$refs.canvas) return
      const inputs = this.$refs.canvas.querySelectorAll('.textbox-input')
      this.textBoxes.forEach((box, idx) => {
        const el = inputs[idx]
        if (el) box.text = (el.innerText || '').trim()
      })
    },
    ensureDefaultTitleBoxes() {
      const canvas = this.$refs.canvas
      const w = canvas ? canvas.offsetWidth : 800
      const h = canvas ? canvas.offsetHeight : 500
      const hasMain = this.textBoxes.some(t => t.id === MAIN_TITLE_ID)
      const hasSub = this.textBoxes.some(t => t.id === SUB_TITLE_ID)
      if (!hasMain) {
        this.textBoxes.unshift({
          id: MAIN_TITLE_ID,
          x: Math.max(0, (w - 600) / 2) + 2,
          y: Math.max(0, (h - 180) / 2 - 55),
          width: 600,
          height: 90,
          text: '',
          placeholder: '在此输入大标题',
          rotation: 0,
          fontSize: 56,
          fontFamily: DEFAULT_FONT.fontFamily,
          color: DEFAULT_FONT.color
        })
      }
      if (!hasSub) {
        const insertIdx = this.textBoxes.findIndex(t => t.id === MAIN_TITLE_ID)
        const at = insertIdx >= 0 ? insertIdx + 1 : 0
        this.textBoxes.splice(at, 0, {
          id: SUB_TITLE_ID,
          x: Math.max(0, (w - 500) / 2) + 2,
          y: Math.max(0, (h - 180) / 2 + 55),
          width: 500,
          height: 52,
          text: '',
          placeholder: '在此输入副标题',
          rotation: 0,
          fontSize: 20,
          fontFamily: DEFAULT_FONT.fontFamily,
          color: DEFAULT_FONT.color
        })
      }
    },
    loadFromData(data) {
      if (!data) return
      const raw = typeof data === 'string' ? (() => { try { return JSON.parse(data) } catch (_) { return null } })() : data
      if (!raw) return
      if (raw.icons && Array.isArray(raw.icons)) {
        this.icons = raw.icons.map(i => ({
          id: i.id || genId(),
          x: Number(i.x) || 0,
          y: Number(i.y) || 0,
          width: Number(i.width) || 60,
          height: Number(i.height) || 60,
          rotation: Number(i.rotation) || 0,
          src: i.src || ''
        }))
      }
      if (raw.textBoxes && Array.isArray(raw.textBoxes)) {
        this.textBoxes = raw.textBoxes.map(t => {
          const id = t.id || genId()
          const isMain = id === MAIN_TITLE_ID
          const isSub = id === SUB_TITLE_ID
          return {
            id,
            x: Number(t.x) || 0,
            y: Number(t.y) || 0,
            width: Number(t.width) || (isMain ? 600 : isSub ? 500 : 100),
            height: Number(t.height) || (isMain ? 90 : isSub ? 52 : 60),
            text: String(t.text || ''),
            placeholder: isMain ? '在此输入大标题' : isSub ? '在此输入副标题' : undefined,
            rotation: Number(t.rotation) || 0,
            fontSize: Number(t.fontSize) || (isMain ? 56 : isSub ? 20 : DEFAULT_FONT.fontSize),
            fontFamily: t.fontFamily || DEFAULT_FONT.fontFamily,
            color: t.color || DEFAULT_FONT.color
          }
        })
      }
      if (raw.background) {
        this.$emit('background-change', raw.background)
      }
      if (raw.taskSubTitles && Array.isArray(raw.taskSubTitles)) {
        this.$emit('task-sub-titles-change', raw.taskSubTitles)
      }
      this.$nextTick(() => {
        this.ensureDefaultTitleBoxes()
        this.syncTextBoxContent()
      })
    },
    getDataForSave() {
      return {
        background: this.backgroundImage || null,
        icons: this.icons.map(i => ({
          id: i.id,
          x: i.x,
          y: i.y,
          width: i.width,
          height: i.height,
          rotation: i.rotation || 0,
          src: i.src
        })),
        textBoxes: this.textBoxes.map(t => ({
          id: t.id,
          x: t.x,
          y: t.y,
          width: t.width,
          height: t.height,
          text: t.text || '',
          placeholder: t.placeholder || undefined,
          rotation: t.rotation || 0,
          fontSize: t.fontSize || DEFAULT_FONT.fontSize,
          fontFamily: t.fontFamily || DEFAULT_FONT.fontFamily,
          color: t.color || DEFAULT_FONT.color
        })),
        taskSubTitles: this.taskSubTitles || []
      }
    },
    debouncedSave() {
      if (this._saveTimer) clearTimeout(this._saveTimer)
      this._saveTimer = setTimeout(() => {
        this._saveTimer = null
        this.$emit('save-request')
      }, 300)
    },
    deleteTextBox(id) {
      const idx = this.textBoxes.findIndex(t => t.id === id)
      if (idx >= 0) this.textBoxes.splice(idx, 1)
      if (this.selectedId === id) this.selectedId = null
    },
    onKeydown(e) {
      if (!this.selectedId) return
      if (e.key === 'Delete' || e.key === 'Backspace') {
        if (document.activeElement?.contentEditable === 'true') return
        e.preventDefault()
        const icon = this.icons.find(i => i.id === this.selectedId)
        const box = this.textBoxes.find(t => t.id === this.selectedId)
        if (icon) this.deleteIcon(icon.id)
        else if (box) this.deleteTextBox(box.id)
      }
    },
    startRotateTextBox(e, box) {
      e.stopPropagation()
      const rect = this.$refs.canvas.getBoundingClientRect()
      const cx = box.x + box.width / 2
      const cy = box.y + box.height / 2
      const startAngle = Math.atan2(e.clientY - rect.top - cy, e.clientX - rect.left - cx)
      const startRot = box.rotation || 0
      const onMove = (ev) => {
        const angle = Math.atan2(ev.clientY - rect.top - cy, ev.clientX - rect.left - cx)
        const delta = ((angle - startAngle) * 180 / Math.PI)
        box.rotation = (startRot + delta + 360) % 360
      }
      const onUp = () => {
        document.removeEventListener('mousemove', onMove)
        document.removeEventListener('mouseup', onUp)
      }
      document.addEventListener('mousemove', onMove)
      document.addEventListener('mouseup', onUp)
    }
  }
}
</script>

<style scoped>
.task-page-content {
  display: flex;
  flex-direction: column;
  height: 100%;
}

.task-canvas {
  flex: 1;
  position: relative;
  min-height: 200px;
  background: #fafafa;
}

.task-canvas.insert-icon {
  cursor: crosshair;
}

.task-canvas.insert-textbox {
  cursor: crosshair;
}

.canvas-icon {
  position: absolute;
  cursor: default;
  border: 2px solid transparent;
}

.canvas-icon.selected {
  border-color: #008aff;
  z-index: 10;
}

.icon-body {
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  border-radius: 0;
  overflow: hidden;
}

.icon-image {
  width: 100%;
  height: 100%;
  object-fit: contain;
}

.icon-placeholder {
  font-size: 24px;
  color: #008aff;
}

.delete-handle {
  position: absolute;
  top: -28px;
  right: -4px;
  width: 24px;
  height: 24px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: #fff;
  border: 1px solid #999;
  border-radius: 4px;
  cursor: pointer;
  pointer-events: auto;
  color: #f56c6c;
  font-size: 14px;
}

.delete-handle:hover {
  background: #f56c6c;
  color: #fff;
  border-color: #f56c6c;
}

.textbox-delete {
  top: -28px;
  right: -4px;
}

.icon-resize-handle {
  position: absolute;
  left: -4px;
  right: -4px;
  top: -4px;
  bottom: -4px;
  pointer-events: none;
}

.icon-resize-handle .handle {
  position: absolute;
  width: 8px;
  height: 8px;
  background: #008aff;
  border: 1px solid #fff;
  border-radius: 2px;
  pointer-events: auto;
}

.icon-resize-handle .handle.nw { left: -4px; top: -4px; cursor: nw-resize; }
.icon-resize-handle .handle.n { left: 50%; top: -4px; margin-left: -4px; cursor: n-resize; }
.icon-resize-handle .handle.ne { right: -4px; top: -4px; cursor: ne-resize; }
.icon-resize-handle .handle.e { right: -4px; top: 50%; margin-top: -4px; cursor: e-resize; }
.icon-resize-handle .handle.se { right: -4px; bottom: -4px; cursor: se-resize; }
.icon-resize-handle .handle.s { left: 50%; bottom: -4px; margin-left: -4px; cursor: s-resize; }
.icon-resize-handle .handle.sw { left: -4px; bottom: -4px; cursor: sw-resize; }
.icon-resize-handle .handle.w { left: -4px; top: 50%; margin-top: -4px; cursor: w-resize; }

.rotate-handle {
  position: absolute;
  top: -24px;
  left: 50%;
  margin-left: -6px;
  width: 12px;
  height: 12px;
  background: #fff;
  border: 2px solid #008aff;
  border-radius: 50%;
  cursor: grab;
  pointer-events: auto;
}

/* 文本框：默认显示细黑边框 */
.canvas-textbox {
  position: absolute;
  cursor: default;
  border: 1px solid #333;
  background: transparent;
  border-radius: 0;
  overflow: visible;
}

/* 编辑模式：虚线边框 + 空心圆控制点 + 旋转手柄 */
.canvas-textbox.selected {
  border: 1px dashed #333;
  z-index: 10;
}

.textbox-resize-handle {
  position: absolute;
  left: -6px;
  right: -6px;
  top: -6px;
  bottom: -6px;
  pointer-events: none;
}

.textbox-resize-handle .handle {
  position: absolute;
  width: 10px;
  height: 10px;
  background: #fff;
  border: 1px solid #999;
  border-radius: 50%;
  pointer-events: auto;
  box-sizing: border-box;
}

.textbox-resize-handle .handle.nw { left: -5px; top: -5px; cursor: nw-resize; }
.textbox-resize-handle .handle.n { left: 50%; top: -5px; margin-left: -5px; cursor: n-resize; }
.textbox-resize-handle .handle.ne { right: -5px; top: -5px; cursor: ne-resize; }
.textbox-resize-handle .handle.e { right: -5px; top: 50%; margin-top: -5px; cursor: e-resize; }
.textbox-resize-handle .handle.se { right: -5px; bottom: -5px; cursor: se-resize; }
.textbox-resize-handle .handle.s { left: 50%; bottom: -5px; margin-left: -5px; cursor: s-resize; }
.textbox-resize-handle .handle.sw { left: -5px; bottom: -5px; cursor: sw-resize; }
.textbox-resize-handle .handle.w { left: -5px; top: 50%; margin-top: -5px; cursor: w-resize; }

.textbox-rotate-handle {
  position: absolute;
  top: -28px;
  left: 50%;
  margin-left: -6px;
  width: 12px;
  height: 12px;
  background: #fff;
  border: 1px solid #999;
  border-radius: 50%;
  cursor: grab;
  pointer-events: auto;
}


/* Office 风格格式工具栏：仅选中文字右键时显示，固定定位在右键位置 */
.textbox-format-toolbar-fixed {
  position: fixed;
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 6px 10px;
  background: #fff;
  border: 1px solid #ddd;
  border-radius: 6px;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
  z-index: 10000;
  margin-top: 4px;
}
.textbox-format-toolbar-fixed .format-font { width: 120px; }
.textbox-format-toolbar-fixed .format-size { width: 70px; }
.textbox-format-toolbar-fixed .format-color { vertical-align: middle; }
.textbox-format-toolbar-fixed .format-color ::v-deep .el-color-picker__trigger {
  width: 24px;
  height: 24px;
  padding: 2px;
}

.textbox-drag-bar {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 8px;
  cursor: default;
  background: transparent;
}

.textbox-input-wrapper {
  position: absolute;
  top: 8px;
  left: 0;
  right: 0;
  bottom: 0;
}

.textbox-placeholder {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  padding: 8px;
  outline: none;
  overflow: hidden;
  background: transparent;
  color: #999;
  cursor: text;
  pointer-events: auto;
}

.textbox-input {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  padding: 8px;
  font-size: 14px;
  outline: none;
  overflow: auto;
  background: transparent;
  color: #333;
}
.textbox-input::selection {
  background: #c8c8c8;
  color: inherit;
}

.drawing-textbox {
  position: absolute;
  border: 2px dashed #008aff;
  background: rgba(0, 138, 255, 0.05);
  pointer-events: none;
}

</style>
