<template>
  <div class="container" :class="{ 'is-single-page': isSinglePage }">
    <el-card
      :shadow="device === 'desktop' ? 'always' : 'never'"
      class="card"
      :style="{
        border: device === 'desktop' ? '' : 'none',
        background: device === 'desktop' ? '' : '#f7f8fa',
        'min-height': device === 'desktop' ? '' : '100vh',
      }"
    >
      <template #header>
        <p>{{ config.customModel ? config.customModel.name : "办理" }}</p>
      </template>

      <template>
        <Steps :logs="config.logs" :current-node="node"></Steps>
        
        <!-- 关联流程组件 -->
        <RelatedFlows
          v-model="form.related_flow_ids"
          :readonly="/\/detail/.test($route.path) || (!!$route.query.flow_id && !isFirstNode)"
          :flow-id="$route.query.flow_id"
          :collapsible="(!/\/detail/.test($route.path)) && (!$route.query.flow_id || isFirstNode)"
          style="margin-bottom: 20px;"
        ></RelatedFlows>
        
        <!-- 关联的支付信息（查看/办理：只要有 flow_id 就展示，没数据时展示空态） -->
        <el-card
          v-if="$route.query.flow_id"
          shadow="never"
          style="margin-bottom: 20px;"
          v-loading="loadingPayments"
        >
          <div slot="header" class="clearfix">
            <span style="font-weight: bold;">关联的支付信息</span>
            <span v-if="relatedPayments.length > 0" style="float: right; color: #909399; font-size: 12px;">
              共 {{ relatedPayments.length }} 条
            </span>
          </div>
          <div v-if="relatedPayments.length === 0" style="color:#909399; padding: 8px 0;">
            暂无关联的支付信息
          </div>

          <div v-for="(payment, index) in relatedPayments" v-else :key="payment.id" style="margin-bottom: 20px;">
            <el-divider v-if="index > 0"></el-divider>
            <div class="payment-info">
              <el-row :gutter="20">
                <el-col :span="12">
                  <div class="info-item">
                    <span class="label">支付编号：</span>
                    <span class="value">{{ payment.serial_number || '-' }}</span>
                  </div>
                </el-col>
                <el-col :span="12">
                  <div class="info-item">
                    <span class="label">支付状态：</span>
                    <el-tag :type="getPaymentStatusType(payment.status)" size="small">
                      {{ payment.status_text || '-' }}
                    </el-tag>
                  </div>
                </el-col>
              </el-row>
              <el-row :gutter="20" style="margin-top: 10px;">
                <el-col :span="12">
                  <div class="info-item">
                    <span class="label">支付金额：</span>
                    <span class="value" style="color: #F56C6C; font-weight: bold;">
                      ¥{{ formatAmount(payment.total_amount) }}
                    </span>
                  </div>
                </el-col>
                <el-col :span="12">
                  <div class="info-item">
                    <span class="label">支付日期：</span>
                    <span class="value">{{ formatDate(payment.payment_date) || '-' }}</span>
                  </div>
                </el-col>
              </el-row>
              <el-row :gutter="20" style="margin-top: 10px;" v-if="payment.payment_type_info">
                <el-col :span="12">
                  <div class="info-item">
                    <span class="label">支付类型：</span>
                    <span class="value">{{ payment.payment_type_info.payment_type_text || '-' }}</span>
                  </div>
                </el-col>
                <el-col :span="12" v-if="payment.payment_type_info.breadcrumb">
                  <div class="info-item">
                    <span class="label">支付分类：</span>
                    <span class="value">{{ formatBreadcrumb(payment.payment_type_info) }}</span>
                  </div>
                </el-col>
              </el-row>
              <el-row :gutter="20" style="margin-top: 10px;" v-if="payment.description">
                <el-col :span="24">
                  <div class="info-item">
                    <span class="label">支付说明：</span>
                    <span class="value">{{ payment.description }}</span>
                  </div>
                </el-col>
              </el-row>

              <!-- 支付模板字段（fields_data / element_values 双写）：只读展示 -->
              <div
                v-if="getVisiblePaymentTemplateElements(payment).length > 0"
                style="margin-top: 15px;"
                v-loading="loadingPaymentTemplateElements"
              >
                <el-descriptions :column="1" border size="small" class="payment-extra-descriptions">
                  <el-descriptions-item
                    v-for="el in getVisiblePaymentTemplateElements(payment)"
                    :key="`pay_el_${el.id}`"
                    :label="el.name || '字段'"
                  >
                    <!-- checklist：纯展示（不渲染表单控件） -->
                    <div v-if="el.type === 'checklist'" class="oa-readonly-checklist">
                      <div
                        v-for="(opt, idx) in (getPaymentChecklistOptions(el) || [])"
                        :key="opt.value || idx"
                        v-show="isChecklistChecked(getPaymentTemplateElementValue(el, payment), opt.value) || getPaymentChecklistRemark(el, opt.value, payment)"
                        class="oa-checklist-option-item"
                      >
                        <div class="oa-checklist-row">
                          <span
                            class="oa-checklist-box"
                            :class="{ checked: isChecklistChecked(getPaymentTemplateElementValue(el, payment), opt.value) }"
                            aria-hidden="true"
                          ></span>
                          <span class="oa-checklist-text">
                            <span class="oa-checklist-label">{{ `${idx + 1}、${opt.label}` }}</span>
                            <span v-if="getPaymentChecklistRemark(el, opt.value, payment)" class="oa-checklist-remark">
                              {{ getPaymentChecklistRemark(el, opt.value, payment) }}
                            </span>
                          </span>
                        </div>
                      </div>
                    </div>

                    <!-- 会议纪要：点击抽屉查看 -->
                    <div v-else-if="el.type === 'meeting_minutes'">
                      <el-link
                        v-if="extractMeetingMinuteId(getPaymentTemplateElementValue(el, payment))"
                        type="primary"
                        :underline="false"
                        @click="openMeetingMinutesDrawer(extractMeetingMinuteId(getPaymentTemplateElementValue(el, payment)))"
                      >
                        {{ (getPaymentTemplateElementValue(el, payment) && getPaymentTemplateElementValue(el, payment).title) ? getPaymentTemplateElementValue(el, payment).title : '查看会议纪要' }}
                      </el-link>
                      <span v-else>-</span>
                    </div>

                    <!-- 明细表格 -->
                    <div v-else-if="el.type === 'detail_table'">
                      <div class="oa-table-x-scroll">
                        <el-table
                          :data="Array.isArray(getPaymentTemplateElementValue(el, payment)) ? getPaymentTemplateElementValue(el, payment) : []"
                          border
                          size="small"
                          :fit="false"
                          style="width: 100%;"
                        >
                          <el-table-column type="index" label="#" width="60" align="center" />
                          <el-table-column
                            v-for="col in (getPaymentDetailTableFields(el.id) || [])"
                            :key="col.id || col.field_key"
                            :prop="col.field_key"
                            :label="col.field_name || col.field_key"
                            min-width="120"
                            show-overflow-tooltip
                          />
                        </el-table>
                      </div>
                    </div>

                    <!-- 审批流程：显示 no/title，若有 model_id 则可点开流程抽屉 -->
                    <div v-else-if="el.type === 'approval_flow'">
                      <el-link
                        v-if="getPaymentTemplateElementValue(el, payment) && el.model_id"
                        type="primary"
                        :underline="false"
                        @click="openFlowDrawer({
                          flowId: Number(getPaymentTemplateElementValue(el, payment)),
                          customModelId: Number(el.model_id),
                          title: getPaymentFlowDisplayInfo(Number(getPaymentTemplateElementValue(el, payment)))
                        })"
                      >
                        {{ getPaymentFlowDisplayInfo(Number(getPaymentTemplateElementValue(el, payment))) }}
                      </el-link>
                      <span v-else>{{ getPaymentFlowDisplayInfo(Number(getPaymentTemplateElementValue(el, payment))) || '-' }}</span>
                    </div>

                    <!-- 附件 -->
                    <div v-else-if="el.type === 'form_element' && el.field_type === 'attachment'">
                      <div v-for="(f, idx) in getPaymentFileItems(el, payment)" :key="`${el.id}_${idx}`" style="margin-bottom: 6px;">
                        <el-link v-if="f.url" type="primary" :underline="false" :href="f.url" target="_blank">
                          {{ f.name || '附件' }}
                        </el-link>
                        <span v-else>{{ f.name || '附件' }}</span>
                      </div>
                      <span v-if="getPaymentFileItems(el, payment).length === 0">-</span>
                    </div>

                    <!-- 默认展示 -->
                    <span v-else>
                      {{ formatReadonlyValue(getPaymentTemplateElementValue(el, payment)) }}
                    </span>
                  </el-descriptions-item>
                </el-descriptions>
              </div>

              <!-- 非直接支付：还原“非直接支出(PlannedExpenditure)”的元素模板与内容 -->
              <el-card
                v-if="getPlannedExpenditureIdsFromPayment(payment).length > 0"
                shadow="never"
                style="margin-top: 15px;"
                v-loading="loadingIndirectExpenditures"
              >
                <div slot="header" class="clearfix">
                  <span style="font-weight: bold;">相关事前流程</span>
                </div>

                <div v-for="(expId, di) in getPlannedExpenditureIdsFromPayment(payment)" :key="`exp-${expId}`" style="margin-bottom: 20px;">
                  <el-divider v-if="di > 0"></el-divider>
                  <div v-if="indirectExpenditureMap[expId] && indirectExpenditureMap[expId].title" style="margin-bottom: 10px; font-weight: 600; color:#303133;">
                    {{ indirectExpenditureMap[expId].title }}
                  </div>

                  <el-descriptions :column="1" border class="indirect-payment-descriptions">
                    <template v-for="f in getIndirectTemplateFields((indirectExpenditureMap[expId] && indirectExpenditureMap[expId].category_id))">
                      <el-descriptions-item
                        v-if="shouldRenderPlannedField(expId, f)"
                        :key="f.key || (f.element_id || f.label)"
                        :label="f.label || f.name || '字段'"
                      >
                      <!-- oa_custom_model / out_custom_model：多条流程，顿号分隔，点击抽屉查看 -->
                      <template v-if="f.element_type === 'oa_custom_model' || f.element_type === 'out_custom_model'">
                        <span v-if="getOaCustomModelBindings(expId, f).length">
                          <span v-for="(b, bi) in getOaCustomModelBindings(expId, f)" :key="`${b.flow_instance_id}_${bi}`">
                            <span v-if="bi > 0">、</span>
                            <el-link
                              type="primary"
                              :underline="false"
                              @click="openFlowDrawer({
                                flowId: b.flow_instance_id,
                                customModelId: b.flow_custom_model_id,
                                title: getFlowDisplayName(b.flow_instance_id, b.flow_display_name)
                              })"
                            >
                              {{ getFlowDisplayName(b.flow_instance_id, b.flow_display_name) }}
                            </el-link>
                          </span>
                        </span>
                        <span v-else>-</span>
                      </template>

                      <!-- checklist：全量展示选项 + 勾选 + 备注 -->
                      <template v-else-if="f.element_type === 'checklist'">
                        <div class="oa-readonly-checklist">
                          <div
                            v-for="(opt, idx) in (getPlannedChecklistOptions(f) || [])"
                            :key="opt.value || idx"
                            class="oa-checklist-option-item"
                          >
                            <div class="oa-checklist-row">
                              <span
                                class="oa-checklist-box"
                                :class="{ checked: isChecklistChecked(getIndirectFieldValue(expId, getTplFieldKey(f)), opt.value) }"
                                aria-hidden="true"
                              ></span>
                              <span class="oa-checklist-text">
                                <span class="oa-checklist-label">{{ `${idx + 1}、${opt.label}` }}</span>
                                <span v-if="getPlannedChecklistRemark(expId, f, opt.value)" class="oa-checklist-remark">
                                  {{ getPlannedChecklistRemark(expId, f, opt.value) }}
                                </span>
                              </span>
                            </div>
                          </div>
                        </div>
                      </template>

                      <!-- 附件/文件：还原原始名并可点击 -->
                      <template v-else-if="f.element_type === 'attachment' || f.element_type === 'file'">
                        <div v-for="(fi, fidx) in getPlannedFileItems(expId, f)" :key="`${expId}_${f.element_id || f.key}_${fidx}`" style="margin-bottom: 6px;">
                          <el-link
                            v-if="fi.url"
                            type="primary"
                            :underline="false"
                            :href="fi.url"
                            target="_blank"
                            class="file-link"
                            @click="handlePlannedFileClick(fi)"
                          >
                            {{ fi.name || '附件' }}
                          </el-link>
                          <el-link
                            v-else
                            type="primary"
                            :underline="false"
                            class="file-link file-link--no-url"
                            @click="handlePlannedFileClick(fi)"
                          >
                            {{ fi.name || '附件' }}
                          </el-link>
                        </div>
                        <span v-if="getPlannedFileItems(expId, f).length === 0">-</span>
                      </template>

                      <!-- meeting_minutes：点击抽屉查看 -->
                      <template v-else-if="f.element_type === 'meeting_minutes'">
                        <el-link
                          v-if="extractMeetingMinuteId(getIndirectFieldValue(expId, getTplFieldKey(f)))"
                          type="primary"
                          :underline="false"
                          @click="openMeetingMinutesDrawer(extractMeetingMinuteId(getIndirectFieldValue(expId, getTplFieldKey(f))))"
                        >
                          查看会议纪要
                        </el-link>
                        <span v-else>-</span>
                      </template>

                      <!-- detail_table：渲染为表格 -->
                      <template v-else-if="f.element_type === 'detail_table'">
                        <div class="oa-table-x-scroll">
                          <el-table
                            :data="Array.isArray(getIndirectFieldValue(expId, getTplFieldKey(f))) ? getIndirectFieldValue(expId, getTplFieldKey(f)) : []"
                            border
                            size="small"
                            :fit="false"
                            style="width: 100%;"
                          >
                            <el-table-column
                              v-for="col in (indirectDetailTableFieldsMap[f.element_id] || [])"
                              :key="col.id || col.field_key"
                              :prop="col.field_key"
                              :label="col.field_name || col.field_key"
                              min-width="120"
                              show-overflow-tooltip
                            >
                              <template slot-scope="scope">
                                {{ formatDetailTableCell(scope.row ? scope.row[col.field_key] : null, col) }}
                              </template>
                            </el-table-column>
                          </el-table>
                        </div>
                      </template>

                      <!-- 其他类型：通用展示 -->
                      <template v-else>
                        {{ formatReadonlyValue(getIndirectFieldValue(expId, getTplFieldKey(f))) }}
                      </template>
                      </el-descriptions-item>
                    </template>
                  </el-descriptions>
                </div>
              </el-card>

              <!-- 关联合同：合同信息链 + 合同相关支付列表 -->
              <el-card
                v-if="getContractIdFromPayment(payment)"
                shadow="never"
                style="margin-top: 15px;"
                v-loading="loadingContractPayments"
              >
                <div slot="header" class="clearfix">
                  <span style="font-weight: bold;">相关合同信息</span>
                </div>

                <!-- 合同支付时序信息 -->
                <div class="contract-payments-summary" style="margin-bottom: 12px;">
                  <el-row :gutter="20">
                    <el-col :span="12">
                      <div class="info-item">
                        <span class="label">合同相关支付次数：</span>
                        <span class="value">{{ getContractPaymentStats(payment.id).total_count }}</span>
                      </div>
                    </el-col>
                    <el-col :span="12">
                      <div class="info-item">
                        <span class="label">当前为第几次支付：</span>
                        <span class="value">
                          {{
                            getContractPaymentStats(payment.id).current_index
                              ? `${getContractPaymentStats(payment.id).current_index} / ${getContractPaymentStats(payment.id).total_count}`
                              : '-'
                          }}
                        </span>
                      </div>
                    </el-col>
                  </el-row>
                </div>

                <!-- 合同基本信息 -->
                <el-descriptions v-if="contractDetail" :column="2" border size="small" style="margin-bottom: 12px;">
                  <el-descriptions-item label="合同编号">
                    {{ contractDetail.contract_no || '-' }}
                  </el-descriptions-item>
                  <el-descriptions-item label="合同名称">
                    {{ contractDetail.title || '-' }}
                  </el-descriptions-item>
                  <el-descriptions-item label="甲方">
                    {{ contractDetail.party_a || '-' }}
                  </el-descriptions-item>
                  <el-descriptions-item label="乙方">
                    {{ contractDetail.party_b || '-' }}
                  </el-descriptions-item>
                  <el-descriptions-item label="合同总额">
                    <span style="color: #F56C6C; font-weight: bold;">¥{{ formatAmount(contractDetail.amount_total) }}</span>
                  </el-descriptions-item>
                  <el-descriptions-item label="签订日期">
                    {{ contractDetail.sign_date ? formatDate(contractDetail.sign_date) : '-' }}
                  </el-descriptions-item>
                </el-descriptions>
                <div v-else style="color:#909399; padding: 6px 0; margin-bottom: 12px;">
                  未找到合同信息
                </div>

                <div v-if="contractPayments && contractPayments.length">
                  <div style="font-weight: bold; margin-bottom: 10px; color: #606266;">合同相关支付列表</div>
                  <el-table :data="contractPayments" border size="small" style="width: 100%">
                    <el-table-column prop="id" label="流水号" width="90" />
                    <el-table-column label="创建日期" width="120">
                      <template slot-scope="scope">
                        {{ scope.row.created_at ? formatDate(scope.row.created_at) : '-' }}
                      </template>
                    </el-table-column>
                    <el-table-column prop="total_amount" label="金额" width="140" align="right">
                      <template slot-scope="scope">
                        <span style="color: #F56C6C; font-weight: bold;">¥{{ formatAmount(scope.row.total_amount) }}</span>
                      </template>
                    </el-table-column>
                    <el-table-column prop="status_text" label="状态" width="120" />
                    <el-table-column prop="description" label="说明" min-width="160" show-overflow-tooltip />
                  </el-table>
                </div>
                <div v-else style="color:#909399; padding: 6px 0;">
                  暂无合同相关支付
                </div>
              </el-card>
              <el-row :gutter="20" style="margin-top: 10px;">
                <el-col :span="12">
                  <div class="info-item">
                    <span class="label">创建时间：</span>
                    <span class="value">{{ formatDateTime(payment.created_at) || '-' }}</span>
                  </div>
                </el-col>
                <el-col :span="12" v-if="payment.processed_at">
                  <div class="info-item">
                    <span class="label">处理时间：</span>
                    <span class="value">{{ formatDateTime(payment.processed_at) || '-' }}</span>
                  </div>
                </el-col>
              </el-row>
            </div>
          </div>
          <div v-if="loadingPayments" style="text-align: center; padding: 20px;">
            <i class="el-icon-loading"></i> 加载中...
          </div>
        </el-card>
        
        <div v-if="/\/detail/.test($route.path) && config && config.flow">
            <div v-if="config.flow.hasOwnProperty('out_contracts') && config.flow.out_contracts && config.flow.out_contracts.length > 0" style="margin-bottom: 10px;color:#F56C6C;">
              <!-- 单条数据 -->
              <div v-if="config.flow.out_contracts.length === 1">
                <span @click="showContract(config.flow.out_contracts[0].id)" style="cursor: pointer;">
                  本流程已关联资金预算管理：<span style="color:#409eff;">{{ config.flow.out_contracts[0].name }}</span>，点击查看完整信息链。
                </span>
              </div>
              <!-- 多条数据 -->
              <div v-else>
                <span>本流程已关联{{ config.flow.out_contracts.length }}条资金预算管理，分别是</span>
                <span v-for="(contract, index) in config.flow.out_contracts" :key="contract.id">
                  <span v-if="index > 0">、</span>
                  <span @click="showContract(contract.id)" style="cursor: pointer;color:#409eff;">{{ contract.name }}</span>
                </span>
                <span>，可分别点击查看完整信息链。</span>
              </div>
            </div>

            <!-- out_pay 处理 -->
            <div v-if="config.flow.hasOwnProperty('out_pay') && config.flow.out_pay && config.flow.out_pay.length > 0" style="margin-bottom: 10px;color:#F56C6C;">
              <!-- 单条数据 -->
              <div v-if="config.flow.out_pay.length === 1">
                <span @click="showContract(config.flow.out_pay[0].contract_id)" style="cursor: pointer;">
                  本流程已关联资金预算管理：<span style="color:#409eff;">{{ config.flow.out_pay[0].contract?config.flow.out_pay[0].contract.name:'未知支出'+config.flow.out_pay[0].contract_id }}</span>，点击查看完整信息链。
                </span>
              </div>
              <!-- 多条数据 -->
              <div v-else>
                <span>本流程已关联{{ config.flow.out_pay.length }}条资金预算管理，分别是</span>
                <span v-for="(pay, index) in config.flow.out_pay" :key="pay.id">
                  <span v-if="index > 0">、</span>
                  <span @click="showContract(pay.contract_id)" style="cursor: pointer;color:#409eff;">{{ pay.contract?pay.contract.name:'未知支出'+pay.contract_id }}</span>
                </span>
                <span>，可分别点击查看完整信息链。</span>
              </div>
            </div>
          </div>

          <div class="form-container" id="print-content">
          <DesktopForm
            :device="device"
            ref="desktopForm"
            :config="config"
            :is-first-node="isFirstNode"
            :sub-form="subConfig"
            :fields="fields"
            :original-form="form"
            :readable.sync="readableFields"
            :script-content="scriptContent"
            :writeable.sync="writeableFields"
            :rules="rules"
            :sub-rules="subRules"
            :logs="config.logs"
          ></DesktopForm>
        </div>
      </template>

      <!--      审批日志-->
      <div v-if="/\/detail/.test($route.path)" style="margin-top: 10px">
        <div>流转记录</div>
        <vxe-table
          style="margin-top: 10px;"
          show-footer
          ref="table"
          stripe
          class="log-table-scroll"
          keep-source
          show-overflow
          :column-config="{ resizable: true }"
          :print-config="{}"
          :export-config="{}"
          :custom-config="{ mode: 'popup' }"
          :footer-data="footerData"
          :data="config.logs || []"
          @cell-dblclick="cellDblclickEvent"
        >
          <vxe-column
            type="seq"
            width="62"
            align="center"
            field="seq"
            title="编号"
          />
          <vxe-column
            width="140"
            title="节点名称"
            align="center"
            field="node.name"
            :formatter="({ cellValue }) => cellValue || '节点已调整'"
          ></vxe-column>
          <vxe-column
            width="80"
            align="center"
            title="办理状态"
            field="status"
            :formatter="({ cellValue }) => myStatus.get(cellValue)"
          >
            <template #default="{ row }">
              <el-tag
                size="mini"
                :type="statusColor.get(row.status)"
                effect="dark"
                >{{ myStatus.get(row.status) }}</el-tag
              >
            </template>
          </vxe-column>
          <vxe-column
            align="center"
            width="80"
            title="承办人员"
            field="user.name"
          ></vxe-column>
          <vxe-column
            align="center"
            width="200"
            title="流转时间"
            field="created_at"
            :formatter="
              ({ cellValue }) =>
                $moment(cellValue).format('YYYY年MM月DD日 HH:mm:ss')
            "
          ></vxe-column>
          <vxe-column
            min-width="200"
            header-align="center"
            title="退回原因"
            field="reason"
          ></vxe-column>
          <vxe-column
            align="center"
            width="200"
            title="办理时间"
            field="updated_at"
          >
            <template #default="{ row }">
              <span
                :style="{
                  color:
                    row.deadline &&
                    row.updated_at &&
                    $moment(row.updated_at).isAfter(
                      $moment(row.deadline).endOf('day')
                    )
                      ? 'red'
                      : '',
                }"
                >{{
                  $moment(row.updated_at).format("YYYY年MM月DD日 HH:mm:ss")
                }}</span
              >
            </template>
          </vxe-column>
          <vxe-column align="center" title="耗时" field="use_time" width="120">
            <template #default="{ row }">
              <span>{{ diffTime(row.updated_at, row.created_at) }}</span>
            </template>
          </vxe-column>
        </vxe-table>
      </div>

      <div class="btns" ref="btns">
        <template v-if="!/\/detail/.test($route.path)">
          <el-button
            v-if="$route.query.flow_id"
            icon="el-icon-arrow-left"
            type="danger"
            size="small"
            @click="isShowRollback = true"
            >退回</el-button
          >
          <el-button
            v-if="$route.query.flow_id"
            icon="el-icon-caret-right"
            type="primary"
            plain
            size="small"
            @click="isShowForward = true"
            >部门内转办</el-button
          >
          <el-button
            v-if="$route.query.flow_id"
            icon="el-icon-document-add"
            type="info"
            size="small"
            @click="submit('only-submit')"
            >暂存不流转</el-button
          >
          <el-button type="primary" size="small" @click="submit('assign')"
            >保存并流转 <i class="el-icon-right"></i
          ></el-button>
          <el-button
            v-if="!$route.query.flow_id"
            type="info"
            size="small"
            @click="$router.go(-1)"
            >返回</el-button
          >
        </template>
        <template v-else>
          <el-button
            v-if="$store.state.user.adminId === 1 && $route.query.flow_id"
            icon="el-icon-arrow-left"
            type="danger"
            size="small"
            @click="isShowRollback = true"
          >退回</el-button
          >
          <el-button plain size="small" @click="$router.go(-1)">返回</el-button>
          <el-button plain size="small" @click="print(false)">打印</el-button>
          <!-- <el-button plain size="small" @click="print(true)">打印(带审批记录)</el-button> -->
          <!-- <el-button plain size="small">下载附件</el-button> -->
        </template>
      </div>
    </el-card>

    <assign
      ref="assign"
      :visible.sync="isShowAssign"
      :config="config"
      :result="result"
    ></assign>

    <forward
      ref="forward"
      :is-show.sync="isShowForward"
      :flow="config.flow"
    ></forward>

    <rollback
      ref="rollback"
      :is-show.sync="isShowRollback"
      :flow="config.flow"
    ></rollback>

    <el-backtop></el-backtop>
    <!-- 更改时间 -->
    <el-dialog
      title="请选择时间"
      :visible.sync="isShowTime"
      :close-on-click-modal="false"
      width="30%"
    >
      <!-- 日期时间选择器 -->
      <el-date-picker
        v-model="selectedDateTime"
        type="datetime"
        placeholder="选择日期时间"
        format="yyyy-MM-dd HH:mm:ss"
        value-format="yyyy-MM-dd HH:mm:ss"
        :clearable="false"
        style="width: 100%"
      ></el-date-picker>

      <div slot="footer" class="dialog-footer">
        <el-button @click="isShowTime = false,timeId='',selectedDateTime='',selectedDateType=''">取消</el-button>
        <el-button type="primary" @click="updateTime">确定</el-button>
      </div>
    </el-dialog>
    <!-- 更改承办人员 -->
    <el-dialog
      title="请选择承办人员"
      :visible.sync="isShowUserDialog"
      :close-on-click-modal="false"
      width="30%"
    >
      <!-- 用户选择下拉框 -->
      <el-select
        v-model="selectedUserId"
        placeholder="请选择承办人员"
        filterable
        style="width: 100%"
      >
        <el-option
          v-for="user in userList"
          :key="user.id"
          :label="user.name"
          :value="user.id"
        ></el-option>
      </el-select>

      <div slot="footer" class="dialog-footer">
        <el-button @click="isShowUserDialog = false,currentRowId='',selectedUserId=''">取消</el-button>
        <el-button type="primary" @click="updateUser">确定</el-button>
      </div>
    </el-dialog>
    <!-- 打开支出 详情 -->
    <vxe-modal
      :id="contractModalId"
      v-model="isShowModal"
      :z-index="999999"
      transfer
      show-zoom
      resize
      :fullscreen="$store.getters.device === 'mobile'"
      title="查看"
      width="100%"
      height="100%"
      esc-closable
      :padding="false"
    >
      <iframe
        :src="contractUrl"
        style="display: block;width: 100%;height: 100%;border: 0;"
        frameborder="0"
      />
    </vxe-modal>

    <!-- 右侧抽屉：流程详情 / 会议纪要 -->
    <el-drawer
      :visible.sync="rightDrawerVisible"
      :title="rightDrawerTitle"
      direction="rtl"
      size="60%"
      :with-header="true"
    >
      <!-- 流程详情：iframe -->
      <div v-if="rightDrawerType === 'flow'" style="width: 100%; height: 100%;">
        <iframe
          :src="rightDrawerUrl"
          style="display: block; width: 100%; height: calc(100vh - 120px); border: 0;"
          frameborder="0"
        />
      </div>

      <!-- 会议纪要：请求数据展示 -->
      <div v-else-if="rightDrawerType === 'meetingMinutes'" v-loading="loadingMeetingMinutes">
        <div v-if="meetingMinutesDetail">
          <el-descriptions :column="1" border>
            <el-descriptions-item label="标题">
              {{ meetingMinutesDetail.title || '-' }}
            </el-descriptions-item>
            <el-descriptions-item label="内容清单">
              <div v-if="meetingMinutesDetail.items && meetingMinutesDetail.items.length">
                <div v-for="(it, idx) in meetingMinutesDetail.items" :key="idx" style="margin-bottom: 8px;">
                  <span style="color:#409EFF;">{{ it.type || '类型' }}：</span>
                  <span>{{ it.content || '-' }}</span>
                </div>
              </div>
              <span v-else>无</span>
            </el-descriptions-item>
            <el-descriptions-item label="附件" v-if="meetingMinutesDetail.files_details && meetingMinutesDetail.files_details.length">
              <div v-for="(f, idx) in meetingMinutesDetail.files_details" :key="idx" style="margin-bottom: 6px;">
                <el-link type="primary" :underline="false" :href="f.url" target="_blank">
                  {{ f.original_name || f.name || '附件' }}
                </el-link>
              </div>
            </el-descriptions-item>
          </el-descriptions>
        </div>
        <div v-else style="color:#909399;">未获取到会议纪要数据</div>
      </div>
    </el-drawer>
  </div>
