<template>
  <a-modal
    v-model:open="visible"
    :title="`发起钉钉审批 · ${tplData?.templateName || ''}`"
    :width="multiRow ? 1120 : 940"
    :confirm-loading="submitting"
    ok-text="发起审批"
    cancel-text="取消"
    destroy-on-close
    :body-style="{ padding: 0 }"
    @ok="handleSubmit"
    @cancel="visible = false"
  >
    <div class="dal-body">
      <!-- ══════════ 左侧：审批流时间轴 ══════════ -->
      <div class="dal-timeline-panel">
        <div class="dal-panel-title">审批流程</div>

        <div v-if="!selectedFlowId" class="dal-timeline-empty">
          <div class="dal-timeline-empty-icon">🔗</div>
          <div>请在右侧「审批流配置」<br>页签中选择审批流</div>
        </div>

        <a-spin v-else-if="previewLoading" style="display:flex;justify-content:center;padding:32px 0" />

        <div v-else class="dal-timeline">
          <div class="dal-ts-step">
            <div class="dal-ts-left">
              <div class="dal-ts-dot dal-ts-dot--start"></div>
              <div class="dal-ts-line" v-if="approverPreview.length > 0"></div>
            </div>
            <div class="dal-ts-content">
              <div class="dal-ts-name">发起人</div>
              <div class="dal-ts-sub">所有人可发起</div>
            </div>
          </div>

          <div v-for="(node, ni) in approverPreview" :key="node.nodeId || ni" class="dal-ts-step">
            <div class="dal-ts-left">
              <div class="dal-ts-dot"
                :class="[node.nodeType==='cc'?'dal-ts-dot--cc':'dal-ts-dot--approver', !node.allResolved?'dal-ts-dot--warn':'']">
              </div>
              <div class="dal-ts-line" v-if="ni < approverPreview.length - 1"></div>
            </div>
            <div class="dal-ts-content">
              <div class="dal-ts-tags">
                <span class="dal-ts-badge" :class="node.nodeType==='cc'?'dal-ts-badge--cc':'dal-ts-badge--approver'">
                  {{ node.nodeType==='cc'?'抄送':'审批' }}
                </span>
                <span v-if="node.nodeType!=='cc'" class="dal-ts-mode">{{ modeLabel(node.multiMode) }}</span>
              </div>
              <div class="dal-ts-name">{{ node.nodeName }}</div>
              <div class="dal-ts-users">
                <template v-for="(u, ui) in node.users" :key="u.username">
                  <span :class="u.resolved?'dal-ts-user--ok':'dal-ts-user--err'">{{ u.realname }}</span>
                  <span v-if="ui < node.users.length-1" style="color:#ccc;margin:0 2px">·</span>
                </template>
              </div>
              <div v-if="!node.allResolved" class="dal-ts-unresolved">⚠ 有未解析成员，请补充手机号</div>
            </div>
          </div>

          <div class="dal-ts-step" v-if="approverPreview.length > 0">
            <div class="dal-ts-left"><div class="dal-ts-dot dal-ts-dot--end"></div></div>
            <div class="dal-ts-content"><div class="dal-ts-name" style="color:#888">结束</div></div>
          </div>
        </div>
      </div>

      <div class="dal-panel-divider"></div>

      <!-- ══════════ 中间：主内容（表单字段 + 审批流配置） ══════════ -->
      <div class="dal-content-panel">
        <a-tabs v-model:activeKey="activeTab" size="small" class="dal-tabs">

          <!-- ── 表单展示（只读） ── -->
          <a-tab-pane key="form" tab="表单字段">
            <div class="dal-form-scroll">
              <a-spin :spinning="loading" tip="加载表单字段中...">
                <a-alert v-if="loadError" type="error" :message="loadError" show-icon style="margin-bottom:12px" />
                <template v-else-if="!loading">
                  <a-alert
                    type="info"
                    show-icon
                    style="margin-bottom:12px"
                    message="字段已根据绑定配置从业务单据自动填充，不可手动修改。如需调整请先更新审批模板绑定中的字段映射。"
                  />
                  <div v-if="dingFields.length === 0" class="dal-form-empty">该模板暂无表单字段</div>
                  <template v-for="field in dingFields" :key="field.label">
                    <div v-if="field.componentName === 'TextNote'" class="dal-form-note">{{ field.label }}</div>

                    <!-- 明细表（只读展示） -->
                    <template v-else-if="field.componentName === 'TableField'">
                      <div class="dal-form-item">
                        <div class="dal-field-label" :class="{'dal-field-label--required': field.required}">{{ field.label }}</div>
                        <div class="dal-table-wrap">
                          <table class="dal-table">
                            <thead>
                              <tr>
                                <th style="width:40px;text-align:center">#</th>
                                <th v-for="child in field.children||[]" :key="child.label">{{ child.label }}</th>
                              </tr>
                            </thead>
                            <tbody>
                              <tr v-for="(row, rowIdx) in getTableRows(field.label)" :key="rowIdx">
                                <td style="text-align:center;color:#aaa">{{ rowIdx+1 }}</td>
                                <td v-for="child in field.children||[]" :key="child.label">
                                  <span class="dal-readonly-cell">{{ row[child.label] ?? '—' }}</span>
                                </td>
                              </tr>
                              <tr v-if="getTableRows(field.label).length === 0">
                                <td :colspan="(field.children?.length||0)+1" style="text-align:center;color:#bbb;padding:10px 0">暂无数据</td>
                              </tr>
                            </tbody>
                          </table>
                        </div>
                      </div>
                    </template>

                    <!-- 普通字段（只读） -->
                    <div v-else class="dal-form-item">
                      <div class="dal-field-label" :class="{'dal-field-label--required': field.required}">{{ field.label }}</div>
                      <a-input
                        :value="displayValue(field)"
                        disabled
                        class="dal-readonly-input"
                      />
                    </div>
                  </template>
                </template>
              </a-spin>
            </div>
          </a-tab-pane>

          <!-- ── 审批流配置（只读查看） ── -->
          <a-tab-pane key="flow">
            <template #tab>
              审批流配置
              <a-badge v-if="hasUnresolved" color="red" style="margin-left:4px" />
            </template>
            <div class="dal-form-scroll">
              <a-alert
                type="info"
                show-icon
                style="margin-bottom:14px"
              >
                <template #message>
                  审批流程仅供查看，不可在此修改。如需调整，请前往
                  <a class="flow-readonly-link" @click="goToTplConfig">钉钉审批模板配置</a>
                  中更改绑定的审批流。
                </template>
              </a-alert>

              <div class="flow-select-row">
                <a-select
                  v-model:value="selectedFlowId"
                  style="flex:1;min-width:0"
                  placeholder="（未绑定审批流）"
                  :loading="flowLoading"
                  :options="flowSelectOptions"
                  disabled
                >
                  <template #option="{ label, status, remark }">
                    <div class="flow-opt-item">
                      <span class="flow-opt-name">{{ label }}</span>
                      <span style="display:flex;align-items:center;gap:6px;flex-shrink:0">
                        <span v-if="remark" class="flow-opt-remark">{{ remark }}</span>
                        <a-tag :color="status==='1'?'green':status==='2'?'default':'orange'"
                          style="margin:0;font-size:11px;line-height:16px;padding:0 5px">
                          {{ status==='1'?'已发布':status==='2'?'已停用':'草稿' }}
                        </a-tag>
                      </span>
                    </div>
                  </template>
                </a-select>
              </div>

              <template v-if="selectedFlowId">
                <a-divider style="margin:14px 0 10px" />
                <div class="preview-title">
                  审批节点 · 人员解析
                  <a-spin :spinning="previewLoading" size="small" style="margin-left:8px" />
                </div>
                <div v-if="!previewLoading && approverPreview.length === 0" style="color:#bbb;font-size:12px;margin-top:6px">
                  该审批流暂无审批人节点
                </div>
                <div v-for="(node, ni) in approverPreview" :key="node.nodeId||ni" class="preview-node"
                  :class="{'preview-node--cc': node.nodeType==='cc'}">
                  <div class="preview-node-hd">
                    <a-tag :color="node.nodeType==='cc'?'blue':'orange'" style="margin:0 6px 0 0;font-size:11px">
                      {{ node.nodeType==='cc'?'抄送':'审批' }}
                    </a-tag>
                    <span class="preview-node-name">{{ node.nodeName }}</span>
                    <span class="preview-node-mode">{{ node.nodeType==='cc'?'位置自动判断':modeLabel(node.multiMode) }}</span>
                  </div>
                  <div v-for="u in node.users" :key="u.username" class="preview-user">
                    <a-tag v-if="u.resolved" color="success" style="margin:0">{{ u.realname }}（{{ u.username }}）✓</a-tag>
                    <a-tag v-else-if="u.unsupported" color="default" style="margin:0">{{ u.realname }}（不支持自动解析）</a-tag>
                    <a-tag v-else color="error" style="margin:0">{{ u.realname }}（{{ u.username }}）未找到钉钉账号</a-tag>
                  </div>
                  <div v-if="!node.allResolved" class="preview-supplement">
                    <a-input
                      v-model:value="supplementPhones[node.nodeId||String(ni)]"
                      :placeholder="node.nodeType==='cc'?'补充抄送人手机号，多个用逗号分隔':'补充审批人手机号，多个用逗号分隔'"
                      allow-clear size="small"
                    />
                    <div class="dal-field-hint" style="margin-top:3px">手机号需在企业钉钉注册，与自动解析的成员合并</div>
                  </div>
                </div>
                <a-alert v-if="hasUnresolved" type="warning" show-icon style="margin-top:10px;font-size:12px"
                  message="部分节点有未解析成员，请补充手机号后再发起审批" />
              </template>
            </div>
          </a-tab-pane>

        </a-tabs>
      </div>

      <!-- ══════════ 右侧：已选数据列表（多条时显示） ══════════ -->
      <template v-if="multiRow">
        <div class="dal-panel-divider"></div>
        <div class="dal-rows-panel">
          <div class="dal-panel-title">
            已选数据
            <span class="dal-rows-badge">{{ allRows.length }}</span>
          </div>
          <div class="dal-rows-list">
            <div
              v-for="(row, idx) in allRows"
              :key="idx"
              class="dal-row-item"
              :class="{ 'dal-row-item--active': idx === currentRowIndex }"
              @click="switchRow(idx)"
            >
              <div class="dal-row-index">{{ idx + 1 }}</div>
              <div class="dal-row-label">{{ getRowLabel(row, idx) }}</div>
            </div>
          </div>
        </div>
      </template>
    </div>
  </a-modal>
