<script setup lang="ts">
/* eslint-disable no-useless-escape */
import { computed, nextTick, reactive, ref, watch } from 'vue';
import type { FormRules } from 'element-plus';
import dayjs from 'dayjs';
import { RDMS_REQ_PRIORITY_DICT_CODE, RDMS_TASK_ITEM_TYPE_DICT_CODE } from '@/constants/dict';
import { fetchPerformanceSheetPage } from '@/service/api';
import { useForm, useFormRules } from '@/hooks/common/form';
import { useDict } from '@/hooks/business/dict';
import BusinessFormDialog from '@/components/custom/business-form-dialog.vue';
import PerformanceExcelEditorDrawer from '../../../my-performance/modules/performance-excel-editor-drawer.vue';
import {
  type WorkReportStructuredSection,
  type WorkReportStructuredTask,
  getStructuredSections
} from '../../shared/types';
import { formatPeriodLabel } from '../../shared/types';

const props = defineProps<{
  reportType: string;
  period: string;
  mode?: 'add' | 'edit' | 'detail';
  scene?: 'fill' | 'detail' | 'approval' | 'sign-off';
  baseInfo?: Api.WorkReport.Monthly.MonthlyReport | null;
  model: Api.WorkReport.Monthly.MonthlyReportSaveParams;
  approvalModel: Api.WorkReport.Monthly.MonthlyReportApproveParams;
  supervisorSignaturePreviewUrl?: string;
  supervisorSignatureLoading?: boolean;
  employeeSignaturePreviewUrl?: string;
}>();

const emit = defineEmits<{
  back: [];
  requestApprove: [];
  requestReject: [];
  requestConfirmSignOff: [];
  changeApproval: [payload: Api.WorkReport.Monthly.MonthlyReportApproveParams];
  viewAudit: [];
}>();

const { formRef: pageFormRef, validate: validatePageForm } = useForm();
const { formRef: auditFormRef } = useForm();
const { createRequiredRule } = useFormRules();

interface ReviewItem {
  workItem: string;
  days: number;
  hours?: number;
  content: string;
  contentHtml: string;
  contentSections?: StructuredSection[];
  reflection: string;
  removable?: boolean;
}

interface PlanItem {
  workItem: string;
  target: string;
  targetHtml: string;
  targetSections?: StructuredSection[];
  supportNeed: string;
  removable?: boolean;
}

interface StructuredTask {
  title: string;
  priority?: string;
  progress?: number;
  hours?: number;
}

interface StructuredSection {
  category: string;
  tasks: StructuredTask[];
}

const formTitle = computed(() => `${props.reportType || '个人月报'}审批页`);
const EMPTY_TEXT = '';
const { getLabel: getTaskItemTypeLabel } = useDict(RDMS_TASK_ITEM_TYPE_DICT_CODE);
const { dictData: priorityDictData, getLabel: getPriorityLabel } = useDict(RDMS_REQ_PRIORITY_DICT_CODE);
const todayText = computed(() => dayjs().format('YYYY-MM-DD'));
const performanceDrawerVisible = ref(false);
const performanceDrawerLoading = ref(false);
const performanceDrawerMode = ref<'create' | 'edit'>('create');
const performanceDrawerRow = ref<Api.Performance.Sheet.Sheet | null>(null);
const isSignOffScene = computed(() => props.scene === 'sign-off');
const hasSupervisorSignaturePreview = computed(() => Boolean(props.supervisorSignaturePreviewUrl));
const hasEmployeeSignaturePreview = computed(() => Boolean(props.employeeSignaturePreviewUrl));

const auditDialogVisible = ref(false);
const auditForm = reactive({
  conclusion: '通过',
  opinion: ''
});

const rejectOpinionRequired = computed(() => auditForm.conclusion === '退回');
const pageValidationModel = reactive({
  get meetingDate() {
    return props.approvalModel.meetingDate || '';
  },
  get performanceResult() {
    return props.approvalModel.performanceResult || '';
  }
});

function formatDisplayDate(value: unknown) {
  const text = String(value ?? '')
    .replace(/^\[\s*/u, '')
    .replace(/\s*\]$/u, '')
    .trim();
  if (!text) return '--';

  const commaDateMatch = text.match(/^(\d{4})\s*,\s*(\d{1,2})\s*,\s*(\d{1,2})$/u);
  if (commaDateMatch) {
    const [, year, month, day] = commaDateMatch;
    return `${year}-${month.padStart(2, '0')}-${day.padStart(2, '0')}`;
  }

  return text;
}

const pageRules = computed<FormRules>(() => {
  if (isSignOffScene.value) {
    return {
      meetingDate: [createRequiredRule('请选择面谈日期')]
    };
  }

  return {
    performanceResult: [
      createRequiredRule('请输入绩效考核结果'),
      {
        validator: (_rule, value: string, callback) => {
          if (!value?.trim()) {
            callback(new Error('请输入绩效考核结果'));
            return;
          }

          callback();
        },
        trigger: 'blur'
      }
    ]
  };
});
const auditRules = computed<FormRules>(() => ({
  opinion: rejectOpinionRequired.value
    ? [
        createRequiredRule('请输入退回原因'),
        {
          validator: (_rule, value: string, callback) => {
            if (!value?.trim()) {
              callback(new Error('请输入退回原因'));
              return;
            }

            callback();
          },
          trigger: 'blur'
        }
      ]
    : []
}));

function patchApproval<K extends keyof Api.WorkReport.Monthly.MonthlyReportApproveParams>(
  key: K,
  value: Api.WorkReport.Monthly.MonthlyReportApproveParams[K]
) {
  emit('changeApproval', {
    ...props.approvalModel,
    [key]: value
  });
}

const feedbackForm = reactive({
  get strengthDesc() {
    return props.approvalModel.strengthDesc || '';
  },
  set strengthDesc(value: string) {
    patchApproval('strengthDesc', value);
  },
  get strengthExample() {
    return props.approvalModel.strengthExample || '';
  },
  set strengthExample(value: string) {
    patchApproval('strengthExample', value);
  },
  get weaknessDesc() {
    return props.approvalModel.weaknessDesc || '';
  },
  set weaknessDesc(value: string) {
    patchApproval('weaknessDesc', value);
  },
  get weaknessExample() {
    return props.approvalModel.weaknessExample || '';
  },
  set weaknessExample(value: string) {
    patchApproval('weaknessExample', value);
  },
  get improvementSuggestion() {
    return props.approvalModel.improvementSuggestion || '';
  },
  set improvementSuggestion(value: string) {
    patchApproval('improvementSuggestion', value);
  }
});

const performanceForm = reactive({
  get score() {
    return props.approvalModel.performanceResult || '';
  },
  set score(value: string) {
    patchApproval('performanceResult', value);
  }
});

const performanceTargetOption = computed(() => {
  const reporterId = props.baseInfo?.reporterId || '';
  const reporterName = props.baseInfo?.reporterName || '';

  if (!reporterId || !reporterName) return [];

  return [{ label: reporterName, value: reporterId }];
});