</template>

<script>
import Steps from "./components/Steps.vue";
import DesktopForm from "./DesktopForm.vue";
import MobileForm from "./MobileForm.vue";
import assign from "./components/assign.vue";
import forward from "./components/forward.vue";
import rollback from "./components/rollback.vue";
import RelatedFlows from "./components/RelatedFlows.vue";
import { generateRandomString } from '@/utils'
import { userListNoAuth, departmentListNoAuth } from "@/api/common";
import {
  create,
  deal,
  fieldConfig, flowList,
  preConfig,
  preDeal,
  updateNodeTime,
  view,
} from "@/api/flow";
import { getPaymentsByFlowId, getPaymentsByContractId, getBudgetContractDetail, getPlannedExpenditure, getPlannedExpenditureTemplatesByCategory, getPaymentCategoryTemplateElements, getDetailTableFields, getOaFlowDetails, getTemplateElementDetail } from "@/api/payment";
import { show as meetingMinutesShow } from "@/api/meetingMinutes";
import { deepCopy } from "@/utils";
import { validation, validationName } from "@/utils/validate";
import { print } from "@/utils/print";
import JSONBigint from 'json-bigint'
import { getToken, setToken } from "@/utils/auth";
export default {
  components: {
    Steps,
    DesktopForm,
    MobileForm,
    assign,
    forward,
    rollback,
    RelatedFlows,
  },
  data() {
    return {
      // vxe-ui v3.5+ 要求 modal.id
      contractModalId: `flow-contract-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
      isShowModal:false,
      contractUrl:'',
      printKey: 0,
      isShowRollback: false,
      isShowForward: false,
      isShowAssign: false,
      timeId: '',
      isShowTime: false,
      selectedDateTime: '',
      selectedDateType: '',
      isShowUserDialog: false,
      selectedUserId: '',
      userList: [],
      currentRowId: '',
      info: [],
      config: {},
      writeableFields: [],
      readableFields: [],
      subConfig: new Map(),
      myStatus: new Map([
        [-2, "会签回退"],
        [-1, "退回"],
        [0, "办理中"],
        [1, "已完成"],
      ]),
      statusColor: new Map([
        [-2, "warning"],
        [-1, "warning"],
        [0, ""],
        [1, "success"],
      ]),

      form: {},
      result: {},
      fileList: {},
      subFileList: {},
      rules: {},
      subRules: {},
      flows: [],
      csrf_token: '',
      // 关联的支付信息
      relatedPayments: [],
      loadingPayments: false,
      // 支付模板字段（动态字段：checklist/附件/detail_table/审批流程等）
      paymentTemplateElements: [], // {id,name,type,field_type,options,model_id,sort_order,...}
      loadingPaymentTemplateElements: false,
      paymentDetailTableFieldsMap: {}, // { [elementId]: columns[] }
      paymentFlowDetailsMap: {}, // { [flowId]: {display_name,no,title,custom_model_id,...} }
      paymentChecklistOptionsMap: {}, // { [elementId]: options[] } 从 template-elements/{id} 拉取

      // 合同相关支付列表
      contractDetail: null,
      contractPayments: [],
      loadingContractPayments: false,
      // 非直接支付：还原“非直接支出(PlannedExpenditure)”的模板字段与内容
      indirectExpenditureMap: {}, // { [expenditureId]: PlannedExpenditureDetail }
      indirectTemplateConfigMap: {}, // { [categoryId]: templateConfig }
      indirectDetailTableFieldsMap: {}, // { [elementId]: columns[] }
      oaFlowDetailsMap: {}, // { [flowId]: {display_name,no,title,custom_model_id,...} }
      loadingIndirectExpenditures: false,
      // 事前流程深度还原：checklist options（按预算模板元素定义拉取）
      plannedChecklistOptionsMap: {}, // { [elementId]: options[] }
      // detail_table：部门/人员映射
      simpleDepartmentMap: {}, // { [id]: name }
      simpleUserMap: {}, // { [id]: name }
      loadedSimpleDepartments: false,
      loadedSimpleUsers: false,

      // 右侧抽屉（流程详情/会议纪要）
      rightDrawerVisible: false,
      rightDrawerType: '', // 'flow' | 'meetingMinutes'
      rightDrawerTitle: '',
      rightDrawerUrl: '',
      meetingMinutesDetail: null,
      loadingMeetingMinutes: false,
      // fill_flow_title 自动填充：专用 watcher（避免 deep watcher oldVal/newVal 引用问题）
      _unwatchFillFlowTitle: null,
    };
  },
  watch:{
    isShowModal(val){
      if(!val){
        this.contractUrl = ''
      }
    },
  },
  methods: {
    // 模板字段 key 兼容：config 里可能是 key 或 field_key
    getTplFieldKey(field) {
      return (field && (field.key || field.field_key)) ? (field.key || field.field_key) : '';
    },

    // 相关事前流程字段是否渲染：
    // 1. 最顶层拦截：如果画布配置中 visible === false，则不显示
    // 2. 对 oa_custom_model/out_custom_model 做"空值隐藏"
    // 3. 对 meeting_minutes 和 attachment/file 做"空值隐藏"
    shouldRenderPlannedField(expenditureId, field) {
      if (!field) return false;
      
      // 最顶层拦截：如果画布配置中关闭了元素的显示（visible === false），则不显示
      if (field.visible === false) {
        return false;
      }
      
      // oa_custom_model/out_custom_model 类型：仅当有绑定值时才显示
      if (field.element_type === 'oa_custom_model' || field.element_type === 'out_custom_model') {
        return this.getOaCustomModelBindings(expenditureId, field).length > 0;
      }
      
      // meeting_minutes 类型：仅当有会议纪要ID时才显示
      if (field.element_type === 'meeting_minutes') {
        const meetingMinuteId = this.extractMeetingMinuteId(
          this.getIndirectFieldValue(expenditureId, this.getTplFieldKey(field))
        );
        return meetingMinuteId !== null && meetingMinuteId !== undefined;
      }
      
      // attachment/file 类型：仅当有文件项时才显示
      if (field.element_type === 'attachment' || field.element_type === 'file') {
        const fileItems = this.getPlannedFileItems(expenditureId, field);
        return fileItems && fileItems.length > 0;
      }
      
      // 其他类型：默认显示（已通过 visible 检查）
      return true;
    },

    normalizeFlowId(val) {
      if (val === null || val === undefined || val === '') return null;
      const s = String(val).trim();
      if (!s) return null;
      if (!/^\d+$/.test(s)) return null;
      const n = Number(s);
      return n && !Number.isNaN(n) ? n : null;
    },
    extractFlowIds(val) {
      if (val === null || val === undefined || val === '') return [];
      if (Array.isArray(val)) {
        return val.map(v => this.normalizeFlowId(v)).filter(Boolean);
      }
      if (typeof val === 'number') {
        return [this.normalizeFlowId(val)].filter(Boolean);
      }
      if (typeof val === 'string') {
        return val
          .split(/[,，、]/g)
          .map(v => this.normalizeFlowId(v))
          .filter(Boolean);
      }
      if (typeof val === 'object') {
        const id = this.normalizeFlowId(val.instance_id ?? val.flow_instance_id ?? val.id);
        return id ? [id] : [];
      }
      return [];
    },
    // ---------- 支付模板字段：辅助 ----------
    isEmptyValue(val) {
      if (val === null || val === undefined) return true;
      if (typeof val === 'string') return val.trim() === '';
      if (Array.isArray(val)) return val.length === 0;
      if (typeof val === 'object') return Object.keys(val).length === 0;
      return false;
    },
    isChecklistChecked(selected, value) {
      const arr = Array.isArray(selected) ? selected : (selected ? [selected] : []);
      // 与后端可能的 string/number 混用兼容
      const target = value === null || value === undefined ? '' : String(value);
      return arr.map(v => (v === null || v === undefined ? '' : String(v))).includes(target);
    },
    getPaymentChecklistRemark(el, optionValue, payment) {
      const p = payment || {};
      const key = `${el.id}_remark_${optionValue}`;
      const v = p?.fields?.[key];
      return v ? String(v) : '';
    },
    getPaymentTemplateElementValue(el, payment) {
      const p = payment || {};
      return p?.fields?.[el.id];
    },
    getVisiblePaymentTemplateElements(payment) {
      const p = payment || {};
      const list = Array.isArray(this.paymentTemplateElements) ? this.paymentTemplateElements : [];
      return list.filter(el => {
        const val = this.getPaymentTemplateElementValue(el, p);
        if (el.type === 'checklist') {
          const options = this.getPaymentChecklistOptions(el);
          const anyChecked = (options || []).some(opt => this.isChecklistChecked(val, opt.value));
          const anyRemark = (options || []).some(opt => !!this.getPaymentChecklistRemark(el, opt.value, p));
          return anyChecked || anyRemark;
        }
        if (el.type === 'detail_table') {
          return Array.isArray(val) && val.length > 0;
        }
        if (el.type === 'form_element' && el.field_type === 'attachment') {
          return Array.isArray(val) && val.length > 0;
        }
        return !this.isEmptyValue(val);
      });
    },
    getPaymentChecklistOptions(el) {
      const id = el?.id ? Number(el.id) : null;
      if (id && this.paymentChecklistOptionsMap?.[id] && this.paymentChecklistOptionsMap[id].length) {
        return this.paymentChecklistOptionsMap[id];
      }
      return Array.isArray(el?.options) ? el.options : [];
    },
    async loadPaymentChecklistOptionsIfNeeded(elementId) {
      const id = Number(elementId);
      if (!id || Number.isNaN(id)) return;
      if (this.paymentChecklistOptionsMap?.[id]) return;
      try {
        const detail = await getTemplateElementDetail(id);
        const opts = Array.isArray(detail?.options) ? detail.options : [];
        this.$set(this.paymentChecklistOptionsMap, id, opts);
      } catch (e) {
        this.$set(this.paymentChecklistOptionsMap, id, []);
      }
    },
    getPaymentDetailTableFields(elementId) {
      return this.paymentDetailTableFieldsMap?.[elementId] || [];
    },
    getPaymentFlowDisplayInfo(flowId) {
      if (!flowId) return null;
      const d = this.paymentFlowDetailsMap?.[flowId];
      if (!d) return `流程ID: ${flowId}`;
      return d.display_name || [d.no, d.title].filter(Boolean).join(' - ') || `流程ID: ${flowId}`;
    },
    // 附件：兼容字符串/对象/数组
    getPaymentFileItems(el, payment) {
      const v = this.getPaymentTemplateElementValue(el, payment);
      if (!v) return [];
      const toItem = (x, idx) => {
        if (!x) return null;
        if (typeof x === 'string') {
          const s = x.trim();
          if (!s) return null;
          const name = s.split('/').pop();
          return { name: name || `附件${idx + 1}`, url: s };
        }
        if (typeof x === 'object') {
          const url = x.url || x.path || x.file_url || x.download_url || x.href || x.preview_url;
          const name =
            x.original_name ||
            x.file_name ||
            x.name ||
            x.filename ||
            x.originalName ||
            (url ? String(url).split('/').pop() : null);
          if (url || name) return { name: name || `附件${idx + 1}`, url };
          return null;
        }
        return null;
      };
      const arr = Array.isArray(v) ? v : [v];
      return arr.map(toItem).filter(Boolean);
    },
    // ---------- 合同相关 ----------
    getContractIdFromPayment(payment) {
      const p = payment || {};
      if (p.related_type === 'contract' && p.related_id) return p.related_id;
      if (p.contract_id) return p.contract_id;
      return null;
    },
    async loadContractPayments(contractId, currentPaymentId) {
      const cid = Number(contractId);
      if (!cid || Number.isNaN(cid)) {
        this.contractPayments = [];
        return;
      }
      this.loadingContractPayments = true;
      try {
        // 与打印页一致：按 payment.id 升序、包含当前支付在内，用于正确计算“第几次支付”
        const list = await getPaymentsByContractId(cid, { all: true, order_by: 'id', order: 'asc' });
        const arr = Array.isArray(list) ? list : [];
        this.contractPayments = arr.slice();
      } catch (e) {
        this.contractPayments = [];
      } finally {
        this.loadingContractPayments = false;
      }
    },

    async loadContractDetail(contractId) {
      const cid = Number(contractId);
      if (!cid || Number.isNaN(cid)) {
        this.contractDetail = null;
        return;
      }
      try {
        const detail = await getBudgetContractDetail(cid);
        this.contractDetail = detail || null;
      } catch (e) {
        this.contractDetail = null;
      }
    },

    getContractPaymentStats(currentPaymentId) {
      const list = Array.isArray(this.contractPayments) ? this.contractPayments : [];
      const total = list.length;
      const curId = Number(currentPaymentId);
      const idx = list.findIndex(p => Number(p?.id) === curId);
      return {
        total_count: total,
        current_index: idx >= 0 ? idx + 1 : null
      };
    },
    /**
     * 为 fill_flow_title=1 的字段建立精确 watcher：
     * - 只在新建流程时生效
     * - 保留 isFirstNode 判断
     * - 输入/默认值变更都能触发（不依赖 deep watcher）
     */
    setupFillFlowTitleWatcher(fields) {
      try {
        if (typeof this._unwatchFillFlowTitle === 'function') {
          this._unwatchFillFlowTitle();
        }
      } catch (e) {
        // ignore
      }
      this._unwatchFillFlowTitle = null;

      if (this.$route.query.flow_id) return; // 只处理新建
      const list = Array.isArray(fields) ? fields : [];
      const fillField = list.find((f) => Number(f?.fill_flow_title) === 1 && f?.type === 'text' && f?.name);
      if (!fillField) return;

      const fieldName = fillField.name;
      this._unwatchFillFlowTitle = this.$watch(
        () => (this.form ? this.form[fieldName] : undefined),
        (newVal) => {
          if (!this.isFirstNode || this.$route.query.flow_id) return;
          const v = newVal === null || newVal === undefined ? "" : String(newVal).trim();
          if (!v) return;
          this.$set(this.form, "flow_title", v);
        },
        { immediate: true }
      );
    },
    // 打开 支出的链接
    showContract(id){
      this.contractUrl = `${process.env.VUE_APP_BASE_API}/ht/#/contract-flow?auth_token=${window.encodeURIComponent(
            getToken()
          )}&out_contract_id=`+id
      this.isShowModal = true
    },
    // 处理url中default_json
    handleDefaultJSON() {
      console.log("123")
      this.form['form_canal'] = 'oa'
      try {
        if(!this.$route.query?.default_json) {
          // 即使没有default_json，也要处理form_canal参数
          if(this.$route.query?.form_canal) {
            this.form['form_canal'] = this.$route.query.form_canal
          }
          return
        }
        const res = JSON.parse(this.$route.query?.default_json)
        for (let key in this.$route.query) {
          if(/^out_(.*)_id/.test(key)) {
            this.form[key] = this.$route.query[key]
          }
          if(/^borrow_id/.test(key)) {
            this.form[key] = this.$route.query[key]
          }
          if(/^form_canal/.test(key)){
            this.form[key] = this.$route.query[key]
          }
        }
        for (let key in res) {
          try {
            let jsonObj = JSON.parse(res[key]);
            if (this.form.hasOwnProperty(key)) {
              this.form[key] = jsonObj;
            }

          } catch (err) {
            if (this.form.hasOwnProperty(key)) {
              this.form[key] = res[key];
            }
          }
        }
      } catch (err) {
        console.error(err)
      }
    },
    async print(isLog=false) {
      const _this = this
      let customModelId = this.config.customModel.id || this.$route.query.module_id
      const modelRes = await fieldConfig(customModelId,true)
      let pickTemplate = 0
      const printTemplates = [{
        id: 0,
        name: '基础模版',
        print_format: modelRes.customModel.print_format
      },...modelRes.customModel.print_formats]
      const h = this.$createElement;
      await this.$msgbox({
        title: '打印模版选择',
        message: h('div',{
          class: 'print-template-radios',
          key: this.printKey++
        },[
          h('div',{
          },printTemplates.map(i => h('div',{
            style: {
              display: 'flex',
              'align-items': 'center',
              'margin-top': '4px',
            }
          },[
            h('span',{
              class: 'el-radio__input'
            },[
              h('span', {
                class: 'el-radio__inner custom-cursor-on-hover'
              }),
              h('input', {
                style: {
                  cursor: 'pointer',
                  opacity: 0,
                  position: 'absolute',
                  top: 0,
                  left: 0,
                  right: 0,
                  bottom: 0,
                  margin: 0
                },
                attrs: {
                  // 添加属性
                  type: "radio",
                  id: `print-radio-${i.id}`,
                  name: "Radio",
                  value: i.id,
                  checked: pickTemplate === i.id,
                },
                on: {
                  change: () => {
                    pickTemplate = i.id
                  }
                }
              })
            ]),
            h('label', {
              style: {
                flex: 1
              },
              attrs: {
                for: `print-radio-${i.id}`
              },
              class: 'el-radio__label',
            }, i.name)
          ])))
        ]),
        showCancelButton: true,
        confirmButtonText: '确定',
        cancelButtonText: '取消',
      })
      const printText = printTemplates.find(i => i.id === pickTemplate)?.print_format
      if(isLog) {
        const res = await this.$refs['table'].exportData({
          type: 'html',
          download: false
        })
        await print.bind(this)(printText, isLog, _this.config.flow, res.content)
      } else {
        await print.bind(this)(printText, isLog, _this.config.flow)
      }
    },

    generateForm(object, fields, relation = false, pname) {
      fields.forEach((field) => {
        if (field.rules && field.rules.length > 0 && this.writeableFields.find(i => i === field.id) && !relation) {
          this.rules[field.name] = field.rules.map((rule) => {
            switch (rule) {
              case "required":
                if (field.type === 'relation') {
                  return {
                    validator: (myRule, value, callback) => {
                      if (value instanceof Array && value.length > 0) {
                        callback()
                      } else {
                        callback(`请填写${field.label}`)
                      }
                    },
                    message: `请填写${field.label}`,
                    trigger: "blur",
                  };
                } else {
                  return {
                    required: true,
                    message: `请填写${field.label}`,
                    trigger: "blur",
                  };
                }
              default:
                return {
                  validator: (myRule, value, callback) => {
                    if (validation.get(rule).test(value) || value === '') {
                      callback();
                    } else {
                      callback(
                        new Error(
                          `${field.label}必须为${validationName.get(rule)}`
                        )
                      );
                    }
                  },
                  trigger: "blur",
                  message: `${field.label}必须为${validationName.get(rule)}`,
                };
            }
          });
        }
        if (relation) {
          this.subRules[`${pname}_rules`][field.name] = field.rules.map((rule) => {
            switch (rule) {
              case "required":
                return {
                  required: true,
                  message: `请填写${field.label}`,
                };
              default:
                return {
                  validator: this.device === 'desktop' ? ({ cellValue }) => {
                    return new Promise((resolve, reject) => {
                      if (validation.get(rule).test(cellValue) || cellValue === '') {
                        resolve()
                      } else {
                        reject(
                          new Error(
                            `${field.label}必须为${validationName.get(rule)}`
                          )
                        );
                      }
                    })
                  } : (myRule, value, callback) => {
                    if (validation.get(rule).test(value) || value === '') {
                      callback();
                    } else {
                      callback(
                        new Error(
                          `${field.label}必须为${validationName.get(rule)}`
                        )
                      );
                    }
                  },
                  trigger: "blur",
                  pattern: validation.get(rule),
                  message: `${field.label}必须为${validationName.get(rule)}`,
                };
            }
          });
        }
        if (field.type === "relation") {
          this.subRules[`${field.name}_rules`] = {}
          let temp = {}
          this.subConfig.get(field.sub_custom_model_id)?.customModel?.fields?.forEach(field => temp[field.name] = '')
          object[field.name] = [temp];

          this.generateForm(
            object[field.name][0],
            this.subConfig.get(field.sub_custom_model_id)?.customModel?.fields,
            true,
            field.name
          );
        } else {
          if (/\/detail/.test(this.$route.path) && this.$route.query.flow_id) {
            object[field.name] = "";
          } else {
            if (this.writeableFields.indexOf(field.id) !== -1 || this.readableFields.indexOf(field.id) !== -1) {
              object[field.name] = (this.writeableFields.indexOf(field.id) !== -1 && field.default_value) ? field.default_value : (field.type === 'file' ? [] : "");
            }
          }
        }
      });
      // 初始化关联流程字段
      if (!object.hasOwnProperty('related_flow_ids')) {
        object['related_flow_ids'] = '';
      }
      
      // 查找 fill_flow_title=1 的字段
      const fillFlowTitleField = (fields || []).find(f =>
        Number(f?.fill_flow_title) === 1 && ['text', 'textarea', 'select'].includes(f?.type)
      );
      
      // 设置 flow_title 初始值
      if (this.config?.flow?.title) {
        // 已有流程，使用已有标题
        this.form['flow_title'] = this.config.flow.title;
      } else if (fillFlowTitleField && !this.$route.query.flow_id) {
        // 新建流程且存在 fill_flow_title=1 的字段，不设置默认值，等待字段值填充
        // 注意：这里不判断 isFirstNode，因为 generateForm 可能在 isFirstNode 计算之前调用
        // 实际的自动填充逻辑在 watch 中会再次检查 isFirstNode
        this.form['flow_title'] = '';
      } else {
        // 新建流程且不存在 fill_flow_title=1 的字段，使用默认格式
        this.form['flow_title'] = `${this.config.customModel.name}（${this.$store.getters.name} ${this.$moment().format('YYYY-MM-DD HH:mm')}）`;
      }
    },

    formatTime(time) {
      const days = parseInt(time / (1000 * 60 * 60 * 24));
      const hours = parseInt((time % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
      const minutes = parseInt((time % (1000 * 60 * 60)) / (1000 * 60));
      const seconds = (time % (1000 * 60)) / 1000;
      return `${days > 0 ? days + "天" : ""}${
        hours > 0 ? hours + "时" : ""
      }${minutes}分${seconds}秒`;
    },
    // 获取支付状态标签类型
    getPaymentStatusType(status) {
      const statusMap = {
        'pending': 'warning',
        'processing': 'primary',
        'completed': 'success',
        'cancelled': 'danger',
      };
      return statusMap[status] || 'info';
    },
    // 格式化金额
    formatAmount(amount) {
      if (!amount && amount !== 0) return '0.00';
      return parseFloat(amount).toLocaleString('zh-CN', {
        minimumFractionDigits: 2,
        maximumFractionDigits: 2
      });
    },
    // 格式化日期
    formatDate(date) {
      if (!date) return '';
      return this.$moment(date).format('YYYY-MM-DD');
    },
    // 格式化日期时间
    formatDateTime(dateTime) {
      if (!dateTime) return '';
      return this.$moment(dateTime).format('YYYY-MM-DD HH:mm:ss');
    },
    // 显示支付分类面包屑（兼容字符串/数组）
    formatBreadcrumb(paymentTypeInfo) {
      if (!paymentTypeInfo || !paymentTypeInfo.breadcrumb) return '';
      const bc = paymentTypeInfo.breadcrumb;
      if (Array.isArray(bc)) {
        return bc.join(' / ');
      }
      return bc;
    },

    // 非直接支付：加载每条明细对应的 PlannedExpenditure 详情 & 模板配置
    async loadIndirectExpenditures(payment) {
      this.indirectExpenditureMap = {};
      this.indirectDetailTableFieldsMap = {};
      this.oaFlowDetailsMap = {};
      this.loadingIndirectExpenditures = true;
      try {
        const ids = this.getPlannedExpenditureIdsFromPayment(payment);
        if (ids.length === 0) return;

        // 并发拉取 PlannedExpenditure 详情
        const expenditureResults = await Promise.all(ids.map(id => getPlannedExpenditure(id).catch(() => null)));
        expenditureResults.forEach(exp => {
          if (exp && exp.id) {
            this.$set(this.indirectExpenditureMap, exp.id, exp);
          }
        });

        // 再按 category_id 拉模板（用于字段顺序/标题）
        const categoryIds = Array.from(new Set(Object.values(this.indirectExpenditureMap).map(exp => exp.category_id).filter(Boolean)));
        const tplResults = await Promise.all(categoryIds.map(cid => getPlannedExpenditureTemplatesByCategory(cid).catch(() => null)));
        tplResults.forEach((tplList, idx) => {
          const cid = categoryIds[idx];
          // getByCategory 返回数组，通常每个分类 1 个模板
          const firstTpl = Array.isArray(tplList) ? tplList[0] : null;
          const config = firstTpl && firstTpl.config ? firstTpl.config : null;
          this.$set(this.indirectTemplateConfigMap, cid, config || null);
        });

        // 加载 detail_table 的列定义（按模板配置中的 element_id）
        const detailTableElementIds = new Set();
        // checklist：从元素定义取“原始 options”
        const checklistElementIds = new Set();
        categoryIds.forEach(cid => {
          const fields = this.getIndirectTemplateFields(cid);
          fields.forEach(f => {
            if (f && f.element_type === 'detail_table' && f.element_id) {
              detailTableElementIds.add(f.element_id);
            }
            if (f && f.element_type === 'checklist' && f.element_id) {
              checklistElementIds.add(f.element_id);
            }
          });
        });
        const idsToLoad = Array.from(detailTableElementIds);
        const colResults = await Promise.all(idsToLoad.map(id => getDetailTableFields(id).catch(() => [])));
        let needDept = false;
        let needUser = false;
        colResults.forEach((cols, i) => {
          const id = idsToLoad[i];
          const arr = Array.isArray(cols) ? cols.slice() : [];
          // 后端字段：field_name, field_key, sort_order
          arr.sort((a, b) => (a.sort_order || 0) - (b.sort_order || 0));
          this.$set(this.indirectDetailTableFieldsMap, id, arr);
          if (arr.some(f => f && f.field_type === 'department')) needDept = true;
          if (arr.some(f => f && f.field_type === 'user')) needUser = true;
        });
        if (needDept) {
          await this.loadSimpleDepartmentMap();
        }
        if (needUser) {
          await this.loadSimpleUserMap();
        }

        // checklist：预加载 options（按元素定义）
        const checklistIds = Array.from(checklistElementIds);
        for (const id of checklistIds) {
          // eslint-disable-next-line no-await-in-loop
          await this.loadPlannedChecklistOptionsIfNeeded(id);
        }

        // 批量获取 oa_custom_model 的流程详情，用于显示标题而不是“流程ID”
        const flowIds = [];
        Object.values(this.indirectExpenditureMap).forEach(exp => {
          const bindings = Array.isArray(exp?.flow_bindings) ? exp.flow_bindings : [];
          bindings.forEach(b => {
            if (b && b.flow_instance_id) {
              flowIds.push(b.flow_instance_id);
            }
          });
        });
        const uniqFlowIds = Array.from(new Set(flowIds.filter(Boolean)));
        if (uniqFlowIds.length) {
          try {
            // request.js 成功时直接返回 res.data（这里是 map）
            const map = await getOaFlowDetails(uniqFlowIds);
            if (map && typeof map === 'object') {
              this.oaFlowDetailsMap = map;
            }
          } catch (e) {
            this.oaFlowDetailsMap = {};
          }
        }
      } finally {
        this.loadingIndirectExpenditures = false;
      }
    },

    // 从支付对象中提取“关联事前流程(PlannedExpenditure)”ID 列表（兼容新旧结构）
    getPlannedExpenditureIdsFromPayment(payment) {
      const ids = [];
      const p = payment || {};

      // 新结构：payment.related_type / related_id
      if (p.related_type === 'planned_expenditure' && p.related_id) {
        ids.push(p.related_id);
      }

      // 新结构：payment.details[*].related_type / related_id
      const details = Array.isArray(p.details) ? p.details : [];
      details.forEach(d => {
        if (!d) return;
        if (d.related_type === 'planned_expenditure' && d.related_id) {
          ids.push(d.related_id);
        }
        // 旧结构兼容：expenditure_type/expenditure_id
        if (d.expenditure_type === 'indirect' && d.expenditure_id) {
          ids.push(d.expenditure_id);
        }
      });

      return Array.from(new Set(ids.filter(Boolean).map(v => Number(v)).filter(v => v && !Number.isNaN(v))));
    },

    getFlowDisplayName(flowId, fallback = '') {
      const f = this.oaFlowDetailsMap?.[flowId];
      if (!f) return fallback || (flowId ? `流程ID: ${flowId}` : '-');
      return f.display_name || [f.no, f.title].filter(Boolean).join(' - ') || fallback || (flowId ? `流程ID: ${flowId}` : '-');
    },

    // 从模板 config 提取字段列表（按配置顺序）
    getIndirectTemplateFields(categoryId) {
      const cfg = this.indirectTemplateConfigMap?.[categoryId];
      if (!cfg || typeof cfg !== 'object') return [];
      const sections = Object.values(cfg);
      const fields = [];
      sections.forEach(sec => {
        if (!sec) return;
        if (Array.isArray(sec.fields)) {
          sec.fields.forEach(f => f && fields.push(f));
        }
        if (Array.isArray(sec.rounds)) {
          sec.rounds.forEach(r => {
            if (r && Array.isArray(r.fields)) {
              r.fields.forEach(f => f && fields.push(f));
            }
          });
        }
      });
      return fields;
    },

    // 非直接支出字段取值（优先用 element_values 映射）
    getIndirectFieldValue(expenditureId, fieldKey) {
      const exp = this.indirectExpenditureMap?.[expenditureId];
      const map = exp?.element_values || {};
      return map?.[fieldKey];
    },

    // 取 oa_custom_model 绑定（用于显示标题与打开流程详情）
    getOaCustomModelBinding(expenditureId, fieldKey) {
      const exp = this.indirectExpenditureMap?.[expenditureId];
      const bindings = Array.isArray(exp?.flow_bindings) ? exp.flow_bindings : [];
      return bindings.find(b => b && b.field_key === fieldKey) || null;
    },

    // 取 oa_custom_model 多条绑定（用于“顿号分隔”的展示）
    getOaCustomModelBindings(expenditureId, fieldOrKey) {
      const exp = this.indirectExpenditureMap?.[expenditureId];
      const bindings = Array.isArray(exp?.flow_bindings) ? exp.flow_bindings : [];
      const fieldKey = typeof fieldOrKey === 'object' ? this.getTplFieldKey(fieldOrKey) : String(fieldOrKey || '');
      const elementIdRaw = typeof fieldOrKey === 'object' ? fieldOrKey?.element_id : null;
      const elementId = elementIdRaw ? Number(elementIdRaw) : null;

      // 1) 优先按 field_key / element_id 精确匹配（flow_bindings 的 field_key/element_id 可能只存在其一）
      const matched = bindings.filter(b => {
        if (!b) return false;
        if (fieldKey && b.field_key === fieldKey) return true;
        if (elementId && b.element_id && Number(b.element_id) === elementId) return true;
        return false;
      });
      if (matched.length) return matched;

      // 2) 没有绑定时，尝试从字段值解析多个 flow_id
      if (!fieldKey) return [];
      const rawVal = this.getIndirectFieldValue(expenditureId, fieldKey);
      const ids = this.extractFlowIds(rawVal);
      if (!ids.length) return [];
      return ids.map(id => {
        const hit = bindings.find(b => Number(b?.flow_instance_id) === Number(id));
        return {
          flow_instance_id: id,
          flow_custom_model_id: hit?.flow_custom_model_id || null,
          flow_display_name: hit?.flow_display_name || `流程流水号: ${id}`,
        };
      });
    },

    // 判断 oa_custom_model 字段是否有值
    hasOaCustomModelValue(expenditureId, field) {
      return this.getOaCustomModelBindings(expenditureId, field).length > 0;
    },

    // ---------- 事前流程 checklist：options/remark ----------
    async loadPlannedChecklistOptionsIfNeeded(elementId) {
      const id = Number(elementId);
      if (!id || Number.isNaN(id)) return;
      if (this.plannedChecklistOptionsMap?.[id]) return;
      try {
        const detail = await getTemplateElementDetail(id);
        const opts = Array.isArray(detail?.options) ? detail.options : [];
        this.$set(this.plannedChecklistOptionsMap, id, opts);
      } catch (e) {
        this.$set(this.plannedChecklistOptionsMap, id, []);
      }
    },
    getPlannedChecklistOptions(field) {
      const id = field?.element_id ? Number(field.element_id) : null;
      if (id && this.plannedChecklistOptionsMap?.[id] && this.plannedChecklistOptionsMap[id].length) {
        return this.plannedChecklistOptionsMap[id];
      }
      return Array.isArray(field?.options) ? field.options : [];
    },
    getPlannedChecklistRemark(expenditureId, field, optionValue) {
      const elementId = field?.element_id;
      if (!elementId || optionValue === null || optionValue === undefined || optionValue === '') return '';
      const remarkKey = `element_${elementId}_remark_${optionValue}`;
      const v = this.getIndirectFieldValue(expenditureId, remarkKey);
      return v ? String(v) : '';
    },

    // ---------- 事前流程附件/文件 ----------
    // 构建完整的文件URL（如果是相对路径，自动拼接当前域名）
    buildFileUrl(path) {
      if (!path) return null;
      
      const pathStr = String(path).trim();
      if (!pathStr) return null;
      
      // 如果已经是完整URL（http:// 或 https://），直接返回
      if (pathStr.startsWith('http://') || pathStr.startsWith('https://')) {
        return pathStr;
      }
      
      // 如果是相对路径（以 / 开头），拼接当前域名
      if (pathStr.startsWith('/')) {
        const origin = window.location.origin;
        return `${origin}${pathStr}`;
      }
      
      // 其他情况，尝试拼接（可能是 storage/files/xxx 这种格式）
      if (pathStr.includes('/')) {
        const origin = window.location.origin;
        return `${origin}/${pathStr.replace(/^\//, '')}`;
      }
      
      // 无法处理的格式，返回原始值
      return pathStr;
    },

    // 处理附件点击
    handlePlannedFileClick(file) {
      if (file.url) {
        try {
          window.open(file.url, '_blank');
        } catch (error) {
          this.$message.error('打开文件失败：' + error.message);
        }
      } else {
        this.$message.warning('文件链接不可用');
      }
    },

    getPlannedFileItems(expenditureId, field) {
      const v = this.getIndirectFieldValue(expenditureId, field?.key);
      if (!v) return [];
      const toItem = (x, idx) => {
        if (!x) return null;
        if (typeof x === 'string') {
          const s = x.trim();
          if (!s) return null;
          const name = s.split('/').pop();
          const url = this.buildFileUrl(s);
          return { name: name || `附件${idx + 1}`, url };
        }
        if (typeof x === 'object') {
          // 尝试从多个可能的字段中获取路径
          let rawPath = x.url || x.path || x.file_url || x.download_url || x.href || x.preview_url || x.value || x.file_path || x.folder;
          
          // 如果还是没有，检查对象的所有值，看是否有字符串类型的路径
          if (!rawPath) {
            for (const [key, value] of Object.entries(x)) {
              if (typeof value === 'string' && (value.includes('/') || value.includes('storage') || value.includes('files'))) {
                rawPath = value;
                break;
              }
            }
          }
          
          const url = rawPath ? this.buildFileUrl(rawPath) : null;
          const name =
            x.original_name ||
            x.file_name ||
            x.name ||
            x.filename ||
            x.originalName ||
            (url ? String(url).split('/').pop() : null) ||
            (rawPath ? String(rawPath).split('/').pop() : null);
          return (url || name) ? { name: name || `附件${idx + 1}`, url } : null;
        }
        return null;
      };
      const arr = Array.isArray(v) ? v : [v];
      return arr.map(toItem).filter(Boolean);
    },

    // ---------- detail_table：部门/人员映射 ----------
    async loadSimpleDepartmentMap() {
      if (this.loadedSimpleDepartments) return;
      try {
        const list = await departmentListNoAuth({ rows: 10000 });
        const arr = Array.isArray(list) ? list : (list?.data || []);
        const map = {};
        (arr || []).forEach(d => {
          if (d && d.id) map[d.id] = d.name || d.title || d.label || String(d.id);
        });
        this.simpleDepartmentMap = map;
      } catch (e) {
        this.simpleDepartmentMap = {};
      } finally {
        this.loadedSimpleDepartments = true;
      }
    },
    async loadSimpleUserMap() {
      if (this.loadedSimpleUsers) return;
      try {
        const list = await userListNoAuth({ rows: 10000 });
        const arr = Array.isArray(list) ? list : (list?.data || []);
        const map = {};
        (arr || []).forEach(u => {
          if (u && u.id) map[u.id] = u.name || u.realname || u.title || u.label || String(u.id);
        });
        this.simpleUserMap = map;
      } catch (e) {
        this.simpleUserMap = {};
      } finally {
        this.loadedSimpleUsers = true;
      }
    },
    formatDetailTableCell(value, col) {
      if (value === null || value === undefined || value === '') return '-';
      const type = col?.field_type;
      if (type === 'department') {
        return this.simpleDepartmentMap?.[value] || `部门ID: ${value}`;
      }
      if (type === 'user') {
        return this.simpleUserMap?.[value] || `用户ID: ${value}`;
      }
      return value;
    },

    extractFlowId(val) {
      if (val === null || val === undefined) return null;
      if (typeof val === 'number' && val > 0) return val;
      if (typeof val === 'string' && /^\d+$/.test(val)) return parseInt(val, 10);
      if (typeof val === 'object') {
        const id = val.instance_id || val.flow_instance_id || val.id;
        if (id && /^\d+$/.test(String(id))) return parseInt(String(id), 10);
      }
      return null;
    },

    extractDisplayName(val) {
      if (!val) return '';
      if (typeof val === 'string') return val;
      if (typeof val === 'object') {
        return val.display_name || val.name || val.title || '';
      }
      return '';
    },

    extractMeetingMinuteId(val) {
      if (val === null || val === undefined) return null;
      if (typeof val === 'number' && val > 0) return val;
      if (typeof val === 'string' && /^\d+$/.test(val)) return parseInt(val, 10);
      if (typeof val === 'object') {
        const id = val.meeting_minute_id || val.id;
        if (id && /^\d+$/.test(String(id))) return parseInt(String(id), 10);
      }
      return null;
    },

    async openFlowDrawer({ flowId, customModelId, title }) {
      const fid = Number(flowId);
      if (!fid || Number.isNaN(fid)) return;

      let cmid = customModelId ? Number(customModelId) : null;
      if (!cmid || Number.isNaN(cmid)) {
        // 优先从已批量缓存的 flow details 取
        const d = this.oaFlowDetailsMap?.[fid];
        cmid = d?.custom_model_id ? Number(d.custom_model_id) : null;
      }
      if (!cmid || Number.isNaN(cmid)) {
        // 兜底：调用 OA flow/view 获取 customModel.id
        try {
          const res = await view(fid);
          cmid = Number(res?.customModel?.id || res?.flow?.custom_model_id);
        } catch (e) {
          cmid = null;
        }
      }
      if (!cmid || Number.isNaN(cmid)) {
        this.$message && this.$message.warning ? this.$message.warning('无法打开流程：缺少 custom_model_id') : null;
        return;
      }

      this.rightDrawerType = 'flow';
      this.rightDrawerTitle = title || `流程 ${fid}`;
      this.rightDrawerUrl = `/oa/#/flow/detail?module_id=${cmid}&flow_id=${fid}&isSinglePage=1&auth_token=${encodeURIComponent(getToken())}`;
      this.rightDrawerVisible = true;
    },

    async openMeetingMinutesDrawer(meetingMinuteId) {
      if (!meetingMinuteId) return;
      this.rightDrawerType = 'meetingMinutes';
      this.rightDrawerTitle = '查看会议纪要';
      this.rightDrawerUrl = '';
      this.meetingMinutesDetail = null;
      this.rightDrawerVisible = true;
      this.loadingMeetingMinutes = true;
      try {
        // meetingMinutesShow 成功时返回 res.data
        const detail = await meetingMinutesShow({ id: meetingMinuteId });
        this.meetingMinutesDetail = detail || null;
        this.rightDrawerTitle = detail?.title || '查看会议纪要';
      } catch (e) {
        console.error('加载会议纪要失败:', e);
        this.meetingMinutesDetail = null;
      } finally {
        this.loadingMeetingMinutes = false;
      }
    },

    // 通用只读展示
    formatReadonlyValue(val) {
      if (val === undefined || val === null || val === '') return '-';
      if (Array.isArray(val)) return val.join('，');
      if (typeof val === 'object') return JSON.stringify(val);
      return String(val);
    },

    // 加载关联的支付信息
    async loadRelatedPayments() {
      if (!this.$route.query.flow_id) {
        return;
      }
      
      this.loadingPayments = true;
      try {
        // request.js 成功时会直接返回 res.data（也就是 payment 对象或 null）
        const payment = await getPaymentsByFlowId(this.$route.query.flow_id, { all: true });
        // 接口约定：要么无数据(null)，要么唯一的一条记录
        if (payment) {
          // 兼容 breadcrumb 为字符串或数组
          if (payment.payment_type_info && payment.payment_type_info.breadcrumb) {
            const bc = payment.payment_type_info.breadcrumb;
            if (Array.isArray(bc)) {
              payment.payment_type_info.breadcrumb = bc;
            } else if (typeof bc === 'string') {
              payment.payment_type_info.breadcrumb = bc.split(/\s*>\s*|\s*\/\s*/).filter(Boolean);
            }
          }
          this.relatedPayments = [payment];

          // 支付模板元素（用于展示 fields）
          await this.loadPaymentTemplateElements(payment);

          // 合同相关支付列表（若有合同）
          const contractId = this.getContractIdFromPayment(payment);
          if (contractId) {
            await this.loadContractDetail(contractId);
            await this.loadContractPayments(contractId, payment.id);
          } else {
            this.contractDetail = null;
            this.contractPayments = [];
          }

          // 关联事前流程：加载 PlannedExpenditure 模板与内容（兼容 payment.related_type / 明细 related_type）
          if (this.getPlannedExpenditureIdsFromPayment(payment).length > 0) {
            await this.loadIndirectExpenditures(payment);
          } else {
            this.indirectExpenditureMap = {};
          }
        } else {
          this.relatedPayments = [];
          this.indirectExpenditureMap = {};
          this.paymentTemplateElements = [];
          this.contractDetail = null;
          this.contractPayments = [];
        }
      } catch (err) {
        console.error('加载支付信息失败:', err);
        this.relatedPayments = [];
        this.indirectExpenditureMap = {};
        this.paymentTemplateElements = [];
        this.contractDetail = null;
        this.contractPayments = [];
      } finally {
        this.loadingPayments = false;
      }
    },

    // 加载“支付分类绑定的模板元素”并预加载 checklist/options 与 detail_table 列，以及 approval_flow 的流程展示信息
    async loadPaymentTemplateElements(payment) {
      const categoryId = Number(payment?.payment_category_id);
      if (!categoryId || Number.isNaN(categoryId)) {
        this.paymentTemplateElements = [];
        return;
      }
      this.loadingPaymentTemplateElements = true;
      try {
        const list = await getPaymentCategoryTemplateElements(categoryId);
        const arr = Array.isArray(list) ? list.slice() : [];
        arr.sort((a, b) => (a.sort_order || 0) - (b.sort_order || 0));
        this.paymentTemplateElements = arr;

        // checklist：按元素定义拉 options（最原始）
        const checklistIds = arr
          .filter(el => el && el.type === 'checklist' && el.id)
          .map(el => Number(el.id))
          .filter(Boolean);
        for (const id of checklistIds) {
          // eslint-disable-next-line no-await-in-loop
          await this.loadPaymentChecklistOptionsIfNeeded(id);
        }

        // detail_table：预加载列定义
        const dtIds = arr
          .filter(el => el && el.type === 'detail_table' && el.id)
          .map(el => Number(el.id))
          .filter(Boolean);
        const colResults = await Promise.all(dtIds.map(id => getDetailTableFields(id).catch(() => [])));
        colResults.forEach((cols, idx) => {
          const id = dtIds[idx];
          const c = Array.isArray(cols) ? cols.slice() : [];
          c.sort((a, b) => (a.sort_order || 0) - (b.sort_order || 0));
          this.$set(this.paymentDetailTableFieldsMap, id, c);
        });

        // approval_flow：批量拿 no/title 展示
        const flowIds = [];
        arr.forEach(el => {
          if (el && el.type === 'approval_flow' && el.id) {
            const v = this.getPaymentTemplateElementValue(el, payment);
            if (v) {
              const fid = Number(v);
              if (fid && !Number.isNaN(fid) && !this.paymentFlowDetailsMap?.[fid]) flowIds.push(fid);
            }
          }
        });
        const uniq = Array.from(new Set(flowIds));
        if (uniq.length) {
          try {
            const map = await getOaFlowDetails(uniq);
            if (map && typeof map === 'object') {
              Object.keys(map).forEach(k => {
                const fid = Number(k);
                if (fid && map[k]) {
                  this.$set(this.paymentFlowDetailsMap, fid, map[k]);
                }
              });
            }
          } catch (e) {
            // ignore
          }
        }
      } catch (e) {
        this.paymentTemplateElements = [];
      } finally {
        this.loadingPaymentTemplateElements = false;
      }
    },

    async getConfig() {
      const loading = this.$loading({
        lock: true,
        text: "拼命加载中",
        spinner: "el-icon-loading",
        background: "rgba(0, 0, 0, 0.8)",
      });
      // 路由为detail为详情
      if (/\/detail/.test(this.$route.path) && this.$route.query.flow_id) {
        try {
          const res = await view(this.$route.query.flow_id);
          const { fields } = res?.customModel;
          let subFormRequest = [];
          const getSubForm = (id) => {
            subFormRequest.push(fieldConfig(id));
          };
          fields.forEach((field) => {
            if (field.sub_custom_model_id) {
              getSubForm(field.sub_custom_model_id);
            }
          });
          const subConfigs = await Promise.all(subFormRequest);
          subConfigs.forEach((sub) => {
            if (sub.customModel?.id) {
              this.subConfig.set(sub.customModel?.id, sub);
            }
          });
          this.config = res;
          this.readableFields = /\/detail/.test(this.$route.path)
              ? this.fields?.map((i) => i.id)
              : this.config?.currentNode?.readable || [];
          this.writeableFields = this.config?.currentNode?.writeable || [];
          // 生成空的form表单对象
          this.generateForm(this.form, fields);
          // 加载关联流程ID（如果有）
          if (res?.flow?.related_flows && res.flow.related_flows.length > 0) {
            const relatedIds = res.flow.related_flows.map(f => f.id).join(',');
            this.form.related_flow_ids = relatedIds;
          }
          // form赋值
          const { data } = res?.flow;
          for (let key in data) {
            try {
              let jsonObj = JSON.parse(data[key]);
              jsonObj.forEach(item => {
                // 遍历对象中的每个键值对
                for (const key in item) {
                  if (typeof item[key] === 'string') {
                    try {
                      // 尝试解析字符串为 JSON 对象
                      const parsedValue = JSONBigint({ storeAsString: true }).parse(item[key]);
                      // 如果解析成功，替换原始字符串
                      item[key] = parsedValue;
                    } catch (e) {
                      // 如果解析失败，继续保持原始字符串
                    }
                  }
                }
              })
              if (this.form.hasOwnProperty(key)) {
                this.form[key] = jsonObj;
              }
            } catch (err) {
              // 允许所有字段被赋值，包括 _display 字段
              if (this.form.hasOwnProperty(key) || key.endsWith('_display')) {
                if (data[key] instanceof Array) {
                  if (data[key].length > 0 && data[key][0].hasOwnProperty('url')) {
                    this.form[key] = data[key].map(i => ({
                      name: i.original_name,
                      url: i.url,
                      response: i,
                    }))
                  } else {
                    this.form[key] = ''
                  }
                } else {
                  this.form[key] = data[key];
                }
              }
            }
          }
          this.form = Object.assign({}, this.form);
          loading.close();
          
          // 加载关联的支付信息（详情模式下）
          if (this.$route.query.flow_id) {
            this.loadRelatedPayments();
          }
        } catch (err) {
          console.error(err);
          this.$message.error("配置失败");
          loading.close();
        }
      } else if (!this.$route.query.flow_id) {
        // 新建
        try {
          this.csrf_token = generateRandomString()
          const res = await preConfig(this.$route.query.module_id);
          const { fields } = res?.customModel;
          let subFormRequest = [];
          const getSubForm = (id) => {
            subFormRequest.push(fieldConfig(id));
          };
          fields.forEach((field) => {
            if (field.sub_custom_model_id) {
              getSubForm(field.sub_custom_model_id);
            }
          });
          const subConfigs = await Promise.all(subFormRequest);
          subConfigs.forEach((sub) => {
            if (sub.customModel?.id) {
              this.subConfig.set(sub.customModel?.id, sub);
            }
          });
          this.config = res;
          this.readableFields = /\/detail/.test(this.$route.path)
            ? this.fields?.map((i) => i.id)
            : this.config?.currentNode?.readable || [];
          this.writeableFields = this.config?.currentNode?.writeable || [];
          this.generateForm(this.form, fields);
          this.handleDefaultJSON();
          this.form = Object.assign({}, this.form);
          // 新建完成后建立 fill_flow_title watcher（确保 default_json 等已写入后再监听）
          this.setupFillFlowTitleWatcher(fields);
          loading.close();
        } catch (err) {
          console.error(err);
          this.$message.error("配置失败");
          loading.close();
        }
      } else {
        // 待办
        try {
          const res = await preDeal(this.$route.query.flow_id);
          const { fields } = res?.customModel;
          let subFormRequest = [];
          const getSubForm = (id) => {
            subFormRequest.push(fieldConfig(id));
          };
          fields.forEach((field) => {
            if (field.sub_custom_model_id) {
              getSubForm(field.sub_custom_model_id);
            }
          });
          const subConfigs = await Promise.all(subFormRequest);
          subConfigs.forEach((sub) => {
            if (sub.customModel?.id) {
              this.subConfig.set(sub.customModel?.id, sub);
            }
          });
          this.config = res;
          this.readableFields = /\/detail/.test(this.$route.path)
            ? this.fields?.map((i) => i.id)
            : this.config?.currentNode?.readable || [];
          this.writeableFields = this.config?.currentNode?.writeable || [];
          this.generateForm(this.form, fields);
          // 加载关联流程ID（如果有）
          if (res?.flow?.related_flows && res.flow.related_flows.length > 0) {
            const relatedIds = res.flow.related_flows.map(f => f.id).join(',');
            this.form.related_flow_ids = relatedIds;
          }
          this.handleDefaultJSON();
          const { data } = res?.flow;
          for (let key in data) {
            try {
              let jsonObj = JSON.parse(data[key]);
              jsonObj.forEach(item => {
                // 遍历对象中的每个键值对
                for (const key in item) {
                  if (typeof item[key] === 'string') {
                    try {
                      // 尝试解析字符串为 JSON 对象
                      const parsedValue = JSONBigint({ storeAsString: true }).parse(item[key]);
                      // 如果解析成功，替换原始字符串
                      item[key] = parsedValue;
                    } catch (e) {
                      // 如果解析失败，继续保持原始字符串
                    }
                  }
                }
              })
              if (this.form.hasOwnProperty(key)) {
                this.form[key] = jsonObj;
              }
            } catch (err) {
              // 允许所有字段被赋值，包括 _display 字段
              if (this.form.hasOwnProperty(key) || key.endsWith('_display')) {
                if (data[key] instanceof Array) {
                  if (data[key].length > 0) {
                    this.form[key] = data[key];
                  } else {
                    this.form[key] = ''
                  }
                }
                if (data[key] && data[key] !== 'null' && data[key] !== 'undefined') {
                  this.form[key] = data[key];
                }
              }
            }
          }
          this.form = Object.assign({}, this.form);
          loading.close();
          
          // 加载关联的支付信息（办理模式下）
          if (this.$route.query.flow_id) {
            this.loadRelatedPayments();
          }
        } catch (err) {
          console.error(err);
          this.$message.error("配置失败");
          loading.close();
        }
      }
    },

    async submit(type) {
      if (window.$_uploading) {
        this.$message.warning("文件正在上传中")
        return
      }
      let copyForm;
      try {
        await this.$refs['desktopForm'].validate()
      } catch (err) {
        console.warn(err)
        this.$message.warning('数据校验失败')
        return
      }

      copyForm = deepCopy(this.$refs["desktopForm"].form);
      const uploadHandler = (form, fields) => {
        let keys = Object.keys(form)
        if(fields){
          keys.forEach(key => {
          if (form[key] instanceof Array) {
           if (form[key].length > 0 && typeof form[key][0] === 'object') {
            console.log("key",key,fields)
             const myField = fields.find(field => field.name === key)
             if (myField) {
               if (myField.type === 'file') {
                 form[key] = form[key].map(i => i.hasOwnProperty('response') ? i.response : i)
               } else {
                 form[key].forEach(i => {
                   uploadHandler(i, this.subConfig.get(myField.sub_custom_model_id)?.customModel?.fields)
                 })
               }
             }
           } else if (form[key].length > 0) {

           } else {
             form[key] = ''
           }
          } else {
            if (form[key] === 'null' || form[key] === 'undefined') {
              form[key] = ''
            }
          }
        })
        }

      }
      console.log("copyForm",copyForm,this.fields)
      console.log("this.writeableFields",this.writeableFields)
      uploadHandler(copyForm, this.fields)
      for (let key in copyForm) {
        let myField = this.fields.find(i => i.name === key)

        // 保留关联流程ID字段，即使它不在writeableFields中
        if (key === 'related_flow_ids') {
          continue;
        }

        if (myField && this.writeableFields.indexOf(myField.id) === -1) {
          delete copyForm[key]
        }
      }

      copyForm["current_node_id"] = this.config.currentNode.id;
      try {
        let callback = () => {};
        switch (type) {
          case "only-submit":
            if (this.$route.query.flow_id) {
              copyForm["temporary_save"] = 1;
            }
            callback = () => this.$router.push("/flow/list/todo")
            break;
          case "assign":
            if (this.$route.query.flow_id) {
              copyForm["temporary_save"] = 0;
            }
            callback = () => (this.isShowAssign = true);
            break;
        }
      // 确保 related_flow_ids 被包含在提交数据中
      // 无论是否有值，都要传递这个字段，以便后端能正确处理
      copyForm.related_flow_ids = this.form.related_flow_ids || ''
      console.log("copyForm",copyForm,this.$refs["desktopForm"].form)
      console.log("related_flow_ids:", copyForm.related_flow_ids, "form.related_flow_ids:", this.form.related_flow_ids)
      // return
        if (this.$route.query.flow_id) {
          copyForm.id = this.$route.query.flow_id;
          const { flow, is_last_handled_log } = await deal(
            this.$route.query.flow_id,
            copyForm
          );
          this.result = flow;
          if (!is_last_handled_log) {
            await this.$alert(
              "办理成功，其他会签办理完成后，由最后办理的成员流转到下一节点。",
              "提示",
              {
                showClose: false,
              }
            );
            callback = () => this.$router.push("/flow/list/todo");
          }
        } else {
          copyForm['csrf_token'] = this.csrf_token
          this.result = await create(this.$route.query.module_id, copyForm);
          
          // 如果流程创建成功且在iframe中，发送postMessage通知父窗口
          if (this.result && this.result.id && window.self !== window.top) {
            try {
              window.parent.postMessage({
                type: 'OA_FLOW_CREATED',
                flowId: this.result.id,
                success: true,
                message: '流程创建成功'
              }, '*')
            } catch (error) {
              console.error('发送postMessage失败:', error)
            }
          }
        }
        callback();
      } catch (err) {
        console.error(err);
        // 如果流程创建失败且在iframe中，发送postMessage通知父窗口
        if (window.self !== window.top) {
          try {
            window.parent.postMessage({
              type: 'OA_FLOW_CREATED',
              flowId: null,
              success: false,
              message: err.message || '流程创建失败'
            }, '*')
          } catch (postError) {
            console.error('发送postMessage失败:', postError)
          }
        }
      }
    },

    async cellDblclickEvent({ row, column }) {
      // if(this.$store.state.user.username !== 'admin') return
      if(!this.$store.state.user.roles.includes("全局流程监管")) return
      if(column.field === 'created_at' || column.field === 'updated_at') {
        this.timeId = row.id
        this.selectedDateType = column.field
        this.selectedDateTime = row[column.field]
        this.isShowTime = true
      }
      if(column.field === "user.name"){
        this.currentRowId = row.id
        this.selectedUserId = row.user ? row.user.id : ''
        this.isShowUserDialog = true
        this.getUserList()
      }
    },
    updateTime() {
      updateNodeTime({
        id: this.timeId,
        date: this.selectedDateTime,
        date_type: this.selectedDateType
      }).then(_ => {
        this.$message.success('更新成功')
        this.timeId = ''
        this.selectedDateTime = ''
        this.selectedDateType = ''
        this.isShowTime = false
        this.getConfig()
      })
    },
    async getUserList() {
      try {
        const userRes = await userListNoAuth({
          page: 1,
          rows: 9999,
        })
        this.userList = userRes.data || []
      } catch (err) {
        console.error(err)
        this.$message.error('获取用户列表失败')
      }
    },
    updateUser() {
      if (!this.selectedUserId) {
        this.$message.warning('请选择承办人员')
        return
      }
      updateNodeTime({
        id: this.currentRowId,
        user_id: this.selectedUserId
      }).then(_ => {
        this.$message.success('更新成功')
        this.currentRowId = ''
        this.selectedUserId = ''
        this.isShowUserDialog = false
        this.getConfig()
      })
    },
  },
  computed: {
    device() {
      return this.$store.state.app.device;
    },
    fields() {
      return this.config?.customModel?.fields || [];
    },
    // 放到data中，为了修改
    // readableFields() {
    //   return /\/detail/.test(this.$route.path)
    //     ? this.fields?.map((i) => i.id)
    //     : this.config?.currentNode?.readable || [];
    // },
    // writeableFields() {
    //   return this.config?.currentNode?.writeable || [];
    // },
    node() {
      return this.config?.currentNode || {};
    },
    scriptContent() {
      if (this.config?.customModel?.view_js && this.$route.query.flow_id && /\/detail/.test(this.$route.path)) {
        return this.config?.customModel?.view_js;
      } else if (this.config?.customModel?.js && !/\/detail/.test(this.$route.path)) {
        return this.config?.customModel?.js;
      }
    },
    diffTime() {
      return function (end, start) {
        const diff = this.$moment(end).diff(this.$moment(start));
        return this.formatTime(diff);
      };
    },
    footerData() {
      const diff = this.$moment(this.config?.logs?.at(-1)?.updated_at).diff(
        this.$moment(this.config?.logs?.at(0)?.created_at)
      );
      return [
        {
          seq: "总耗时",
          use_time: this.formatTime(diff),
        },
      ];
    },
    isFirstNode() {
      return this.config?.logs?.length === 0 || this.config?.currentNode?.category === 'start'
    },
    isSinglePage() {
      // 仅当显式传参 isSinglePage=1 时才启用“单页模式”
      // 不能用 window.self !== window.top 作为判定：被主框架 iframe 引入时也会命中，导致 padding/阴影/背景与独立打开不一致
      return this.$route.query.isSinglePage === '1' || this.$route.query.isSinglePage === 1
    }
  },
  created() {
    this.getConfig();
    // 如果是单页模式且在iframe中，调整样式
    if (this.isSinglePage && window.self !== window.top) {
      document.body.style.margin = '0'
      document.body.style.padding = '0'
    }
  },
  mounted() {
    // 如果是单页模式且在iframe中，调整样式
    if (this.isSinglePage && window.self !== window.top) {
      const container = this.$el
      if (container) {
        container.style.margin = '0'
        container.style.padding = '0'
      }
    }
  },
};
</script>