</template>

<script lang="ts" setup>
  import { ref, reactive, computed } from 'vue';
  import { useRouter } from 'vue-router';
  import { useMessage } from '/@/hooks/web/useMessage';
  import {
    getTemplateDetail,
    queryById as queryTplById,
    launchApproval,
    getApprovalFlowList,
    previewFlowApprovers,
  } from '/@/views/xslmes/dingtalk/mesXslDingProcessTpl/MesXslDingProcessTpl.api';
  import { checkCanLaunch } from '/@/views/approval/gate/approvalGate.api';

  const emit = defineEmits(['success']);
  const { createMessage } = useMessage();
  const router = useRouter();

  const visible = ref(false);
  const loading = ref(false);
  const submitting = ref(false);
  const loadError = ref('');
  const activeTab = ref('form');

  interface BindInfo { id: string; templateId: string; templateName: string; fieldMappingJson?: string; bizCode?: string }
  const tplData = ref<BindInfo | null>(null);

  // 多条数据支持
  const allRows = ref<any[]>([]);
  const currentRowIndex = ref(0);
  const multiRow = computed(() => allRows.value.length > 0);

  const dingFields = ref<any[]>([]);
  const formValues = reactive<Record<string, any>>({});
  const tableValues = reactive<Record<string, Record<string, string>[]>>({});

  const flowLoading = ref(false);
  const flowList = ref<any[]>([]);
  const selectedFlowId = ref('');
  const previewLoading = ref(false);
  const approverPreview = ref<any[]>([]);
  const supplementPhones = reactive<Record<string, string>>({});

  const hasUnresolved = computed(() => approverPreview.value.some(n => !n.allResolved));

  const flowSelectOptions = computed(() =>
    flowList.value.map(f => ({
      value: f.id, label: f.flowName || f.name,
      status: f.status, remark: f.remark || '',
    }))
  );

  function modeLabel(mode: string) {
    if (mode === 'none') return '单人';
    if (mode === 'or') return '或签';
    if (mode === 'sequence') return '依次';
    return '会签';
  }

  // ══ 打开弹窗，支持单条或多条 ══

  async function open(bindInfo: BindInfo, rowsOrRow: any[] | any) {
    tplData.value = bindInfo;
    allRows.value = Array.isArray(rowsOrRow) ? rowsOrRow : [rowsOrRow];
    currentRowIndex.value = 0;

    resetFieldValues();
    loadError.value = '';
    activeTab.value = 'form';
    selectedFlowId.value = '';
    approverPreview.value = [];
    visible.value = true;
    loading.value = true;

    try {
      const [detail, flows, tplRecord] = await Promise.all([
        getTemplateDetail(bindInfo.templateId),
        getApprovalFlowList({ pageSize: 200 }),
        queryTplById(bindInfo.templateId),
      ]);

      dingFields.value = detail?.dingFields || [];
      if (detail?.schemaError) loadError.value = detail.schemaError;
      flowList.value = flows?.records || flows || [];

      for (const f of dingFields.value) {
        if (f.componentName === 'TableField') tableValues[f.label] = [];
      }

      applyPrefillForRow(currentRowIndex.value);

      const presetFlowId = tplRecord?.flowId;
      if (presetFlowId) {
        selectedFlowId.value = presetFlowId;
        loadPreview(presetFlowId);
      }
    } catch (e: any) {
      loadError.value = e?.message || '加载模板字段失败';
    } finally {
      loading.value = false;
    }
  }

  function goToTplConfig() {
    visible.value = false;
    router.push('/xslmes/mesXslDingProcessTplList');
  }

  defineExpose({ open });

  // ══ 切换数据行 ══

  function switchRow(idx: number) {
    if (idx === currentRowIndex.value) return;
    currentRowIndex.value = idx;
    resetFieldValues();
    applyPrefillForRow(idx);
    activeTab.value = 'form';
  }

  function resetFieldValues() {
    Object.keys(formValues).forEach(k => delete formValues[k]);
    Object.keys(tableValues).forEach(k => delete tableValues[k]);
    Object.keys(supplementPhones).forEach(k => delete supplementPhones[k]);
    for (const f of dingFields.value) {
      if (f.componentName === 'TableField') tableValues[f.label] = [];
    }
  }

  function applyPrefillForRow(idx: number) {
    const rowData = allRows.value[idx];
    if (rowData && tplData.value?.fieldMappingJson) {
      applyPrefill(dingFields.value, tplData.value.fieldMappingJson, rowData);
    }
  }

  // ══ 行标签（用于右侧列表显示） ══

  const LABEL_SKIP = new Set([
    'id', 'createBy', 'createTime', 'updateBy', 'updateTime',
    'delFlag', 'sysOrgCode', 'tenantId', 'version',
  ]);

  function getRowLabel(row: any, index: number): string {
    for (const [key, val] of Object.entries(row ?? {})) {
      if (LABEL_SKIP.has(key)) continue;
      if (val && typeof val === 'string' && val.length <= 40) return val;
      if (val !== null && val !== undefined && typeof val === 'number') return String(val);
    }
    return `条目 ${index + 1}`;
  }

  // ══ 预填充 ══

  interface MappingItem {
    componentId: string;
    componentLabel: string;
    componentName: string;
    parentId?: string;
    bizField?: string;
  }

  function applyPrefill(fields: any[], mappingJson: string, rowData: any) {
    let mapping: MappingItem[] = [];
    try { mapping = JSON.parse(mappingJson); } catch { return; }

    const byId = new Map(mapping.map(m => [m.componentId, m]));

    for (const field of fields) {
      if (field.componentName === 'TextNote') continue;
      const cid = field.id || field.label;
      const m = byId.get(cid);

      if (field.componentName === 'TableField') {
        const slotName = m?.bizField;
        if (!slotName) continue;
        const arr: any[] = getNestedValue(rowData, slotName);
        if (!Array.isArray(arr) || !arr.length) continue;

        const childMappings = mapping.filter(x => x.parentId === cid && x.bizField);
        tableValues[field.label] = arr.map(element => {
          const row: Record<string, string> = {};
          for (const child of childMappings) {
            const parts = (child.bizField || '').split('.');
            const colKey = parts.slice(1).join('.');
            const val = colKey ? getNestedValue(element, colKey) : undefined;
            row[child.componentLabel] = val !== undefined && val !== null ? String(val) : '';
          }
          for (const child of (field.children || [])) {
            if (!(child.label in row)) row[child.label] = '';
          }
          return row;
        });
      } else {
        if (!m?.bizField) continue;
        const val = getNestedValue(rowData, m.bizField);
        if (val === undefined || val === null) continue;
        formValues[field.label] = formatForDisplay(val, field.componentName);
      }
    }
  }

  function getNestedValue(obj: any, path: string): any {
    if (!obj || !path) return undefined;
    return path.split('.').reduce((acc: any, k: string) => acc?.[k], obj);
  }

  function formatForDisplay(v: any, componentName: string): any {
    if (v === null || v === undefined) return '';
    if (['NumberField', 'MoneyField'].includes(componentName)) {
      return typeof v === 'number' ? v : Number(v) || 0;
    }
    if (componentName === 'DDDateField') {
      const s = String(v);
      return s.includes('T') ? s.split('T')[0] : s.split(' ')[0];
    }
    if (Array.isArray(v)) return v.join(',');
    return String(v);
  }

  function getTableRows(label: string): Record<string, string>[] {
    return tableValues[label] || [];
  }

  function displayValue(field: any): string {
    const v = formValues[field.label];
    if (v === undefined || v === null || v === '') return '';
    if (field.componentName === 'DDDateRangeField' && Array.isArray(v)) return v.join(' ~ ');
    if (field.componentName === 'DDMultiSelectField' && Array.isArray(v)) return v.join(', ');
    return String(v);
  }

  // ══ 审批流预览 ══

  async function loadPreview(flowId: string) {
    previewLoading.value = true;
    try {
      const res = await previewFlowApprovers(flowId);
      approverPreview.value = Array.isArray(res) ? res : [];
    } catch { approverPreview.value = []; }
    finally { previewLoading.value = false; }
  }

  // ══ 提交 ══

  async function handleSubmit() {
    if (!selectedFlowId.value) {
      activeTab.value = 'flow';
      createMessage.warning('请在「审批流配置」页签中选择一个审批流');
      return Promise.reject();
    }
    const unresolvedNodes = approverPreview.value.filter(n => !n.allResolved);
    for (const node of unresolvedNodes) {
      const key = node.nodeId || String(approverPreview.value.indexOf(node));
      if (!supplementPhones[key]?.trim()) {
        activeTab.value = 'flow';
        createMessage.warning(`${node.nodeType==='cc'?'抄送节点':'审批节点'}「${node.nodeName}」有未解析成员，请补充手机号`);
        return Promise.reject();
      }
    }

    const fvList: { name: string; value: string }[] = [];
    for (const field of dingFields.value) {
      if (field.componentName === 'TextNote') continue;
      const label = field.label;
      if (field.componentName === 'TableField') {
        const validRows = getTableRows(label).filter(r => Object.values(r).some(v => v !== ''));
        if (validRows.length === 0) continue;
        fvList.push({
          name: label,
          value: JSON.stringify(validRows.map(row =>
            Object.entries(row).map(([k, v]) => ({ name: k, value: String(v ?? '') }))
          )),
        });
        continue;
      }
      let val = formValues[label];
      if (field.componentName === 'DDDateRangeField' && Array.isArray(val)) {
        val = val.join('~');
      } else if (field.componentName === 'DDMultiSelectField' && Array.isArray(val)) {
        val = val.length > 0 ? JSON.stringify(val) : null;
      } else if (['InnerContactField', 'RelateField', 'DDPhotoField'].includes(field.componentName)) {
        const raw = val ? String(val).trim() : '';
        val = raw ? JSON.stringify(raw.split(',').map((s: string) => s.trim()).filter(Boolean)) : null;
      } else {
        val = val !== undefined && val !== null ? String(val) : null;
      }
      if (val === null || val === '') { if (!field.required) continue; val = ''; }
      fvList.push({ name: label, value: val as string });
    }

    const approverOverrides = Object.entries(supplementPhones)
      .filter(([, phones]) => phones?.trim())
      .map(([nodeId, phones]) => ({ nodeId, phones: phones.trim() }));

    const row = allRows.value[currentRowIndex.value];
    const flow = flowList.value.find((f) => f.id === selectedFlowId.value);
    const bizTable = flow?.bizTable;
    const bizDataId = row?.id != null ? String(row.id) : '';
    const bizTitle = getRowLabel(row, currentRowIndex.value);

    //update-begin---author:GHT ---date:2026-06-04 for：【QH-MES审批台账】钉钉发起前统一门禁校验-----
    if (bizTable && bizDataId) {
      const gate = await checkCanLaunch({ bizTable, bizDataId });
      if (!gate?.allowed) {
        createMessage.warning(gate?.reason || '当前不允许发起审批');
        return Promise.reject();
      }
    }
    //update-end---author:GHT ---date:2026-06-04 for：【QH-MES审批台账】钉钉发起前统一门禁校验-----

    submitting.value = true;
    try {
      const result = await launchApproval({
        id: tplData.value!.templateId,
        formValues: fvList,
        flowId: selectedFlowId.value,
        approverOverrides,
        bizTable,
        bizDataId,
        bizTitle,
        bizCode: tplData.value?.bizCode,
      });
      createMessage.success(typeof result === 'string' ? result : '审批发起成功！审批人将在钉钉「待我审批」中收到任务');
      visible.value = false;
      emit('success', result);
    } catch (e: any) {
      createMessage.error(e?.message || '发起失败');
      return Promise.reject(e);
    } finally {
      submitting.value = false;
    }
  }
