<template>
  <PageWrapper :title="pageTitle" contentBackground dense contentFullHeight>
    <template #extra>
      <a-space>
        <a-button @click="goBack">返回列表</a-button>
        <a-button type="primary" v-auth="'print:template:edit'" @click="handleSave" :loading="saving">保存模板</a-button>
        <a-button v-auth="'print:template:edit'" @click="handlePreview">浏览器预览打印</a-button>
      </a-space>
    </template>
    <a-card :bordered="false" class="hiprint-card">
      <a-alert
        type="info"
        show-icon
        message="从左侧拖拽组件到画布；选中控件后在右侧设置属性。文本中可使用变量占位（与业务传入的 JSON 字段对应）。"
        class="mb-2"
      />
      <a-row :gutter="8" class="designer-row">
        <a-col :span="4" class="left-col">
          <div class="col-title">组件库</div>
          <a-collapse :bordered="false" defaultActiveKey="basic">
            <a-collapse-panel key="basic" header="常用组件">
              <div class="ep-draggable-item" tid="defaultModule.text">文本</div>
              <div class="ep-draggable-item" tid="defaultModule.longText">长文本</div>
              <div class="ep-draggable-item" tid="defaultModule.html">富文本(HTML)</div>
              <div class="ep-draggable-item" tid="defaultModule.image">图片</div>
              <div class="ep-draggable-item" tid="defaultModule.qrcode">二维码</div>
              <div class="ep-draggable-item" tid="defaultModule.barcode">条形码</div>
              <div class="ep-draggable-item" tid="defaultModule.hline">横线</div>
              <div class="ep-draggable-item" tid="defaultModule.vline">竖线</div>
              <div class="ep-draggable-item" tid="defaultModule.rect">矩形</div>
            </a-collapse-panel>
            <a-collapse-panel key="detail" header="明细表" forceRender>
              <div class="ep-draggable-item" tid="qhmesModule.tableSimple">普通明细表(默认列)</div>
              <div class="help-tip">默认普通表头；字段/绑定在右侧属性里修改。</div>
            </a-collapse-panel>
            <a-collapse-panel key="section" header="报表区块(布局)" forceRender>
              <div class="section-tip">这些是“更像 HttpPrinter”的常用组件预设（本质仍是 Hiprint 元素）。</div>
              <div class="ep-draggable-item" tid="qhmesModule.reportTitle">报表标题</div>
              <div class="ep-draggable-item" tid="qhmesModule.subTitle">副标题</div>
              <div class="ep-draggable-item" tid="qhmesModule.labelValue">标签:值</div>
              <div class="ep-draggable-item" tid="qhmesModule.pageNo">页码</div>
              <div class="ep-draggable-item" tid="qhmesModule.qrcode">二维码(预设)</div>
              <div class="ep-draggable-item" tid="qhmesModule.barcode">条形码(预设)</div>
            </a-collapse-panel>
          </a-collapse>
        </a-col>
        <a-col :span="13" class="center-col">
          <div class="col-title">画布</div>
          <div id="hiprint-printTemplate" class="hiprint-printTemplate"></div>
          <div class="hiprint-printPagination"></div>
        </a-col>
        <a-col :span="7" class="right-col">
          <div class="col-title">属性</div>
          <div id="PrintElementOptionSetting"></div>
        </a-col>
      </a-row>
      <a-divider>预览数据（JSON，供「预览打印」合并变量）</a-divider>
      <a-textarea v-model:value="printDataJson" :rows="6" placeholder='例如：{"title":"标题","orderNo":"001"}' />
      <a-divider>普通明细表列配置（用于 qhmesModule.tableSimple）</a-divider>
      <a-space direction="vertical" style="width: 100%">
        <a-textarea
          v-model:value="tableColumnsJson"
          :rows="6"
          placeholder='例如：[{"title":"物料","field":"name","width":90},{"title":"数量","field":"qty","width":45}]'
          @focus="onColumnsEditorFocus"
          @blur="onColumnsEditorBlur"
        />
        <a-space>
          <a-button @click="buildColumnsFromSampleData">从预览数据推导列</a-button>
          <a-button type="primary" @click="applyTableColumnsConfig">应用到“普通明细表”组件</a-button>
        </a-space>
      </a-space>
      <a-divider>明细分组配置（可视化，多级）</a-divider>
      <a-space direction="vertical" style="width: 100%">
        <a-space>
          <span>启用分组</span>
          <a-switch v-model:checked="groupEnabled" />
        </a-space>
        <a-space>
          <span>保真预览（样式优先）</span>
          <a-switch v-model:checked="preserveDesignStyle" />
        </a-space>
        <a-select
          v-model:value="groupFields"
          mode="multiple"
          style="width: 100%"
          :options="tableFieldOptions"
          placeholder="选择分组字段（按选择顺序分层：一级→二级→三级）"
        />
        <div class="help-tip">
          规则：按所选字段顺序做层级分组，相同值将隐藏重复内容并居中展示（视觉合并效果），无需写代码。
        </div>
      </a-space>
    </a-card>
  </PageWrapper>
</template>