<style scoped lang="scss">
::v-deep .el-step__title {
  font-size: 14px;
  line-height: 1.5;
}
::v-deep .el-step__icon.is-icon {
  border-radius: 100%;
  box-shadow: 2px 0 8px 0 rgba(29, 35, 41, 0.05);
  width: 36px;
  height: 36px;
  border: solid 2px;
}
::v-deep .el-step.is-center .el-step__line {
  top: 50%;
}
 
.container {
  padding: 20px;

  .card {
    position: relative;
  }
  .btns {
    display: flex;
    justify-content: center;
    margin-top: 10px;
    flex-wrap: wrap;
  }

  // 单页模式（弹窗模式）
  &.is-single-page {
    padding: 0;
    margin: 0;
    height: 100vh;
    overflow-y: auto;

    .card {
      border: none;
      box-shadow: none;
      min-height: 100vh;
      // iframe 引入时也保持白底（与独立打开一致）
      background: #fff;
    }
  }
}

@media (max-width: 768px) {
  .container {
    padding: 0;
  }
  .btns {
    justify-content: space-evenly;
    & > * {
      margin: 4px 6px;
    }
  }

  ::v-deep .el-steps--horizontal {
    display: flex;
    flex-wrap: wrap;
  }
}

/* 支付信息展示样式 */
.payment-info {
  .info-item {
    margin-bottom: 8px;
    .label {
      color: #606266;
      font-size: 14px;
      margin-right: 8px;
    }
    .value {
      color: #303133;
      font-size: 14px;
    }
  }
}

