<!-- eslint-disable vue/no-v-html -->
<template>
  <ModuleBase ref="base" :type="data.type">
    <template #content>
      <!-- eslint-disable max-len -->
      <div v-loading="loading" class="fill-wrapper">
        <RichText
          v-if="property.isGetContent"
          ref="richText"
          v-model="data.content"
          :is-fill="true"
          toolbar="fontselect fontsizeselect forecolor backcolor | lineheight paragraphSpacing fullwidthspace underline | bold italic strikethrough alignleft aligncenter alignright bullist numlist dotEmphasis"
          :wordlimit-num="false"
          :font-size="data?.unified_attrib?.font_size"
          :font-family="data?.unified_attrib?.font"
          :font-color="data?.unified_attrib?.text_color"
        />
        <div v-if="data.property.fill_type === fillTypeList[1].value" class="select-vocabulary">
          <h5 class="title">选词列表：</h5>
          <el-button size="mini" @click="openAddWord">添加词汇</el-button>
          <ul class="word-list">
            <li v-for="(item, index) in data.word_list" :key="item.mark" class="word-item">
              <span v-html="sanitizeHTML(item.content)"></span>
              <el-button type="text" size="mini" class="delete-word" @click="editWord(index)">
                <SvgIcon icon-class="edit" size="14" />
              </el-button>
              <el-button type="text" size="mini" class="delete-word" style="margin-left: 0" @click="removeWord(index)">
                <SvgIcon icon-class="delete-black" size="12" />
              </el-button>
            </li>
          </ul>
        </div>

        <span class="tips">在需要加空的内容处插入 3 个或以上的下划线“_”。</span>
        <el-divider content-position="left">
          <span>显示效果</span>
          <el-button
            type="text"
            icon="el-icon-refresh"
            title="刷新"
            class="refresh-pinyin-btn"
            @click.native="parsedContentPinyin(true)"
          />
        </el-divider>
        <div class="pinyin-wrapper">
          <div
            v-for="(group, gi) in groupedModelEssay"
            :key="`model-essay-group-${gi}`"
            class="model-essay-group"
            :style="groupStyle(group)"
          >
            <div v-for="(item, i) in group.items" :key="`item-${group.indexes[i]}`" class="pinyin-text-list">
              <PinyinText
                :key="`pinyin-${group.indexes[i]}`"
                ref="PinyinText"
                :is-show-pinyin="isEnable(data.property.view_pinyin)"
                :rich-text-list="item.rich_text_list"
                :pinyin-position="data.property.pinyin_position"
                :body-styles="pinyinBodyStyles"
                @fillCorrectPinyin="fillCorrectPinyin($event, group.indexes[i], -1, 'model_essay')"
              />
            </div>
          </div>
        </div>
        <div v-if="data.audio_file_id">
          <SoundRecord :wav-blob.sync="data.audio_file_id" />
        </div>
        <template v-else>
          <div :class="['upload-audio-play']">
            <UploadAudio
              v-if="data.property.audio_generation_method === 'upload'"
              :file-id="data.audio_file_id"
              :show-upload="!data.audio_file_id"
              @upload="uploads"
              @deleteFile="deleteFiles"
            />
            <div v-else-if="data.property.audio_generation_method === 'auto'" class="auto-matic" @click="handleMatic">
              <SvgIcon icon-class="voiceprint-line" class="record" />
              <span class="auto-btn">{{ data.audio_file_id ? '已生成' : '生成音频' }}</span
              >{{ data.audio_file_id ? '成功' : '' }}
            </div>
            <SoundRecord v-else :wav-blob.sync="data.audio_file_id" />
          </div>
        </template>

        <div>
          <el-button @click="parsedContentPinyin()">识别</el-button>
          <el-button @click="openMultilingual">多语言</el-button>
          <WordProofread button-style="margin-left: 10px" :word-data="wordData" @save="saveWord" />
        </div>

        <div v-if="data.answer.answer_list.length > 0" class="title">答案：</div>
        <div class="correct-answer">
          <el-input
            v-for="(item, i) in data.answer.answer_list.filter(({ type }) => type === 'any_one')"
            :key="item.mark"
            v-model="item.value"
            placeholder="多个答案可用‘/’分割"
            @blur="handleTone(item.value, item)"
          >
            <span slot="prefix">{{ i + 1 }}.</span>
          </el-input>
        </div>
      </div>

      <MultilingualFill
        :visible.sync="multilingualVisible"
        :text="data.content"
        :translations="data.multilingual"
        @SubmitTranslation="handleMultilingualTranslation"
      />
      <AnswerAnalysisList
        v-if="data.answer_list?.length > 0 || data.analysis_list?.length > 0"
        :answer-list="data.answer_list"
        :analysis-list="data.analysis_list"
        :unified-attrib="data.unified_attrib"
        @updateAnswerAnalysisFileList="updateAnswerAnalysisFileList"
        @deleteAnswerAnalysis="deleteAnswerAnalysis"
      />
      <AddWord :visible.sync="visibleWord" :word="editingWord" @add-word="addWord" @edit-word="editWordConfirm" />
    </template>
  </ModuleBase>