<script lang="ts" setup>
  import { computed, nextTick, onUnmounted, ref, watch } from 'vue';
  import { useRoute, useRouter } from 'vue-router';
  import { PageWrapper } from '/@/components/Page';
  import { useMessage } from '/@/hooks/web/useMessage';
  import $ from 'jquery';
  import { hiprint, defaultElementTypeProvider, disAutoConnect } from 'vue-plugin-hiprint';
  import 'vue-plugin-hiprint/dist/print-lock.css';
  import { queryById, saveJson } from './printTemplate.api';
  import { createQhmesProvider } from './hiprint/qhmesProvider';

  defineOptions({ name: 'PrintDesigner' });

  const route = useRoute();
  const router = useRouter();
  const { createMessage } = useMessage();

  const tplName = ref('');
  const saving = ref(false);
  const printDataJson = ref(
    '{\n  "title": "演示标题",\n  "orderNo": "SO20260101001",\n  "table": [\n    { "name": "物料A", "qty": 1, "amount": 100 }\n  ]\n}'
  );
  const tableColumnsJson = ref(
    '[\n  { "title": "物料", "field": "name", "width": 90 },\n  { "title": "数量", "field": "qty", "width": 45 },\n  { "title": "金额", "field": "amount", "width": 45 }\n]'
  );
  const groupEnabled = ref(false);
  const groupFields = ref<string[]>([]);
  const preserveDesignStyle = ref(true);

  const pageTitle = computed(() => (tplName.value ? `打印设计 - ${tplName.value}` : '打印设计器'));

  let hiprintTemplate: any = null;
  let hiprintInited = false;
  let fieldsTimer: any = null;
  let columnsSyncTimer: any = null;
  const columnsEditorFocused = ref(false);
  const lastSyncedColumnsKey = ref('');

  function ensureJqueryGlobal() {
    (window as any).$ = $;
    (window as any).jQuery = $;
  }

  function destroyDesigner() {
    try {
      hiprintTemplate = null;
      $('#hiprint-printTemplate').empty();
      $('#PrintElementOptionSetting').empty();
      $('.hiprint-printPagination').empty();
    } catch (e) {
      console.warn(e);
    }
  }

  function initHiprintRuntime() {
    if (hiprintInited) {
      return;
    }
    ensureJqueryGlobal();
    disAutoConnect();
    hiprint.init({
      providers: [new defaultElementTypeProvider(), createQhmesProvider()],
      lang: 'cn',
    });
    hiprintInited = true;
  }

  function parseTemplateJson(str: string | undefined) {
    if (!str || str === '{}') {
      return {};
    }
    try {
      return JSON.parse(str);
    } catch {
      createMessage.warning('模板 JSON 解析失败，已使用空白模板');
      return {};
    }
  }

  function parsePrintData(): Record<string, any> {
    try {
      return JSON.parse(printDataJson.value || '{}');
    } catch {
      createMessage.error('预览数据不是合法 JSON');
      return {};
    }
  }

  const tableFieldOptions = computed(() => {
    const data = parsePrintData();
    const firstRow = Array.isArray(data?.table) && data.table.length > 0 ? data.table[0] : null;
    if (!firstRow || Object.prototype.toString.call(firstRow) !== '[object Object]') {
      return [];
    }
    return Object.keys(firstRow).map((k) => ({ label: k, value: k }));
  });

  function buildFieldsFromJson(value: any, prefix = ''): Array<{ field: string; text: string }> {
    const res: Array<{ field: string; text: string }> = [];
    if (value == null) {
      return res;
    }
    const isObj = Object.prototype.toString.call(value) === '[object Object]';
    const isArr = Array.isArray(value);
    if (!isObj && !isArr) {
      if (prefix) {
        res.push({ field: prefix, text: prefix });
      }
      return res;
    }

    if (isArr) {
      // 数组字段：允许绑定数组本身，也允许绑定数组元素的字段（table[0].xx 推导为 table.xx）
      if (prefix) {
        res.push({ field: prefix, text: prefix });
      }
      const first = value.length > 0 ? value[0] : null;
      if (first && Object.prototype.toString.call(first) === '[object Object]') {
        Object.keys(first).forEach((k) => {
          const f = prefix ? `${prefix}.${k}` : k;
          res.push({ field: f, text: f });
        });
      }
      return res;
    }

    Object.keys(value).forEach((k) => {
      const f = prefix ? `${prefix}.${k}` : k;
      const v = value[k];
      // 先把自己加进去（让属性面板可直接选择）
      res.push({ field: f, text: f });
      // 再递归对象/数组
      res.push(...buildFieldsFromJson(v, f));
    });
    return res;
  }

  function applyFieldsToTemplate() {
    if (!hiprintTemplate) {
      return;
    }
    const data = parsePrintData();
    const fields = buildFieldsFromJson(data);
    // 去重
    const uniq = new Map<string, { field: string; text: string }>();
    fields.forEach((it) => {
      if (it?.field && !uniq.has(it.field)) {
        uniq.set(it.field, it);
      }
    });
    try {
      hiprintTemplate.setFields(Array.from(uniq.values()));
    } catch (e) {
      // setFields 非强依赖，失败不影响设计器
      console.warn(e);
    }
  }

  type SimpleColumn = {
    title: string;
    field: string;
    width?: number;
    align?: 'left' | 'center' | 'right';
  };

  function normalizeColumns(cols: any[]): any[] {
    return cols.map((c) => ({
      title: c.title || c.field || '列',
      field: c.field || '',
      width: Number(c.width || 60),
      align: c.align || 'left',
      colspan: 1,
      rowspan: 1,
    }));
  }

  function compactColumnsForEditor(cols: any[]) {
    return normalizeColumns(cols).map((c) => ({
      title: c.title,
      field: c.field,
      width: c.width,
      align: c.align,
    }));
  }

  function onColumnsEditorFocus() {
    columnsEditorFocused.value = true;
  }

  function onColumnsEditorBlur() {
    columnsEditorFocused.value = false;
  }

  function getTableSimpleFormatter() {
    // 与 qhmesProvider.ts 保持一致：多级分组按 groupFields 顺序计算 rowspan
    return `
function(t,e,printData){
  var opts = (t && t.options) ? t.options : {};
  var list = printData && Array.isArray(printData[opts.field || 'table']) ? printData[opts.field || 'table'] : [];
  var globalCols = printData && Array.isArray(printData.__qhmesTableColumns) ? printData.__qhmesTableColumns : [];
  var columns = Array.isArray(opts.columns) && opts.columns.length ? opts.columns : (globalCols.length ? globalCols : [
    { title: '物料', field: 'name', width: 90, align: 'left' },
    { title: '数量', field: 'qty', width: 45, align: 'right' },
    { title: '金额', field: 'amount', width: 45, align: 'right' }
  ]);
  var globalGroups = printData && Array.isArray(printData.__qhmesGroupFields) ? printData.__qhmesGroupFields : [];
  var groupFields = Array.isArray(opts.groupFields) && opts.groupFields.length ? opts.groupFields : globalGroups;
  var style = opts.__qhmesStyle || {};
  var fontSize = style.fontSize || 10;
  var borderColor = style.borderColor || '#000';
  var borderWidth = style.borderWidth || 1;
  var cellPadding = style.cellPadding || '2pt 4pt';
  var headerBg = style.headerBg || '';
  var tableWidth = style.tableWidth || '100%';
  function esc(v){
    if (v === null || v === undefined) return '';
    return String(v).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
  }
  function isGroupCol(field){ return groupFields.indexOf(field) > -1; }
  var rowspanMap = {};
  for (var c=0;c<columns.length;c++){ var f = columns[c].field; rowspanMap[f] = new Array(list.length).fill(1); }
  for (var g=0; g<groupFields.length; g++){
    var gf = groupFields[g];
    // 分组字段可能不在 columns 中，需先兜底初始化，避免 rowspanMap[gf] 未定义报错
    if (!rowspanMap[gf]) rowspanMap[gf] = new Array(list.length).fill(1);
    var i = 0;
    while(i < list.length){
      var j = i + 1;
      while(j < list.length){
        var upperOk = true;
        for (var up=0; up<g; up++){
          var uf = groupFields[up];
          if ((list[j][uf] ?? '') !== (list[i][uf] ?? '')) { upperOk = false; break; }
        }
        if (!upperOk) break;
        if ((list[j][gf] ?? '') === (list[i][gf] ?? '')) j++; else break;
      }
      var span = j - i;
      if (!rowspanMap[gf]) rowspanMap[gf] = new Array(list.length).fill(1);
      rowspanMap[gf][i] = span;
      for (var k=i+1;k<j;k++) rowspanMap[gf][k] = 0;
      i = j;
    }
  }
  var html = '<table style="width:'+tableWidth+';border-collapse:collapse;table-layout:fixed;font-size:'+fontSize+'pt;">';
  html += '<thead><tr>';
  for (var h=0;h<columns.length;h++){
    var hc = columns[h];
    var hw = hc.width ? ('width:'+hc.width+'pt;') : '';
    var hbg = headerBg ? ('background:'+headerBg+';') : '';
    html += '<th style="border:'+borderWidth+'px solid '+borderColor+';padding:'+cellPadding+';text-align:center;'+hbg+hw+'">'+esc(hc.title || hc.field || '')+'</th>';
  }
  html += '</tr></thead><tbody>';
  for (var r=0;r<list.length;r++){
    html += '<tr>';
    for (var cc=0;cc<columns.length;cc++){
      var col = columns[cc];
      var field = col.field;
      var align = col.align || 'left';
      if (isGroupCol(field)){
        var rs = rowspanMap[field][r];
        if (rs > 0){
          html += '<td rowspan="'+rs+'" style="border:'+borderWidth+'px solid '+borderColor+';padding:'+cellPadding+';text-align:center;vertical-align:middle;">'+esc(list[r][field])+'</td>';
        }
      } else {
        html += '<td style="border:'+borderWidth+'px solid '+borderColor+';padding:'+cellPadding+';text-align:'+align+';">'+esc(list[r][field])+'</td>';
      }
    }
    html += '</tr>';
  }
  html += '</tbody></table>';
  return html;
}
    `;
  }

  function parseTemplateObjectFromInstance(): any {
    if (!hiprintTemplate) return null;
    const json = hiprintTemplate.getJson();
    if (typeof json === 'string') {
      try {
        return JSON.parse(json);
      } catch {
        return null;
      }
    }
    return json || null;
  }

  function extractColumnsFromElement(el: any) {
    if (Array.isArray(el?.options?.columns) && el.options.columns.length) {
      return compactColumnsForEditor(el.options.columns);
    }
    if (Array.isArray(el?.columns) && el.columns.length) {
      const headerRow = Array.isArray(el.columns[0]) ? el.columns[0] : [];
      if (headerRow.length) {
        return compactColumnsForEditor(headerRow);
      }
    }
    return [];
  }

  function extractGroupFieldsFromElement(el: any) {
    if (Array.isArray(el?.options?.groupFields)) {
      return [...el.options.groupFields];
    }
    return [];
  }

  function findCurrentTableElementFromCanvas() {
    if (!hiprintTemplate) return null;
    const templateObj = parseTemplateObjectFromInstance();
    if (!templateObj) return null;
    try {
      const selected = hiprintTemplate.getSelectEls?.();
      if (Array.isArray(selected) && selected.length > 0) {
        const hit = selected.find((el: any) => {
          const tid = el?.tid;
          const type = el?.type;
          return tid === 'qhmesModule.tableSimple' || tid === 'defaultModule.table' || type === 'table' || type === 'html';
        });
        if (hit) return hit;
      }
    } catch (e) {
      console.warn(e);
    }
    const visited = new Set<any>();
    const walk = (node: any): any => {
      if (!node || typeof node !== 'object' || visited.has(node)) return null;
      visited.add(node);
      const isTableLike =
        node?.tid === 'qhmesModule.tableSimple' ||
        node?.tid === 'defaultModule.table' ||
        node?.type === 'table' ||
        (node?.type === 'html' && typeof node?.options?.formatter === 'string' && node.options.formatter.includes('<table'));
      if (isTableLike) return node;
      for (const k of Object.keys(node)) {
        const v = node[k];
        if (Array.isArray(v)) {
          for (const item of v) {
            const found = walk(item);
            if (found) return found;
          }
        } else if (v && typeof v === 'object') {
          const found = walk(v);
          if (found) return found;
        }
      }
      return null;
    };
    return walk(templateObj);
  }

  function findAllTableElementsFromCanvas() {
    if (!hiprintTemplate) return [];
    const templateObj = parseTemplateObjectFromInstance();
    if (!templateObj) return [];
    const result: any[] = [];
    const visited = new Set<any>();
    const walk = (node: any) => {
      if (!node || typeof node !== 'object' || visited.has(node)) return;
      visited.add(node);
      const isTableLike =
        node?.tid === 'qhmesModule.tableSimple' ||
        node?.tid === 'defaultModule.table' ||
        node?.type === 'table' ||
        (node?.type === 'html' && typeof node?.options?.formatter === 'string' && node.options.formatter.includes('<table'));
      if (isTableLike) result.push(node);
      Object.keys(node).forEach((k) => {
        const v = node[k];
        if (Array.isArray(v)) v.forEach((it) => walk(it));
        else if (v && typeof v === 'object') walk(v);
      });
    };
    walk(templateObj);
    return result;
  }

  function pickBestHeaderSource(elements: any[]) {
    if (!Array.isArray(elements) || elements.length === 0) return null;
    const score = (el: any) => {
      const cols = extractColumnsFromElement(el);
      if (!cols.length) return -1;
      let s = cols.length; // 列越多越优先
      cols.forEach((c: any) => {
        const t = String(c?.title || '');
        const f = String(c?.field || '');
        if (t && f && t !== f) s += 3; // 标题与字段不同，说明是人工表头
        if (/[^\x00-\x7F]/.test(t)) s += 4; // 含中文再加权
      });
      return s;
    };
    return [...elements].sort((a, b) => score(b) - score(a))[0];
  }

  function patchTableSimpleElements(templateObj: any): number {
    if (!templateObj) return 0;

    let parsedCols: any[] = [];
    try {
      parsedCols = JSON.parse(tableColumnsJson.value || '[]');
    } catch {
      parsedCols = [];
    }
    const mergedCols = normalizeColumns(parsedCols).map((c) => ({
      title: c.title,
      field: c.field,
      width: c.width,
      align: c.align,
    }));
    const mergedGroups = groupEnabled.value ? [...groupFields.value] : [];
    let changedCount = 0;
    let candidateCount = 0;

    const visited = new Set<any>();
    const walk = (node: any) => {
      if (!node || typeof node !== 'object' || visited.has(node)) return;
      visited.add(node);

      const hasOptionsObj = node.options && typeof node.options === 'object';
      // 安全策略：只处理系统托管组件，避免覆盖用户已有明细组件
      const isManagedTableElement =
        node?.tid === 'qhmesModule.tableSimple' || (hasOptionsObj && node.options.__qhmesManaged === true);

      if (isManagedTableElement) {
        candidateCount++;
        node.type = 'html';
        node.tid = 'qhmesModule.tableSimple';
        node.options = node.options || {};
        node.options.__qhmesManaged = true;
        node.options.field = node.options.field || 'table';
        node.options.columns = mergedCols;
        node.options.groupFields = mergedGroups;
        node.options.formatter = getTableSimpleFormatter();
        changedCount++;
      }

      // 递归所有子属性（兼容 printPanels/panels/printElements 等不同结构）
      Object.keys(node).forEach((k) => {
        const v = node[k];
        if (Array.isArray(v)) {
          v.forEach((item) => walk(item));
        } else if (v && typeof v === 'object') {
          walk(v);
        }
      });
    };

    walk(templateObj);
    (templateObj as any).__qhmesPatchStat = { changedCount, candidateCount };
    return changedCount;
  }

  function syncExistingTableSimpleElements(showTip = false) {
    if (!hiprintTemplate) return;
    const templateObj = parseTemplateObjectFromInstance();
    if (!templateObj) return;
    const changedCount = patchTableSimpleElements(templateObj);
    const stat = (templateObj as any).__qhmesPatchStat || { changedCount, candidateCount: 0 };
    if (changedCount > 0) {
      try {
        hiprintTemplate.update(templateObj);
        if (showTip) {
          createMessage.success(`已同步 ${changedCount} 个明细组件分组配置（识别候选 ${stat.candidateCount}）`);
        }
      } catch (e) {
        console.warn(e);
      }
    } else if (showTip) {
      createMessage.warning('未识别到系统托管的分组明细组件，请先拖入“普通明细表(默认列)”后再应用');
    }
  }

  function syncColumnsConfigFromCanvas() {
    if (!hiprintTemplate || columnsEditorFocused.value) return;
    const templateObj = parseTemplateObjectFromInstance();
    if (!templateObj) return;

    // 优先取当前选中的元素
    let targetEl: any = null;
    try {
      const selected = hiprintTemplate.getSelectEls?.();
      if (Array.isArray(selected) && selected.length > 0) {
        targetEl = selected.find((el: any) => {
          const tid = el?.tid;
          const type = el?.type;
          return tid === 'qhmesModule.tableSimple' || tid === 'defaultModule.table' || type === 'table' || type === 'html';
        });
      }
    } catch (e) {
      console.warn(e);
    }

    // 若无选中，则取模板中第一个可识别明细组件
    if (!targetEl) {
      const visited = new Set<any>();
      const walk = (node: any): any => {
        if (!node || typeof node !== 'object' || visited.has(node)) return null;
        visited.add(node);
        const isTableLike =
          node?.tid === 'qhmesModule.tableSimple' ||
          node?.tid === 'defaultModule.table' ||
          node?.type === 'table' ||
          (node?.type === 'html' && typeof node?.options?.formatter === 'string' && node.options.formatter.includes('<table'));
        if (isTableLike) return node;
        for (const k of Object.keys(node)) {
          const v = node[k];
          if (Array.isArray(v)) {
            for (const item of v) {
              const found = walk(item);
              if (found) return found;
            }
          } else if (v && typeof v === 'object') {
            const found = walk(v);
            if (found) return found;
          }
        }
        return null;
      };
      targetEl = walk(templateObj);
    }

    if (!targetEl) return;

    const cols = extractColumnsFromElement(targetEl);
    if (cols.length === 0) return;
    const nextJson = JSON.stringify(cols, null, 2);
    const nextKey = JSON.stringify(cols);
    if (nextKey !== lastSyncedColumnsKey.value) {
      lastSyncedColumnsKey.value = nextKey;
      tableColumnsJson.value = nextJson;
      const gfs = extractGroupFieldsFromElement(targetEl);
      if (gfs.length > 0) {
        groupEnabled.value = true;
        groupFields.value = gfs;
      }
    }
  }

  function buildPreviewTemplateWithGrouping() {
    const templateObj = parseTemplateObjectFromInstance();
    if (!templateObj) return null;
    // 仅用于预览的临时模板，避免污染设计器当前画布
    const previewTemplate = JSON.parse(JSON.stringify(templateObj));

    let parsedCols: any[] = [];
    try {
      parsedCols = JSON.parse(tableColumnsJson.value || '[]');
    } catch {
      parsedCols = [];
    }
    const mergedCols = normalizeColumns(parsedCols).map((c) => ({
      title: c.title,
      field: c.field,
      width: c.width,
      align: c.align,
    }));
    const mergedGroups = groupEnabled.value ? [...groupFields.value] : [];

    const extractColumnsFromElement = (el: any) => {
      // 1) 新结构：options.columns
      if (Array.isArray(el?.options?.columns) && el.options.columns.length) {
        return el.options.columns.map((c: any) => ({
          title: c.title || c.field || '列',
          field: c.field || '',
          width: Number(c.width || 60),
          align: c.align || 'left',
        }));
      }
      // 2) 旧 table 结构：element.columns（二维表头）
      if (Array.isArray(el?.columns) && el.columns.length) {
        const headerRow = Array.isArray(el.columns[0]) ? el.columns[0] : [];
        if (headerRow.length) {
          return headerRow.map((c: any) => ({
            title: c.title || c.field || '列',
            field: c.field || '',
            width: Number(c.width || 60),
            align: c.align || 'left',
          }));
        }
      }
      return [];
    };

    const visited = new Set<any>();
    const walk = (node: any) => {
      if (!node || typeof node !== 'object' || visited.has(node)) return;
      visited.add(node);

      // 预览阶段宽松匹配：尽可能覆盖用户已有明细表组件
      const hasOptionsObj = node.options && typeof node.options === 'object';
      const maybeTableElement =
        node?.tid === 'qhmesModule.tableSimple' ||
        node?.tid === 'defaultModule.table' ||
        node?.type === 'table' ||
        (hasOptionsObj && Array.isArray(node.options.columns) && (node.options.field === 'table' || !node.options.field));

      if (maybeTableElement) {
        const originOptions = node.options || {};
        const originColumns = extractColumnsFromElement(node);
        node.type = 'html';
        node.tid = 'qhmesModule.tableSimple';
        node.options = node.options || {};
        node.options.__qhmesManaged = true;
        node.options.field = node.options.field || 'table';
        // 优先保留用户已有列定义（包括旧 table 的 element.columns），其次才用可视化配置列
        if (originColumns.length > 0) {
          node.options.columns = originColumns;
        } else if (!Array.isArray(node.options.columns) || node.options.columns.length === 0) {
          node.options.columns = mergedCols;
        }
        node.options.groupFields = mergedGroups;
        node.options.__qhmesStyle = {
          fontSize: originOptions.fontSize,
          borderColor: originOptions.borderColor,
          borderWidth: originOptions.borderWidth,
          cellPadding: originOptions.cellPadding,
          headerBg: originOptions.headerBg,
          tableWidth: originOptions.tableWidth,
        };
        node.options.formatter = getTableSimpleFormatter();
      }

      Object.keys(node).forEach((k) => {
        const v = node[k];
        if (Array.isArray(v)) v.forEach((it) => walk(it));
        else if (v && typeof v === 'object') walk(v);
      });
    };

    walk(previewTemplate);
    return previewTemplate;
  }

  function applyTableColumnsConfig(silent = false) {
    let cols: SimpleColumn[] = [];
    try {
      const parsed = JSON.parse(tableColumnsJson.value || '[]');
      if (!Array.isArray(parsed) || parsed.length === 0) {
        createMessage.warning('列配置必须是非空数组');
        return;
      }
      cols = parsed;
    } catch {
      createMessage.error('明细列配置 JSON 格式错误');
      return;
    }

    try {
      // 动态更新 provider 元素定义：后续拖入的“普通明细表”会使用新列配置
      hiprint.updateElementType('qhmesModule.tableSimple', (type: any) => {
        const mergedCols = normalizeColumns(cols).map((col) => {
          if (groupEnabled.value && groupFields.value.includes(col.field)) {
            return { ...col, align: 'center' };
          }
          return col;
        });
        type.options = type.options || {};
        type.options.columns = mergedCols.map((c) => ({
          title: c.title,
          field: c.field,
          width: c.width,
          align: c.align,
        }));
        type.options.groupFields = groupEnabled.value ? [...groupFields.value] : [];
        return type;
      });
      // 同步到当前画布上已存在的“普通明细表”组件，避免只影响新拖拽组件
      syncExistingTableSimpleElements(!silent);
      if (!silent) {
        createMessage.success('已更新普通明细表列与分组配置');
      }
    } catch (e) {
      console.warn(e);
      createMessage.error('更新组件列配置失败');
    }
  }

  async function buildColumnsFromSampleData() {
    const data = parsePrintData();
    const firstRow = Array.isArray(data?.table) && data.table.length > 0 ? data.table[0] : null;
    if (!firstRow || Object.prototype.toString.call(firstRow) !== '[object Object]') {
      createMessage.warning('预览数据中未找到 table[0] 对象，无法推导');
      return;
    }
    // 先强制提交右侧属性面板输入框，避免“已编辑但未提交”导致仍读到旧值
    const commitPropertyPanelEdits = async () => {
      try {
        const container = document.querySelector('#PrintElementOptionSetting');
        if (!container) return;
        const els = Array.from(
          container.querySelectorAll('input,textarea,select,.auto-submit')
        ) as Array<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement | Element>;
        els.forEach((el: any) => {
          try {
            if (typeof el.dispatchEvent === 'function') {
              el.dispatchEvent(new Event('input', { bubbles: true }));
              el.dispatchEvent(new Event('change', { bubbles: true }));
              el.dispatchEvent(new FocusEvent('blur', { bubbles: true }));
            }
          } catch (e) {
            console.warn(e);
          }
        });
        await nextTick();
        await new Promise((resolve) => setTimeout(resolve, 80));
      } catch (e) {
        console.warn(e);
      }
    };
    await commitPropertyPanelEdits();

    // title 优先取画布里“最像人工表头”的明细组件
    const selectedEl = findCurrentTableElementFromCanvas();
    const allEls = findAllTableElementsFromCanvas();
    const bestEl = selectedEl || pickBestHeaderSource(allEls);
    const existingCols = bestEl ? extractColumnsFromElement(bestEl) : [];
    const titleMap = new Map<string, string>();
    existingCols.forEach((c: any) => {
      if (c?.field && c?.title) titleMap.set(c.field, c.title);
    });

    // 一级优先：从右侧属性面板读取“标题”输入框（用户刚编辑但尚未回写时也能取到）
    const readTitleFromPropertyPanel = () => {
      try {
        const container = document.querySelector('#PrintElementOptionSetting');
        if (!container) return [] as string[];
        const isColorLike = (v: string) => {
          const s = (v || '').trim().toLowerCase();
          return (
            /^#([0-9a-f]{3}|[0-9a-f]{6}|[0-9a-f]{8})$/.test(s) ||
            /^rgba?\(/.test(s) ||
            /^hsla?\(/.test(s) ||
            /^(transparent|inherit|initial|unset|currentcolor)$/.test(s)
          );
        };
        const rows = Array.from(container.querySelectorAll('.hiprint-option-item'));
        const titles: string[] = [];
        rows.forEach((row) => {
          const labelEl = row.querySelector('.hiprint-option-item-label');
          const label = (labelEl?.textContent || '').trim();
          if (!label || !/(标题|title)/i.test(label)) return;
          const inputEl =
            (row.querySelector('input.auto-submit') as HTMLInputElement | null) ||
            (row.querySelector('input') as HTMLInputElement | null) ||
            (row.querySelector('textarea.auto-submit') as HTMLTextAreaElement | null) ||
            (row.querySelector('textarea') as HTMLTextAreaElement | null);
          const v = (inputEl?.value || '').trim();
          if (v && !isColorLike(v)) titles.push(v);
        });
        // 兜底：如果上面没抓到，直接从属性面板抓取可见文本输入值（按顺序）
        if (titles.length === 0) {
          const allInputs = Array.from(
            container.querySelectorAll('input[type="text"], input:not([type]), textarea')
          ) as Array<HTMLInputElement | HTMLTextAreaElement>;
          allInputs.forEach((el) => {
            const v = (el.value || '').trim();
            if (!v) return;
            // 过滤明显不是标题的值：纯数字、字段变量名样式
            if (/^\d+(\.\d+)?$/.test(v)) return;
            if (/^[a-zA-Z_][a-zA-Z0-9_.]*$/.test(v)) return;
            if (isColorLike(v)) return;
            titles.push(v);
          });
        }
        return titles;
      } catch (e) {
        console.warn(e);
        return [] as string[];
      }
    };

    const panelTitles = readTitleFromPropertyPanel();
    if (panelTitles.length > 0) {
      // 优先按当前组件列顺序映射
      if (existingCols.length > 0 && panelTitles.length >= existingCols.length) {
        existingCols.forEach((c: any, idx: number) => {
          const t = panelTitles[idx];
          if (c?.field && t) titleMap.set(c.field, t);
        });
      } else {
        // 组件列拿不到时，按预览数据字段顺序映射
        Object.keys(firstRow).forEach((k, idx) => {
          const t = panelTitles[idx];
          if (t) titleMap.set(k, t);
        });
      }
    }
    // 二级兜底：使用当前文本框已有 title（用户手动维护过时）
    try {
      const currentCols = JSON.parse(tableColumnsJson.value || '[]');
      if (Array.isArray(currentCols)) {
        currentCols.forEach((c: any) => {
          if (c?.field && c?.title && !titleMap.has(c.field)) {
            titleMap.set(c.field, c.title);
          }
        });
      }
    } catch (e) {
      console.warn(e);
    }
    // 三级兜底：从画布可见表头 DOM 抓取标题（解决 title 一直是英文变量名）
    const readHeaderTitlesFromCanvas = () => {
      try {
        const tables = Array.from(document.querySelectorAll('#hiprint-printTemplate table'));
        let best: string[] = [];
        tables.forEach((tb) => {
          const ths = Array.from(tb.querySelectorAll('thead th'));
          const titles = ths
            .map((th) => (th.textContent || '').trim())
            .filter((t) => !!t);
          if (titles.length > best.length) best = titles;
        });
        return best;
      } catch (e) {
        console.warn(e);
        return [] as string[];
      }
    };

    const keys = Object.keys(firstRow);
    const allEnglishLike = keys.every((k) => {
      const t = titleMap.get(k);
      return !t || t === k;
    });
    if (allEnglishLike) {
      const domTitles = readHeaderTitlesFromCanvas();
      if (domTitles.length > 0) {
        // 优先按 existingCols 字段顺序映射（更接近画布列顺序）
        if (existingCols.length > 0 && domTitles.length === existingCols.length) {
          existingCols.forEach((c: any, idx: number) => {
            if (c?.field && domTitles[idx]) {
              titleMap.set(c.field, domTitles[idx]);
            }
          });
        } else if (domTitles.length === keys.length) {
          // 次选：按预览数据字段顺序映射
          keys.forEach((k, idx) => {
            if (domTitles[idx]) titleMap.set(k, domTitles[idx]);
          });
        }
      }
    }
    // 最终兜底：常见字段中文名映射（确保按钮点击后可见变化）
    const zhFallback: Record<string, string> = {
      fbillno: '单号',
      billno: '单号',
      orderno: '订单号',
      order_no: '订单号',
      name: '物料',
      material: '物料',
      qty: '数量',
      quantity: '数量',
      amount: '金额',
      unit: '单位',
      price: '单价',
      total: '合计',
    };
    const cols = keys.map((k) => ({
      title: titleMap.get(k) || zhFallback[k.toLowerCase()] || k,
      field: k,
      width: 60,
      align: typeof firstRow[k] === 'number' ? 'right' : 'left',
    }));
    tableColumnsJson.value = JSON.stringify(cols, null, 2);
    createMessage.success('已根据 table[0] 推导列配置');
  }

  function buildGroupedRowsVisualMerge(rows: any[], groups: string[]) {
    if (!Array.isArray(rows) || rows.length === 0 || !Array.isArray(groups) || groups.length === 0) {
      return rows;
    }
    const out = rows.map((r) => ({ ...r }));
    for (let i = 1; i < out.length; i++) {
      for (let level = 0; level < groups.length; level++) {
        const field = groups[level];
        // 上层一致才比较当前层
        let upperSame = true;
        for (let up = 0; up < level; up++) {
          const upField = groups[up];
          if ((out[i][upField] ?? '') !== (out[i - 1][upField] ?? '')) {
            upperSame = false;
            break;
          }
        }
        if (!upperSame) break;
        if ((out[i][field] ?? '') === (out[i - 1][field] ?? '')) {
          // 保真模式下只清空重复值，保持原组件样式不变
          out[i][field] = '';
        } else {
          break;
        }
      }
    }
    return out;
  }

  function parseTemplatePayload(str: string | undefined) {
    if (!str || str === '{}') {
      return { template: {}, ext: null as any };
    }
    try {
      const obj = JSON.parse(str);
      // 兼容历史格式：直接存 template json
      if (obj?.printPanels || obj?.panels) {
        return { template: obj, ext: null as any };
      }
      // 新格式：{ template, ext }
      if (obj?.template && (obj.template.printPanels || obj.template.panels || Object.keys(obj.template).length >= 0)) {
        return { template: obj.template, ext: obj.ext || null };
      }
      return { template: obj, ext: null as any };
    } catch {
      createMessage.warning('模板 JSON 解析失败，已使用空白模板');
      return { template: {}, ext: null as any };
    }
  }

  async function bootDesigner() {
    const id = route.query.id as string;
    if (!id) {
      createMessage.error('缺少模板 id');
      goBack();
      return;
    }
    const record = await queryById(id);
    if (!record?.id) {
      createMessage.error('模板不存在');
      goBack();
      return;
    }
    tplName.value = record.templateName || record.templateCode || '';

    initHiprintRuntime();
    destroyDesigner();
    await nextTick();

    hiprint.PrintElementTypeManager.buildByHtml($('.ep-draggable-item'));
    // 兼容 a-collapse-panel 延迟渲染：初始化后再补一次，确保所有拖拽项都绑定到拖拽事件上
    setTimeout(() => {
      hiprint.PrintElementTypeManager.buildByHtml($('.ep-draggable-item'));
    }, 300);

    const payload = parseTemplatePayload(record.templateJson);
    const template = payload.template;
    if (payload.ext) {
      if (typeof payload.ext.printDataJson === 'string' && payload.ext.printDataJson.trim()) {
        printDataJson.value = payload.ext.printDataJson;
      }
      if (typeof payload.ext.tableColumnsJson === 'string') {
        tableColumnsJson.value = payload.ext.tableColumnsJson;
      }
      groupEnabled.value = !!payload.ext.groupEnabled;
      if (Array.isArray(payload.ext.groupFields)) {
        groupFields.value = payload.ext.groupFields;
      }
    }
    hiprintTemplate = new hiprint.PrintTemplate({
      template,
      settingContainer: '#PrintElementOptionSetting',
      paginationContainer: '.hiprint-printPagination',
      history: true,
    });
    hiprintTemplate.design('#hiprint-printTemplate');
    applyFieldsToTemplate();
    applyTableColumnsConfig(true);
    // 实时从画布反向同步列配置（不打断手输）
    if (columnsSyncTimer) clearInterval(columnsSyncTimer);
    columnsSyncTimer = setInterval(() => {
      syncColumnsConfigFromCanvas();
    }, 800);
  }

  function goBack() {
    router.push('/print/template');
  }

  async function handleSave() {
    if (!hiprintTemplate || !route.query.id) {
      return;
    }
    const json = hiprintTemplate.getJson();
    let templateObj: any = {};
    if (typeof json === 'string') {
      try {
        templateObj = JSON.parse(json);
      } catch {
        templateObj = {};
      }
    } else {
      templateObj = json;
    }
    const payload = {
      template: templateObj,
      ext: {
        printDataJson: printDataJson.value,
        tableColumnsJson: tableColumnsJson.value,
        groupEnabled: groupEnabled.value,
        groupFields: groupFields.value,
      },
    };
    const templateJson = JSON.stringify(payload);
    saving.value = true;
    try {
      await saveJson({ id: route.query.id as string, templateJson });
      createMessage.success('模板已保存');
    } finally {
      saving.value = false;
    }
  }

  function handlePreview() {
    if (!hiprintTemplate) {
      return;
    }
    const data = parsePrintData();
    if (preserveDesignStyle.value) {
      // 样式优先：不改模板结构，只改分组字段的显示数据
      if (groupEnabled.value && Array.isArray(data.table) && groupFields.value.length > 0) {
        data.table = buildGroupedRowsVisualMerge(data.table, groupFields.value);
      }
      hiprintTemplate.print(data, {}, { callback: () => {} });
      return;
    }
    // 兜底：即使元素 options 未同步，预览时也从全局参数读取列和分组配置
    let parsedCols: any[] = [];
    try {
      parsedCols = JSON.parse(tableColumnsJson.value || '[]');
    } catch {
      parsedCols = [];
    }
    data.__qhmesTableColumns = normalizeColumns(parsedCols).map((c) => ({
      title: c.title,
      field: c.field,
      width: c.width,
      align: c.align,
    }));
    data.__qhmesGroupFields = groupEnabled.value ? [...groupFields.value] : [];
    const previewTemplate = buildPreviewTemplateWithGrouping();
    if (previewTemplate) {
      const previewPrintTpl = new (hiprint as any).PrintTemplate({ template: previewTemplate });
      previewPrintTpl.print(data, {}, { callback: () => {} });
      return;
    }
    hiprintTemplate.print(data, {}, { callback: () => {} });
  }

  watch(
    () => printDataJson.value,
    () => {
      // 输入过程中做一个简单防抖
      if (fieldsTimer) {
        clearTimeout(fieldsTimer);
      }
      fieldsTimer = setTimeout(() => {
        applyFieldsToTemplate();
      }, 400);
    }
  );

  // 分组配置不再自动覆盖画布组件，避免误改用户自定义明细

  // 首次进入与 query.id 变化时加载（避免 onMounted + watch 重复初始化）
  watch(
    () => route.query.id as string,
    (id) => {
      if (id) {
        bootDesigner();
      }
    },
    { immediate: true }
  );

  onUnmounted(() => {
    if (fieldsTimer) {
      clearTimeout(fieldsTimer);
      fieldsTimer = null;
    }
    if (columnsSyncTimer) {
      clearInterval(columnsSyncTimer);
      columnsSyncTimer = null;
    }
    destroyDesigner();
  });
</script>

<style scoped lang="less">
  .hiprint-card {
    min-height: calc(100vh - 120px);
  }

  .designer-row {
    min-height: 520px;
  }

  .col-title {
    font-weight: 600;
    margin-bottom: 8px;
  }

  .left-col .ep-draggable-item {
    padding: 8px 10px;
    margin-bottom: 6px;
    border: 1px dashed #d9d9d9;
    border-radius: 4px;
    text-align: center;
    cursor: move;
    background: #fafafa;
    font-size: 12px;
    user-select: none;
  }

  .help-tip,
  .section-tip {
    font-size: 12px;
    color: rgba(0, 0, 0, 0.65);
    margin: 6px 0 0;
    line-height: 18px;
  }

  .center-col {
    border: 1px solid #f0f0f0;
    border-radius: 4px;
    padding: 8px;
    background: #fff;
    min-height: 520px;
    overflow: auto;
  }

  .right-col {
    border: 1px solid #f0f0f0;
    border-radius: 4px;
    padding: 8px;
    background: #fff;
    min-height: 520px;
    overflow: auto;
  }

  #hiprint-printTemplate {
    min-height: 400px;
  }

  .mb-2 {
    margin-bottom: 8px;
  }
</style>
