<script setup lang="ts">
import { DRAWER_SIZE, LABEL_WIDTH } from "../../../constants/ui";
import { computed, ref } from "vue";
import { Plus, Picture } from "@element-plus/icons-vue";
import { ElMessageBox, ElImageViewer } from "element-plus";
import type { UploadFile, UploadFiles, UploadProps, UploadUserFile } from "element-plus";
import MaterialPictureCardFileItem from "./MaterialPictureCardFileItem.vue";
import { compressImageFile } from "../../../utils/compress";
import AgendaSampleImage from "../../../static/会议日程.png";
import SignInSampleImage from "../../../static/签到表.png";
import InvitationSampleImage from "../../../static/邀请函.png";
import ExpertProfileSampleImage from "../../../static/会议串场.png";
import PhotoSampleImage1 from "../../../static/专题授课.png";
import PhotoSampleImage2 from "../../../static/会议主持.png";
import PhotoSampleImage3 from "../../../static/会议讨论.png";
import PhotoSampleImage4 from "../../../static/大会主持.png";
import PhotoSampleImage5 from "../../../static/会议总结.png";
import PhotoSampleImage6 from "../../../static/会议总结2.png";
import LaborProtocolSampleImage from "../../../static/劳务费协议.png";
import LaborInvoiceSampleImage from "../../../static/劳务费发票.png";



const handleUploadChangeWithCompression = async (
  uploadFile: UploadFile,
  uploadFiles: UploadFiles,
  originalHandler?: any
) => {
  if (uploadFile.status === "ready" && uploadFile.raw && uploadFile.raw.type.startsWith("image/")) {
    try {
      uploadFile.raw = await compressImageFile(uploadFile.raw);
      uploadFile.size = uploadFile.raw.size;
    } catch (e) {
      console.warn("Image compression failed", e);
    }
  }
  if (originalHandler) {
    originalHandler(uploadFile, uploadFiles);
  }
};

const runMaterialPreview = (handler: UploadProps["onPreview"] | undefined, uploadFile: UploadUserFile) => {
  if (handler) {
    void handler(uploadFile as UploadFile);
  }
};

const runMaterialUploadRemove = (handler: UploadProps["onRemove"] | undefined, uploadFile: UploadUserFile) => {
  if (handler) {
    void handler(uploadFile as UploadFile, [] as unknown as UploadFiles);
  }
};

const extractExtDrawer = (fileName: string) => {
  const n = String(fileName || "").trim();
  const i = n.lastIndexOf(".");
  return i >= 0 ? n.slice(i).toLowerCase() : "";
};
const isPdfByNameOrKey = (name: string, ossKey: string) => {
  const key = String(ossKey || "").trim();
  const base = key.split("/").pop() || key;
  return (
    extractExtDrawer(name) === ".pdf"
    || extractExtDrawer(base) === ".pdf"
    || key.toLowerCase().endsWith(".pdf")
  );
};

const laborProtocolPdfLabel = (row: Record<string, any>) => {
  const fn = String(row?.protocolFileName || "").trim();
  if (fn) {
    return fn;
  }
  const key = String(row?.protocolOssKey || "").trim();
  const tail = key.split("/").pop() || key;
  return tail || "已上传PDF";
};

const materialDialogVisible = defineModel<boolean>("materialDialogVisible", { required: true });
const selectedModuleCode = defineModel<string>("selectedModuleCode", { required: true });
const expertSubModule = defineModel<string>("expertSubModule", { required: true });
const selectedExpertLaborAmountYuan = defineModel<number>("selectedExpertLaborAmountYuan", { required: true });

const props = defineProps<{
  materialDialogTitle: string;
  materialLoading: boolean;
  materialBudgetLoading: boolean;
  materialReviewNotice: string;
  materialBudgetSummary: Record<string, any>;
  basicForm: Record<string, any>;
  boundExpertRows: any[];
  isReviewApproved: (itemKey: string | string[]) => boolean;
  toReviewTagType: (itemKey: string | string[]) => "success" | "danger" | "info";
  toReviewResultText: (itemKey: string | string[]) => string;
  toReviewReasonText: (itemKey: string | string[]) => string;
  agendaUploadFileList: any[];
  agendaDocRows: any[];
  beforeAgendaUpload: (rawFile: any, maxSizeMb?: number) => boolean | Promise<any>;
  handleAgendaPictureChange: (uploadFile: any, uploadFiles: any, maxSizeMb?: number) => void;
  handleAgendaPicturePreview: UploadProps["onPreview"];
  handleAgendaPictureRemove: UploadProps["onRemove"];
  signInUploadFileList: any[];
  beforeSignInUpload: (rawFile: any, maxSizeMb?: number) => boolean | Promise<any>;
  handleSignInPictureChange: (uploadFile: any, uploadFiles: any, maxSizeMb?: number) => void;
  handleSignInPicturePreview: UploadProps["onPreview"];
  handleSignInPictureRemove: UploadProps["onRemove"];
  themePhotoUploadFileList: any[];
  beforeThemePhotoUpload: (rawFile: any, maxSizeMb?: number) => boolean | Promise<any>;
  handleThemePhotoPictureChange: (uploadFile: any, uploadFiles: any, maxSizeMb?: number) => void;
  handleThemePhotoPicturePreview: UploadProps["onPreview"];
  handleThemePhotoPictureRemove: UploadProps["onRemove"];
  invitationUploadFileList: any[];
  beforeInvitationUpload: (rawFile: any, maxSizeMb?: number) => boolean | Promise<any>;
  handleInvitationPictureChange: (uploadFile: any, uploadFiles: any, maxSizeMb?: number) => void;
  handleInvitationPicturePreview: UploadProps["onPreview"];
  handleInvitationPictureRemove: UploadProps["onRemove"];
  invitationDocRows: any[];
  expertProfileUploadFileList: any[];
  beforeExpertProfileUpload: (rawFile: any, maxSizeMb?: number) => boolean | Promise<any>;
  handleExpertProfileFileChange: (uploadFile: any, uploadFiles: any, maxSizeMb?: number) => void;
  handleExpertProfileFilePreview: UploadProps["onPreview"];
  handleExpertProfileFileRemove: UploadProps["onRemove"];
  selectedExpertMaterialId: number | null;
  handleSelectExpertForMaterial: (row: any) => void;
  expertModuleDoneText: (expertId: number) => string;
  beforePhotoUpload: UploadProps["beforeUpload"];
  onPhotoFileChange: UploadProps["onChange"];
  photoForm: Record<string, any>;
  photoSummaryByExpert: Record<number, string>;
  selectedExpertPhotos: any[];
  buildPhotoItemKey: (row: any, idx: number) => string;
  handleViewOss: (ossKey: string) => void;
  /** 核销材料/发票等同款：弹窗内预览图片或 PDF（OSS 预签名） */
  previewOssDocument: (ossKey: string, fileNameHint?: string) => void | Promise<void>;
  removeSelectedExpertPhoto: (index: number) => void;
  beforeLaborProtocolUpload: UploadProps["beforeUpload"];
  onLaborProtocolFileChange: UploadProps["onChange"];
  laborProtocolUploadFileList: any[];
  handleLaborProtocolFileRemove: UploadProps["onRemove"];
  selectedExpertLabor: Record<string, any>;
  beforeLaborInvoiceUpload: UploadProps["beforeUpload"];
  onLaborInvoiceFileChange: UploadProps["onChange"];
  laborInvoiceUploadFileList: any[];
  handleLaborInvoiceFileRemove: UploadProps["onRemove"];
  currentLaborReviewKey: string;
  meetingInvoiceSectionDefs: any[];
  meetingInvoiceOtherUploadFileList: any[];
  beforeMeetingInvoiceUpload: UploadProps["beforeUpload"];
  onMeetingInvoiceOtherFileChange: UploadProps["onChange"];
  handleMeetingInvoiceSectionPicturePreview: UploadProps["onPreview"];
  handleMeetingInvoiceOtherPictureRemove: UploadProps["onRemove"];
  getMeetingInvoiceOtherFileFieldKey: (item: UploadUserFile) => string;
  buildMeetingInvoiceOtherReviewItemKey: (item: UploadUserFile) => string;
  getMeetingInvoiceSectionUploadFileList: any;
  buildMeetingInvoiceUploadChangeHandler: any;
  buildMeetingInvoicePictureRemoveHandler: any;
  getMeetingInvoiceFileRow: any;
  openOcrRawDialog: any;
  runMeetingInvoiceOcr: any;
  meetingInvoiceForm: Record<string, any>;
  buildMeetingInvoiceFieldItemKey: any;
  buildMeetingInvoiceAmountItemKey: any;
  toYuan: (cent?: number) => string;
  materialMaxFileSizeMb: number;
  materialReadonly: boolean;
  canMaterialSave: boolean;
  canMaterialSubmit: boolean;
  checkFormChanged?: () => boolean;
  doSaveMaterial?: () => Promise<boolean>;
}>();

const protocolIsPdf = computed(() =>
  isPdfByNameOrKey(
    String(props.selectedExpertLabor?.protocolFileName || props.selectedExpertLabor?.protocolName || ""),
    String(props.selectedExpertLabor?.protocolOssKey || ""),
  ),
);

const canEditMaterial = computed(() => props.canMaterialSave && !props.materialReadonly);
const isMaterialFieldDisabled = (itemKey: string | string[]) =>
  props.materialReadonly || props.isReviewApproved(itemKey);
const canEditReviewedItem = (itemKey: string | string[]) => !isMaterialFieldDisabled(itemKey);
const canEditAgenda = computed(() => canEditReviewedItem("agenda"));
const canEditSignInSheet = computed(() => canEditReviewedItem("signInSheet"));
const canEditThemePhoto = computed(() => canEditReviewedItem("themePhoto"));
const canEditInvitation = computed(() => !props.materialReadonly);
const canEditExpertProfile = computed(() =>
  canEditReviewedItem(["expert_profile_file", "profileFile"]),
);
const canEditOnsiteSummary = computed(() =>
  canEditReviewedItem(`onsite_summary:${props.selectedExpertMaterialId}`),
);
const canEditCurrentLabor = computed(() => canEditReviewedItem(props.currentLaborReviewKey));
const canEditMeetingInvoiceOther = computed(() => !props.materialReadonly);
const canEditMeetingInvoiceField = (sectionCode: string, fieldKey: string) =>
  canEditReviewedItem(props.buildMeetingInvoiceFieldItemKey(sectionCode, fieldKey));