const performanceInitialEmployeeId = computed(() => props.baseInfo?.reporterId || '');
const performanceInitialPeriodMonth = computed(() => {
  const periodDate = props.baseInfo?.periodStartDate || props.baseInfo?.periodEndDate || '';
  if (!periodDate) return '';

  const targetMonth = dayjs(periodDate);
  return targetMonth.isValid() ? targetMonth.format('YYYY-MM') : '';
});

/** 绩效分数输入限制：0-100，最多一位小数 */
function handlePerformanceScoreInput(value: string) {
  // 只允许数字和一个小数点
  let filtered = value.replace(/[^\d.]/g, '');
  // 只保留第一个小数点
  const dotIndex = filtered.indexOf('.');
  if (dotIndex !== -1) {
    filtered = filtered.slice(0, dotIndex + 1) + filtered.slice(dotIndex + 1).replace(/\./g, '');
  }
  // 最多一位小数
  const match = filtered.match(/^(\d+)(?:\.(\d{0,1}))?/);
  if (match) {
    filtered = match[1] + (match[2] !== undefined ? `.${match[2]}` : '');
  }
  // 限制 0-100
  if (filtered !== '' && filtered !== '.') {
    const num = Number(filtered);
    if (num > 100) {
      filtered = '100';
    }
  }
  // 防止前导零（如 007），但允许 0 和 0.x
  if (/^0\d/.test(filtered) && !filtered.startsWith('0.')) {
    filtered = filtered.replace(/^0+/, '') || '0';
  }
  if (filtered !== value) {
    performanceForm.score = filtered;
  }
}

const mainForm = reactive({
  get reporter() {
    return props.baseInfo?.reporterName || '--';
  },
  get deptName() {
    return props.baseInfo?.reporterDeptName || '--';
  },
  get postName() {
    return props.baseInfo?.reporterPostName || '--';
  },
  get supervisor() {
    return props.baseInfo?.supervisorName || '--';
  },
  get meetingDate() {
    return props.approvalModel.meetingDate || '';
  },
  set meetingDate(value: string) {
    patchApproval('meetingDate', value);
  }
});

const signatureForm = reactive({
  get employeeDate() {
    return props.approvalModel.employeeSignedDate || '';
  },
  set employeeDate(value: string) {
    patchApproval('employeeSignedDate', value);
  },
  get supervisorDate() {
    return props.approvalModel.supervisorSignedDate || todayText.value;
  },
  set supervisorDate(value: string) {
    patchApproval('supervisorSignedDate', value);
  }
});

function normalizeTask(task: WorkReportStructuredTask): StructuredTask {
  return {
    title: task.title || '',
    priority: normalizePriorityCode(task.priority),
    progress: typeof task.progress === 'number' ? task.progress : undefined,
    hours: typeof task.hours === 'number' ? task.hours : undefined
  };
}

function normalizePriorityCode(value?: string | number | null) {
  const text = String(value ?? '').trim();
  if (!text) return undefined;

  const matchedByValue = priorityDictData.value.find(item => String(item.value) === text);
  if (matchedByValue) return String(matchedByValue.value);

  const matchedByLabel = priorityDictData.value.find(item => item.label === text);
  if (matchedByLabel) return String(matchedByLabel.value);

  const priorityLabelMatch = text.match(/^P(\d+)$/iu);
  if (priorityLabelMatch) return priorityLabelMatch[1];

  return text;
}

function resolvePriorityLabel(value?: string | number | null) {
  const priorityCode = normalizePriorityCode(value);
  if (!priorityCode) return '';
  return getPriorityLabel(priorityCode, {
    fallback: /^P\d+$/iu.test(priorityCode) ? priorityCode : `P${priorityCode}`
  });
}

function resolveTaskItemTypeLabel(value: string) {
  return getTaskItemTypeLabel(value, { fallback: value || '工作内容' });
}

function normalizeSection(section: WorkReportStructuredSection): StructuredSection {
  return {
    category: resolveTaskItemTypeLabel(section.category || '工作内容'),
    tasks: section.tasks.map(normalizeTask)
  };
}

function mergeSectionsByCategory(sections: StructuredSection[]) {
  const sectionMap = new Map<string, StructuredSection>();

  sections.forEach(section => {
    const category = section.category || '未分类';
    const existing = sectionMap.get(category);
    if (existing) {
      existing.tasks.push(...section.tasks);
    } else {
      sectionMap.set(category, { category, tasks: [...section.tasks] });
    }
  });

  return Array.from(sectionMap.values());
}