</script>

<style lang="less" scoped>
  .dal-body {
    display: flex;
    min-height: 480px;
    max-height: 70vh;
  }

  .dal-timeline-panel {
    width: 210px;
    flex-shrink: 0;
    background: #fafafa;
    border-right: 1px solid #f0f0f0;
    padding: 16px 14px;
    overflow-y: auto;
  }

  .dal-panel-title {
    font-size: 12px;
    font-weight: 600;
    color: #888;
    letter-spacing: .5px;
    text-transform: uppercase;
    margin-bottom: 14px;
    display: flex;
    align-items: center;
    gap: 6px;
  }

  .dal-timeline-empty {
    text-align: center; color: #bbb; font-size: 12px; padding-top: 32px; line-height: 1.8;
    .dal-timeline-empty-icon { font-size: 24px; margin-bottom: 8px; }
  }

  .dal-ts-step { display: flex; align-items: flex-start; gap: 8px; }
  .dal-ts-left { display: flex; flex-direction: column; align-items: center; flex-shrink: 0; width: 12px; padding-top: 2px; }
  .dal-ts-dot {
    width: 10px; height: 10px; border-radius: 50%; flex-shrink: 0;
    position: relative; z-index: 1; border: 2px solid currentColor; background: #fff;
    &--start  { color: #1677ff; background: #1677ff; border-color: #1677ff; }
    &--end    { color: #d9d9d9; background: #d9d9d9; border-color: #d9d9d9; width: 8px; height: 8px; }
    &--approver { color: #fa8c16; }
    &--cc     { color: #1677ff; }
    &--warn   { color: #ff4d4f !important; }
  }
  .dal-ts-line { width: 2px; flex: 1; min-height: 22px; background: linear-gradient(to bottom, #e0e0e0 50%, transparent 100%); margin: 3px 0 0; }
  .dal-ts-content { flex: 1; padding-bottom: 18px; min-width: 0; }
  .dal-ts-sub { font-size: 11px; color: #aaa; }
  .dal-ts-tags { display: flex; align-items: center; gap: 4px; margin-bottom: 2px; }
  .dal-ts-badge {
    font-size: 10px; padding: 0 5px; border-radius: 3px; line-height: 16px; font-weight: 500;
    &--approver { background: #fff7e6; color: #d46b08; border: 1px solid #ffd591; }
    &--cc       { background: #e6f4ff; color: #0958d9; border: 1px solid #91caff; }
  }
  .dal-ts-mode { font-size: 10px; color: #aaa; background: #f5f5f5; padding: 0 4px; border-radius: 2px; line-height: 14px; }
  .dal-ts-name { font-size: 12px; font-weight: 500; color: #333; line-height: 1.4; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .dal-ts-users { font-size: 11px; color: #888; margin-top: 2px; line-height: 1.5; }
  .dal-ts-user--ok  { color: #52c41a; }
  .dal-ts-user--err { color: #ff4d4f; }
  .dal-ts-unresolved { font-size: 10px; color: #ff7a00; margin-top: 2px; }

  .dal-panel-divider { width: 1px; background: #f0f0f0; flex-shrink: 0; }

  .dal-content-panel {
    flex: 1; min-width: 0; display: flex; flex-direction: column; overflow: hidden;
  }

  .dal-tabs {
    height: 100%; display: flex; flex-direction: column;
    :deep(.ant-tabs-nav) { padding: 0 16px; margin-bottom: 0; }
    :deep(.ant-tabs-content-holder) { flex: 1; overflow: hidden; }
    :deep(.ant-tabs-content) { height: 100%; }
    :deep(.ant-tabs-tabpane) { height: 100%; }
  }

  .dal-form-scroll { height: 100%; overflow-y: auto; padding: 14px 18px; }

  .dal-form-item { margin-bottom: 14px; }
  .dal-form-empty { color: #bbb; text-align: center; padding: 32px 0; font-size: 13px; }

  .dal-field-label {
    font-size: 13px; color: #555; margin-bottom: 5px; font-weight: 500;
    &--required::before { content: '* '; color: #ff4d4f; }
  }

  .dal-field-hint { font-size: 11px; color: #aaa; margin-top: 3px; }

  .dal-form-note {
    background: #f8f8f8; border-left: 3px solid #ddd; padding: 6px 10px;
    font-size: 12px; color: #777; margin-bottom: 12px; border-radius: 0 4px 4px 0;
  }

  .dal-readonly-input {
    :deep(.ant-input[disabled]) {
      color: rgba(0,0,0,.75) !important;
      background: #f8f8f8 !important;
      cursor: default;
    }
  }

  .dal-readonly-cell {
    display: block;
    padding: 2px 6px;
    color: rgba(0,0,0,.75);
    font-size: 13px;
    line-height: 1.5;
  }

  .dal-table-wrap { border: 1px solid #ebebeb; border-radius: 6px; overflow: hidden; }
  .dal-table {
    width: 100%; border-collapse: collapse; font-size: 13px;
    th { background: #fafafa; padding: 7px 10px; border-bottom: 1px solid #ebebeb; font-weight: 500; color: #666; font-size: 12px; }
    td { padding: 4px 6px; border-bottom: 1px solid #f5f5f5; vertical-align: middle; }
    tr:last-child td { border-bottom: none; }
  }

  // 审批流配置
  .flow-select-row { display: flex; gap: 8px; align-items: center; margin-bottom: 8px; }

  .flow-readonly-link {
    color: inherit;
    text-decoration: none;
    border-bottom: 1px dashed currentColor;
    cursor: pointer;
    transition: color .15s;
    &:hover { color: #1677ff; }
  }

  .flow-opt-item { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
  .flow-opt-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .flow-opt-remark { font-size: 11px; color: #aaa; }

  .preview-title { font-size: 12px; font-weight: 600; color: #555; margin-bottom: 8px; display: flex; align-items: center; }
  .preview-node {
    background: #fafafa; border: 1px solid #f0f0f0; border-radius: 6px; padding: 8px 10px; margin-bottom: 8px;
    &--cc { background: #f0f7ff; border-color: #bae0ff; }
  }
  .preview-node-hd { display: flex; align-items: center; gap: 4px; margin-bottom: 6px; }
  .preview-node-name { font-size: 12px; font-weight: 500; flex: 1; }
  .preview-node-mode { font-size: 11px; color: #aaa; }
  .preview-user { margin-bottom: 4px; }
  .preview-supplement { margin-top: 8px; }

  // ══ 右侧：已选数据列表 ══
  .dal-rows-panel {
    width: 170px;
    flex-shrink: 0;
    background: #fafafa;
    padding: 16px 10px;
    overflow-y: auto;
  }

  .dal-rows-badge {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: #ff6900;
    color: #fff;
    border-radius: 10px;
    font-size: 10px;
    font-weight: 700;
    min-width: 16px;
    height: 16px;
    padding: 0 4px;
    line-height: 1;
  }

  .dal-rows-list {
    display: flex;
    flex-direction: column;
    gap: 4px;
  }

  .dal-row-item {
    display: flex;
    align-items: flex-start;
    gap: 6px;
    padding: 7px 8px;
    border-radius: 6px;
    cursor: pointer;
    border: 1px solid transparent;
    transition: all .15s;
    background: #fff;
    border-color: #f0f0f0;

    &:hover {
      border-color: #ff6900;
      background: #fff8f3;
    }

    &--active {
      border-color: #ff6900 !important;
      background: #fff3eb !important;

      .dal-row-index {
        background: #ff6900;
        color: #fff;
      }
    }
  }

  .dal-row-index {
    flex-shrink: 0;
    width: 18px;
    height: 18px;
    border-radius: 50%;
    background: #e8e8e8;
    color: #666;
    font-size: 10px;
    font-weight: 700;
    display: flex;
    align-items: center;
    justify-content: center;
    transition: all .15s;
    margin-top: 1px;
  }

  .dal-row-label {
    flex: 1;
    min-width: 0;
    font-size: 12px;
    color: #333;
    line-height: 1.5;
    word-break: break-all;
    display: -webkit-box;
    -webkit-line-clamp: 3;
    -webkit-box-orient: vertical;
    overflow: hidden;
  }
</style>