const canEditMeetingInvoiceAmount = (sectionCode: string) =>
  canEditReviewedItem(props.buildMeetingInvoiceAmountItemKey(sectionCode));
const getAgendaReviewItemKey = (file: UploadUserFile) => {
  const key = String((file as UploadUserFile & { ossKey?: string }).ossKey || "").trim();
  const idx = props.agendaDocRows.findIndex((item) => String(item?.ossKey || "").trim() === key);
  if (idx < 0) {
    return "agenda";
  }
  return idx === 0 ? [props.agendaDocRows[idx].itemKey, "agenda"] : props.agendaDocRows[idx].itemKey;
};
const canEditAgendaItem = (file: UploadUserFile) => canEditReviewedItem(getAgendaReviewItemKey(file));
const getInvitationReviewItemKey = (file: UploadUserFile) => {
  const key = String((file as UploadUserFile & { ossKey?: string }).ossKey || "").trim();
  const idx = props.invitationDocRows.findIndex((item) => String(item?.ossKey || "").trim() === key);
  return idx >= 0 ? props.invitationDocRows[idx].itemKey : "";
};
const canEditInvitationItem = (file: UploadUserFile) => canEditReviewedItem(getInvitationReviewItemKey(file));
const canEditMeetingInvoiceOtherItem = (file: UploadUserFile) =>
  canEditReviewedItem(props.buildMeetingInvoiceOtherReviewItemKey(file));

const handleLaborProtocolUploadPreview: UploadProps["onPreview"] = async (uploadFile) => {
  const row = uploadFile as UploadUserFile & { ossKey?: string };
  const key = String(row?.ossKey || "").trim();
  const name = String(row?.name || "").trim();
  if (key) {
    await props.previewOssDocument(key, name);
  } else if (row.url) {
    window.open(row.url, "_blank");
  } else if (row.raw) {
    window.open(URL.createObjectURL(row.raw), "_blank");
  }
};
const handleLaborInvoiceUploadPreview: UploadProps["onPreview"] = async (uploadFile) => {
  const row = uploadFile as UploadUserFile & { ossKey?: string };
  const key = String(row?.ossKey || "").trim();
  const name = String(row?.name || "").trim();
  if (key) {
    await props.previewOssDocument(key, name);
  } else if (row.url) {
    window.open(row.url, "_blank");
  } else if (row.raw) {
    window.open(URL.createObjectURL(row.raw), "_blank");
  }
};
const handlePhotoPreview = async (row: any) => {
  const key = String(row?.ossKey || "").trim();
  const name = String(row?.name || "").trim();
  if (key) {
    await props.previewOssDocument(key, name);
  } else if (row.previewUrl) {
    window.open(row.previewUrl, "_blank");
  } else if (row.raw) {
    window.open(URL.createObjectURL(row.raw), "_blank");
  }
};

const handleBeforeCloseDrawer = async (done: () => void) => {
  if (props.checkFormChanged && props.checkFormChanged()) {
    try {
      await ElMessageBox.confirm('当前模块有未保存的更改，是否要保存？', '提示', {
        confirmButtonText: '保存并关闭',
        cancelButtonText: '放弃更改',
        type: 'warning',
        distinguishCancelAndClose: true,
      });
      if (props.doSaveMaterial) {
        const success = await props.doSaveMaterial();
        if (success) {
          done();
        }
      } else {
        done();
      }
    } catch (action) {
      if (action === 'cancel') {
        done();
      }
    }
  } else {
    done();
  }
};

const handleCancelClick = () => {
  handleBeforeCloseDrawer(() => {
    materialDialogVisible.value = false;
  });
};

const handleBeforeTabLeave = async (activeName: string | number, oldActiveName: string | number) => {
  if (!oldActiveName || oldActiveName === activeName) return true;
  if (!materialDialogVisible.value) return true;
  if (props.checkFormChanged && props.checkFormChanged()) {
    try {
      await ElMessageBox.confirm('当前模块有未保存的更改，是否要保存？', '提示', {
        confirmButtonText: '保存并切换',
        cancelButtonText: '放弃更改',
        type: 'warning',
        distinguishCancelAndClose: true,
      });
      if (props.doSaveMaterial) {
        const success = await props.doSaveMaterial();
        return success;
      }
      return true;
    } catch (action) {
      if (action === 'cancel') {
        return true;
      } else {
        return false;
      }
    }
  }
  return true;
};

const emit = defineEmits<{
  materialTabChange: [name: string];
  saveMaterial: [];
  submitMaterial: [];
}>();

const handleSidebarMenuClick = async (moduleCode: string) => {
  if (selectedModuleCode.value === moduleCode) return;
  const canLeave = await handleBeforeTabLeave(moduleCode, selectedModuleCode.value);
  if (canLeave) {
    selectedModuleCode.value = moduleCode;
    emit('materialTabChange', moduleCode);
  }
};

const displayModuleTitle = computed(() => {
  const map: Record<string, string> = {
    BASIC_INFO: '会议基本信息',
    WRITE_OFF_DOCS: '核销材料',
    EXPERT_PROFILE: '专家简介/串场',
    EXPERT_LIST: '专家配置及材料',
    MEETING_INVOICE: '会议各项发票'
  };
  return map[selectedModuleCode.value] || '会议资料';
});


</script>