/* 非直接支付相关信息样式 */
.indirect-payment-descriptions {
  ::v-deep .el-descriptions-item__label {
    white-space: nowrap;
    color: #303133;
    font-weight: 500;
  }
  ::v-deep .el-descriptions-item__content {
    color: #303133;
  }
}

/* 支付模板字段：checklist 纯展示样式（最简线条） */
.oa-readonly-checklist {
  width: 100%;
}
.oa-checklist-option-item {
  margin: 6px 0;
}
.oa-checklist-row {
  display: flex;
  align-items: flex-start;
  gap: 8px;
}
.oa-checklist-box {
  width: 14px;
  height: 14px;
  border: 1px solid #111827;
  border-radius: 3px;
  margin-top: 2px;
  flex-shrink: 0;
  position: relative;
  background: transparent;
}
.oa-checklist-box.checked::after {
  content: '';
  position: absolute;
  left: 50%;
  top: 50%;
  width: 6px;
  height: 10px;
  border-right: 2px solid #111827;
  border-bottom: 2px solid #111827;
  transform: translate(-50%, -58%) rotate(45deg);
}
.oa-checklist-text {
  flex: 1;
  min-width: 0;
  line-height: 18px;
  word-break: break-word;
}
.oa-checklist-label {
  color: #111827;
}
.oa-checklist-remark {
  margin-left: 10px;
  color: #6b7280;
}

