<template>
  <div
    ref="courserware"
    class="courserware"
    id="selectable-area-preview"
    :style="computedCourserwareStyle()"
    @mouseup="handleTextSelection"
    @mousedown="startSelection"
    @mousemove="updateSelection"
    @mouseleave="endSelection"
  >
    <template v-for="(row, i) in data.row_list">
      <div v-show="computedRowVisibility(row.row_id)" :key="i" class="row" :style="getMultipleColStyle(i)">
        <el-checkbox
          v-if="
            isShowGroup &&
            groupRowList.find(({ row_id, is_pre_same_group }) => row.row_id === row_id && !is_pre_same_group)
          "
          v-model="rowCheckList[row.row_id]"
          :class="['row-checkbox', `${row.row_id}`]"
        />
        <!-- 列 -->
        <template v-for="(col, j) in row.col_list">
          <div :key="j" :class="['col', `col-${i}-${j}`]" :style="computedColStyle(col)">
            <!-- 网格 -->
            <template v-for="(grid, k) in col.grid_list">
              <component
                :is="previewComponentList[grid.type]"
                :id="grid.id"
                :key="k"
                ref="preview"
                :content="computedColContent(grid.id)"
                :background="computedColBackground(grid.id)"
                :class="[grid.id, { active: curSelectId === grid.id }]"
                :data-id="grid.id"
                :style="{
                  gridArea: grid.grid_area,
                  height: grid.height,
                }"
                @click.native="selectedComponent(grid.id)"
                @contextmenu.native.prevent="handleContextMenu($event, grid.id)"
                @handleHeightChange="handleHeightChange"
              />

              <!-- <div
                v-if="showMenu && componentId === grid.id"
                :key="'menu' + grid.id + k"
                class="custom-context-menu"
                :style="{ left: menuPosition.x + 'px', top: menuPosition.y + 'px' }"
                @click="handleMenuItemClick"
              >
                添加批注
              </div> -->
              <div
                v-if="showRemark && Object.keys(componentRemarkObj).length !== 0 && componentRemarkObj[grid.id]"
                :key="'show' + grid.id + k"
              >
                <el-popover
                  v-for="(items, indexs) in componentRemarkObj[grid.id]"
                  :key="indexs"
                  placement="bottom"
                  trigger="click"
                  popper-class="menu-remark-info"
                >
                  <div v-html="items.content"></div>
                  <template #reference>
                    <SvgIcon
                      slot="reference"
                      icon-class="icon-info"
                      size="24"
                      class="remark-info"
                      :style="{ left: items.position_x - 12 + 'px', top: items.position_y - 12 + 'px' }"
                    />
                  </template>
                </el-popover>
              </div>
            </template>
          </div>
        </template>
      </div>
    </template>
    <div
      v-if="menuPosition.endX - menuPosition.startX > 3 && menuPosition.endY - menuPosition.startY > 3"
      :style="{
        position: 'absolute',
        top: `${menuPosition.startY}px`,
        left: `${menuPosition.startX}px`,
        width: `${menuPosition.endX - menuPosition.startX}px`,
        height: `${menuPosition.endY - menuPosition.startY}px`,
        border: '2px solid #165DFF',
      }"
    ></div>
    <!-- 选中文本的工具栏 -->
    <div v-show="showToolbar" class="contentmenu" :style="contentmenu">
      <template v-if="canRemark">
        <!-- <span class="button" @click="handleMenuItemClick($event, 'tool')">
          <SvgIcon icon-class="sidebar-pushpin" size="14" /> 添加批注
        </span> -->
      </template>
      <template v-else>
        <span class="button" @click="setNote"><SvgIcon icon-class="sidebar-text" size="14" /> 笔记</span>
        <span class="line"></span>
        <span class="button" @click="setCollect"><SvgIcon icon-class="sidebar-collect" size="14" /> 收藏 </span>
        <span class="line"></span>
        <span class="button" @click="setTranslate"> <SvgIcon icon-class="sidebar-translate" size="14" /> 翻译</span>
        <!-- <span class="line"></span>
        <span class="button" @click="setFeedback"> <SvgIcon icon-class="sidebar-feedback" size="14" /> 用户反馈</span> -->
      </template>
    </div>
    <template v-if="showRemark && Object.keys(componentRemarkObj).length !== 0 && componentRemarkObj['WHOLE']">
      <el-popover
        v-for="(items, indexs) in componentRemarkObj['WHOLE']"
        :key="'menu-remark-info' + indexs"
        placement="bottom"
        trigger="click"
        popper-class="menu-remark-info"
      >
        <div v-html="items.content"></div>
        <template v-if="items.file_list.length > 0">
          <div v-for="(item, index) in items.file_list" :key="indexs + '-' + index" class="remark-file-item">
            <SvgIcon :icon-class="item.icon_type" />
            <span class="file-item-name">{{ item.file_name }}</span>
            <SvgIcon icon-class="uploadPreview" @click="viewDialog(item)" />
            <SvgIcon icon-class="download" @click="downLoad(item)" />
          </div>
        </template>
        <template #reference>
          <div
            v-if="items.position_br_x - items.position_x > 3 && items.position_br_y - items.position_y > 3"
            slot="reference"
            :style="{
              position: 'absolute',
              top: `${items.position_y}px`,
              left: `${items.position_x}px`,
              width: `${items.position_br_x - items.position_x}px`,
              height: `${items.position_br_y - items.position_y}px`,
              border: '2px solid #165DFF',
            }"
          ></div>
        </template>
      </el-popover>
    </template>
    <el-dialog
      v-if="visible"
      :visible.sync="visible"
      :show-close="true"
      :close-on-click-modal="true"
      :modal-append-to-body="true"
      :append-to-body="true"
      :lock-scroll="true"
      :width="'80%'"
      top="0"
    >
      <iframe v-if="visible" :src="newpath" width="100%" :height="iframeHeight" frameborder="0"></iframe>
    </el-dialog>
  </div>