<template>
<el-drawer v-model="materialDialogVisible" size="80vw" :with-header="false" :before-close="handleBeforeCloseDrawer" destroy-on-close class="custom-material-drawer">
  <div class="material-layout">
    <div class="material-sidebar">
      <div class="sidebar-header">
        <div class="sidebar-title">{{ materialDialogTitle }}</div>
      </div>
      <ul class="sidebar-menu">
        <li :class="{ 'is-active': selectedModuleCode === 'BASIC_INFO'}" @click="handleSidebarMenuClick('BASIC_INFO')">会议基本信息</li>
        <li :class="{ 'is-active': selectedModuleCode === 'WRITE_OFF_DOCS'}" @click="handleSidebarMenuClick('WRITE_OFF_DOCS')">核销材料</li>
        <li :class="{ 'is-active': selectedModuleCode === 'EXPERT_PROFILE'}" @click="handleSidebarMenuClick('EXPERT_PROFILE')">专家简介/串场</li>
        <li :class="{ 'is-active': selectedModuleCode === 'EXPERT_LIST'}" @click="handleSidebarMenuClick('EXPERT_LIST')">专家列表</li>
        <li :class="{ 'is-active': selectedModuleCode === 'MEETING_INVOICE'}" @click="handleSidebarMenuClick('MEETING_INVOICE')">会议发票</li>
      </ul>

      <div class="sidebar-budget-card" v-if="materialBudgetSummary.ready">
        <div class="budget-title">预算看板</div>
        <div class="budget-item"><span class="text-secondary">设定预算</span> <span>¥ {{ toYuan(materialBudgetSummary.budgetCent) }}</span></div>
        <div class="budget-item"><span class="text-secondary">劳务总计</span> <span>¥ {{ toYuan(materialBudgetSummary.laborTotalCent) }}</span></div>
        <div class="budget-item"><span class="text-secondary">发票总计</span> <span>¥ {{ toYuan(materialBudgetSummary.meetingInvoiceTotalCent) }}</span></div>
        <div class="budget-item"><span class="text-secondary">已用额度</span> <span>¥ {{ toYuan(materialBudgetSummary.usedTotalCent) }}</span></div>
        <div class="budget-divider"></div>
        <div class="budget-item" :class="materialBudgetSummary.overCent > 0 ? 'text-danger fw-bold' : 'text-success fw-bold'">
          <span>{{ materialBudgetSummary.overCent > 0 ? '超发预算' : '剩余额度' }}</span>
          <span>¥ {{ toYuan(materialBudgetSummary.overCent > 0 ? materialBudgetSummary.overCent : materialBudgetSummary.remainCent) }}</span>
        </div>
      </div>
      <div v-else-if="materialBudgetLoading" class="sidebar-budget-card sidebar-budget-card--loading">
        <div class="budget-title">预算看板</div>
        <div class="budget-loading-text">预算数据加载中...</div>
      </div>

      <div v-if="materialReviewNotice" class="sidebar-notice-card">
        <div class="notice-title">审核通知</div>
        <div class="notice-content">{{ materialReviewNotice }}</div>
      </div>
    </div>

    <div class="material-main">
      <div class="main-header">
        <div class="main-title">{{ displayModuleTitle }}</div>
        <div class="main-actions">
          <span v-if="materialReadonly" class="main-readonly-hint">当前会议资料审核中或已审核通过，仅支持查看</span>
          <el-button @click="handleCancelClick" round>关闭页面</el-button>
          <el-button v-if="canEditMaterial" type="primary" @click="emit('saveMaterial')" round>保存更改</el-button>
        </div>
      </div>
      <div v-loading="materialLoading" element-loading-text="会议资料加载中..." class="main-content scrollbar-custom">
        <div class="material-module-card" v-if="selectedModuleCode === 'BASIC_INFO'">
          <el-form label-position="left" :label-width="LABEL_WIDTH.lg">

    <el-form-item label="大会主席">
      <el-select v-model="basicForm.chairmanExpertIds" multiple filterable clearable class="w-full" :disabled="isMaterialFieldDisabled('chairmanExpertIds')">
        <el-option v-for="item in boundExpertRows" :key="`chair-${item.expertId}`" :label="item.expertName" :value="item.expertId" />
      </el-select>
      <div class="mt-sm">
        <el-tag v-if="toReviewResultText('chairmanExpertIds')" size="small" :type="toReviewTagType('chairmanExpertIds')">{{ toReviewResultText('chairmanExpertIds') }}</el-tag>
        <span v-if="toReviewReasonText('chairmanExpertIds')" class="ml-sm-text-danger">不通过原因：{{ toReviewReasonText("chairmanExpertIds") }}</span>
      </div>
    </el-form-item>
    <el-form-item label="会议讲者">
      <el-select v-model="basicForm.speakerExpertIds" multiple filterable clearable class="w-full" :disabled="isMaterialFieldDisabled('speakerExpertIds')">
        <el-option v-for="item in boundExpertRows" :key="`speaker-${item.expertId}`" :label="item.expertName" :value="item.expertId" />
      </el-select>
      <div class="mt-sm">
        <el-tag v-if="toReviewResultText('speakerExpertIds')" size="small" :type="toReviewTagType('speakerExpertIds')">{{ toReviewResultText('speakerExpertIds') }}</el-tag>
        <span v-if="toReviewReasonText('speakerExpertIds')" class="ml-sm-text-danger">不通过原因：{{ toReviewReasonText("speakerExpertIds") }}</span>
      </div>
    </el-form-item>
    <el-form-item label="会议主持">
      <el-select v-model="basicForm.hostExpertIds" multiple filterable clearable class="w-full" :disabled="isMaterialFieldDisabled('hostExpertIds')">
        <el-option v-for="item in boundExpertRows" :key="`host-${item.expertId}`" :label="item.expertName" :value="item.expertId" />
      </el-select>
      <div class="mt-sm">
        <el-tag v-if="toReviewResultText('hostExpertIds')" size="small" :type="toReviewTagType('hostExpertIds')">{{ toReviewResultText('hostExpertIds') }}</el-tag>
        <span v-if="toReviewReasonText('hostExpertIds')" class="ml-sm-text-danger">不通过原因：{{ toReviewReasonText("hostExpertIds") }}</span>
      </div>
    </el-form-item>
    <el-form-item label="讨论嘉宾">
      <el-select v-model="basicForm.discussionGuestExpertIds" multiple filterable clearable class="w-full" :disabled="isMaterialFieldDisabled('discussionGuestExpertIds')">
        <el-option v-for="item in boundExpertRows" :key="`discussion-guest-${item.expertId}`" :label="item.expertName" :value="item.expertId" />
      </el-select>
      <div class="mt-sm">
        <el-tag v-if="toReviewResultText('discussionGuestExpertIds')" size="small" :type="toReviewTagType('discussionGuestExpertIds')">{{ toReviewResultText('discussionGuestExpertIds') }}</el-tag>
        <span v-if="toReviewReasonText('discussionGuestExpertIds')" class="ml-sm-text-danger">不通过原因：{{ toReviewReasonText("discussionGuestExpertIds") }}</span>
      </div>
    </el-form-item>
    <el-form-item label="嘉宾人数">
      <el-input-number v-model="basicForm.guestCount" :min="0" disabled />
      <div class="mt-sm">
        <el-tag v-if="toReviewResultText('guestCount')" size="small" :type="toReviewTagType('guestCount')">{{ toReviewResultText('guestCount') }}</el-tag>
        <span v-if="toReviewReasonText('guestCount')" class="ml-sm-text-danger">不通过原因：{{ toReviewReasonText("guestCount") }}</span>
      </div>
    </el-form-item>
    <el-form-item label="计划参会人数">
      <el-input-number v-model="basicForm.attendeeCount" :min="1" :disabled="isMaterialFieldDisabled('attendeeCount')" />
      <div class="mt-sm">
        <el-tag v-if="toReviewResultText('attendeeCount')" size="small" :type="toReviewTagType('attendeeCount')">{{ toReviewResultText('attendeeCount') }}</el-tag>
        <span v-if="toReviewReasonText('attendeeCount')" class="ml-sm-text-danger">不通过原因：{{ toReviewReasonText("attendeeCount") }}</span>
      </div>
    </el-form-item>
    <el-form-item label="实到人数">
      <el-input-number v-model="basicForm.attendeeActualCount" :min="0" :disabled="isMaterialFieldDisabled('attendeeActualCount')" />
      <div class="mt-sm">
        <el-tag v-if="toReviewResultText('attendeeActualCount')" size="small" :type="toReviewTagType('attendeeActualCount')">{{ toReviewResultText('attendeeActualCount') }}</el-tag>
        <span v-if="toReviewReasonText('attendeeActualCount')" class="ml-sm-text-danger">不通过原因：{{ toReviewReasonText("attendeeActualCount") }}</span>
      </div>
    </el-form-item>
    <el-form-item label="主要参会对象">
      <el-input v-model="basicForm.targetAudience" :disabled="isMaterialFieldDisabled('targetAudience')" placeholder="描述参会对象为哪些领域的医生" />
      <div class="mt-sm">
        <el-tag v-if="toReviewResultText('targetAudience')" size="small" :type="toReviewTagType('targetAudience')">{{ toReviewResultText('targetAudience') }}</el-tag>
        <span v-if="toReviewReasonText('targetAudience')" class="ml-sm-text-danger">不通过原因：{{ toReviewReasonText("targetAudience") }}</span>
      </div>
    </el-form-item>
    <el-form-item label="主要议程">
      <el-input v-model="basicForm.mainAgenda" type="textarea" :rows="5" :disabled="isMaterialFieldDisabled('mainAgenda')" placeholder="描述会议内容如：