/* detail_table：横向滚动（避免被父级/祖先容器裁剪） */
.oa-table-x-scroll {
  display: block;
  width: 100%;
  max-width: 100%;
  overflow-x: auto;
  overflow-y: hidden;
  -webkit-overflow-scrolling: touch;
}
/* 兜底：强制表格内容可“撑开”，由外层容器产生横向滚动条 */
.oa-table-x-scroll ::v-deep .el-table {
  width: auto;
  min-width: 600px; /* 有列时保证一定宽度 */
}
.oa-table-x-scroll ::v-deep .el-table__header-wrapper,
.oa-table-x-scroll ::v-deep .el-table__body-wrapper {
  overflow-x: visible; /* 让宽度由最外层滚动容器接管 */
}
.oa-table-x-scroll ::v-deep .el-table__header,
.oa-table-x-scroll ::v-deep .el-table__body {
  width: max-content; /* 列多时撑开 */
}

/* 关键：el-descriptions 是 table layout(auto) 时会被内容撑宽，导致“越界但无滚动条”；
   仅对本页两块 descriptions 约束表格宽度（并显式设置 label 宽度避免重叠） */
.payment-extra-descriptions,
.indirect-payment-descriptions {
  ::v-deep .el-descriptions__table {
    width: 100%;
    table-layout: fixed;
  }
  ::v-deep .el-descriptions-item__label {
    width: 250px;
    white-space: nowrap;
  }
  ::v-deep .el-descriptions-item__content {
    width: auto;
    min-width: 0;
    word-break: break-word;
  }
}
</style>
<style lang="scss">
.log-table-scroll {
  ::-webkit-scrollbar {
    height: 0;
  }
}
.print-template-radios .el-radio__input:has(input[type=radio]:checked) .el-radio__inner {
  border-color: var(--theme-color);
  background: var(--theme-color);
  &::after {
    transform: translate(-50%,-50%) scale(1);
  }
}
/** 移动端展示 **/
@media screen and (max-width: 500px) {
    .el-picker-panel__sidebar {
      width: 100%;
    }
    .el-picker-panel {
      width: 400px!important;
    }
    .el-picker-panel__content {
      width: 100%;
    }
    .el-picker-panel__body{
      margin-left: 0!important;
      display: flex;
      flex-direction: column;
      min-width: auto!important;
    }
    .el-picker-panel__sidebar {
      position: relative;
    }
    .el-picker-panel__body-wrapper {
      display: flex;
      flex-direction: column;
      align-items: center;
    }
  }
</style>