</template>

<script>
import ModuleMixin from '../../common/ModuleMixin';
import SoundRecord from '@/views/book/courseware/create/components/question/fill/components/SoundRecord.vue';
import UploadAudio from '@/views/book/courseware/create/components/question/fill/components/UploadAudio.vue';
import PinyinText from '@/components/PinyinText.vue';
import AddWord from './components/AddWord.vue';
import WordProofread from '@/views/book/courseware/create/components/common/WordProofread.vue';
import { buildWordProofreadData } from '@/views/book/courseware/create/components/common/wordProofread';

import { getFillData, arrangeTypeList, fillTypeList } from '@/views/book/courseware/data/fill';
import { addTone, handleToneValue, mergePunctuationToWords } from '@/views/book/courseware/data/common';
import { getRandomNumber } from '@/utils';
import { TextToAudioFile } from '@/api/app';
import { sanitizeHTML } from '@/utils/common';
import { PinyinBuild_OldFormat } from '@/api/book';

export default {
  name: 'FillPage',
  components: {
    SoundRecord,
    UploadAudio,
    PinyinText,
    AddWord,
    WordProofread,
  },
  mixins: [ModuleMixin],
  data() {
    return {
      data: getFillData(),
      fillTypeList,
      sanitizeHTML,
      visibleWord: false,
      editingIndex: null, // 当前编辑的词汇索引，null 表示新增模式
      wordData: { detail: [] }, // 分词校对数据
      inited: false,
      loading: false,
    };
  },
  computed: {
    editingWord() {
      if (this.editingIndex === null || this.editingIndex === undefined) return null;
      return this.data.word_list[this.editingIndex] || null;
    },
    pinyinBodyStyles() {
      const unifiedAttrib = this.data?.unified_attrib || {};
      return {
        fontFamily: unifiedAttrib.font || undefined,
        fontSize: unifiedAttrib.font_size || undefined,
        color: unifiedAttrib.text_color || undefined,
      };
    },
    /**
     * 按段落对齐方式对 model_essay 分组，便于整体应用 text-align，
     * 让同一段内的文字块与填空连续排列（可换行），而不是每块独占一行。
     * @returns {Array} [{ align, items, indexes }]
     */
    groupedModelEssay() {
      const groups = [];
      (this.data.model_essay || []).forEach((item, index) => {
        const align = item.align || '';
        const last = groups[groups.length - 1];
        if (last && last.align === align) {
          last.items.push(item);
          last.indexes.push(index);
        } else {
          groups.push({ align, items: [item], indexes: [index] });
        }
      });
      return groups;
    },
  },
  watch: {
    'data.property.arrange_type': 'handleMindMap',
    'data.property.fill_font': 'handleMindMap',
    'data.content': {
      handler(newVal) {
        if (!this.inited && newVal) {
          this.ensureWordProofreadData().finally(() => {
            this.inited = true;
          });
        }
      },
    },
    'data.property.view_pinyin': {
      handler(val) {
        if (this.property.isSilentPropertyWatch) return;
        if (val === 'true') {
          this.parsedContentPinyin();
        }
      },
      deep: true,
    },
  },
  methods: {
    /**
     * 分组容器样式：有对齐时应用块级与对齐，无对齐时保持行内流式。
     * @param {Object} group 分组对象
     * @returns {Object} 样式对象
     */
    groupStyle(group) {
      if (!group || !group.align) return {};
      return {
        display: 'block',
        textAlign: group.align,
      };
    },
    async ensureWordProofreadData() {
      this.wordData = { detail: [{ sentences: [], segList: [] }] };
      this.data.rich_text_list?.forEach((item) => {
        let sentence = (item.word_list || []).reduce((acc, itemss) => `${acc + itemss.text}`, '');
        let seg = (item.word_list || []).map((itemss) => itemss.text);
        this.wordData.detail[0].sentences.push(sentence);
        this.wordData.detail[0].segList.push(seg);
      });
    },
    /**
     * 解析文本内容，生成分词和拼音
     * @param {boolean} isPrompt 是否为提示操作，默认为 false
     */
    async parsedContentPinyin(isPrompt = false, fcList = []) {
      if (isPrompt) {
        try {
          await this.$confirm('刷新将会重置填空和拼音，是否刷新？', '提示', {
            confirmButtonText: '确定',
            cancelButtonText: '取消',
            type: 'warning',
          });
        } catch (e) {
          return;
        }
      }
      this.loading = true;
      PinyinBuild_OldFormat({
        text: this.data.content,
        is_first_sentence_first_hz_pinyin_first_char_upper_case:
          this.data.property.is_first_sentence_first_hz_pinyin_first_char_upper_case,
        is_fill_space: 'true',
        is_rich_text: 'true',
        is_custom_fc: fcList.length > 0,
        fc_paragraph_list: fcList,
      })
        .then(({ rich_text, parsed_text }) => {
          if (!rich_text) return;
          if (!Array.isArray(rich_text.text_list)) return;

          this.data.rich_text_list = rich_text.text_list.map((item) => {
            // 非 word_list 项，原样返回
            if (item.is_style === 'true' || !Array.isArray(item.word_list)) {
              return item;
            }
            return {
              ...item,
              word_list: mergePunctuationToWords(item.word_list),
            };
          });
          this.wordData = buildWordProofreadData(parsed_text?.paragraph_list || []);
          this.data.model_essay = this.parseRichText();
        })
        .finally(() => {
          this.loading = false;
        });
    },
    async saveWord(saveArr) {
      await this.parsedContentPinyin(false, saveArr);
    },
    /**
     * 判断文本是否包含可见内容（去除 HTML 标签后是否仍有非空白字符）
     * @param {String} content 文本内容
     * @returns {Boolean} 是否包含可见内容
     */
    hasVisibleText(content = '') {
      return (
        String(content)
          .replace(/<[^>]*>/g, '')
          .trim().length > 0
      );
    },
    /**
     * 追加文本块；当文本块不含可见内容（仅闭合标签/空白）时，
     * 并入前一个文本块，避免孤立闭合标签破坏 PinyinText 渲染。
     * @param {Array} blocks 目标文本块数组
     * @param {String} content 文本内容
     * @param {Array} richTextList 富文本列表
     */
    appendTextBlock(blocks, content, richTextList = []) {
      if (!this.hasVisibleText(content) && blocks.length > 0) {
        // 填空（input）属于当前段落的一部分：若段落以填空结尾，紧随其后的闭合标签和
        // 段间换行 \n 不能合并到填空前面的文本块中，否则 PinyinText 会把 \n 渲染成
        // <br> 出现在填空之前，导致填空换到下一行显示。此时将换行保留为填空后的独立文本块。
        const lastBlock = blocks[blocks.length - 1];
        if (lastBlock && lastBlock.type === 'input' && String(content).includes('\n')) {
          blocks.push({
            content,
            type: 'text',
            rich_text_list: richTextList,
          });
          return;
        }

        for (let i = blocks.length - 1; i >= 0; i--) {
          if (blocks[i] && blocks[i].type === 'text') {
            blocks[i].content = `${blocks[i].content || ''}${content}`;
            blocks[i].rich_text_list = [...(blocks[i].rich_text_list || []), ...richTextList];
            return;
          }
        }
      }

      blocks.push({
        content,
        type: 'text',
        rich_text_list: richTextList,
      });
    },
    /**
     * 判断最后一个文本块是否为块级（居中对齐）。
     * 居中的块级段落自身已产生换行，其后的段间 \n 是冗余的。
     * @param {Array} blocks 已生成的文本块数组
     * @returns {Boolean}
     */
    isLastTextBlockAligned(blocks) {
      for (let i = blocks.length - 1; i >= 0; i--) {
        const block = blocks[i];
        if (!block || block.type !== 'text') continue;
        return (block.rich_text_list || []).some((item) => {
          const tagText = String(item?.text || '');
          return /^<(p|div|h[1-6]|li|blockquote)\b[^>]*>/i.test(tagText) && /text-align\s*:\s*center/i.test(tagText);
        });
      }
      return false;
    },
    /**
     * 提取每个块的对齐方式并写入 align 字段，同时从块级标签中移除 text-align，
     * 避免 PinyinText 为每个块设置 display:block，导致段内文字被拆成多行。
     * 对齐效果改由上层按 align 分组后整体应用。
     * @param {Array} blocks 文本块数组
     * @returns {Array} 处理后的文本块数组
     */
    finalizeAlign(blocks) {
      return (blocks || []).map((block) => {
        let align = block.align || '';
        const richTextList = (block.rich_text_list || []).map((item) => {
          const tagText = String(item?.text || '');
          if (!/^<(p|div|h[1-6]|li|blockquote)\b[^>]*>/i.test(tagText)) return item;
          const alignMatch = tagText.match(/text-align\s*:\s*([^;"']+)/i);
          if (!alignMatch) return item;
          if (!align) align = alignMatch[1].trim();
          return { ...item, text: tagText.replace(/text-align\s*:\s*[^;"']+;?\s*/i, '') };
        });
        return { ...block, align, rich_text_list: richTextList };
      });
    },
    // 解析富文本，构建 model_essay 结构
    parseRichText() {
      let text_list = this.data.rich_text_list || [];
      const preservedAnyOneAnswers = (this.data.answer.answer_list || [])
        .filter(({ type }) => type === 'any_one')
        .map(({ value }) => value);
      const preservedAnyOneState = { index: 0 };
      this.data.answer.answer_list = [];
      const arr = [];
      let totalText = '';
      let totalRichText = [];
      const openStyleTagStack = [];
      for (let i = 0; i < text_list.length; i++) {
        const textItem = text_list[i];
        const text = textItem.text || '';
        const isStyle = textItem.is_style === 'true' || textItem.is_style === true;
        if (isStyle) {
          const isRichFill = /class=\s*(\\?["'])rich-fill\1/.test(text);
          if (isRichFill) {
            if (totalText.length > 0) {
              this.appendTextBlock(arr, totalText, totalRichText);
              totalText = '';
              totalRichText = [];
            }
            let nextData = text_list[i + 1] || {};
            let nextTag = text_list[i + 2] || {};
            const mark = getRandomNumber();
            arr.push({
              content: text + (nextData.text || '') + (nextTag.text || ''),
              type: 'input',
              input: '',
              audio_answer_list: [],
              mark,
              rich_text_list: [textItem, nextData, nextTag],
            });
            this.data.answer.answer_list.push({
              value: nextData.text || '',
              mark,
              type: 'only_one',
            });
            // 跳过下一个文本和标签数据
            i += 2;
          } else {
            totalText += text;
            totalRichText = totalRichText.concat(textItem);
            this.syncOpenStyleTagStack(openStyleTagStack, textItem);
          }
        } else {
          // 段间空白（如 </p> 之后的 \n）：前一段落为块级（带对齐）时冗余，丢弃；
          // 否则保留作为段落换行分隔，避免破坏正常换行结构。
          if (!String(text).trim() && this.isLastTextBlockAligned(arr)) {
            // 先把累积的闭合标签并入前一个文本块，保持标签配对
            if (totalText.length > 0) {
              this.appendTextBlock(arr, totalText, totalRichText);
              totalText = '';
              totalRichText = [];
            }
            continue;
          }

          const splitBlocks = this.splitTextItemByUnderline(textItem, preservedAnyOneAnswers, preservedAnyOneState);

          const currentOpenStyleTags = openStyleTagStack.map((tagItem) => ({ ...tagItem }));
          const firstBlockPrefix =
            totalText.length > 0
              ? this.buildFirstBlockStylePrefix(totalRichText, currentOpenStyleTags)
              : currentOpenStyleTags;

          // 样式标签单独成块会导致 PinyinText 的样式栈断开，需并入紧随其后的文本块。
          if (splitBlocks.length > 0) {
            if (totalText.length > 0) {
              splitBlocks[0].content = `${totalText}${splitBlocks[0].content || ''}`;
            }

            splitBlocks.forEach((block, blockIndex) => {
              const prevRichTextList = block.rich_text_list || [];
              block.rich_text_list = [
                ...(blockIndex === 0 ? firstBlockPrefix : currentOpenStyleTags),
                ...prevRichTextList,
              ];
            });

            totalText = '';
            totalRichText = [];
          } else if (totalText.length > 0) {
            this.appendTextBlock(arr, totalText, firstBlockPrefix);
            totalText = '';
            totalRichText = [];
          }

          splitBlocks.forEach((block) => {
            if (block.type === 'text') {
              this.appendTextBlock(arr, block.content, block.rich_text_list);
            } else {
              arr.push(block);
            }
          });
        }
      }

      if (totalText.length > 0) {
        this.appendTextBlock(arr, totalText, totalRichText);
      }

      return this.finalizeAlign(arr);
    },
    /**
     * 从标签文本中提取标签名称，支持开标签和闭标签
     * @param {String} tagText 标签文本，如 "<b>"、"</b>"、"<br/>"
     * @returns {String} 标签名称，如 "b"、"br"，如果不是有效标签则返回空字符串
     */
    getStyleTagName(tagText = '') {
      const trimmedText = String(tagText).trim();
      const closeMatch = trimmedText.match(/^<\/(\w+)>$/);
      if (closeMatch) return closeMatch[1].toLowerCase();

      const openMatch = trimmedText.match(/^<(\w+)([^>]*)>$/);
      if (openMatch) return openMatch[1].toLowerCase();

      return '';
    },
    /**
     * 判断标签项是否为有效的开标签（非自闭合标签且非换行标签）
     * @param {Object} tagItem 标签项对象，包含 text 和 is_style 属性
     * @returns {Boolean} 是否为开标签
     */
    isOpenStyleTag(tagItem = {}) {
      const tagText = String(tagItem?.text || '').trim();
      const isStyleTag = tagItem?.is_style === 'true' || tagItem?.is_style === true;
      if (!isStyleTag) return false;
      if (/^<\//.test(tagText)) return false;
      if (/^<br\s*\/?\s*>$/i.test(tagText)) return false;
      return /^<\w+[^>]*>$/.test(tagText);
    },
    /**
     * 同步更新样式标签栈，遇到开标签入栈，遇到闭标签出栈
     * @param {Array} openStyleTagStack 当前的开标签栈
     * @param {Object} styleTagItem 当前处理的标签项对象
     */
    syncOpenStyleTagStack(openStyleTagStack, styleTagItem) {
      const tagText = String(styleTagItem?.text || '').trim();
      const isStyleTag = styleTagItem?.is_style === 'true' || styleTagItem?.is_style === true;
      if (!isStyleTag) return;
      if (/^<br\s*\/?\s*>$/i.test(tagText)) return;

      const closeMatch = tagText.match(/^<\/(\w+)>$/);
      if (closeMatch) {
        const closeTagName = closeMatch[1].toLowerCase();
        for (let i = openStyleTagStack.length - 1; i >= 0; i--) {
          const tagName = this.getStyleTagName(openStyleTagStack[i]?.text);
          if (tagName === closeTagName) {
            openStyleTagStack.splice(i, 1);
            break;
          }
        }
        return;
      }

      if (this.isOpenStyleTag(styleTagItem)) {
        openStyleTagStack.push(styleTagItem);
      }
    },
    /**
     * 获取开标签的身份标识，用于匹配开闭标签，通常是标签名称加上关键属性（如 class），以区分不同样式的同名标签
     * @param {Object} tagItem 标签项对象，包含 text 和 is_style 属性
     * @return {String} 开标签的身份标识，如 "b"、"span.class1"，如果不是有效开标签则返回空字符串
     */
    getOpenStyleTagIdentity(tagItem = {}) {
      return String(tagItem?.text || '')
        .trim()
        .replace(/\s+/g, ' ');
    },
    /**
     * 构建文本块的样式前缀，包含当前仍应处于打开状态的样式标签（外层→内层）
     * 以及过渡标签中的换行等不影响样式栈的特殊项。
     * 开标签已由 currentOpenStyleTags 完整覆盖，闭标签在独立渲染的块中无法
     * 匹配到上一块的开标签，需丢弃，避免误关外层同名标签。
     * @param {Array} transitionStyleTags 过渡样式标签列表，可能包含开标签和闭标签
     * @param {Array} currentOpenStyleTags 当前仍应处于打开状态的样式标签列表
     * @returns {Array} 构建好的样式前缀列表
     */
    buildFirstBlockStylePrefix(transitionStyleTags = [], currentOpenStyleTags = []) {
      // 仅保留换行等不影响样式栈的特殊项（br 不入栈，但需要渲染换行）
      const lineBreakTags = transitionStyleTags.filter((tagItem) =>
        /^<br\s*\/?\s*>$/i.test(String(tagItem?.text || '').trim()),
      );

      return [...currentOpenStyleTags, ...lineBreakTags];
    },
    /**
     * 根据文本中的连续下划线分割文本块，并将下划线部分转换为输入块
     * @param {Object} textItem 富文本中的一个文本项
     * @param {Array} preservedAnyOneAnswers 预先保存的 any_one 类型答案列表
     * @param {Object} preservedAnyOneState any_one 答案的当前索引状态
     */
    splitTextItemByUnderline(textItem, preservedAnyOneAnswers = [], preservedAnyOneState = { index: 0 }) {
      const text = textItem?.text || '';
      const matcher = /_{3,}/g;
      const blocks = [];
      let lastIndex = 0;
      let match = matcher.exec(text);

      while (match) {
        const underlineText = match[0] || '';
        const start = match.index;
        const end = start + underlineText.length;

        if (start > lastIndex) {
          const textContent = text.slice(lastIndex, start);
          blocks.push({
            content: textContent,
            type: 'text',
            rich_text_list: [
              {
                ...textItem,
                text: textContent,
                word_list: this.sliceWordListByTextRange(textItem.word_list, lastIndex, start),
              },
            ],
          });
        }

        const mark = getRandomNumber();
        blocks.push({
          content: underlineText,
          type: 'input',
          input: '',
          audio_answer_list: [],
          mark,
          rich_text_list: [
            {
              ...textItem,
              text: underlineText,
              word_list: this.sliceWordListByTextRange(textItem.word_list, start, end),
            },
          ],
        });
        this.data.answer.answer_list.push({
          value: preservedAnyOneAnswers[preservedAnyOneState.index] || '',
          mark,
          type: 'any_one',
        });
        preservedAnyOneState.index += 1;

        lastIndex = end;
        match = matcher.exec(text);
      }

      if (lastIndex < text.length) {
        const textContent = text.slice(lastIndex);
        blocks.push({
          content: textContent,
          type: 'text',
          rich_text_list: [
            {
              ...textItem,
              text: textContent,
              word_list: this.sliceWordListByTextRange(textItem.word_list, lastIndex, text.length),
            },
          ],
        });
      }

      if (blocks.length === 0) {
        return [
          {
            content: text,
            type: 'text',
            rich_text_list: [textItem],
          },
        ];
      }

      return blocks;
    },
    /**
     * 根据文本范围切割词列表，返回切割后的词列表
     * @param {Array} wordList 富文本项中的词列表，每个词项包含 text 和其他属性
     * @param {Number} rangeStart 切割范围的起始位置（相对于文本开头的字符索引）
     * @param {Number} rangeEnd 切割范围的结束位置（相对于文本开头的字符索引）
     * @returns {Array} 切割后的词列表，包含在切割范围内的词项，且每个词项的 text 已根据切割范围调整
     */
    sliceWordListByTextRange(wordList = [], rangeStart = 0, rangeEnd = 0) {
      if (!Array.isArray(wordList) || rangeEnd <= rangeStart) return [];

      const result = [];
      let cursor = 0;

      wordList.forEach((wordItem) => {
        const wordText = wordItem?.text || '';
        const wordStart = cursor;
        const wordEnd = wordStart + wordText.length;
        cursor = wordEnd;

        const overlapStart = Math.max(rangeStart, wordStart);
        const overlapEnd = Math.min(rangeEnd, wordEnd);

        if (overlapStart >= overlapEnd) return;

        const relativeStart = overlapStart - wordStart;
        const relativeEnd = overlapEnd - wordStart;

        result.push(this.sliceWordItem(wordItem, relativeStart, relativeEnd));
      });

      return result;
    },
    /**
     * 根据文本范围切割单个词项，返回切割后的词项
     * @param {Object} wordItem 词项对象，包含 text 和其他属性
     * @param {Number} start 切割范围的起始位置（相对于词项的字符索引）
     * @param {Number} end 切割范围的结束位置（相对于词项的字符索引）
     * @returns {Object} 切割后的词项，包含在切割范围内的字符，且 text 已根据切割范围调整
     */
    sliceWordItem(wordItem, start, end) {
      const fullText = wordItem?.text || '';
      const slicedText = fullText.slice(start, end);
      const slicedWord = {
        ...wordItem,
        text: slicedText,
      };

      const sourcePinyinList = Array.isArray(wordItem?.pinyin_list) ? wordItem.pinyin_list : [];

      if (sourcePinyinList.length === fullText.length) {
        slicedWord.pinyin_list = sourcePinyinList.slice(start, end);
        slicedWord.pinyin = (slicedWord.pinyin_list || []).join(' ').trim();
        return slicedWord;
      }

      if (start === 0 && end === fullText.length) {
        slicedWord.pinyin_list = [...sourcePinyinList];
        return slicedWord;
      }

      slicedWord.pinyin_list = new Array(slicedText.length).fill('');
      slicedWord.pinyin = '';
      return slicedWord;
    },

    handleTone(value, item) {
      if (!item || !/^[a-zA-Z0-9\s]+$/.test(value)) return;
      item.value = value
        .trim()
        .split(/\s+/)
        .map((valItem) => {
          return handleToneValue(valItem);
        })
        .map((valItem) =>
          valItem.map(({ number, con }) => (number && con ? addTone(Number(number), con) : number || con || '')),
        )
        .filter((valItem) => valItem.length > 0)
        .join(' ');
    },
    uploads(file_id) {
      this.data.audio_file_id = file_id;
    },
    deleteFiles() {
      this.data.audio_file_id = '';
    },
    // 自动生成音频
    handleMatic() {
      TextToAudioFile({
        text: this.data.content.replace(/<[^>]+>/g, ''),
        voice_type: this.data.property.voice_type,
        emotion: this.data.property.emotion,
        speed_ratio: this.data.property.speed_ratio,
      })
        .then(({ status, file_id }) => {
          if (status === 1) {
            this.data.audio_file_id = file_id;
          }
        })
        .catch(() => {});
    },
    /**
     * @description 处理思维导图数据
     */
    handleMindMap() {
      const { arrange_type } = this.data.property;
      const arrangeLabel = arrangeTypeList.find((item) => item.value === arrange_type)?.label || '';
      this.data.mind_map.node_list = [
        {
          name: `${arrangeLabel}填空组件`,
        },
      ];
    },
    handleViewPinyin() {
      if (!this.isEnable(this.data.property.view_pinyin)) {
        return;
      }

      this.data.model_essay.forEach((item, i) => {
        item.forEach((option, j) => {
          const text = option.content;
          option.paragraph_list_parameter.text = text;

          this.createParsedTextInfoPinyin(text, i, j, 'model_essay');
        });
      });
    },
    openAddWord() {
      this.editingIndex = null;
      this.visibleWord = true;
    },
    /**
     * 编辑词汇
     * @param {number} index 词汇在 word_list 中的索引
     */
    editWord(index) {
      this.editingIndex = index;
      this.visibleWord = true;
    },
    /**
     * 确认编辑词汇
     * @param {string} word 编辑后的词汇内容
     */
    editWordConfirm(word) {
      if (!word) return;
      if (this.editingIndex === null || this.editingIndex === undefined) return;
      this.$set(this.data.word_list, this.editingIndex, {
        ...this.data.word_list[this.editingIndex],
        content: word,
      });
      this.editingIndex = null;
    },
    /**
     * 添加词汇
     * @param {string} word 词汇内容
     */
    addWord(word) {
      if (!word) return;
      this.data.word_list.push({
        content: word,
        mark: getRandomNumber(),
      });
    },
    removeWord(index) {
      this.data.word_list.splice(index, 1);
    },
  },
};
</script>

<style lang="scss" scoped>
.fill-wrapper {
  display: flex;
  flex-direction: column;
  row-gap: 16px;
  align-items: flex-start;

  :deep .rich-wrapper {
    width: 100%;
  }

  .select-vocabulary {
    .title {
      margin: 0 0 8px;
    }

    .word-list {
      display: flex;
      flex-wrap: wrap;
      gap: 8px;
      margin-top: 8px;

      .word-item {
        display: flex;
        gap: 6px;
        align-items: center;
        padding: 4px 6px;
        border: $border;
        border-radius: 4px;

        :deep p {
          margin: 0;
        }

        .delete-word {
          display: flex;
          align-items: center;
          padding: 0;
          line-height: 1;
        }
      }
    }
  }

  .tips {
    font-size: 12px;
    color: #999;
  }

  .auto-matic,
  .upload-audio-play {
    :deep .upload-wrapper {
      margin-top: 0;
    }

    .audio-wrapper {
      :deep .audio-play {
        width: 16px;
        height: 16px;
        color: #000;
        background-color: initial;
      }

      :deep .audio-play.not-url {
        color: #a1a1a1;
      }

      :deep .voice-play {
        width: 16px;
        height: 16px;
      }
    }
  }

  .auto-matic {
    display: flex;
    flex-shrink: 0;
    column-gap: 12px;
    align-items: center;
    width: 200px;
    padding: 5px 12px;
    background-color: $fill-color;
    border-radius: 2px;

    .auto-btn {
      font-size: 16px;
      font-weight: 400;
      line-height: 22px;
      color: #1d2129;
      cursor: pointer;
    }
  }

  .correct-answer {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;

    .el-input {
      width: 180px;

      :deep &__prefix {
        display: flex;
        align-items: center;
        color: $text-color;
      }
    }
  }
}

.pinyin-wrapper {
  .model-essay-group {
    display: inline;
  }

  .pinyin-text-list {
    display: inline;

    :deep .pinyin-area {
      display: inline;
    }

    :deep .pinyin-area .rich-text-container,
    :deep .pinyin-area .pinyin-paragraph,
    :deep .pinyin-area .pinyin-sentence {
      display: inline;
    }
  }
}
</style>