1.从内科视角看肝癌的治疗；
2.丙酚替诺福韦（TAF）在慢乙肝治疗中的肾脏安全性评估；
3.病例分享"/>
      <div class="mt-sm">
        <el-tag v-if="toReviewResultText('mainAgenda')" size="small" :type="toReviewTagType('mainAgenda')">{{ toReviewResultText('mainAgenda') }}</el-tag>
        <span v-if="toReviewReasonText('mainAgenda')" class="ml-sm-text-danger">不通过原因：{{ toReviewReasonText("mainAgenda") }}</span>
      </div>
    </el-form-item>
    <el-form-item label="不足/改进建议">
      <el-input v-model="basicForm.improvementSuggestion" type="textarea" :rows="2" :disabled="isMaterialFieldDisabled('improvementSuggestion')" placeholder="没有可以写无"/>
      <div class="mt-sm">
        <el-tag v-if="toReviewResultText('improvementSuggestion')" size="small" :type="toReviewTagType('improvementSuggestion')">{{ toReviewResultText('improvementSuggestion') }}</el-tag>
        <span v-if="toReviewReasonText('improvementSuggestion')" class="ml-sm-text-danger">不通过原因：{{ toReviewReasonText("improvementSuggestion") }}</span>
      </div>
    </el-form-item>
    <el-form-item label="会议效果">
      <el-input v-model="basicForm.meetingEffect" type="textarea" :rows="2" :disabled="isMaterialFieldDisabled('meetingEffect')" placeholder="会议什么时间在哪里举办，聚焦哪些领域，多少人参会；会议进过进行描述：由谁开场主持授课讨论内容等；最后取得了什么成功。"/>
      <div class="mt-sm">
        <el-tag v-if="toReviewResultText('meetingEffect')" size="small" :type="toReviewTagType('meetingEffect')">{{ toReviewResultText('meetingEffect') }}</el-tag>
        <span v-if="toReviewReasonText('meetingEffect')" class="ml-sm-text-danger">不通过原因：{{ toReviewReasonText("meetingEffect") }}</span>
      </div>
    </el-form-item>
  </el-form>
  </div>
        <div class="material-module-card" v-else-if="selectedModuleCode === 'WRITE_OFF_DOCS'">
          <el-form label-position="left" :label-width="LABEL_WIDTH.lg">
    <el-alert title="核销材料按会议实际上传，不再套用模板" type="info" :closable="false" class="mb-md" />
    <el-form-item label="上传会议日程">
      <div class="flex gap-md w-full">
        <div>
          <el-upload
            :class="{ 'expert-profile-upload--limit': agendaUploadFileList.length >= 5 }"
            :file-list="agendaUploadFileList"
            list-type="picture-card"
            :auto-upload="false"
            :limit="5"
            :disabled="!canEditAgenda"
            :before-upload="(f) => beforeAgendaUpload(f, 20)"
            :on-change="(f, l) => handleUploadChangeWithCompression(f, l, (uf: any, ufs: any) => handleAgendaPictureChange(uf, ufs, 20))"
            :on-preview="handleAgendaPicturePreview"
            :on-remove="handleAgendaPictureRemove"
            accept=".jpg,.jpeg,.png,.webp,.gif,.bmp,.pdf"
            multiple
          >
            <template #file="{ file }">
              <MaterialPictureCardFileItem
                :file="file"
                :disabled="!canEditMaterial || !canEditAgendaItem(file)"
                @open-preview="runMaterialPreview(handleAgendaPicturePreview, $event)"
                @remove-file="runMaterialUploadRemove(handleAgendaPictureRemove, $event)"
              />
            </template>
            <el-icon v-if="canEditAgenda && agendaUploadFileList.length < 5"><Plus /></el-icon>
          </el-upload>
        </div>
        <div class="flex-col justify-start ml-md pt-sm">
          <el-popover placement="right" trigger="click" :width="500">
            <template #reference>
              <el-button type="primary" plain size="small">
                <el-icon class="mr-xs"><Picture /></el-icon>
                查看示例
              </el-button>
            </template>
            <el-image :src="AgendaSampleImage" style="width: 100%" :preview-src-list="[AgendaSampleImage]" preview-teleported />
          </el-popover>
        </div>
      </div>
      <div class="w-full mt-sm">
        <div
          v-for="(item, idx) in agendaDocRows"
          :key="item.itemKey"
          class="mb-sm"
        >
          <span class="text-regular">{{ item.name || item.itemKey }}</span>
          <el-tag v-if="toReviewResultText(idx === 0 ? [item.itemKey, 'agenda'] : item.itemKey)"
            size="small"
            :type="toReviewTagType(idx === 0 ? [item.itemKey, 'agenda'] : item.itemKey)"
            class="ml-sm"
          >
            {{ toReviewResultText(idx === 0 ? [item.itemKey, 'agenda'] : item.itemKey) }}
          </el-tag>
          <span
            v-if="toReviewReasonText(idx === 0 ? [item.itemKey, 'agenda'] : item.itemKey)"
            class="ml-sm-text-danger"
          >
            不通过原因：{{ toReviewReasonText(idx === 0 ? [item.itemKey, 'agenda'] : item.itemKey) }}
          </span>
        </div>
      </div>
    </el-form-item>
    <el-form-item label="上传签到表">
      <div class="flex gap-md w-full">
        <div>
          <el-upload
            :class="{ 'expert-profile-upload--limit': signInUploadFileList.length >= 1 }"
            :file-list="signInUploadFileList"
            list-type="picture-card"
            :auto-upload="false"
            :limit="1"
            :disabled="!canEditSignInSheet"
            :before-upload="(f) => beforeSignInUpload(f, 2)"
            :on-change="(f, l) => handleUploadChangeWithCompression(f, l, (uf: any, ufs: any) => handleSignInPictureChange(uf, ufs, 2))"
            :on-preview="handleSignInPicturePreview"
            :on-remove="handleSignInPictureRemove"
            accept=".jpg,.jpeg,.png,.webp,.gif,.bmp,.pdf"
          >
            <template #file="{ file }">
              <MaterialPictureCardFileItem
                :file="file"
                :disabled="!canEditMaterial || !canEditSignInSheet"
                @open-preview="runMaterialPreview(handleSignInPicturePreview, $event)"
                @remove-file="runMaterialUploadRemove(handleSignInPictureRemove, $event)"
              />
            </template>
            <el-icon v-if="canEditSignInSheet && signInUploadFileList.length < 1"><Plus /></el-icon>
          </el-upload>
        </div>
        <div class="flex-col justify-start ml-md pt-sm">
          <el-popover placement="right" trigger="click" :width="500">
            <template #reference>
              <el-button type="primary" plain size="small">
                <el-icon class="mr-xs"><Picture /></el-icon>
                查看示例
              </el-button>
            </template>
            <el-image :src="SignInSampleImage" style="width: 100%" :preview-src-list="[SignInSampleImage]" preview-teleported />
          </el-popover>
        </div>
      </div>
      <div class="w-full mt-sm">
        <el-tag v-if="toReviewResultText('signInSheet')" size="small" :type="toReviewTagType('signInSheet')">{{ toReviewResultText('signInSheet') }}</el-tag>
        <span v-if="toReviewReasonText('signInSheet')" class="ml-sm-text-danger">不通过原因：{{ toReviewReasonText("signInSheet") }}</span>
      </div>
    </el-form-item>
    <el-form-item label="上传主题照片（含听众、主办单位、会议主题）">
      <div class="flex gap-md w-full">
        <div>
          <el-upload
            :class="{ 'expert-profile-upload--limit': themePhotoUploadFileList.length >= 1 }"
            :file-list="themePhotoUploadFileList"
            list-type="picture-card"
            :auto-upload="false"
            :limit="1"
            :disabled="!canEditThemePhoto"
            :before-upload="(f) => beforeThemePhotoUpload(f, 20)"
            :on-change="(f, l) => handleUploadChangeWithCompression(f, l, (uf: any, ufs: any) => handleThemePhotoPictureChange(uf, ufs, 20))"
            :on-preview="handleThemePhotoPicturePreview"
            :on-remove="handleThemePhotoPictureRemove"
            accept=".jpg,.jpeg,.png,.webp,.gif,.bmp"
          >
            <template #file="{ file }">
              <MaterialPictureCardFileItem
                :file="file"
                :disabled="!canEditMaterial || !canEditThemePhoto"
                @open-preview="runMaterialPreview(handleThemePhotoPicturePreview, $event)"
                @remove-file="runMaterialUploadRemove(handleThemePhotoPictureRemove, $event)"
              />
            </template>
            <el-icon v-if="canEditThemePhoto && themePhotoUploadFileList.length < 1"><Plus /></el-icon>
          </el-upload>
        </div>
      </div>
      <div class="w-full mt-sm">
        <el-tag v-if="toReviewResultText('themePhoto')" size="small" :type="toReviewTagType('themePhoto')">{{ toReviewResultText('themePhoto') }}</el-tag>
        <span v-if="toReviewReasonText('themePhoto')" class="ml-sm-text-danger">不通过原因：{{ toReviewReasonText("themePhoto") }}</span>
      </div>
    </el-form-item>
    <el-form-item label="上传邀请函">
      <div class="flex gap-md w-full">
        <div>
          <el-upload
            :file-list="invitationUploadFileList"
            list-type="picture-card"
            :auto-upload="false"
            :disabled="!canEditInvitation"
            :before-upload="(f) => beforeInvitationUpload(f, 20)"
            :on-change="(f, l) => handleUploadChangeWithCompression(f, l, (uf: any, ufs: any) => handleInvitationPictureChange(uf, ufs, 20))"
            :on-preview="handleInvitationPicturePreview"
            :on-remove="handleInvitationPictureRemove"
            accept=".jpg,.jpeg,.png,.webp,.gif,.bmp,.pdf"
          >
            <template #file="{ file }">
              <MaterialPictureCardFileItem
                :file="file"
                :disabled="!canEditMaterial || !canEditInvitationItem(file)"
                @open-preview="runMaterialPreview(handleInvitationPicturePreview, $event)"
                @remove-file="runMaterialUploadRemove(handleInvitationPictureRemove, $event)"
              />
            </template>
            <el-icon v-if="canEditInvitation"><Plus /></el-icon>
          </el-upload>
        </div>
        <div class="flex-col justify-start ml-md pt-sm">
          <el-popover placement="right" trigger="click" :width="500">
            <template #reference>
              <el-button type="primary" plain size="small">
                <el-icon class="mr-xs"><Picture /></el-icon>
                查看示例
              </el-button>
            </template>
            <el-image :src="InvitationSampleImage" style="width: 100%" :preview-src-list="[InvitationSampleImage]" preview-teleported />
          </el-popover>
        </div>
      </div>
      <div class="w-full mt-sm">
        <div
          v-for="item in invitationDocRows"
          :key="item.itemKey"
          class="mb-sm"
        >
          <span class="text-regular">{{ item.name || item.itemKey }}</span>
          <el-tag v-if="toReviewResultText(item.itemKey)" size="small" :type="toReviewTagType(item.itemKey)" class="ml-sm">{{ toReviewResultText(item.itemKey) }}</el-tag>
          <span v-if="toReviewReasonText(item.itemKey)" class="ml-sm-text-danger">不通过原因：{{ toReviewReasonText(item.itemKey) }}</span>
        </div>
      </div>
    </el-form-item>
  </el-form>
  </div>
        <div class="material-module-card" v-else-if="selectedModuleCode === 'EXPERT_PROFILE'">
          <el-form label-position="left" :label-width="LABEL_WIDTH.lg">
    <el-form-item label="上传文件">
      <div class="flex gap-md w-full">
        <div class="flex-col w-half gap-sm">
          <el-upload
            class="expert-profile-upload w-full"
            :class="{ 'expert-profile-upload--limit': expertProfileUploadFileList.length >= 1 }"
            :file-list="expertProfileUploadFileList"
            :auto-upload="false"
            drag
            :limit="1"
            :disabled="!canEditExpertProfile"
            :before-upload="(f) => beforeExpertProfileUpload(f, 20)"
            :on-change="(f, l) => handleUploadChangeWithCompression(f, l, (uf: any, ufs: any) => handleExpertProfileFileChange(uf, ufs, 20))"
            :on-preview="handleExpertProfileFilePreview"
            :on-remove="handleExpertProfileFileRemove"
            accept=".pdf,.ppt,.pptx,application/pdf,application/vnd.ms-powerpoint,application/vnd.openxmlformats-officedocument.presentationml.presentation"
          >
            <el-icon v-if="canEditExpertProfile && expertProfileUploadFileList.length < 1"><Plus /></el-icon>
            <div class="el-upload__text" v-if="canEditExpertProfile && expertProfileUploadFileList.length < 1">
              拖拽文件到此处或 <em>点击上传</em>
            </div>
          </el-upload>
          <span class="text-secondary">仅支持PDF、PPT，限1个文件</span>
          <div>
            <el-tag v-if="toReviewResultText(['expert_profile_file', 'profileFile'])" size="small" :type="toReviewTagType(['expert_profile_file', 'profileFile'])">{{ toReviewResultText(['expert_profile_file', 'profileFile']) }}</el-tag>
            <span
              v-if="toReviewReasonText(['expert_profile_file', 'profileFile'])"
              class="ml-sm-text-danger"
            >
              不通过原因：{{ toReviewReasonText(["expert_profile_file", "profileFile"]) }}
            </span>
          </div>
        </div>
        <div class="flex-col justify-start ml-md pt-sm">
          <el-popover placement="right" trigger="click" :width="500">
            <template #reference>
              <el-button type="primary" plain size="small">
                <el-icon class="mr-xs"><Picture /></el-icon>
                查看示例
              </el-button>
            </template>
            <el-image :src="ExpertProfileSampleImage" style="width: 100%" :preview-src-list="[ExpertProfileSampleImage]" preview-teleported />
          </el-popover>
        </div>
      </div>
    </el-form-item>
  </el-form>
  </div>
  <div class="material-module-card p-0" v-else-if="selectedModuleCode === 'EXPERT_LIST'">
    <div class="expert-list-layout">
      <!-- Sidebar -->
      <div class="expert-sidebar">
        <div class="expert-sidebar-header">
          <div class="fw-bold" style="font-size: 16px;">专家资料列表</div>
          <div class="text-secondary text-sm mt-xs">选择专家上传材料</div>
        </div>
        <div class="expert-sidebar-list scrollbar-custom">
          <div
            v-for="row in boundExpertRows"
            :key="row.expertId"
            class="expert-card"
            :class="{ 'is-selected': selectedExpertMaterialId === row.expertId }"
            @click="handleSelectExpertForMaterial(row)"
          >
            <div class="expert-card-avatar">
              {{ row.expertName.charAt(0) }}
            </div>
            <div class="expert-card-info">
              <div class="expert-card-name">{{ row.expertName }}</div>
              <div class="expert-card-roles">
                <span v-if="basicForm.chairmanExpertIds?.includes(row.expertId)" class="role-tag role-tag-primary">大会主席</span>
                <span v-if="basicForm.speakerExpertIds?.includes(row.expertId)" class="role-tag role-tag-success">会议讲者</span>
                <span v-if="basicForm.hostExpertIds?.includes(row.expertId)" class="role-tag role-tag-warning">会议主持</span>
                <span v-if="basicForm.discussionGuestExpertIds?.includes(row.expertId)" class="role-tag role-tag-info">讨论嘉宾</span>
                <span v-if="!basicForm.chairmanExpertIds?.includes(row.expertId) && !basicForm.speakerExpertIds?.includes(row.expertId) && !basicForm.hostExpertIds?.includes(row.expertId) && !basicForm.discussionGuestExpertIds?.includes(row.expertId)" class="role-tag role-tag-default">未分配</span>
              </div>
            </div>
          </div>
        </div>
      </div>

      <!-- Main Content Area -->
      <div class="expert-main" :class="{ 'expert-main-empty': !selectedExpertMaterialId }">
        <div v-if="!selectedExpertMaterialId" class="expert-empty-state">
          <div class="expert-empty-icon">
            <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
              <path d="M17 21v-2a4 4 0 00-4-4H5a4 4 0 00-4 4v2"/>
              <circle cx="9" cy="7" r="4"/>
              <path d="M23 21v-2a4 4 0 00-3-3.87"/>
              <path d="M16 3.13a4 4 0 010 7.75"/>
            </svg>
          </div>
          <div class="expert-empty-title">请先从左侧选择一位专家</div>
          <div class="expert-empty-desc">选择后即可在此处上传现场照片、签署劳务协议并管理发票明细</div>
        </div>
        
        <template v-else>
          <div class="expert-toolbar">
            <div class="custom-segmented-control expert-segment">
              <div class="segmented-item" :class="{ 'is-active': expertSubModule === 'ONSITE_PHOTO' }" @click="expertSubModule = 'ONSITE_PHOTO'">现场照片</div>
              <div class="segmented-item" :class="{ 'is-active': expertSubModule === 'LABOR_PROTOCOL' }" @click="expertSubModule = 'LABOR_PROTOCOL'">劳务信息</div>
            </div>
            <div class="expert-toolbar-actions">
               <el-tag v-if="expertModuleDoneText && expertModuleDoneText(selectedExpertMaterialId) !== '未填'" size="small" type="success">资料状态: {{ expertModuleDoneText(selectedExpertMaterialId) }}</el-tag>
            </div>
          </div>

          <div class="expert-content-scroll scrollbar-custom">
            
            <!-- ONSITE_PHOTO sub-module -->
            <template v-if="expertSubModule === 'ONSITE_PHOTO'">
              <div class="section-card-flat">
                <div class="section-header flex items-center justify-between">
                  <div class="section-title">现场照片 / Action Photos</div>
                  <el-popover placement="left" trigger="click" :width="500">
                    <template #reference>
                      <el-button plain size="small">
                        <el-icon class="mr-xs"><Picture /></el-icon>查看示例
                      </el-button>
                    </template>
                    <el-row :gutter="10">
                      <el-col :span="12" class="mb-sm">
                        <div class="text-center mb-xs text-sm">专题授课</div>
                        <el-image :src="PhotoSampleImage1" style="width: 100%" :preview-src-list="[PhotoSampleImage1]" preview-teleported />
                      </el-col>
                      <el-col :span="12" class="mb-sm">
                        <div class="text-center mb-xs text-sm">会议主持</div>
                        <el-image :src="PhotoSampleImage2" style="width: 100%" :preview-src-list="[PhotoSampleImage2]" preview-teleported />
                      </el-col>
                      <el-col :span="12">
                        <div class="text-center mb-xs text-sm">会议讨论</div>
                        <el-image :src="PhotoSampleImage3" style="width: 100%" :preview-src-list="[PhotoSampleImage3]" preview-teleported />
                      </el-col>
                      <el-col :span="12">
                        <div class="text-center mb-xs text-sm">大会主持</div>
                        <el-image :src="PhotoSampleImage4" style="width: 100%" :preview-src-list="[PhotoSampleImage4]" preview-teleported />
                      </el-col>
                    </el-row>
                  </el-popover>
                </div>

                <div class="mb-md">
                  <div class="form-label-custom">现场内容总结说明</div>
                  <textarea 
                    class="custom-textarea w-full" 
                    rows="2" 
                    :disabled="!canEditOnsiteSummary"
                    v-model="photoSummaryByExpert[selectedExpertMaterialId!]" 
                    placeholder="如：签到处、主会场、合影等"
                  ></textarea>
                  <div class="mt-xs flex items-center gap-sm">
                    <span class="status-badge" :class="`status-${toReviewTagType('onsite_summary:' + selectedExpertMaterialId)}`" v-if="toReviewResultText('onsite_summary:' + selectedExpertMaterialId)">
                      {{ toReviewResultText('onsite_summary:' + selectedExpertMaterialId) }}
                    </span>
                    <span v-if="toReviewReasonText('onsite_summary:' + selectedExpertMaterialId)" class="text-danger text-sm">驳回原因：{{ toReviewReasonText('onsite_summary:' + selectedExpertMaterialId) }}</span>
                  </div>
                </div>

                <div class="photo-grid">
                  <el-upload
                    class="photo-upload-card"
                    :auto-upload="false"
                    :show-file-list="false"
                    multiple
                    :disabled="!canEditOnsiteSummary"
                    :before-upload="beforePhotoUpload"
                    :on-change="(f, l) => handleUploadChangeWithCompression(f, l, onPhotoFileChange)"
                    accept=".jpg,.jpeg,.png,.webp,.gif,.bmp,.pdf"
                  >
                    <div v-if="canEditOnsiteSummary" class="upload-inner">
                      <el-icon><Plus /></el-icon>
                      <span class="upload-text">点击上传图片</span>
                      <span class="upload-hint">单文件&lt;{{ materialMaxFileSizeMb }}MB</span>
                    </div>
                  </el-upload>
                  
                  <div v-for="(row, $index) in selectedExpertPhotos" :key="$index" class="photo-card">
                    <el-image
                      v-if="row.previewUrl && !isPdfByNameOrKey(row.name, row.ossKey)"
                      :src="row.previewUrl"
                      fit="cover"
                      :preview-src-list="[row.previewUrl]"
                      preview-teleported
                      class="photo-card-img"
                    />
                    <div v-else class="photo-card-file">
                      <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="width: 32px; height: 32px; color: #94a3b8;"><path d="M13 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V9z"/><path d="M13 2v7h7"/></svg>
                      <span style="font-size: 11px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; padding: 0 8px; width: 100%; text-align: center; margin-top: 8px;">{{ row.name }}</span>
                    </div>

                    <div class="photo-card-overlay">
                      <span class="photo-name">{{ row.name }}</span>
                      <div class="flex gap-sm">
                        <el-button type="info" circle size="small" @click.stop="handlePhotoPreview(row)" title="预览">
                           <svg viewBox="0 0 24 24" fill="none" style="width: 14px; height: 14px;"><path stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" /><path stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z" /></svg>
                        </el-button>
                        <el-button v-if="canEditReviewedItem(buildPhotoItemKey(row, $index))" type="danger" circle size="small" @click.stop="removeSelectedExpertPhoto($index)" title="删除">
                           <svg viewBox="0 0 24 24" fill="none" style="width: 14px; height: 14px;"><path d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>
                        </el-button>
                      </div>
                    </div>
                    
                    <div class="review-status-float" v-if="toReviewResultText(buildPhotoItemKey(row, $index))">
                      <span class="status-badge-small" :class="`status-${toReviewTagType(buildPhotoItemKey(row, $index))}`">
                        {{ toReviewResultText(buildPhotoItemKey(row, $index)) }}
                      </span>
                    </div>
                  </div>
                </div>
              </div>
            </template>

            <!-- LABOR_PROTOCOL sub-module -->
            <template v-else-if="expertSubModule === 'LABOR_PROTOCOL'">
              <div class="section-card-flat mt-md">
                <el-row :gutter="20">
                  <el-col :span="12">
                    <div class="attachment-panel">
                      <div class="attachment-header">
                        <span class="attachment-title">劳务协议附件</span>
                        <el-popover placement="right" trigger="click" :width="500">
                          <template #reference>
                            <el-button plain size="small"><el-icon class="mr-xs"><Picture /></el-icon>示例</el-button>
                          </template>
                          <el-image :src="LaborProtocolSampleImage" style="width: 100%" :preview-src-list="[LaborProtocolSampleImage]" preview-teleported />
                        </el-popover>
                      </div>
                      
                      <div class="attachment-body">
                        <div v-if="laborProtocolUploadFileList.length || selectedExpertLabor.protocolPreviewUrl" class="file-preview-card">
                          <el-image
                            v-if="selectedExpertLabor.protocolPreviewUrl && !protocolIsPdf"
                            :src="selectedExpertLabor.protocolPreviewUrl"
                            :preview-src-list="[selectedExpertLabor.protocolPreviewUrl]"
                            preview-teleported
                            class="file-img-cover"
                            fit="cover"
                          />
                          <div v-else class="file-doc-cover">
                            <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" class="w-8 h-8"><path d="M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8z"/><path d="M14 2v6h6"/></svg>
                            <span style="font-size: 13px; margin-top: 8px;">已上传协议文件</span>
                          </div>
                          <div class="file-overlay-actions" style="gap: 12px;">
                             <el-button 
                               size="small" 
                               type="info" 
                               round 
                               @click="laborProtocolUploadFileList.length ? handleLaborProtocolUploadPreview(laborProtocolUploadFileList[0]) : handleLaborProtocolUploadPreview({ name: selectedExpertLabor?.protocolFileName, ossKey: selectedExpertLabor?.protocolOssKey } as any)"
                             >预览</el-button>
                             <el-button
                               v-if="canEditCurrentLabor"
                               size="small"
                               type="danger"
                               round
                               plain
                               @click="runMaterialUploadRemove(handleLaborProtocolFileRemove, laborProtocolUploadFileList.length ? laborProtocolUploadFileList[0] : ({ name: selectedExpertLabor?.protocolFileName, ossKey: selectedExpertLabor?.protocolOssKey } as any))"
                             >删除</el-button>
                             <el-upload
                                v-if="canEditCurrentLabor"
                                :auto-upload="false"
                                :show-file-list="false"
                                :before-upload="beforeLaborProtocolUpload"
                                :on-change="(f, l) => handleUploadChangeWithCompression(f, l, onLaborProtocolFileChange)"
                                accept=".jpg,.jpeg,.png,.webp,.gif,.bmp,.pdf"
                             >
                                <el-button size="small" round>重新上传</el-button>
                             </el-upload>
                          </div>
                        </div>
                        
                        <el-upload
                          v-else
                          class="dragger-upload"
                          drag
                          :auto-upload="false"
                          :show-file-list="false"
                          :disabled="!canEditCurrentLabor"
                          :before-upload="beforeLaborProtocolUpload"
                          :on-change="(f, l) => handleUploadChangeWithCompression(f, l, onLaborProtocolFileChange)"
                          accept=".jpg,.jpeg,.png,.webp,.gif,.bmp,.pdf"
                        >
                          <el-icon class="el-icon--upload"><Plus /></el-icon>
                          <div class="el-upload__text">点击或拖拽上传协议</div>
                          <div class="el-upload__tip pt-2" style="font-size: 12px; color: #94a3b8;">大小不超过{{ materialMaxFileSizeMb }}MB</div>
                        </el-upload>
                      </div>
                    </div>
                  </el-col>
                  
                  <el-col :span="12">
                    <div class="attachment-panel" style="height: 100%;">
                      <div class="attachment-header">
                        <span class="attachment-title">劳务金额信息</span>
                      </div>
                      <div class="attachment-body" style="justify-content: flex-start; padding: 20px;">
                        <div class="amount-input-group">
                          <label class="amount-label">实发劳务金额（元）</label>
                          <el-input-number v-model="selectedExpertLaborAmountYuan" :min="0" :step="0.01" :precision="2" size="large" class="w-full amount-stepper" :disabled="!canEditCurrentLabor" />
                          <div class="hint-text mt-sm">请核对您上传的协议金额，确保一致。</div>
                        </div>
                      </div>
                    </div>
                  </el-col>
                </el-row>
              </div>
              <div class="section-card-flat mt-md">
                <el-row :gutter="20" class="mb-md">
                  <el-col :span="24">
                    <div class="attachment-panel">
                       <div class="attachment-header">
                         <span class="attachment-title">发票附件（专家个人开票需上传）</span>
                         <el-popover placement="right" trigger="click" :width="500">
                           <template #reference>
                             <el-button plain size="small"><el-icon class="mr-xs"><Picture /></el-icon>示例</el-button>
                           </template>
                           <el-image :src="LaborInvoiceSampleImage" style="width: 100%" :preview-src-list="[LaborInvoiceSampleImage]" preview-teleported />
                         </el-popover>
                       </div>
                      <div class="attachment-body" style="display: block;">
                        <el-upload
                          :file-list="laborInvoiceUploadFileList"
                          list-type="picture-card"
                          :auto-upload="false"
                          multiple
                          :disabled="!canEditCurrentLabor"
                          :before-upload="beforeLaborInvoiceUpload"
                          :on-change="(f, l) => handleUploadChangeWithCompression(f, l, onLaborInvoiceFileChange)"
                          :on-preview="handleLaborInvoiceUploadPreview"
                          :on-remove="handleLaborInvoiceFileRemove"
                          accept=".jpg,.jpeg,.png,.webp,.gif,.bmp,.pdf"
                        >
                          <template #file="{ file }">
                            <MaterialPictureCardFileItem
                              :file="file"
                              :disabled="!canEditMaterial || !canEditCurrentLabor"
                              @open-preview="runMaterialPreview(handleLaborInvoiceUploadPreview, $event)"
                              @remove-file="runMaterialUploadRemove(handleLaborInvoiceFileRemove, $event)"
                            />
                          </template>
                          <el-icon v-if="canEditCurrentLabor"><Plus /></el-icon>
                        </el-upload>
                        <div class="mt-sm" style="font-size: 12px; color: #94a3b8;">
                          当前专家可上传多张发票，发票会与该专家的劳务金额一起保存。
                        </div>
                      </div>
                    </div>
                  </el-col>
                </el-row>
              </div>
            </template>

          </div>
        </template>
      </div>
    </div>
  </div>

        <div class="material-module-card" v-else-if="selectedModuleCode === 'MEETING_INVOICE'">
          <el-form label-position="left" :label-width="LABEL_WIDTH.lg">
    <el-alert title="会议发票按分项上传，单个上传位文件数量以分项配置为准，单文件不超2MB" type="info" :closable="false" class="mb-md" />
    <div
      v-for="(section, sectionIndex) in meetingInvoiceSectionDefs"
      :key="section.code"
      class="meeting-invoice-section"
    >
      <div class="meeting-invoice-section-title flex items-center justify-between">
        <span>{{ sectionIndex + 1 }}、{{ section.title }}</span>
        <el-popover v-if="section.sampleImage" placement="left" trigger="click" :width="500">
          <template #reference>
            <el-button type="primary" plain size="small">
              <el-icon class="mr-xs"><Picture /></el-icon>
              查看示例
            </el-button>
          </template>
          <template v-if="Array.isArray(section.sampleImage)">
            <el-row :gutter="10">
              <el-col :span="12" class="mb-sm" v-for="(img, idx) in section.sampleImage" :key="idx">
                <el-image :src="img" style="width: 100%" :preview-src-list="section.sampleImage" preview-teleported />
              </el-col>
            </el-row>
          </template>
          <template v-else>
            <el-image :src="section.sampleImage" style="width: 100%" :preview-src-list="[section.sampleImage]" preview-teleported />
          </template>
        </el-popover>
      </div>
      <template v-if="section.code === 'OTHER'">
        <el-form-item label="上传附件" required>
          <el-upload
            :class="{ 'expert-profile-upload--limit': meetingInvoiceOtherUploadFileList.length >= section.maxUploadCount }"
            :file-list="meetingInvoiceOtherUploadFileList"
            list-type="picture-card"
            :auto-upload="false"
            :limit="section.maxUploadCount"
            multiple
            :disabled="!canEditMeetingInvoiceOther"
            :before-upload="beforeMeetingInvoiceUpload"
            :on-change="(f, l) => handleUploadChangeWithCompression(f, l, onMeetingInvoiceOtherFileChange)"
            :on-preview="handleMeetingInvoiceSectionPicturePreview"
            :on-remove="handleMeetingInvoiceOtherPictureRemove"
            accept=".jpg,.jpeg,.png,.webp,.gif,.bmp,.pdf"
          >
            <template #file="{ file }">
              <MaterialPictureCardFileItem
                :file="file"
                :disabled="!canEditMaterial || !canEditMeetingInvoiceOtherItem(file)"
                @open-preview="runMaterialPreview(handleMeetingInvoiceSectionPicturePreview, $event)"
                @remove-file="runMaterialUploadRemove(handleMeetingInvoiceOtherPictureRemove, $event)"
              />
            </template>
            <el-icon v-if="canEditMeetingInvoiceOther && meetingInvoiceOtherUploadFileList.length < section.maxUploadCount"><Plus /></el-icon>
          </el-upload>
          <div class="w-full mt-sm">
            <div
              v-for="item in meetingInvoiceOtherUploadFileList"
              :key="String(getMeetingInvoiceOtherFileFieldKey(item) || item.uid)"
              class="mb-sm"
            >
              <span class="text-regular">{{ item.name || "其他附件" }}</span>
              <el-tag v-if="toReviewResultText(buildMeetingInvoiceOtherReviewItemKey(item))"
                size="small"
                :type="toReviewTagType(buildMeetingInvoiceOtherReviewItemKey(item))"
                class="ml-sm"
              >
                {{ toReviewResultText(buildMeetingInvoiceOtherReviewItemKey(item)) }}
              </el-tag>
              <span
                v-if="toReviewReasonText(buildMeetingInvoiceOtherReviewItemKey(item))"
                class="ml-sm-text-danger"
              >
                不通过原因：{{ toReviewReasonText(buildMeetingInvoiceOtherReviewItemKey(item)) }}
              </span>
            </div>
          </div>
        </el-form-item>
      </template>
      <template v-else>
        <el-form-item
          v-for="uploadField in section.uploadFields"
          :key="`${section.code}-${uploadField.key}`"
          :label="uploadField.label"
          required
        >
          <el-upload
            :class="{
              'expert-profile-upload--limit':
                getMeetingInvoiceSectionUploadFileList(section.code, uploadField.key).length >= section.maxUploadCount,
            }"
            :file-list="getMeetingInvoiceSectionUploadFileList(section.code, uploadField.key)"
            list-type="picture-card"
            :auto-upload="false"
            :limit="section.maxUploadCount"
            :disabled="!canEditMeetingInvoiceField(section.code, uploadField.key)"
            :before-upload="beforeMeetingInvoiceUpload"
            :on-change="(f, l) => handleUploadChangeWithCompression(f, l, buildMeetingInvoiceUploadChangeHandler(section.code, uploadField.key))"
            :on-preview="handleMeetingInvoiceSectionPicturePreview"
            :on-remove="buildMeetingInvoicePictureRemoveHandler(section.code, uploadField.key)"
            accept=".jpg,.jpeg,.png,.webp,.gif,.bmp,.pdf"
          >
            <template #file="{ file }">
              <MaterialPictureCardFileItem
                :file="file"
                :disabled="!canEditMaterial || !canEditMeetingInvoiceField(section.code, uploadField.key)"
                @open-preview="runMaterialPreview(handleMeetingInvoiceSectionPicturePreview, $event)"
                @remove-file="
                  runMaterialUploadRemove(
                    buildMeetingInvoicePictureRemoveHandler(section.code, uploadField.key),
                    $event,
                  )
                "
              />
            </template>
            <el-icon v-if="canEditMeetingInvoiceField(section.code, uploadField.key) && getMeetingInvoiceSectionUploadFileList(section.code, uploadField.key).length < section.maxUploadCount"><Plus /></el-icon>
          </el-upload>
          <div class="w-full mt-sm">
            <el-tag v-if="toReviewResultText(buildMeetingInvoiceFieldItemKey(section.code, uploadField.key))" size="small" :type="toReviewTagType(buildMeetingInvoiceFieldItemKey(section.code, uploadField.key))">
              {{ toReviewResultText(buildMeetingInvoiceFieldItemKey(section.code, uploadField.key)) }}
            </el-tag>
            <span
              v-if="toReviewReasonText(buildMeetingInvoiceFieldItemKey(section.code, uploadField.key))"
              class="ml-sm-text-danger"
            >
              不通过原因：{{ toReviewReasonText(buildMeetingInvoiceFieldItemKey(section.code, uploadField.key)) }}
            </span>
          </div>
          <div v-if="uploadField.key === 'invoiceFile'" class="w-full mt-sm">
            <template v-if="getMeetingInvoiceFileRow(section.code, uploadField.key)?.ocr?.status === 'running'">
              <el-tag size="small" type="info">OCR识别中..</el-tag>
            </template>
            <template v-else-if="getMeetingInvoiceFileRow(section.code, uploadField.key)?.ocr?.status === 'pending_confirm'">
              <el-tag size="small" type="warning">待确认回填</el-tag>
              <span class="ml-sm-text-warning">请在识别结果弹窗中确认后回填</span>
            </template>
            <template v-else-if="getMeetingInvoiceFileRow(section.code, uploadField.key)?.ocr?.status === 'success'">
              <el-tag size="small" type="success">OCR已识别</el-tag>
              <span class="ml-sm-text-regular">
                发票代码：{{ getMeetingInvoiceFileRow(section.code, uploadField.key)?.ocr?.normalized?.invoiceCode || "-" }}
                ，发票号码：{{ getMeetingInvoiceFileRow(section.code, uploadField.key)?.ocr?.normalized?.invoiceNum || "-" }}
                ，金额：{{ toYuan(getMeetingInvoiceFileRow(section.code, uploadField.key)?.ocr?.normalized?.totalAmountCent || 0) }} （元）
                ，税额：{{ toYuan(getMeetingInvoiceFileRow(section.code, uploadField.key)?.ocr?.normalized?.taxCent || 0) }} （元）
              </span>
              <!-- <el-button size="small" class="ml-sm" @click="openOcrRawDialog(section.code, uploadField.key)">查看原始</el-button> -->
            </template>
            <template v-else-if="getMeetingInvoiceFileRow(section.code, uploadField.key)?.ocr?.status === 'failed'">
              <el-tag size="small" type="danger">OCR失败</el-tag>
              <span class="ml-sm-text-danger">
                {{ getMeetingInvoiceFileRow(section.code, uploadField.key)?.ocr?.message || "识别失败" }}
              </span>
              <!-- <el-button size="small" class="ml-sm" @click="runMeetingInvoiceOcr(section.code, uploadField.key)">重试识别</el-button>
              <el-button size="small" class="ml-sm" @click="openOcrRawDialog(section.code, uploadField.key)">查看原始</el-button> -->
            </template>
          </div>
        </el-form-item>
      </template>
      <el-form-item v-if="section.needAmount" label="总费用（元）" required>
        <el-input-number
          v-model="meetingInvoiceForm.sections[section.code].totalAmountYuan"
          :min="0"
          :precision="2"
          :step="100"
          :disabled="!canEditMeetingInvoiceAmount(section.code)"
          placeholder="请输入金额（单位：元）"
          class="w-input-lg"
        />
        <el-tag v-if="toReviewResultText(buildMeetingInvoiceAmountItemKey(section.code))" size="small" class="ml-sm" :type="toReviewTagType(buildMeetingInvoiceAmountItemKey(section.code))">
          {{ toReviewResultText(buildMeetingInvoiceAmountItemKey(section.code)) }}
        </el-tag>
        <span
          v-if="toReviewReasonText(buildMeetingInvoiceAmountItemKey(section.code))"
          class="ml-sm-text-danger"
        >
          不通过原因：{{ toReviewReasonText(buildMeetingInvoiceAmountItemKey(section.code)) }}
        </span>
      </el-form-item>
    </div>
  </el-form>
  <!-- <el-form label-width="100px">
    <el-form-item label="备注">
      <el-input v-model="materialForm.remark" />
    </el-form-item>
  </el-form> -->
  <!-- Footer replaced by main-header -->
        </div>
      </div>
    </div>
  </div>