</template>

<script>
import { previewComponentList } from '@/views/book/courseware/data/bookType';
import { getToken, getConfig } from '@/utils/auth';
import _ from 'lodash';

export default {
  name: 'CoursewarePreview',
  provide() {
    return {
      getDragStatus: () => false,
      bookInfo: this.bookInfo,
    };
  },
  props: {
    data: {
      type: Object,
      default: () => ({}),
    },
    coursewareId: {
      type: String,
      default: '',
    },
    background: {
      type: Object,
      default: () => ({}),
    },
    componentList: {
      type: Array,
      required: true,
    },
    canRemark: {
      type: Boolean,
      default: false,
    },
    showRemark: {
      type: Boolean,
      default: false,
    },
    componentRemarkObj: {
      type: Object,
      default: () => ({}),
    },
    groupRowList: {
      type: Array,
      default: () => [],
    },
    isShowGroup: {
      type: Boolean,
      default: false,
    },
    groupShowAll: {
      type: Boolean,
      default: true,
    },
    project: {
      type: Object,
      default: () => ({}),
    },
    type: {
      type: String,
      default: '',
    },
  },
  data() {
    return {
      previewComponentList,
      courseware_id: this.coursewareId,
      bookInfo: {
        theme_color: '',
      },
      showMenu: false,
      divPosition: {
        left: 0,
        top: 0,
      }, // courserware盒子原始距离页面顶部和左边的距离
      menuPosition: { x: 0, y: 0, select_node: '', startX: null, startY: null, endX: null, endY: null }, // 用于存储菜单的位置
      componentId: '', // 添加批注的组件id
      rowCheckList: {},
      showToolbar: false,
      contentmenu: {
        left: 0,
        top: 0,
      },
      selectedInfo: null,
      selectHandleInfo: null,
      curSelectId: '', // 当前选中组件id

      isSelecting: false, // 是否开始框选内容
      file_preview_url: getConfig() ? getConfig().doc_preview_service_address : '',
      visible: false,
      newpath: '',
      iframeHeight: `${window.innerHeight - 100}px`,
    };
  },
  watch: {
    groupRowList: {
      handler(val) {
        if (!val) return;
        this.rowCheckList = val
          .filter(({ is_pre_same_group }) => !is_pre_same_group)
          .reduce((acc, row) => {
            acc[row.row_id] = false;
            return acc;
          }, {});
      },
    },
    coursewareId: {
      handler(val) {
        this.courseware_id = val;
      },
    },
  },
  mounted() {
    const element = this.$refs.courserware;
    const rect = element.getBoundingClientRect();
    this.divPosition = {
      left: rect.left,
      top: rect.top,
    };
    window.addEventListener('mousedown', this.handleMouseDown);
  },
  beforeDestroy() {
    window.removeEventListener('mousedown', this.handleMouseDown);
  },
  methods: {
    /**
     * 处理选中组件事件
     * 这个事件只在编辑预览模式下触发
     * @param {string} component_id 组件在课件内部的 ID
     */
    selectedComponent(component_id) {
      if (this.type !== 'edit_preview') return;

      this.curSelectId = component_id;
      const selectedComponent = this.componentList.find((item) => item.component_id === component_id);

      this.$emit('selectedComponent', { courseware_id: selectedComponent?.courseware_id, component_id });
    },
    /**
     * 计算组件内容
     * @param {string} id 组件id
     * @returns {string} 组件内容
     */
    computedColContent(id) {
      if (!id) return '';
      return this.componentList.find((item) => item.component_id === id)?.content || '';
    },
    /**
     * 计算组件背景
     * @param {string} id 组件id
     * @returns {object} 组件背景样式
     */
    computedColBackground(id) {
      if (!id) return {};
      const background = this.componentList.find((item) => item.component_id === id)?.background;
      return background ? JSON.parse(background) : {};
    },
    getMultipleColStyle(i) {
      let row = this.data.row_list[i];
      let col = row.col_list;
      if (col.length <= 1) {
        return {
          gridTemplateColumns: '100fr',
        };
      }
      let gridTemplateColumns = row.width_list.join(' ');

      return {
        gridAutoFlow: 'column',
        gridTemplateColumns,
        gridTemplateRows: 'auto',
      };
    },
    /**
     * 计算行的可见性
     * @params {string} rowId 行的ID
     * @return {boolean} 行是否可见
     */
    computedRowVisibility(rowId) {
      if (this.groupShowAll) return true;
      let { row_id, is_pre_same_group } = this.groupRowList.find(({ row_id }) => row_id === rowId);
      if (is_pre_same_group) {
        const index = this.groupRowList.findIndex(({ row_id }) => row_id === rowId);
        if (index === -1) return false;
        for (let i = index - 1; i >= 0; i--) {
          if (!this.groupRowList[i].is_pre_same_group) {
            return this.rowCheckList[this.groupRowList[i].row_id];
          }
        }
        return false;
      }
      return this.rowCheckList[row_id];
    },
    /**
     * 计算选中分组行的课件信息
     * @returns {object} 选中分组行的课件信息
     */
    computedSelectedGroupCoursewareInfo() {
      if (Object.keys(this.rowCheckList).length === 0) {
        return {};
      }
      // 根据 rowCheckList 过滤出选中的行，获取这些行的组件信息
      let coursewareInfo = structuredClone(this.data);
      coursewareInfo.row_list = coursewareInfo.row_list.filter((row) => {
        let groupRow = this.groupRowList.find(({ row_id }) => row_id === row.row_id);
        if (!groupRow.is_pre_same_group) {
          return this.rowCheckList[groupRow.row_id];
        }
        const index = this.groupRowList.findIndex(({ row_id }) => row_id === row.row_id);
        if (index === -1) return false;
        for (let i = index - 1; i >= 0; i--) {
          if (!this.groupRowList[i].is_pre_same_group) {
            return this.rowCheckList[this.groupRowList[i].row_id];
          }
        }
        return false;
      });
      // 获取选中行的所有组件id列表
      let component_id_list = coursewareInfo.row_list.flatMap((row) =>
        row.col_list.flatMap((col) => col.grid_list.map((grid) => grid.id)),
      );
      // 获取选中行的分组列表描述
      let content_group_row_list = this.groupRowList.filter(({ row_id, is_pre_same_group }) => {
        if (!is_pre_same_group) {
          return this.rowCheckList[row_id];
        }
        const index = this.groupRowList.findIndex(({ row_id: id }) => id === row_id);
        if (index === -1) return false;
        for (let i = index - 1; i >= 0; i--) {
          if (!this.groupRowList[i].is_pre_same_group) {
            return this.rowCheckList[this.groupRowList[i].row_id];
          }
        }
        return false;
      });

      let groupIdList = _.cloneDeep(content_group_row_list);
      let groupList = [];
      // 通过判断 is_pre_same_group 将组合并
      for (let i = 0; i < groupIdList.length; i++) {
        if (groupIdList[i].is_pre_same_group) {
          groupList[groupList.length - 1].row_id_list.push(groupIdList[i].row_id);
        } else {
          groupList.push({
            name: '',
            row_id_list: [groupIdList[i].row_id],
            component_id_list: [],
          });
        }
      }

      // 通过合并后的分组，获取对应的组件 id 和分组名称
      groupList.forEach(({ row_id_list, component_id_list }, i) => {
        row_id_list.forEach((row_id, j) => {
          let row = this.data.row_list.find((row) => {
            return row.row_id === row_id;
          });
          // 当前行所有组件id列表
          let gridIdList = row.col_list.map((col) => col.grid_list.map((grid) => grid.id)).flat();
          component_id_list.push(...gridIdList);
          // 查找每组第一行中第一个包含 describe、label 或 stem 的组件
          if (j === 0) {
            let findKey = '';
            let findType = '';
            row.col_list.some((col) => {
              const findItem = col.grid_list.find(({ type }) => {
                return ['describe', 'label', 'stem'].includes(type);
              });
              if (findItem) {
                findKey = findItem.id;
                findType = findItem.type;
                return true;
              }
            });
            let groupName = `组${i + 1}`;

            // 如果有标签类组件，获取对应名称
            if (findKey) {
              let item = this.isEdit
                ? this.findChildComponentByKey(`grid-${findKey}`)
                : this.$refs.previewEdit.findChildComponentByKey(`preview-${findKey}`);
              if (['describe', 'stem'].includes(findType)) {
                groupName = item.data.content.replace(/<[^>]+>/g, '');
              } else if (findType === 'label') {
                groupName = item.data.dynamicTags.map((tag) => tag.text).join(', ');
              }
            }

            groupList[i].name = groupName;
          }
        });
      });

      return {
        content: JSON.stringify(coursewareInfo),
        component_id_list,
        content_group_row_list: JSON.stringify(content_group_row_list),
        content_group_component_list: JSON.stringify(groupList),
      };
    },
    /**
     * 清空行选择列表
     */
    clearRowCheckList() {
      this.rowCheckList = {};
    },
    /**
     * 分割整数为多个 1的倍数
     * @param {number} num
     * @param {number} parts
     */
    splitInteger(num, parts) {
      let base = Math.floor(num / parts);
      let arr = Array(parts).fill(base);
      let remainder = num - base * parts;
      for (let i = 0; remainder > 0; i = (i + 1) % parts) {
        arr[i] += 1;
        remainder -= 1;
      }
      return arr;
    },
    /**
     * 计算列的样式
     * @param {Object} col 列对象
     * @returns {Object} 列的样式对象
     */
    computedColStyle(col) {
      const grid = col.grid_list || [];
      if (grid.length === 0) {
        return {
          width: col.width,
          gridTemplateAreas: '',
          gridTemplateColumns: '',
          gridTemplateRows: '',
        };
      }

      // 先按 row 分组，避免后续重复 filter 扫描
      const rowMap = new Map();
      grid.forEach((item) => {
        if (!rowMap.has(item.row)) {
          rowMap.set(item.row, []);
        }
        rowMap.get(item.row).push(item);
      });

      let maxCol = 0;
      let curMaxRow = 0;
      rowMap.forEach((items, row) => {
        if (items.length > maxCol) {
          maxCol = items.length;
          curMaxRow = row;
        }
      });

      // 计算 grid_template_areas
      let gridTemplateAreas = '';
      rowMap.forEach((items, row) => {
        let rowAreas = [];
        if (row === curMaxRow) {
          rowAreas = items.map((item) => item.grid_area);
        } else {
          const needNum = maxCol - items.length;
          if (items.length === 1) {
            rowAreas = Array(needNum + 1).fill(items[0].grid_area);
          } else {
            const splitArr = this.splitInteger(needNum, items.length);
            rowAreas = items.flatMap((item, index) => Array(splitArr[index] + 1).fill(item.grid_area));
          }
        }
        gridTemplateAreas += `'${rowAreas.join(' ')}' `;
      });

      // 计算 grid_template_columns
      const maxRowItems = rowMap.get(curMaxRow) || [];
      const gridTemplateColumns = maxRowItems.length ? `${maxRowItems.map((item) => item.width).join(' ')} ` : '';

      // 计算 grid_template_rows
      const previewById = new Map();
      (this.$refs.preview || []).forEach((child) => {
        const id = child?.$el?.dataset?.id;
        if (id) {
          previewById.set(id, child);
        }
      });
      const hasOperationById = (id) => {
        const component = previewById.get(id);
        return Boolean(component && component.$el.querySelector('.operation'));
      };
      const toNumberHeight = (height) => {
        const num = Number(String(height).replace('px', ''));
        return Number.isFinite(num) ? num : NaN;
      };

      let gridTemplateRows = '';
      rowMap.forEach((items) => {
        if (items.length === 1) {
          const current = items[0];
          if (current.height === 'auto') {
            gridTemplateRows += 'auto ';
            return;
          }
          let baseHeight = toNumberHeight(current.height);
          if (Number.isNaN(baseHeight)) {
            gridTemplateRows += `${current.height} `;
            return;
          }
          if (hasOperationById(current.id)) {
            baseHeight += 48;
          }
          gridTemplateRows += `${baseHeight}px `;
          return;
        }

        const nonAutoItems = items.filter((item) => item.height !== 'auto');
        if (nonAutoItems.length === 0) {
          gridTemplateRows += 'auto ';
          return;
        }

        let maxItem = null;
        let maxHeight = 0;
        nonAutoItems.forEach((item) => {
          const current = toNumberHeight(item.height);
          if (!Number.isNaN(current) && current > maxHeight) {
            maxHeight = current;
            maxItem = item;
          }
        });

        if (maxItem && hasOperationById(maxItem.id)) {
          maxHeight += 48;
        }
        gridTemplateRows += `${maxHeight}px `;
      });

      return {
        width: col.width,
        gridTemplateAreas,
        gridTemplateColumns,
        gridTemplateRows,
      };
    },

    /**
     * 计算课件背景样式
     * @returns {Object} 课件背景样式对象
     */
    computedCourserwareStyle() {
      const {
        background_image_url: bcImgUrl = '',
        background_position: pos = {},
        background: back,
      } = this.background || {};

      let canvasStyle = {
        backgroundSize: bcImgUrl ? `${pos.width}% ${pos.height}%` : '',
        backgroundPosition: bcImgUrl ? `${pos.left}% ${pos.top}%` : '',
        backgroundImage: bcImgUrl ? `url(${bcImgUrl})` : '',
        backgroundColor: bcImgUrl ? `rgba(255, 255, 255, ${1 - back?.image_opacity / 100})` : '',
      };

      if (back) {
        if (back.mode === 'image') {
          canvasStyle['backgroundBlendMode'] = 'lighten';
        } else {
          canvasStyle['backgroundBlendMode'] = '';
        }

        if (back.imageMode === 'fill') {
          canvasStyle['backgroundRepeat'] = 'repeat';
          canvasStyle['backgroundSize'] = '';
          canvasStyle['backgroundPosition'] = '';
        } else {
          canvasStyle['backgroundRepeat'] = 'no-repeat';
        }

        if (back.imageMode === 'stretch') {
          canvasStyle['backgroundSize'] = '100% 100%';
        }

        if (back.imageMode === 'adapt') {
          canvasStyle['backgroundSize'] = 'contain';
        }

        if (back.imageMode === 'auto') {
          canvasStyle['backgroundPosition'] = `${pos.imgX}% ${pos.imgY}%`;
        }

        if (back.mode === 'color') {
          canvasStyle['backgroundColor'] = back.color;
          canvasStyle['backgroundImage'] = '';
          canvasStyle['backgroundRepeat'] = '';
          canvasStyle['backgroundPosition'] = '';
          canvasStyle['backgroundSize'] = '';
        }

        if (back.enable_border) {
          canvasStyle['border'] = `${back.border_width}px ${back.border_style} ${back.border_color}`;
        } else {
          canvasStyle['border'] = 'none';
        }

        if (back.enable_radius) {
          canvasStyle['border-top-left-radius'] = `${back.top_left_radius}px`;
          canvasStyle['border-top-right-radius'] = `${back.top_right_radius}px`;
          canvasStyle['border-bottom-left-radius'] = `${back.bottom_left_radius}px`;
          canvasStyle['border-bottom-right-radius'] = `${back.bottom_right_radius}px`;
        } else {
          canvasStyle['border-radius'] = '0';
        }
      }

      return canvasStyle;
    },
    handleContextMenu(event, id) {
      if (this.canRemark) {
        event.preventDefault(); // 阻止默认的上下文菜单显示
        this.menuPosition = {
          x: event.clientX - this.divPosition.left,
          y: event.clientY - this.divPosition.top,
        }; // 设置菜单位置
        this.componentId = id;
        this.$emit('computeScroll');
      }
    },
    handleResult(top, left, select_node) {
      this.menuPosition.x = this.menuPosition.x + left;
      this.menuPosition.y = this.menuPosition.y + top;
      this.menuPosition.select_node = select_node;
      // 设置菜单位置
      this.showMenu = true; // 显示菜单
    },
    handleMenuItemClick(event, type) {
      this.showMenu = false; // 隐藏菜单
      let text = '';
      if (type && type === 'tool') {
        let info = this.selectHandleInfo;
        this.menuPosition = {
          x: event.clientX - this.divPosition.left,
          y: event.clientY - this.divPosition.top - 20,
        }; // 设置菜单位置
        this.componentId = info.blockId;
        text = info.text;

        this.showMenu = false;
      }
      this.$emit('computeScroll');
      setTimeout(() => {
        this.$emit(
          'addRemark',
          this.menuPosition.select_node,
          this.menuPosition.startX,
          this.menuPosition.startY,
          this.menuPosition.endX,
          this.menuPosition.endY,
          this.componentId,
          text,
        );
      }, 10);
    },
    handleMouseDown(event) {
      if (event.button === 0 && event.target.className !== 'custom-context-menu') {
        // 0 表示左键
        this.showMenu = false;
      }
    },
    /**
     * 查找子组件
     * @param {string} id 组件的唯一标识符
     * @returns {Promise<HTMLElement|null>} 返回找到的子组件或 null
     */
    async findChildComponentByKey(id) {
      await this.$nextTick();
      if (!this.$refs.preview) {
        // 最多等待 1000ms
        for (let i = 0; i < 20; i++) {
          await this.$nextTick();
          await new Promise((resolve) => setTimeout(resolve, 50));
          if (this.$refs.preview) break;
        }
      }

      // 如果等待后还是不存在，那就返回null
      if (!this.$refs.preview) {
        console.error('$refs.preview 不存在');
        return null;
      }
      return this.$refs.preview.find((child) => child.$el && child.$el.dataset && child.$el.dataset.id === id);
    },
    /**
     * 模拟回答
     * @param {boolean} isJudgingRightWrong 是否判断对错
     * @param {boolean} isShowRightAnswer 是否显示正确答案
     * @param {boolean} disabled 是否禁用
     */
    simulateAnswer(isJudgingRightWrong, isShowRightAnswer, disabled = true) {
      this.$refs.preview.forEach((item) => {
        item.showAnswer(isJudgingRightWrong, isShowRightAnswer, null, disabled);
      });
    },
    /**
     * 处理组件高度变化事件
     * @param {string} id 组件id
     * @param {string} newHeight 组件的新高度
     */
    handleHeightChange(id, newHeight) {
      this.data.row_list.forEach((row) => {
        row.col_list.forEach((col) => {
          col.grid_list.forEach((grid) => {
            if (grid.id === id) {
              grid.height = newHeight;
            }
          });
        });
      });
    },
    // 处理选中文本
    handleTextSelection() {
      this.showToolbar = false;
      // 延迟处理，确保选择已完成
      setTimeout(() => {
        const selection = window.getSelection();
        if (selection.toString().trim() === '') return null;

        const selectedText = selection.toString().trim();
        const range = selection.getRangeAt(0);
        this.selectedInfo = {
          text: selectedText,
          range,
        };
        let selectHandleInfo = this.getSelectionInfo();
        if (!selectHandleInfo || !selectHandleInfo.text) return;
        this.selectHandleInfo = selectHandleInfo;

        this.showToolbar = true;
        const container = document.querySelector('.courserware');
        const boxRect = container.getBoundingClientRect();
        const selectRect = range.getBoundingClientRect();
        this.contentmenu = {
          left: `${Math.round(selectRect.left - boxRect.left + selectRect.width / 2 - 63)}px`,
          top: `${Math.round(selectRect.top - boxRect.top + selectRect.height)}px`, // 向上偏移10px
        };
      }, 100);
      if (this.canRemark) {
        this.endSelection();
      }
    },
    // 笔记
    setNote() {
      debugger;
      this.showToolbar = false;
      this.oldRichData = {};
      let info = this.selectHandleInfo;
      if (!info) return;
      info.coursewareId = this.courseware_id;
      this.$emit('editNote', info);
      this.selectedInfo = null;
    },
    // 加入收藏
    setCollect() {
      this.showToolbar = false;

      let info = this.selectHandleInfo;
      if (!info) return;
      info.coursewareId = this.courseware_id;

      this.$emit('saveCollect', info);
      this.selectedInfo = null;
    },
    // 翻译
    setTranslate() {
      this.showToolbar = false;

      let info = this.selectHandleInfo;
      if (!info) return;
      info.coursewareId = this.courseware_id;

      this.$emit('getTranslate', info);
      this.selectedInfo = null;
    },
    // 反馈
    setFeedback() {
      this.showToolbar = false;
      this.oldRichData = {};
      let info = this.selectHandleInfo;
      if (!info) return;
      info.coursewareId = this.courseware_id;
      this.$emit('editFeedback', info);
      this.selectedInfo = null;
    },
    // 定位
    handleLocation(item) {
      this.scrollToDataId(item.blockId);
    },

    getSelectionInfo() {
      if (!this.selectedInfo) return;
      const range = this.selectedInfo.range;
      let selectedText = this.selectedInfo.text;
      if (!selectedText) return null;

      let commonAncestor = range.commonAncestorContainer;
      if (commonAncestor.nodeType === Node.TEXT_NODE) {
        commonAncestor = commonAncestor.parentNode;
      }

      const blockElement = commonAncestor.closest('[data-id]');
      if (!blockElement) return null;

      const blockId = blockElement.dataset.id;

      // 获取所有汉字元素
      const charElements = blockElement.querySelectorAll('.py-char,.rich-text,.NNPE-chs');

      // 构建包含位置信息的文本数组
      const textFragments = Array.from(charElements)
        .map((el, index) => {
          let text = '';
          if (el.classList.contains('rich-text')) {
            const pElements = Array.from(el.querySelectorAll('p'));
            text = pElements.map((p) => p.textContent.trim()).join('');
          } else if (el.classList.contains('NNPE-chs')) {
            const spanElements = Array.from(el.querySelectorAll('span'));
            spanElements.push(el);
            text = spanElements.map((span) => span.textContent.trim()).join('');
          } else {
            text = el.textContent.trim();
          }

          // 过滤掉拼音和空文本
          if (!text || /^[a-zāáǎàōóǒòēéěèīíǐìūúǔùǖǘǚǜü]+$/i.test(text)) {
            return { text: '', element: el, index };
          }

          return { text, element: el, index };
        })
        .filter((fragment) => fragment.text);

      // 获取完整的纯文本
      const fullText = textFragments.map((f) => f.text).join('');

      // 清理选中文本
      let cleanSelectedText = selectedText.replace(/\n/g, '').trim();
      cleanSelectedText = cleanSelectedText.replace(/[a-zāáǎàōóǒòēéěèīíǐìūúǔùǖǘǚǜü]/gi, '').trim();

      if (!cleanSelectedText) return null;

      // 方案1A：使用Range的边界点精确定位
      try {
        const startContainer = range.startContainer;
        const startOffset = range.startOffset;

        // 找到选择开始的元素在textFragments中的位置
        let startFragmentIndex = -1;
        let cumulativeLength = 0;
        let startIndexInFullText = -1;

        for (let i = 0; i < textFragments.length; i++) {
          const fragment = textFragments[i];

          // 检查这个元素是否包含选择起点
          if (fragment.element.contains(startContainer) || fragment.element === startContainer) {
            // 计算在这个元素内的起始位置
            if (startContainer.nodeType === Node.TEXT_NODE) {
              // 如果是文本节点，需要计算在父元素中的偏移
              const elementText = fragment.text;
              startFragmentIndex = i;
              startIndexInFullText = cumulativeLength + Math.min(startOffset, elementText.length);
              break;
            } else {
              // 如果是元素节点，从0开始
              startFragmentIndex = i;
              startIndexInFullText = cumulativeLength;
              break;
            }
          }
          cumulativeLength += fragment.text.length;
        }

        if (startIndexInFullText === -1) {
          // 如果精确定位失败，回退到文本匹配（但使用更智能的匹配）
          return this.fallbackToTextMatch(fullText, cleanSelectedText, range, textFragments);
        }

        const endIndexInFullText = startIndexInFullText + cleanSelectedText.length;

        return {
          blockId,
          text: cleanSelectedText,
          startIndex: startIndexInFullText,
          endIndex: endIndexInFullText,
          fullText,
        };
      } catch (error) {
        console.warn('精确位置计算失败，使用备选方案:', error);
        return this.fallbackToTextMatch(fullText, cleanSelectedText, range, textFragments);
      }
    },

    // 备选方案：基于DOM位置的智能匹配
    fallbackToTextMatch(fullText, selectedText, range, textFragments) {
      // 获取选择范围的近似位置
      const rangeRect = range.getBoundingClientRect();

      // 找到最接近选择中心的文本片段
      let closestFragment = null;
      let minDistance = Infinity;

      textFragments.forEach((fragment) => {
        const rect = fragment.element.getBoundingClientRect();
        if (rect.width > 0 && rect.height > 0) {
          // 确保元素可见
          const centerX = rect.left + rect.width / 2;
          const centerY = rect.top + rect.height / 2;
          const rangeCenterX = rangeRect.left + rangeRect.width / 2;
          const rangeCenterY = rangeRect.top + rangeRect.height / 2;

          const distance = Math.sqrt(Math.pow(centerX - rangeCenterX, 2) + Math.pow(centerY - rangeCenterY, 2));

          if (distance < minDistance) {
            minDistance = distance;
            closestFragment = fragment;
          }
        }
      });

      if (closestFragment) {
        // 从最近的片段开始向前后搜索匹配
        const fragmentIndex = textFragments.indexOf(closestFragment);
        let cumulativeLength = 0;

        // 计算到当前片段的累计长度
        for (let i = 0; i < fragmentIndex; i++) {
          cumulativeLength += textFragments[i].text.length;
        }

        // 在当前片段附近搜索匹配
        const searchStart = Math.max(0, cumulativeLength - selectedText.length * 3);
        const searchEnd = Math.min(
          fullText.length,
          cumulativeLength + closestFragment.text.length + selectedText.length * 3,
        );

        const searchArea = fullText.substring(searchStart, searchEnd);
        const localIndex = searchArea.indexOf(selectedText);

        if (localIndex !== -1) {
          return {
            startIndex: searchStart + localIndex,
            endIndex: searchStart + localIndex + selectedText.length,
            text: selectedText,
            fullText,
          };
        }
      }

      // 最终回退：使用所有匹配位置，选择最合理的一个
      const allMatches = [];
      let searchIndex = 0;

      while ((searchIndex = fullText.indexOf(selectedText, searchIndex)) !== -1) {
        allMatches.push(searchIndex);
        searchIndex += selectedText.length;
      }

      if (allMatches.length === 1) {
        return {
          startIndex: allMatches[0],
          endIndex: allMatches[0] + selectedText.length,
          text: selectedText,
          fullText,
        };
      } else if (allMatches.length > 1) {
        // 如果有多个匹配，选择位置最接近选择中心的
        if (closestFragment) {
          let cumulativeLength = 0;
          let fragmentStartIndex = 0;

          for (let i = 0; i < textFragments.length; i++) {
            if (textFragments[i] === closestFragment) {
              fragmentStartIndex = cumulativeLength;
              break;
            }
            cumulativeLength += textFragments[i].text.length;
          }

          // 选择最接近当前片段起始位置的匹配
          const bestMatch = allMatches.reduce((best, current) => {
            return Math.abs(current - fragmentStartIndex) < Math.abs(best - fragmentStartIndex) ? current : best;
          });

          return {
            startIndex: bestMatch,
            endIndex: bestMatch + selectedText.length,
            text: selectedText,
            fullText,
          };
        }
      }

      return null;
    },
    /**
     * 滚动到指定data-id的元素
     * @param {string} dataId 元素的data-id属性值
     * @param {number} offset 偏移量
     */
    scrollToDataId(dataId, offset) {
      let _offset = offset;
      if (!_offset) _offset = 0;
      const element = document.querySelector(`div[data-id="${dataId}"]`);
      if (element) {
        element.scrollIntoView({
          behavior: 'smooth', // 滚动行为：'auto' | 'smooth'
          block: 'center', // 垂直对齐：'start' | 'center' | 'end' | 'nearest'
          inline: 'nearest', // 水平对齐：'start' | 'center' | 'end' | 'nearest'
        });
      }
    },
    startSelection(event) {
      if (this.canRemark) {
        this.isSelecting = true;
        let clientRect = document.getElementById(`selectable-area-preview`).getBoundingClientRect();
        this.menuPosition.startX = event.clientX - clientRect.left;
        this.menuPosition.startY = event.clientY - clientRect.top;
        this.menuPosition.endX = null;
        this.menuPosition.endY = null;
      }
    },
    updateSelection(event) {
      if (!this.isSelecting || !this.canRemark) return;
      let clientRect = document.getElementById(`selectable-area-preview`).getBoundingClientRect();

      this.menuPosition.endX = event.clientX - clientRect.left;
      this.menuPosition.endY = event.clientY - clientRect.top;
    },
    endSelection() {
      this.isSelecting = false;

      if (this.menuPosition.startX === this.menuPosition.endX || !this.menuPosition.endX || !this.canRemark) return;
      const width = this.menuPosition.endX - this.menuPosition.startX;
      const height = this.menuPosition.endY - this.menuPosition.startY;
      const x =
        this.menuPosition.endX > this.menuPosition.startX
          ? `${this.menuPosition.startX}px`
          : `${this.menuPosition.endX}px`;
      const y =
        this.menuPosition.endY > this.menuPosition.startY
          ? `${this.menuPosition.startY}px`
          : `${this.menuPosition.endY}px`;
      if (width > 3 && height > 3) {
        this.handleMenuItemClick();
      } else {
        this.resetRemark();
      }
    },
    // 重置框选数据
    resetRemark() {
      this.menuPosition = { x: 0, y: 0, select_node: '', startX: null, startY: null, endX: null, endY: null };
    },
    // 下载文件
    downLoad(file) {
      let userInfor = getToken();
      let AccessToken = '';
      if (userInfor) {
        AccessToken = userInfor.access_token;
      }
      let FileID = file.file_id;
      let data = {
        AccessToken,
        FileID,
      };
      location.href = `${process.env.VUE_APP_EEP}/FileServer/WebFileDownload?AccessToken=${data.AccessToken}&FileID=${data.FileID}`;
    },
    // 预览
    viewDialog(file) {
      this.newpath = `${this.file_preview_url}onlinePreview?url=${Base64.encode(file.file_url)}`;
      this.visible = true;
    },
  },
};
</script>