function escapeHtml(value: string) {
  return value
    .replace(/&/g, '&amp;')
    .replace(/</g, '&lt;')
    .replace(/>/g, '&gt;')
    .replace(/"/g, '&quot;')
    .replace(/'/g, '&#39;');
}

function stripStructuredTaskPrefixV2(value: string) {
  return value.trim().replace(/^\d+[.．、]\s*/u, '');
}

function stripStructuredTaskSuffixV2(value: string) {
  return value.trim().replace(/[。.!！？?]+$/u, '');
}

function resolveTaskMetricsV2(metricsText: string) {
  const parts = metricsText
    .split('/')
    .map(item => item.trim())
    .filter(Boolean);
  const priority = normalizePriorityCode(parts.find(item => /^P?\d+$/iu.test(item)));
  const progressText =
    metricsText.match(/进度\s*(\d+(?:\.\d+)?)%/u)?.[1] ||
    parts.find(item => /^\d+(?:\.\d+)?%$/u.test(item))?.replace(/%$/u, '');
  const hoursText = metricsText.match(/(\d+(?:\.\d+)?)h/u)?.[1];

  return {
    priority,
    progress: progressText === undefined ? undefined : Number(progressText),
    hours: hoursText === undefined ? undefined : Number(hoursText)
  };
}

function formatStructuredTaskLineV2(task: StructuredTask, showHours = false) {
  const title = stripStructuredTaskSuffixV2(stripStructuredTaskPrefixV2(task.title));
  const metrics = [
    task.priority ? resolvePriorityLabel(task.priority) : '',
    typeof task.progress === 'number' ? `${task.progress}%` : '',
    showHours && typeof task.hours === 'number' ? `${task.hours}h` : ''
  ].filter(Boolean);

  return `${title}${metrics.length ? `（${metrics.join(' / ')}）` : ''}`;
}

function createStructuredHtmlV2(sections: StructuredSection[], showHours = false) {
  return sections
    .map(section => {
      const categoryLabel = resolveTaskItemTypeLabel(section.category.trim());
      const tasks = section.tasks
        .map(
          (task, index) =>
            `<div class="rich-task-line">${escapeHtml(`${index + 1}、${formatStructuredTaskLineV2(task, showHours)}`)}</div>`
        )
        .join('');

      return `
        <div class="rich-section">
          <div class="rich-category-line"># ${escapeHtml(categoryLabel)}</div>
          ${tasks ? `<div class="rich-section-tasks">${tasks}</div>` : ''}
        </div>
      `;
    })
    .join('');
}

function createStructuredSectionsFromTextV2(text: string, defaultCategory: string): StructuredSection[] {
  const lines = normalizeEditorText(text).split('\n').filter(Boolean);
  if (!lines.length || lines.join('\n') === EMPTY_TEXT) return [];

  const sections: StructuredSection[] = [];
  const ensureSection = (categoryText: string) => {
    const category = categoryText.trim() || defaultCategory;
    let section = sections.find(item => item.category === category);
    if (!section) {
      section = { category, tasks: [] };
      sections.push(section);
    }
    return section;
  };

  let currentCategory = '';

  lines.forEach(line => {
    const trimmedLine = line.trim();
    if (!trimmedLine) return;

    if (trimmedLine.startsWith('#')) {
      currentCategory = trimmedLine.replace(/^#\s*/u, '').trim();
      if (currentCategory) ensureSection(currentCategory);
      return;
    }

    const legacyMatch = trimmedLine.match(/^(.+?)\s*-\s*(.+?)(?:[（(]([^()（）]*)[）)])?[。.!！？?]*$/u);
    if (legacyMatch) {
      const [, rawCategory, rawTitle, metricsText = ''] = legacyMatch;
      const category = rawCategory.trim();
      const title = rawTitle.trim();
      if (!category || !title) return;

      ensureSection(category).tasks.push({
        title,
        ...resolveTaskMetricsV2(metricsText)
      });
      return;
    }

    const normalizedLine = stripStructuredTaskSuffixV2(stripStructuredTaskPrefixV2(trimmedLine));
    if (!normalizedLine) return;
    const inlineMatch = normalizedLine.match(/^(.*?)(?:[（(]([^()（）]*)[）)])?$/u);
    const title = inlineMatch?.[1]?.trim() || '';
    const metricsText = inlineMatch?.[2]?.trim() || '';
    if (!title) return;

    ensureSection(currentCategory || defaultCategory).tasks.push({
      title,
      ...resolveTaskMetricsV2(metricsText)
    });
  });

  return sections.filter(section => section.tasks.length);
}

function normalizeEditorText(value: string) {
  return value
    .replace(/\u00A0/g, ' ')
    .split('\n')
    .map(line => line.trim())
    .filter(Boolean)
    .join('\n');
}

function toReviewItem(item: Api.WorkReport.Common.PersonalReportReviewItem): ReviewItem {
  const structuredSections = mergeSectionsByCategory(getStructuredSections(item.contentJson).map(normalizeSection));
  const contentSections = structuredSections.length
    ? structuredSections
    : createStructuredSectionsFromTextV2(item.contentText || '', item.itemTitle || '未分类');
  const contentHtml = contentSections.length
    ? createStructuredHtmlV2(contentSections, true)
    : escapeHtml(item.contentText || '').replace(/\n/g, '<br>') || EMPTY_TEXT;

  return {
    workItem: item.itemTitle || '未命名工作',
    days: 0,
    hours: item.workHours || 0,
    content: item.contentText || EMPTY_TEXT,
    contentHtml,
    contentSections,
    reflection: item.reflectionText || EMPTY_TEXT,
    removable: false
  };
}

function toPlanItem(item: Api.WorkReport.Common.PersonalReportPlanItem): PlanItem {
  const structuredSections = mergeSectionsByCategory(getStructuredSections(item.targetJson).map(normalizeSection));
  const targetSections = structuredSections.length
    ? structuredSections
    : createStructuredSectionsFromTextV2(item.targetText || '', item.itemTitle || '未分类');
  const targetHtml = targetSections.length
    ? createStructuredHtmlV2(targetSections)
    : escapeHtml(item.targetText || '').replace(/\n/g, '<br>') || EMPTY_TEXT;

  return {
    workItem: item.itemTitle || '未命名计划',
    target: item.targetText || EMPTY_TEXT,
    targetHtml,
    targetSections,
    supportNeed: item.supportNeed || EMPTY_TEXT,
    removable: false
  };
}

const reviewItems = computed<ReviewItem[]>(() => {
  return (props.model.reviewItems || []).map(toReviewItem);
});

const nextPlans = computed<PlanItem[]>(() => {
  return (props.model.planItems || []).map(toPlanItem);
});

const totalHours = computed(() => {
  const baseTotalWorkHours = Number(props.baseInfo?.totalWorkHours ?? 0);
  if (Number.isFinite(baseTotalWorkHours) && baseTotalWorkHours > 0) return baseTotalWorkHours;

  return (props.model.reviewItems || []).reduce((sum, item) => sum + Number(item.workHours || 0), 0);
});
const periodText = computed(() => formatPeriodLabel(props.model.periodLabel || props.period));

function formatTaskTitle(title: string) {
  const rawTitle = title.trim().replace(/^\d+[.、．]\s*/, '');
  return rawTitle.replace(/[。.!！？?]$/, '');
}

function getTaskPunctuation(title: string) {
  const rawTitle = title.trim().replace(/^\d+[.、．]\s*/, '');
  return rawTitle.match(/[。.!！？?]$/)?.[0] || '';
}

async function submitAudit() {
  if (!auditForm.conclusion) {
    window.$message?.warning('请选择审批结论');
    return;
  }

  // 仅"通过"时才校验面谈时间、绩效考核结果等必填项；"退回"时跳过这些校验。
  if (auditForm.conclusion !== '退回') {
    try {
      await validatePageForm();
    } catch {
      return;
    }
  }

  if (rejectOpinionRequired.value) {
    try {
      await auditFormRef.value?.validateField?.('opinion');
    } catch {
      return;
    }
  }

  patchApproval('employeeSignName', undefined);
  patchApproval('employeeSignedDate', undefined);
  patchApproval('supervisorSignedDate', signatureForm.supervisorDate);
  patchApproval(
    'reason',
    rejectOpinionRequired.value ? auditForm.opinion.trim() : auditForm.opinion.trim() || auditForm.conclusion
  );
  patchApproval('meetingDate', mainForm.meetingDate);
  auditDialogVisible.value = false;
  if (auditForm.conclusion === '退回') {
    emit('requestReject');
  } else {
    emit('requestApprove');
  }
}

function openAuditDialog() {
  Object.assign(auditForm, {
    conclusion: '通过',
    opinion: ''
  });
  auditDialogVisible.value = true;
  nextTick(() => {
    auditFormRef.value?.clearValidate();
  });
}

async function openPerformanceDrawer() {
  const reporterId = props.baseInfo?.reporterId || '';
  const periodMonth = performanceInitialPeriodMonth.value;

  performanceDrawerMode.value = 'create';
  performanceDrawerRow.value = null;

  if (reporterId && periodMonth) {
    performanceDrawerLoading.value = true;
    const { error, data } = await fetchPerformanceSheetPage({
      pageNo: 1,
      pageSize: 1,
      employeeId: reporterId,
      periodMonthRange: [periodMonth, periodMonth]
    });
    performanceDrawerLoading.value = false;

    const existingSheet = !error && data?.list?.length ? data.list[0] : null;
    if (existingSheet) {
      performanceDrawerMode.value = 'edit';
      performanceDrawerRow.value = existingSheet;
    }
  }

  performanceDrawerVisible.value = true;
}

function handlePerformanceSaved(payload: { actualScoreTotal: string }) {
  patchApproval('performanceResult', payload.actualScoreTotal);
}

async function handleConfirmSignOff() {
  try {
    await validatePageForm();
  } catch {
    return;
  }

  emit('requestConfirmSignOff');
}

watch(rejectOpinionRequired, async () => {
  if (!auditDialogVisible.value) return;

  await nextTick();
  auditFormRef.value?.clearValidate('opinion');
});

watch(
  () => auditForm.opinion,
  value => {
    if (!rejectOpinionRequired.value || !value?.trim()) return;
    auditFormRef.value?.clearValidate('opinion');
  }
);
</script>

<template>
  <div class="card form-page">
    <ElForm
      ref="pageFormRef"
      :model="pageValidationModel"
      :rules="pageRules"
      label-position="top"
      :validate-on-rule-change="false"
    >
      <div class="section">
        <div class="section-title">
          <span>基础信息</span>
        </div>
        <div class="compose-grid">
          <div class="field">
            <label>姓名</label>
            <ElInput v-model="mainForm.reporter" disabled />
          </div>
          <div class="field">
            <label>部门/方向</label>
            <ElInput v-model="mainForm.deptName" disabled />
          </div>
          <div class="field">
            <label>岗位</label>
            <ElInput v-model="mainForm.postName" disabled />
          </div>
          <div class="field">
            <label>周期</label>
            <ElInput :model-value="periodText" disabled />
          </div>
          <div class="field">
            <label>直接上级</label>
            <ElInput v-model="mainForm.supervisor" disabled />
          </div>
          <div class="field field-form-item">
            <label>面谈时间</label>
            <ElFormItem class="field-inline-form-item" prop="meetingDate">
              <ElDatePicker
                v-model="mainForm.meetingDate"
                type="date"
                value-format="YYYY-MM-DD"
                disabled
                style="width: 100%"
              />
            </ElFormItem>
          </div>
        </div>
      </div>

      <div class="section">
        <div class="section-title">
          <div class="section-title-left">
            <span>当期重点工作回顾</span>
            <span class="source-chip">{{ reviewItems.length }} 项工作</span>
            <span class="source-chip">共 {{ totalHours }}h</span>
          </div>
        </div>
        <div class="review-grid layout-row">
          <div v-if="!reviewItems.length">{{ EMPTY_TEXT }}</div>
          <div v-for="(item, index) in reviewItems" :key="index" class="review-card">
            <div class="review-index-cell">
              <span class="row-index">{{ index + 1 }}</span>
            </div>
            <div class="review-name-cell">
              <div class="work-title-line">
                <strong>{{ item.workItem }}</strong>
                <span>共 {{ item.hours }}h</span>
              </div>
            </div>
            <div class="review-action-cell"></div>

            <div class="review-editor-grid">
              <div class="field">
                <label>具体工作内容及成果描述</label>
                <div class="rich-editor readonly" v-html="item.contentHtml"></div>
              </div>
              <div class="field">
                <label>工作感悟</label>
                <div class="rich-editor readonly">{{ item.reflection || '' }}</div>
              </div>
            </div>
          </div>
        </div>
      </div>

      <div class="section">
        <div class="section-title">
          <span>当期工作反馈</span>
          <ElTag type="info" size="small" round>直接上级/分管领导填写</ElTag>
        </div>
        <div class="feedback-table">
          <div class="feedback-header">
            <div class="feedback-cell header-cell">优势/劣势项</div>
            <div class="feedback-cell header-cell">优势/劣势描述</div>
            <div class="feedback-cell header-cell">行为事例</div>
            <div class="feedback-cell header-cell">改进提升建议</div>
          </div>
          <div class="feedback-body">
            <div class="feedback-left">
              <div class="feedback-row-content">
                <div class="feedback-cell label-cell">
                  <span class="feedback-tag success">优势</span>
                </div>
                <div class="feedback-cell">
                  <div v-if="isSignOffScene" class="rich-editor readonly feedback-readonly">
                    {{ feedbackForm.strengthDesc || '' }}
                  </div>
                  <ElInput
                    v-else
                    v-model="feedbackForm.strengthDesc"
                    type="textarea"
                    :rows="2"
                    placeholder="请输入优势描述"
                  />
                </div>
                <div class="feedback-cell">
                  <div v-if="isSignOffScene" class="rich-editor readonly feedback-readonly">
                    {{ feedbackForm.strengthExample || '' }}
                  </div>
                  <ElInput
                    v-else
                    v-model="feedbackForm.strengthExample"
                    type="textarea"
                    :rows="2"
                    placeholder="请输入行为事例"
                  />
                </div>
              </div>
              <div class="feedback-row-content">
                <div class="feedback-cell label-cell">
                  <span class="feedback-tag warning">劣势</span>
                </div>
                <div class="feedback-cell">
                  <div v-if="isSignOffScene" class="rich-editor readonly feedback-readonly">
                    {{ feedbackForm.weaknessDesc || '' }}
                  </div>
                  <ElInput
                    v-else
                    v-model="feedbackForm.weaknessDesc"
                    type="textarea"
                    :rows="2"
                    placeholder="请输入劣势描述"
                  />
                </div>
                <div class="feedback-cell">
                  <div v-if="isSignOffScene" class="rich-editor readonly feedback-readonly">
                    {{ feedbackForm.weaknessExample || '' }}
                  </div>
                  <ElInput
                    v-else
                    v-model="feedbackForm.weaknessExample"
                    type="textarea"
                    :rows="2"
                    placeholder="请输入行为事例"
                  />
                </div>
              </div>
            </div>
            <div class="feedback-right">
              <div v-if="isSignOffScene" class="rich-editor readonly feedback-readonly">
                {{ feedbackForm.improvementSuggestion || '' }}
              </div>
              <ElInput
                v-else
                v-model="feedbackForm.improvementSuggestion"
                type="textarea"
                :rows="5"
                placeholder="请输入改进提升建议"
              />
            </div>
          </div>
        </div>
      </div>

      <div class="section">
        <div class="section-title">
          <span>当期绩效考核结果</span>
        </div>
        <div class="performance-result">
          <div class="performance-label">
            绩效考核结果
            <span v-if="!isSignOffScene" class="field-required-mark">*</span>
          </div>
          <ElFormItem class="performance-inline-form-item" prop="performanceResult">
            <div class="performance-input-group">
              <ElInput
                v-model="performanceForm.score"
                class="performance-input"
                placeholder="请输入考核分数"
                readonly
                @input="handlePerformanceScoreInput"
              />
              <ElButton
                v-if="!isSignOffScene"
                plain
                size="small"
                type="primary"
                :loading="performanceDrawerLoading"
                :disabled="!performanceTargetOption.length"
                @click="openPerformanceDrawer"
              >
                填写绩效
              </ElButton>
            </div>
          </ElFormItem>
        </div>
      </div>

      <div class="section">
        <div class="section-title">
          <div class="section-title-left">
            <span>下周期重点工作计划</span>
            <span class="source-chip">{{ nextPlans.length }} 项计划</span>
          </div>
        </div>
        <div class="plan-list layout-row">
          <div v-if="!nextPlans.length">{{ EMPTY_TEXT }}</div>
          <div v-for="(item, index) in nextPlans" :key="index" class="plan-card">
            <div class="plan-index-cell">
              <span class="row-index">{{ index + 1 }}</span>
            </div>
            <div class="plan-name-cell">
              <strong>{{ item.workItem }}</strong>
            </div>
            <div class="plan-action-cell"></div>
            <div class="plan-editor-grid">
              <div class="field">
                <label>具体目标</label>
                <div class="rich-editor readonly" v-html="item.targetHtml"></div>
              </div>
              <div class="field">
                <label>对他人协助的需求</label>
                <div class="rich-editor readonly">{{ item.supportNeed || '' }}</div>
              </div>
            </div>
          </div>
        </div>
      </div>

      <div class="signature-section">
        <template v-if="isSignOffScene">
          <div class="signature-row">
            <span>被考核人签名：</span>
            <div
              class="signature-preview-panel"
              :class="{ 'signature-preview-panel--empty': !hasEmployeeSignaturePreview }"
            >
              <img
                v-if="hasEmployeeSignaturePreview"
                :src="props.employeeSignaturePreviewUrl"
                alt="被考核人电子签名"
                class="signature-preview-image"
              />
              <span v-else>未维护电子签名</span>
            </div>
            <span>日期：</span>
            <div class="signature-readonly">{{ formatDisplayDate(signatureForm.employeeDate) }}</div>
          </div>
          <div class="signature-row">
            <span>上级签名：</span>
            <div
              v-loading="props.supervisorSignatureLoading"
              class="signature-preview-panel"
              :class="{ 'signature-preview-panel--empty': !hasSupervisorSignaturePreview }"
            >
              <img
                v-if="hasSupervisorSignaturePreview"
                :src="props.supervisorSignaturePreviewUrl"
                alt="上级电子签名"
                class="signature-preview-image"
              />
              <span v-else>未维护电子签名</span>
            </div>
            <span>日期：</span>
            <div class="signature-readonly">{{ formatDisplayDate(props.approvalModel.supervisorSignedDate) }}</div>
          </div>
          <div v-if="!hasEmployeeSignaturePreview" class="signature-warning">
            当前账号未维护电子签名，确认后后端将无法回填员工签名图片。
          </div>
        </template>
        <template v-else>
          <div class="signature-row">
            <span>被考核人签名：</span>
            <div class="signature-readonly">将在下签确认时自动补齐</div>
            <span>日期：</span>
            <div class="signature-readonly">将在下签确认时自动补齐</div>
          </div>
          <div class="signature-row">
            <span>上级签名：</span>
            <div
              v-loading="props.supervisorSignatureLoading"
              class="signature-preview-panel"
              :class="{ 'signature-preview-panel--empty': !hasSupervisorSignaturePreview }"
            >
              <img
                v-if="hasSupervisorSignaturePreview"
                :src="props.supervisorSignaturePreviewUrl"
                alt="上级电子签名"
                class="signature-preview-image"
              />
              <span v-else>未维护电子签名</span>
            </div>
            <span>日期：</span>
            <ElDatePicker
              v-model="signatureForm.supervisorDate"
              type="date"
              value-format="YYYY-MM-DD"
              placeholder="选择日期"
              class="signature-date"
            />
          </div>
          <div v-if="!hasSupervisorSignaturePreview" class="signature-warning">
            当前登录用户未维护电子签名，审批提交后将无法回填上级签名图片。
          </div>
        </template>
      </div>
    </ElForm>

    <div class="form-actions approval-form-actions">
      <ElButton @click="emit('back')">退出</ElButton>
      <ElButton v-if="isSignOffScene" type="primary" @click="handleConfirmSignOff">确认</ElButton>
      <ElButton v-else type="primary" @click="openAuditDialog">审批</ElButton>
    </div>

    <BusinessFormDialog
      v-if="!isSignOffScene"
      v-model="auditDialogVisible"
      title="月报审批"
      preset="sm"
      confirm-text="确认提交"
      @confirm="submitAudit"
    >
      <div class="audit-form">
        <div class="audit-field">
          <label>审批结论</label>
          <div class="audit-conclusion">
            <button
              type="button"
              class="conclusion-btn"
              :class="{ active: auditForm.conclusion === '通过', pass: auditForm.conclusion === '通过' }"
              @click="auditForm.conclusion = '通过'"
            >
              <svg width="16" height="16" viewBox="0 0 16 16" fill="none">
                <circle cx="8" cy="8" r="7" stroke="currentColor" stroke-width="1.5" />
                <path
                  d="M5 8.5L7 10.5L11 6"
                  stroke="currentColor"
                  stroke-width="1.5"
                  stroke-linecap="round"
                  stroke-linejoin="round"
                />
              </svg>
              通过
            </button>
            <button
              type="button"
              class="conclusion-btn"
              :class="{ active: auditForm.conclusion === '退回', reject: auditForm.conclusion === '退回' }"
              @click="auditForm.conclusion = '退回'"
            >
              <svg width="16" height="16" viewBox="0 0 16 16" fill="none">
                <circle cx="8" cy="8" r="7" stroke="currentColor" stroke-width="1.5" />
                <path d="M6 6L10 10M10 6L6 10" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" />
              </svg>
              退回
            </button>
          </div>
        </div>
        <ElForm
          ref="auditFormRef"
          :model="auditForm"
          :rules="auditRules"
          label-position="top"
          :validate-on-rule-change="false"
        >
          <ElFormItem
            :label="rejectOpinionRequired ? '退回原因' : '审批意见'"
            prop="opinion"
            :required="rejectOpinionRequired"
          >
            <ElInput
              v-model="auditForm.opinion"
              type="textarea"
              :rows="3"
              maxlength="1000"
              show-word-limit
              :placeholder="rejectOpinionRequired ? '请输入退回原因' : '请输入审批意见'"
            />
          </ElFormItem>
        </ElForm>
      </div>
    </BusinessFormDialog>

    <PerformanceExcelEditorDrawer
      v-if="!isSignOffScene"
      v-model:visible="performanceDrawerVisible"
      :row-data="performanceDrawerRow"
      :mode="performanceDrawerMode"
      :subordinate-options="performanceTargetOption"
      :initial-employee-id="performanceInitialEmployeeId"
      :initial-period-month="performanceInitialPeriodMonth"
      hide-draft-action
      @saved="handlePerformanceSaved"
      @saved-and-sent="handlePerformanceSaved"
    />
  </div>
</template>

<style scoped>
.card {
  border: 1px solid rgba(216, 224, 232, 0.88);
  border-radius: 18px;
  background: rgba(255, 255, 255, 0.86);
  box-shadow: 0 18px 45px rgba(15, 23, 42, 0.12);
  padding-top: 20px;
}

.card-title {
  margin: 0;
  font-size: 18px;
  font-weight: 900;
}

.card-subtitle {
  margin-top: 5px;
  color: #667085;
  font-size: 12px;
}

.form-head {
  display: grid;
  grid-template-columns: 1.2fr 0.8fr;
  gap: 18px;
  padding: 20px;
}

.report-title {
  display: flex;
  gap: 12px;
  align-items: center;
}

.type-mark {
  width: 48px;
  height: 48px;
  display: grid;
  place-items: center;
  flex-shrink: 0;
  border-radius: 16px;
  background: var(--el-color-primary-light-8);
  color: var(--el-color-primary);
  font-weight: 900;
}

.section {
  margin: 0 20px 18px;
  border: 1px solid #d8e0e8;
  border-radius: 16px;
  overflow: hidden;
  background: #fff;
}

.section-title {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 16px;
  padding: 13px 16px;
  background: #f8fbfc;
  border-bottom: 1px solid #d8e0e8;
  font-weight: 900;
}

.section-title-left {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}

.section-title-right {
  display: flex;
  align-items: center;
  gap: 10px;
}

.source-chip {
  display: inline-flex;
  align-items: center;
  height: 28px;
  padding: 0 10px;
  border-radius: 999px;
  background: #f3f7f9;
  color: #475467;
  font-size: 12px;
  font-weight: 800;
}

.compose-grid {
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  gap: 14px;
  padding: 16px;
  align-items: start;
}

@media (max-width: 1180px) {
  .compose-grid {
    grid-template-columns: repeat(3, 1fr);
  }
}

@media (max-width: 768px) {
  .compose-grid {
    grid-template-columns: repeat(2, 1fr);
  }
}

.field {
  display: grid;
  gap: 6px;
}

.field label {
  color: #667085;
  font-size: 12px;
  font-weight: 800;
}

.radio-group-full {
  width: 100%;
  display: flex;
  gap: 12px;
}

.radio-group-full :deep(.el-radio) {
  flex: 1;
  min-width: 0;
  justify-content: center;
  margin-right: 0;
}

.radio-group-full :deep(.el-radio.is-checked) {
  border-color: var(--el-color-primary);
  background: var(--el-color-primary-light-9);
}

.radio-group-full :deep(.el-radio__input.is-checked + .el-radio__label) {
  color: var(--el-color-primary);
}

.review-grid,
.plan-list {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 12px;
  padding: 16px;
}

.review-grid.layout-row,
.plan-list.layout-row {
  grid-template-columns: 1fr;
}

.review-card,
.plan-card {
  display: grid;
  gap: 12px;
  padding: 14px;
  border: 1px solid #e5edf1;
  border-radius: 12px;
  background: #fbfdfe;
}

.review-card {
  grid-template-columns: 88px minmax(225px, 330px) minmax(0, 1.35fr) minmax(0, 1fr) 44px;
  gap: 0;
  padding: 0;
  overflow: hidden;
}

.plan-card {
  grid-template-columns: 88px minmax(225px, 330px) minmax(0, 1.35fr) minmax(0, 1fr) 44px;
  gap: 0;
  padding: 0;
  overflow: hidden;
}

.review-index-cell,
.review-name-cell,
.review-editor-grid,
.review-action-cell,
.plan-index-cell,
.plan-name-cell,
.plan-editor-grid,
.plan-action-cell {
  padding: 14px;
}

.review-index-cell,
.plan-index-cell {
  display: grid;
  place-items: center;
}

.review-name-cell,
.plan-name-cell {
  display: grid;
  align-items: center;
  min-width: 0;
  overflow: hidden;
  border-left: 1px solid #e5edf1;
}

.review-name-cell .work-title-line,
.plan-name-cell {
  justify-content: center;
  text-align: center;
}

.review-name-cell .work-title-line {
  min-width: 0;
  flex-direction: column;
  gap: 4px;
}

.review-name-cell strong,
.plan-name-cell strong {
  display: block;
  width: 100%;
  min-width: 0;
  flex: 0 1 auto;
  color: #606266;
  font-size: 14px;
  font-weight: 400;
  line-height: 1.3;
  overflow: hidden;
  text-overflow: clip;
  white-space: normal;
  overflow-wrap: anywhere;
  word-break: break-word;
}

.review-card-head,
.plan-card-head {
  display: grid;
  grid-template-columns: 34px minmax(0, 1fr) 28px;
  gap: 10px;
  align-items: start;
}

.row-index {
  width: 30px;
  height: 30px;
  display: grid;
  place-items: center;
  border-radius: 999px;
  background: var(--el-color-primary);
  color: #fff;
  font-size: 13px;
  font-weight: 900;
}

.review-title-fields,
.review-title-readonly,
.plan-title-readonly {
  display: grid;
  gap: 10px;
  align-items: center;
}

.review-title-readonly {
  grid-template-columns: 1fr;
}

.work-title-line {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}

.review-title-readonly strong,
.plan-title-readonly strong {
  color: #606266;
  font-size: 14px;
  font-weight: 400;
  line-height: 1.3;
}

.work-title-line span,
.inline-metrics {
  display: flex;
  align-items: center;
  gap: 6px;
  color: #667085;
  font-size: 12px;
  font-weight: 800;
}

.review-name-cell .work-title-line span {
  justify-content: center;
  width: 100%;
}

.inline-metrics :deep(.el-input-number) {
  width: 92px;
}

.review-editor-grid,
.plan-editor-grid {
  display: grid;
  grid-template-columns: minmax(0, calc(57.45% - 6px + 50px)) minmax(0, calc(42.55% - 6px - 50px));
  gap: 12px;
}

.review-editor-grid {
  grid-column: 3 / span 2;
  grid-row: 1;
  border-left: 1px solid #e5edf1;
}

.plan-editor-grid {
  grid-column: 3 / span 2;
  grid-row: 1;
  border-left: 1px solid #e5edf1;
}

.item-remove-btn {
  width: 26px;
  height: 26px;
  display: grid;
  place-items: center;
  border: 1px solid #fecaca;
  border-radius: 999px;
  background: #fff;
  color: #dc2626;
  font: inherit;
  font-size: 18px;
  line-height: 1;
  cursor: pointer;
}

.item-remove-btn:hover {
  background: #fef2f2;
}

.rich-editor,
.structured-input,
.fixed-textarea :deep(.el-textarea__inner),
.styled-textarea :deep(.el-textarea__inner) {
  scrollbar-width: thin;
  scrollbar-color: #94a3b8 transparent;
}

.rich-editor::-webkit-scrollbar,
.structured-input::-webkit-scrollbar,
.fixed-textarea :deep(.el-textarea__inner::-webkit-scrollbar),
.styled-textarea :deep(.el-textarea__inner::-webkit-scrollbar) {
  width: 6px;
  height: 6px;
}

.rich-editor::-webkit-scrollbar-track,
.structured-input::-webkit-scrollbar-track,
.fixed-textarea :deep(.el-textarea__inner::-webkit-scrollbar-track),
.styled-textarea :deep(.el-textarea__inner::-webkit-scrollbar-track) {
  background: transparent;
}

.rich-editor::-webkit-scrollbar-thumb,
.structured-input::-webkit-scrollbar-thumb,
.fixed-textarea :deep(.el-textarea__inner::-webkit-scrollbar-thumb),
.styled-textarea :deep(.el-textarea__inner::-webkit-scrollbar-thumb) {
  border-radius: 999px;
  background: #94a3b8;
}

.rich-editor::-webkit-scrollbar-button,
.structured-input::-webkit-scrollbar-button,
.fixed-textarea :deep(.el-textarea__inner::-webkit-scrollbar-button),
.styled-textarea :deep(.el-textarea__inner::-webkit-scrollbar-button) {
  width: 0;
  height: 0;
  display: none;
}

.rich-editor {
  width: 100%;
  min-height: 86px;
  padding: 5px 11px;
  border: 1px solid #dcdfe6;
  border-radius: 4px;
  background: #fff;
  box-sizing: border-box;
  color: #334155;
  font-size: 13px;
  line-height: 1.6;
  outline: none;
  overflow: auto;
  transition:
    border-color 0.2s,
    box-shadow 0.2s;
}

.rich-editor.readonly {
  background: #fff;
  cursor: default;
}

.rich-editor :deep(.rich-section) {
  display: grid;
  gap: 4px;
  min-width: 0;
}

.rich-editor :deep(.rich-section + .rich-section) {
  margin-top: 8px;
  padding-top: 8px;
  border-top: 1px dashed #cbd5e1;
}

.rich-editor :deep(.rich-section-title) {
  position: relative;
  min-width: 0;
  padding-left: 14px;
  color: var(--el-color-primary);
  font-size: 13px;
  font-weight: 800;
  white-space: normal;
  overflow-wrap: anywhere;
  word-break: break-word;
}

.rich-editor :deep(.rich-section-title::before) {
  content: '';
  position: absolute;
  left: 0;
  top: 4px;
  width: 4px;
  height: 16px;
  border-radius: 999px;
  background: var(--el-color-primary);
}

.rich-editor :deep(.rich-section-task) {
  display: grid;
  gap: 6px;
  padding-left: 14px;
  color: #334155;
  font-size: 13px;
  line-height: 1.6;
  white-space: normal;
  overflow-wrap: anywhere;
  word-break: break-word;
}

.rich-editor :deep(.rich-category-line) {
  color: var(--el-color-primary);
  font-size: 13px;
  font-weight: 700;
  line-height: 1.6;
  white-space: normal;
  overflow-wrap: anywhere;
  word-break: break-word;
}

.rich-editor :deep(.rich-section-tasks) {
  padding-left: 0;
}

.rich-editor :deep(.rich-task-line) {
  color: #334155;
  font-size: 13px;
  line-height: 1.6;
  white-space: normal;
  overflow-wrap: anywhere;
  word-break: break-word;
}

.rich-editor :deep(.rich-section-task) {
  display: block;
  white-space: normal;
}

.rich-editor :deep(.rich-section-task-title) {
  color: #334155;
  overflow-wrap: anywhere;
  word-break: break-word;
}

.rich-editor :deep(.rich-inline-metrics) {
  display: inline-flex;
  align-items: center;
  height: 18px;
  margin-left: 3px;
  padding: 0 6px;
  border-radius: 999px;
  background: #f3f7f9;
  color: #475467;
  font-size: 11px;
  font-weight: 800;
  vertical-align: 1px;
  user-select: text;
  cursor: text;
}

.structured-input {
  border: 1px solid #dcdfe6;
  border-radius: 4px;
  background: #fff;
  padding: 5px 11px;
  cursor: pointer;
  transition: border-color 0.2s;
  height: 86px;
  overflow: auto;
  box-sizing: border-box;
}

.structured-input:hover {
  border-color: #c0c4cc;
}

.structured-input.disabled {
  background: #f8fafc;
  color: #334155;
  cursor: not-allowed;
}

.fixed-textarea :deep(.el-textarea__inner) {
  height: 86px;
  min-height: 86px;
  max-height: 86px;
  resize: none;
  overflow: auto;
  padding: 5px 11px;
  line-height: 1.6;
  white-space: pre-wrap;
}

.styled-textarea :deep(.el-textarea__inner) {
  overflow: auto;
}

.review-editor-grid .field,
.plan-editor-grid .field {
  display: flex;
  flex-direction: column;
  min-height: 0;
}

.review-editor-grid .rich-editor,
.plan-editor-grid .rich-editor {
  flex: 1;
  height: 100%;
}

.structured-input.auto-height {
  height: auto;
  min-height: 86px;
  max-height: none;
  overflow: visible;
}

.structured-input-inner {
  color: #334155;
  line-height: 1.6;
  font-size: 13px;
}

.structured-input-inner.plain {
  white-space: pre-wrap;
}

.structured-section {
  display: grid;
  gap: 4px;
}

.structured-section + .structured-section {
  margin-top: 8px;
  padding-top: 8px;
  border-top: 1px dashed #cbd5e1;
}

.structured-section-title {
  position: relative;
  padding-left: 14px;
  color: var(--el-color-primary);
  font-size: 13px;
  font-weight: 800;
}

.structured-section-title::before {
  content: '';
  position: absolute;
  left: 0;
  top: 4px;
  width: 4px;
  height: 16px;
  border-radius: 999px;
  background: var(--el-color-primary);
}

.structured-section-tasks {
  display: block;
  color: #334155;
  padding-left: 14px;
  font-size: 13px;
  line-height: 1.6;
}

.structured-section-task {
  display: inline;
  margin-right: 5px;
  white-space: normal;
}

.structured-section-task-title {
  color: #334155;
  overflow-wrap: anywhere;
}

.structured-inline-metrics {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  height: 18px;
  margin-left: 3px;
  padding: 0 6px;
  border-radius: 999px;
  background: #f3f7f9;
  color: #475467;
  font-size: 11px;
  font-weight: 800;
  vertical-align: 1px;
  user-select: none;
  cursor: default;
}

.structured-inline-metrics span + span::before {
  content: '•';
  margin-right: 3px;
}

.audit-form {
  display: grid;
  gap: 18px;
}

.audit-field {
  display: grid;
  gap: 8px;
}

.audit-field label {
  color: #475467;
  font-size: 13px;
  font-weight: 800;
}

.audit-conclusion {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 12px;
}

.conclusion-btn {
  height: 44px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  border: 1px solid #d8e0e8;
  border-radius: 8px;
  background: #fff;
  color: #475467;
  font: inherit;
  font-size: 14px;
  font-weight: 800;
  cursor: pointer;
  transition: all 0.18s ease;
}

.conclusion-btn:hover {
  border-color: var(--el-color-primary);
  color: var(--el-color-primary);
}

.conclusion-btn.active.pass {
  border-color: var(--el-color-primary);
  background: var(--el-color-primary-light-9);
  color: var(--el-color-primary);
}

.conclusion-btn.active.reject {
  border-color: #dc2626;
  background: #fef2f2;
  color: #dc2626;
}

.field-form-item {
  align-self: start;
}

.field-required-mark {
  margin-left: 2px;
  color: #f04438;
}

.field-inline-form-item,
.performance-inline-form-item {
  margin-bottom: 0;
}

.field-inline-form-item :deep(.el-form-item__content),
.performance-inline-form-item :deep(.el-form-item__content) {
  display: flex;
  align-items: center;
  line-height: 1;
}

.field-inline-form-item :deep(.el-form-item__error),
.performance-inline-form-item :deep(.el-form-item__error) {
  padding-top: 4px;
}

.field-inline-form-item {
  margin-top: -1px;
}

.field-inline-form-item :deep(.el-date-editor) {
  width: 100%;
}

.feedback-table {
  display: grid;
  border: 1px solid #e2e8f0;
  margin: 16px 16px 16px 16px;
  border-radius: 14px;
  overflow: hidden;
  background: #fff;
  box-shadow: 0 2px 8px rgba(15, 23, 42, 0.04);
}

.feedback-header {
  display: grid;
  grid-template-columns: 120px 1fr 1fr 280px;
  background: #f8fafc;
  border-bottom: 1px solid #e2e8f0;
}

.feedback-body {
  display: grid;
  grid-template-columns: 1fr 350px;
}

.feedback-left {
  display: grid;
}

.feedback-row-content {
  display: grid;
  grid-template-columns: 120px 1fr 1fr;
  border-bottom: 1px solid #f1f5f9;
  transition: background 0.15s ease;
}

.feedback-row-content:last-child {
  border-bottom: none;
}

.feedback-row-content:hover {
  background: #fafbfc;
}

.feedback-right {
  padding: 16px;
  border-left: 1px solid #f1f5f9;
  background: #fafbfc;
  display: flex;
  align-items: stretch;
}

.feedback-right :deep(.el-textarea__inner) {
  background: #fff;
  height: 100% !important;
}

.feedback-readonly {
  width: 100%;
  min-height: 60px;
}

.feedback-cell {
  padding: 16px;
  border-right: 1px solid #f1f5f9;
  display: flex;
  align-items: flex-start;
}

.feedback-cell:last-child {
  border-right: none;
}

.header-cell {
  color: #475569;
  font-size: 13px;
  font-weight: 700;
  justify-content: center;
  letter-spacing: 0.3px;
}

.label-cell {
  justify-content: center;
  align-items: center;
  background: #fafbfc;
}

.feedback-tag {
  padding: 6px 14px;
  border-radius: 20px;
  font-size: 13px;
  font-weight: 800;
  letter-spacing: 0.5px;
}

.feedback-tag.success {
  background: var(--el-color-primary-light-9);
  color: var(--el-color-primary);
  border: 1px solid var(--el-color-primary-light-7);
}

.feedback-tag.warning {
  background: #fff7ed;
  color: #c2410c;
  border: 1px solid #fed7aa;
}

.signature-section {
  margin: 24px 20px;
  padding: 16px 24px;
  border: 1px solid #e2e8f0;
  border-radius: 14px;
  background: #fff;
  box-shadow: 0 2px 8px rgba(15, 23, 42, 0.04);
}

.signature-row {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 8px;
  padding: 10px 0;
  color: #475569;
  font-size: 14px;
  font-weight: 600;
}

.signature-row + .signature-row {
  border-top: 1px solid #f1f5f9;
}

.signature-readonly {
  min-width: 180px;
  padding: 0 4px 4px;
  border-bottom: 1px solid #e2e8f0;
  color: #64748b;
  font-size: 14px;
  text-align: center;
}

.signature-preview-panel {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 116px;
  height: 44px;
  padding: 3px;
  border: 1px dashed #cbd5e1;
  border-radius: 8px;
  background: #f8fafc;
  box-sizing: border-box;
}

.signature-preview-panel--empty {
  color: #94a3b8;
  font-size: 13px;
}

.signature-preview-image {
  max-width: 98px;
  max-height: 28px;
  object-fit: contain;
}

.signature-date {
  width: 130px;
}

.signature-date :deep(.el-input__wrapper) {
  box-shadow: none !important;
  background: transparent;
  border-bottom: 1px solid #e2e8f0;
  border-radius: 0;
  padding: 0 4px;
}

.signature-date :deep(.el-input__inner) {
  text-align: center;
  color: #334155;
  font-weight: 600;
}

.signature-date :deep(.el-input__inner::placeholder) {
  color: #cbd5e1;
  font-weight: 400;
}

.signature-warning {
  margin-top: 8px;
  color: #c2410c;
  font-size: 13px;
  font-weight: 600;
  text-align: right;
}

.performance-result {
  display: flex;
  align-items: center;
  gap: 20px;
  padding: 16px 20px;
  border: 1px solid #e2e8f0;
  border-radius: 12px;
  background: #fff;
  margin: 16px 16px 16px 16px;
}

.performance-label {
  flex: 0 0 auto;
  color: #475569;
  font-size: 14px;
  font-weight: 700;
  white-space: nowrap;
}

.performance-inline-form-item {
  margin-bottom: 0;
  flex: 0 0 auto;
}

.performance-input-group {
  display: inline-flex;
  align-items: center;
  gap: 12px;
}

.performance-input {
  width: 200px;
}

.performance-input :deep(.el-input__wrapper) {
  box-shadow: none !important;
  background: #f8fafc;
  border-bottom: 1px solid #e2e8f0;
  border-radius: 0;
  padding: 0 4px;
}

.performance-input :deep(.el-input__inner) {
  color: #334155;
  font-weight: 600;
  text-align: center;
}

.performance-input :deep(.el-input__inner[readonly]) {
  cursor: default;
}

.performance-input :deep(.el-input__inner::placeholder) {
  color: #cbd5e1;
  font-weight: 400;
}

.form-actions {
  display: flex;
  justify-content: flex-end;
  gap: 10px;
  padding: 14px 20px;
  border-top: 1px solid #d8e0e8;
  background: #fff;
}

.approval-form-actions {
  position: sticky;
  z-index: 5;
  bottom: 0;
  margin-top: auto;
  margin-bottom: 0;
  border-bottom-left-radius: 18px;
  border-bottom-right-radius: 18px;
  background: #f5f7fa;
  box-shadow: 0 -8px 18px rgba(15, 23, 42, 0.06);
}

@media (max-width: 1180px) {
  .form-head,
  .compose-grid,
  .review-title-fields,
  .review-title-readonly,
  .plan-title-readonly,
  .review-editor-grid,
  .plan-editor-grid {
    grid-template-columns: 1fr;
  }

  .review-card,
  .plan-card {
    grid-template-columns: 1fr;
  }

  .review-index-cell,
  .review-name-cell,
  .review-editor-grid,
  .review-action-cell,
  .plan-index-cell,
  .plan-name-cell,
  .plan-editor-grid,
  .plan-action-cell {
    grid-column: auto;
    grid-row: auto;
    border-left: 0;
  }

  .review-name-cell,
  .review-editor-grid,
  .plan-name-cell,
  .plan-editor-grid {
    border-top: 1px solid #e5edf1;
  }

  .section-title {
    align-items: flex-start;
  }
}
</style>