</el-drawer>
</template>

<style scoped>
.meeting-invoice-section {
  border: 1px solid var(--wo-border-light);
  border-radius: 8px;
  padding: 12px 12px 2px;
  margin-bottom: 12px;
}
.meeting-invoice-section-title {
  font-weight: 600;
  margin-bottom: 8px;
  color: var(--wo-text-primary, #303133);
}
.meeting-invoice-file-row {
  margin-top: 8px;
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}
.expert-profile-upload--limit :deep(div.el-upload.is-drag),
.expert-profile-upload--limit :deep(div.el-upload.el-upload--text:not(.is-drag)),
.expert-profile-upload--limit :deep(.el-upload.el-upload--picture-card) {
  display: none;
}
.expert-labor-protocol-form-content,
.expert-labor-invoice-form-content {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 8px;
  width: 100%;
  min-width: 0;
}
.expert-labor-file-hint {
  color: var(--wo-text-secondary);
}
.expert-labor-protocol-preview,
.expert-labor-invoice-preview {
  width: 56px;
  height: 56px;
  border-radius: 4px;
}
.expert-labor-invoice-ocr-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
}

/* Custom Material Drawer Styles */
:global(.custom-material-drawer .el-drawer__body) {
  padding: 0 !important;
  background-color: var(--wo-bg-light, #f5f7fa);
}

.material-layout {
  display: flex;
  height: 100%;
  width: 100%;
  overflow: hidden;
}

.material-sidebar {
  width: 280px;
  background-color: var(--wo-bg-side, #ffffff);
  border-right: 1px solid var(--wo-border-light, #e4e7ed);
  display: flex;
  flex-direction: column;
  flex-shrink: 0;
  padding: 24px 0;
  z-index: 10;
}

.sidebar-header {
  padding: 0 24px;
  margin-bottom: 24px;
}

.sidebar-title {
  font-size: 20px;
  font-weight: 600;
  color: var(--wo-text-primary, #303133);
}

.sidebar-menu {
  list-style: none;
  padding: 0 12px;
  margin: 0;
  flex: 1;
}

.sidebar-menu li {
  padding: 12px 16px;
  margin-bottom: 8px;
  border-radius: 8px;
  cursor: pointer;
  color: var(--wo-text-regular, #606266);
  font-size: 14px;
  transition: all 0.2s ease;
  font-weight: 500;
}

.sidebar-menu li:hover {
  background-color: var(--wo-bg-light, #f5f7fa);
  color: var(--wo-text-primary, #303133);
}

.sidebar-menu li.is-active {
  background-color: var(--el-color-primary-light-9);
  color: var(--el-color-primary);
  font-weight: 600;
}

.sidebar-budget-card {
  margin: 20px 16px 0;
  padding: 16px;
  border-radius: 12px;
  background: linear-gradient(135deg, #fdfbfb 0%, #ebedee 100%);
  border: 1px solid rgba(0,0,0,0.05);
  box-shadow: 0 4px 12px rgba(0,0,0,0.03);
}

.sidebar-budget-card--loading {
  min-height: 132px;
  display: flex;
  flex-direction: column;
  justify-content: center;
}

.budget-loading-text {
  font-size: 13px;
  color: var(--wo-text-secondary, #909399);
}

.sidebar-notice-card {
  margin: 16px;
  padding: 16px;
  border-radius: 12px;
  background-color: var(--el-color-info-light-9);
  border: 1px solid var(--el-color-info-light-7);
}

.notice-title {
  color: var(--el-color-info);
  font-weight: 600;
  font-size: 13px;
  margin-bottom: 8px;
}
.notice-content {
  font-size: 13px;
  color: var(--wo-text-regular);
  line-height: 1.5;
}

.budget-title {
  font-size: 14px;
  font-weight: 600;
  margin-bottom: 12px;
  color: var(--wo-text-primary);
}

.budget-item {
  display: flex;
  justify-content: space-between;
  align-items: center;
  font-size: 13px;
  margin-bottom: 8px;
}

.budget-divider {
  height: 1px;
  background-color: rgba(0,0,0,0.06);
  margin: 12px 0;
}

.material-main {
  flex: 1;
  display: flex;
  flex-direction: column;
  min-width: 0;
  background-color: #f7f8fa;
}

.main-header {
  height: 72px;
  padding: 0 32px;
  display: flex;
  justify-content: space-between;
  align-items: center;
  background-color: #ffffff;
  border-bottom: 1px solid var(--wo-border-light, #e4e7ed);
  flex-shrink: 0;
}

.main-actions {
  display: flex;
  align-items: center;
  gap: 12px;
}

.main-readonly-hint {
  font-size: 13px;
  color: var(--el-color-warning);
}

.main-title {
  font-size: 18px;
  font-weight: 600;
  color: var(--wo-text-primary);
}

.main-content {
  flex: 1;
  padding: 32px;
  overflow-y: auto;
}

.material-module-card {
  background-color: #ffffff;
  border-radius: 12px;
  padding: 32px;
  box-shadow: 0 2px 12px rgba(0,0,0,0.02);
  border: 1px solid var(--wo-border-light, #e4e7ed);
}

.material-module-card.p-0 {
  padding: 0;
  background: transparent;
  box-shadow: none;
  border: none;
}

/* Custom Segmented Control */
.custom-segmented-control {
  display: inline-flex;
  background-color: var(--wo-bg-light, #f5f7fa);
  padding: 4px;
  border-radius: 8px;
}
.segmented-item {
  padding: 8px 24px;
  font-size: 14px;
  font-weight: 500;
  color: var(--wo-text-regular);
  cursor: pointer;
  border-radius: 6px;
  transition: all 0.2s ease;
}
.segmented-item.is-active {
  background-color: #ffffff;
  color: var(--el-color-primary);
  box-shadow: 0 2px 8px rgba(0,0,0,0.06);
}
.expert-list-container {
  display: flex;
  gap: 16px;
  height: 100%;
}
.expert-list-container .section-card-sm, 
.expert-list-container .section-card {
  background-color: #ffffff;
  border-radius: 12px;
  padding: 24px;
  box-shadow: 0 2px 12px rgba(0,0,0,0.02);
  border: 1px solid var(--wo-border-light, #e4e7ed);
}
</style>

<style scoped>
/* EXPERT LIST LAYOUT */
.expert-list-layout {
  display: flex;
  height: 100%;
  border-radius: 12px;
  overflow: hidden;
  background-color: #f8fafc;
}
.expert-sidebar {
  width: 280px;
  background: white;
  border-right: 1px solid #e2e8f0;
  display: flex;
  flex-direction: column;
}
.expert-sidebar-header {
  padding: 20px;
  border-bottom: 1px solid #e2e8f0;
}
.expert-sidebar-list {
  flex: 1;
  overflow-y: auto;
  padding: 12px;
}
.expert-card {
  display: flex;
  align-items: center;
  padding: 12px;
  border-radius: 8px;
  cursor: pointer;
  transition: all 0.2s ease;
  margin-bottom: 8px;
  border: 1px solid transparent;
}
.expert-card:hover {
  background: #f1f5f9;
}
.expert-card.is-selected {
  background: #eff6ff;
  border: 1px solid #bfdbfe;
  box-shadow: 0 1px 2px rgba(0,0,0,0.05);
}
.expert-card-avatar {
  width: 40px;
  height: 40px;
  border-radius: 50%;
  background: #e0e7ff;
  color: #4f46e5;
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: 600;
  font-size: 16px;
  margin-right: 12px;
  flex-shrink: 0;
}
.expert-card-info {
  flex: 1;
  min-width: 0;
}
.expert-card-name {
  font-size: 14px;
  font-weight: 600;
  color: #1e293b;
  margin-bottom: 4px;
}
.expert-card-roles {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
}
.role-tag {
  font-size: 10px;
  padding: 2px 6px;
  border-radius: 4px;
  white-space: nowrap;
}
.role-tag-primary { background: #dbeafe; color: #1d4ed8; }
.role-tag-success { background: #dcfce7; color: #15803d; }
.role-tag-warning { background: #fef9c3; color: #854d0e; }
.role-tag-info { background: #e0f2fe; color: #0369a1; }
.role-tag-default { background: #f1f5f9; color: #64748b; }

.expert-main {
  flex: 1;
  display: flex;
  flex-direction: column;
  background: #f8fafc;
  min-width: 0;
  position: relative;
}
.expert-main-empty {
  align-items: center;
  justify-content: center;
}
.expert-empty-state {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  padding: 40px;
}
.expert-empty-icon {
  width: 80px; height: 80px;
  background: #f1f5f9;
  border-radius: 50%;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  color: #94a3b8;
  margin-bottom: 24px;
}
.expert-empty-icon svg { width: 40px; height: 40px; }
.expert-empty-title { font-size: 18px; font-weight: 600; color: #334155; margin-bottom: 8px; }
.expert-empty-desc { font-size: 14px; color: #64748b; }

.expert-toolbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 16px 24px;
  background: white;
  border-bottom: 1px solid #e2e8f0;
}
.expert-segment { margin: 0; }
.expert-content-scroll {
  flex: 1;
  overflow-y: auto;
  padding: 24px;
}

/* CARDS & FLATS */
.section-card-flat {
  background: white;
  border-radius: 12px;
  padding: 24px;
  box-shadow: 0 1px 3px rgba(0,0,0,0.05);
  border: 1px solid #e2e8f0;
}
.section-header { margin-bottom: 16px; border-bottom: 1px solid #f1f5f9; padding-bottom: 12px; }
.section-title { font-size: 16px; font-weight: 600; color: #1e293b; }

/* CUSTOM INPUTS */
.form-label-custom { font-size: 14px; font-weight: 600; color: #334155; margin-bottom: 8px; }
.custom-textarea {
  border: 1px solid #cbd5e1;
  border-radius: 8px;
  padding: 10px 14px;
  font-size: 14px;
  outline: none;
  transition: border-color 0.2s;
  font-family: inherit;
  resize: vertical;
}
.custom-textarea:focus { border-color: #6366f1; box-shadow: 0 0 0 2px rgba(99,102,241,0.1); }

/* STATUS BADGES */
.status-badge { display: inline-flex; align-items: center; padding: 4px 10px; border-radius: 16px; font-size: 12px; font-weight: 500; }
.status-badge-small { display: inline-flex; align-items: center; padding: 2px 8px; border-radius: 12px; font-size: 11px; }
.status-success { background: #dcfce7; color: #16a34a; }
.status-warning { background: #fef08a; color: #ca8a04; }
.status-danger { background: #fee2e2; color: #dc2626; }
.status-info { background: #f1f5f9; color: #64748b; }

/* PHOTO GRID */
.photo-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(130px, 1fr)); gap: 16px; }
.photo-upload-card, .photo-card {
  width: 130px; height: 130px; border-radius: 8px; overflow: hidden; position: relative;
}
.photo-upload-card { border: 2px dashed #cbd5e1; background: #f8fafc; cursor: pointer; transition: all 0.2s; display: flex; align-items: center; justify-content: center; }
.photo-upload-card:hover { border-color: #818cf8; background: #eff6ff; }
.photo-upload-card:deep(.el-upload) { width: 100%; height: 100%; display: flex; align-items: center; justify-content: center;}
.upload-inner { display: flex; flex-direction: column; align-items: center; justify-content: center; height: 100%; width: 100%; color: #64748b; }
.upload-text { font-size: 13px; font-weight: 500; margin-top: 8px; }
.upload-hint { font-size: 11px; margin-top: 4px; color: #94a3b8; }
.photo-card { border: 1px solid #e2e8f0; background: white;  }
.photo-card-img { width: 100%; height: 100%; object-fit: cover; }
.photo-card-file { width: 100%; height: 100%; display: flex; flex-direction: column; align-items: center; justify-content: center; background: #f1f5f9; }
.photo-card-overlay {
  position: absolute; top: 0; left: 0; right: 0; bottom: 0;
  background: rgba(0,0,0,0.6); display: flex; flex-direction: column; align-items: center; justify-content: center;
  opacity: 0; transition: opacity 0.2s;
}
.photo-card:hover .photo-card-overlay { opacity: 1; pointer-events: auto; }
.photo-name { color: white; font-size: 12px; margin-bottom: 12px; padding: 0 12px; text-align: center; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; width: 100%; }
.review-status-float { position: absolute; top: 6px; right: 6px; z-index: 10; }

/* ATTACHMENT PANELS */
.attachment-panel { border: 1px solid #e2e8f0; border-radius: 8px; background: #fafafa; display: flex; flex-direction: column; }
.attachment-header { padding: 12px 16px; border-bottom: 1px solid #e2e8f0; display: flex; justify-content: space-between; align-items: center; background: white; border-radius: 8px 8px 0 0; }
.attachment-title { font-size: 14px; font-weight: 600; color: #334155; }
.attachment-body { padding: 16px; flex: 1; display: flex; flex-direction: column; justify-content: center; }

/* FILE PREVIEW CARDS (Labor & Invoice) */
.file-preview-card { position: relative; width: 100%; height: 160px; border-radius: 8px; overflow: hidden; border: 1px solid #e2e8f0; background: white; display: flex; justify-content: center; align-items: center; }
.file-img-cover { width: 100%; height: 100%; }
.file-doc-cover { width: 100%; height: 100%; display: flex; flex-direction: column; align-items: center; justify-content: center; color: #64748b; background: #f8fafc; }
.file-overlay-actions { position: absolute; inset: 0; background: rgba(0,0,0,0.5); opacity: 0; transition: 0.2s; display: flex; align-items: center; justify-content: center; z-index: 10; }
.file-preview-card:hover .file-overlay-actions { opacity: 1; pointer-events: auto; }

.dragger-upload:deep(.el-upload-dragger) { padding: 30px; border-radius: 8px; width: 100%; }
.dragger-upload:deep(.el-upload) { width: 100%; }

/* AMOUNT STEPPER OVERRIDE */
.amount-input-group { background: white; padding: 20px; border: 1px solid #e2e8f0; border-radius: 8px; }
.amount-label { font-size: 14px; color: #475569; margin-bottom: 12px; display: block; font-weight: 500; }
.amount-stepper:deep(.el-input__wrapper) { padding: 4px 12px; }
.hint-text { font-size: 12px; color: #94a3b8; }

/* OCR BLOCK */
.receipt-row { display: flex; justify-content: space-between; margin-bottom: 8px; font-size: 13px; color: #4b5563; }
.receipt-row:last-child { margin-bottom: 0; }
.receipt-lbl { color: #6b7280; }
.receipt-val.fb { font-weight: 600; color: #b45309; font-size: 16px; }

</style>