<style lang="scss" scoped>
.courserware {
  position: relative;
  display: flex;
  flex-direction: column;
  row-gap: $component-spacing;
  width: 100%;
  height: 100%;
  min-height: calc(100vh - 226px);
  padding-top: $courseware-top-padding;
  padding-bottom: $courseware-bottom-padding;
  margin: 15px 0;
  background-color: #fff;
  background-repeat: no-repeat;
  border-bottom-right-radius: 12px;
  border-bottom-left-radius: 12px;

  &::before {
    top: -15px;
  }

  &::after {
    bottom: -15px;
  }

  .row {
    display: grid;
    gap: $component-spacing;

    .col {
      display: grid;
      gap: $component-spacing;

      .active {
        box-shadow: 0 0 6px 1px $main-hover-color;
      }
    }

    .row-checkbox {
      position: absolute;
      left: -20px;
    }
  }

  .custom-context-menu,
  .remark-info {
    position: absolute;
    z-index: 999;
    display: flex;
    gap: 3px;
    align-items: center;
    font-size: 14px;
    cursor: pointer;
  }

  .custom-context-menu {
    padding-left: 30px;
    background: url('../../../../assets/icon-publish.png') left center no-repeat;
    background-size: 24px;
  }

  .contentmenu {
    position: absolute;
    z-index: 999;
    display: flex;
    column-gap: 4px;
    align-items: center;
    padding: 8px;
    font-size: 14px;
    color: #000;
    background-color: #e7e7e7;
    border-radius: 4px;
    box-shadow: 0 1px 10px 0 rgba(0, 0, 0, 30%);

    .svg-icon,
    .button {
      cursor: pointer;
    }

    .line {
      min-height: 16px;
      margin: 0 4px;
    }
  }
}
</style>
<style lang="scss">
.menu-remark-info {
  min-width: 2%;
  max-width: 450px;

  video,
  img {
    max-width: 100%;
    height: auto;
  }

  audio {
    max-width: 100%;
  }
}

.remark-file-item {
  display: flex;
  gap: 5px;
  align-items: center;
  padding: 3px;
  margin: 5px 0;
  background: #f2f3f5;
  border-radius: 3px;

  &-name {
    flex: 1;
    font-size: 12px;
    word-break: break-all;
  }

  .svg-icon {
    flex-shrink: 0;
    font-size: 16px;
  }

  .uploadPreview,
  .download {
    cursor: pointer;
  }
}
</style>
