|
|
@@ -6,6 +6,19 @@
|
|
|
<div class="block-title base-info-view__title">人员新增录入</div>
|
|
|
<div class="base-info-view__toolbar">
|
|
|
<el-button type="primary" size="small" @click="openPersonDialog">单人信息录入</el-button>
|
|
|
+ <el-button size="small" :loading="templateDownloading" @click="onDownloadTemplate">
|
|
|
+ 下载导入模板
|
|
|
+ </el-button>
|
|
|
+ <el-upload
|
|
|
+ class="base-info-view__import-upload"
|
|
|
+ :show-file-list="false"
|
|
|
+ :auto-upload="false"
|
|
|
+ accept=".xlsx,.xls,.csv"
|
|
|
+ :disabled="importing"
|
|
|
+ :on-change="onImportFileChange"
|
|
|
+ >
|
|
|
+ <el-button size="small" type="success" :loading="importing">Excel 导入</el-button>
|
|
|
+ </el-upload>
|
|
|
</div>
|
|
|
</div>
|
|
|
<p class="base-info-view__desc">
|
|
|
@@ -195,8 +208,76 @@
|
|
|
</template>
|
|
|
</el-dialog>
|
|
|
|
|
|
- <!-- 本村发放人历史记录 -->
|
|
|
- <PagePanel :title="isAdmin ? '发放人历史记录' : '本村发放人历史记录'" class="base-info-view__panel">
|
|
|
+ <!-- 本村人员底数(服务端分页) -->
|
|
|
+ <PagePanel :title="isAdmin ? '人员列表' : '本村人员列表'" class="base-info-view__panel">
|
|
|
+ <el-form :inline="true" size="small" class="base-info-view__roster-filter">
|
|
|
+ <el-form-item label="姓名">
|
|
|
+ <el-input
|
|
|
+ v-model="rosterFilter.name"
|
|
|
+ clearable
|
|
|
+ placeholder="模糊搜索"
|
|
|
+ style="width: 110px"
|
|
|
+ class="is-editable"
|
|
|
+ @keyup.enter="applyRosterFilter"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="身份证">
|
|
|
+ <el-input
|
|
|
+ v-model="rosterFilter.idCard"
|
|
|
+ clearable
|
|
|
+ placeholder="后几位亦可"
|
|
|
+ style="width: 140px"
|
|
|
+ class="is-editable"
|
|
|
+ @keyup.enter="applyRosterFilter"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="手机号">
|
|
|
+ <el-input
|
|
|
+ v-model="rosterFilter.phone"
|
|
|
+ clearable
|
|
|
+ placeholder="模糊"
|
|
|
+ style="width: 120px"
|
|
|
+ class="is-editable"
|
|
|
+ @keyup.enter="applyRosterFilter"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="业务状态">
|
|
|
+ <el-select
|
|
|
+ v-model="rosterFilter.bizStatus"
|
|
|
+ clearable
|
|
|
+ placeholder="全部"
|
|
|
+ style="width: 130px"
|
|
|
+ class="is-editable"
|
|
|
+ >
|
|
|
+ <el-option
|
|
|
+ v-for="s in rosterStatusOptions"
|
|
|
+ :key="s.value"
|
|
|
+ :label="s.label"
|
|
|
+ :value="s.value"
|
|
|
+ />
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="来源">
|
|
|
+ <el-select
|
|
|
+ v-model="rosterFilter.source"
|
|
|
+ clearable
|
|
|
+ filterable
|
|
|
+ allow-create
|
|
|
+ placeholder="全部"
|
|
|
+ style="width: 120px"
|
|
|
+ class="is-editable"
|
|
|
+ >
|
|
|
+ <el-option v-for="s in rosterSourceOptions" :key="s" :label="s" :value="s" />
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item>
|
|
|
+ <el-button type="primary" @click="applyRosterFilter">查询</el-button>
|
|
|
+ <el-button @click="resetRosterFilter">重置</el-button>
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+ <p class="base-info-view__history-tip">
|
|
|
+ 展示本村在册人员底数(非发放明细);月补标准为街镇当前有效标准。可查看详情、告警与历史发放。
|
|
|
+ </p>
|
|
|
<div class="base-info-view__table-wrap">
|
|
|
<el-table
|
|
|
v-loading="peopleLoading"
|
|
|
@@ -206,26 +287,43 @@
|
|
|
size="small"
|
|
|
height="100%"
|
|
|
>
|
|
|
- <el-table-column prop="batchNo" label="批次号" width="100" align="center" />
|
|
|
<el-table-column prop="name" label="姓名" width="80" />
|
|
|
- <el-table-column label="身份证" min-width="160">
|
|
|
+ <el-table-column label="身份证" min-width="148">
|
|
|
<template #default="{ row }">{{ maskIdCard(row.idCard) }}</template>
|
|
|
</el-table-column>
|
|
|
+ <el-table-column label="手机号" width="118">
|
|
|
+ <template #default="{ row }">{{ row.phoneNumber || '—' }}</template>
|
|
|
+ </el-table-column>
|
|
|
<el-table-column prop="town" label="街镇" width="90" />
|
|
|
<el-table-column prop="village" label="村居" width="90" />
|
|
|
- <el-table-column label="银行卡" min-width="180">
|
|
|
+ <el-table-column label="银行卡" min-width="140">
|
|
|
<template #default="{ row }">{{ maskBankCard(row.bankCard) }}</template>
|
|
|
</el-table-column>
|
|
|
- <el-table-column prop="bankName" label="开户行" min-width="140" show-overflow-tooltip />
|
|
|
- <el-table-column label="标准(元/月)" width="100" align="right">
|
|
|
+ <el-table-column label="开户行" min-width="100" show-overflow-tooltip>
|
|
|
+ <template #default="{ row }">{{ row.bankName || '—' }}</template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="月补标准(街镇)" width="120" align="right">
|
|
|
<template #default="{ row }">{{ formatAmount(row.standard) }}</template>
|
|
|
</el-table-column>
|
|
|
- <el-table-column label="状态" width="110" align="center">
|
|
|
+ <el-table-column label="状态" width="100" align="center">
|
|
|
<template #default="{ row }">
|
|
|
<StatusTag :status="row.status" />
|
|
|
</template>
|
|
|
</el-table-column>
|
|
|
- <el-table-column prop="source" label="来源" width="110" />
|
|
|
+ <el-table-column prop="source" label="来源" width="100" show-overflow-tooltip />
|
|
|
+ <el-table-column label="操作" width="220" align="center" fixed="right">
|
|
|
+ <template #default="{ row }">
|
|
|
+ <el-button link type="primary" size="small" @click="openPersonDetail(row as Person)">
|
|
|
+ 详情
|
|
|
+ </el-button>
|
|
|
+ <el-button link type="warning" size="small" @click="openPersonAlerts(row as Person)">
|
|
|
+ 告警
|
|
|
+ </el-button>
|
|
|
+ <el-button link type="success" size="small" @click="openPersonHistory(row as Person)">
|
|
|
+ 历史发放
|
|
|
+ </el-button>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
</el-table>
|
|
|
</div>
|
|
|
<TablePagination
|
|
|
@@ -237,76 +335,252 @@
|
|
|
</PagePanel>
|
|
|
</div>
|
|
|
|
|
|
- <!-- 人员录入弹窗 -->
|
|
|
- <el-dialog v-model="personDialogVisible" title="单人信息录入" width="640px" destroy-on-close>
|
|
|
+ <!-- 人员详细信息(人员表全字段) -->
|
|
|
+ <el-dialog
|
|
|
+ v-model="personDetailVisible"
|
|
|
+ :title="personDetail ? `详细信息 · ${personDetail.name}` : '详细信息'"
|
|
|
+ width="920px"
|
|
|
+ destroy-on-close
|
|
|
+ @closed="personDetail = null"
|
|
|
+ >
|
|
|
+ <div v-loading="personDetailLoading">
|
|
|
+ <PersonnelDetailDescriptions v-if="personDetail" :person="personDetail" />
|
|
|
+ </div>
|
|
|
+ <template #footer>
|
|
|
+ <el-button type="primary" @click="personDetailVisible = false">关闭</el-button>
|
|
|
+ </template>
|
|
|
+ </el-dialog>
|
|
|
+
|
|
|
+ <!-- 人员告警处理记录 -->
|
|
|
+ <el-dialog
|
|
|
+ v-model="personAlertVisible"
|
|
|
+ :title="personAlertTarget ? `告警记录 · ${personAlertTarget.name}` : '告警记录'"
|
|
|
+ width="900px"
|
|
|
+ destroy-on-close
|
|
|
+ @closed="onPersonAlertClosed"
|
|
|
+ >
|
|
|
+ <el-table v-loading="personAlertLoading" :data="personAlerts" border stripe size="small" max-height="420">
|
|
|
+ <el-table-column prop="createdAt" label="产生时间" width="150" />
|
|
|
+ <el-table-column prop="batchNo" label="批次号" width="96" align="center" />
|
|
|
+ <el-table-column prop="type" label="类型" width="110" show-overflow-tooltip />
|
|
|
+ <el-table-column label="等级" width="72" align="center">
|
|
|
+ <template #default="{ row }">
|
|
|
+ <StatusTag :status="row.level" mode="level" />
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column prop="content" label="内容" min-width="160" show-overflow-tooltip />
|
|
|
+ <el-table-column label="处理状态" width="90" align="center">
|
|
|
+ <template #default="{ row }">
|
|
|
+ <StatusTag :status="row.status" />
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column prop="result" label="处理结果" min-width="120" show-overflow-tooltip />
|
|
|
+ <el-table-column label="操作" width="88" align="center" fixed="right">
|
|
|
+ <template #default="{ row }">
|
|
|
+ <el-button link type="primary" size="small" @click="openAlertLifecycle(row as AlertItem)">
|
|
|
+ 处理流程
|
|
|
+ </el-button>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ </el-table>
|
|
|
+ <el-empty
|
|
|
+ v-if="!personAlertLoading && !personAlerts.length"
|
|
|
+ description="暂无告警/处理记录"
|
|
|
+ :image-size="64"
|
|
|
+ />
|
|
|
+ <template #footer>
|
|
|
+ <el-button type="primary" @click="personAlertVisible = false">关闭</el-button>
|
|
|
+ </template>
|
|
|
+ </el-dialog>
|
|
|
+
|
|
|
+ <!-- 单条告警处理流程 -->
|
|
|
+ <el-dialog
|
|
|
+ v-model="alertLifecycleVisible"
|
|
|
+ :title="alertLifecycleTitle"
|
|
|
+ width="640px"
|
|
|
+ append-to-body
|
|
|
+ destroy-on-close
|
|
|
+ @closed="onAlertLifecycleClosed"
|
|
|
+ >
|
|
|
+ <AlertLifecycleTimeline :lifecycle="alertLifecycleItems" :loading="alertLifecycleLoading" />
|
|
|
+ <template #footer>
|
|
|
+ <el-button type="primary" @click="alertLifecycleVisible = false">关闭</el-button>
|
|
|
+ </template>
|
|
|
+ </el-dialog>
|
|
|
+
|
|
|
+ <!-- 历史发放记录 -->
|
|
|
+ <el-dialog
|
|
|
+ v-model="personHistoryVisible"
|
|
|
+ :title="personHistoryTarget ? `历史发放 · ${personHistoryTarget.name}` : '历史发放'"
|
|
|
+ width="960px"
|
|
|
+ destroy-on-close
|
|
|
+ @closed="onPersonHistoryClosed"
|
|
|
+ >
|
|
|
+ <el-table
|
|
|
+ v-loading="personHistoryLoading"
|
|
|
+ :data="personHistoryRows"
|
|
|
+ border
|
|
|
+ stripe
|
|
|
+ size="small"
|
|
|
+ max-height="420"
|
|
|
+ >
|
|
|
+ <el-table-column prop="batchNo" label="批次号" width="100" align="center" />
|
|
|
+ <el-table-column label="标准金额" width="100" align="right">
|
|
|
+ <template #default="{ row }">
|
|
|
+ {{
|
|
|
+ isAdjustPayRow(row as BatchMemberPerson)
|
|
|
+ ? '—'
|
|
|
+ : formatAmount(row.monthlyAmount ?? row.standard)
|
|
|
+ }}
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="补发金额" width="96" align="right">
|
|
|
+ <template #default="{ row }">{{ formatAmount(row.supplementAmount) }}</template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="丧葬金额" width="96" align="right">
|
|
|
+ <template #default="{ row }">{{ formatAmount(row.funeralAmount) }}</template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="扣减" width="88" align="right">
|
|
|
+ <template #default="{ row }">{{ formatAmount(row.deductAmount) }}</template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="应发合计" width="104" align="right">
|
|
|
+ <template #default="{ row }">
|
|
|
+ <strong>{{ formatAmount(row.payableAmount) }}</strong>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="实发" width="96" align="right">
|
|
|
+ <template #default="{ row }">
|
|
|
+ {{ row.actualAmount == null ? '—' : formatAmount(row.actualAmount) }}
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="回盘结果" width="100" show-overflow-tooltip>
|
|
|
+ <template #default="{ row }">{{ row.returnResult || '—' }}</template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="操作" width="72" align="center" fixed="right">
|
|
|
+ <template #default="{ row }">
|
|
|
+ <el-button
|
|
|
+ link
|
|
|
+ type="primary"
|
|
|
+ size="small"
|
|
|
+ @click="openPersonPay(row as BatchMemberPerson)"
|
|
|
+ >
|
|
|
+ 详情
|
|
|
+ </el-button>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ </el-table>
|
|
|
+ <el-empty
|
|
|
+ v-if="!personHistoryLoading && !personHistoryRows.length"
|
|
|
+ description="暂无历史发放记录"
|
|
|
+ :image-size="64"
|
|
|
+ />
|
|
|
+ <template #footer>
|
|
|
+ <el-button type="primary" @click="personHistoryVisible = false">关闭</el-button>
|
|
|
+ </template>
|
|
|
+ </el-dialog>
|
|
|
+
|
|
|
+ <!-- 单笔发放金额详情 -->
|
|
|
+ <el-dialog
|
|
|
+ v-model="personPayVisible"
|
|
|
+ :title="personPayTitle"
|
|
|
+ width="520px"
|
|
|
+ append-to-body
|
|
|
+ destroy-on-close
|
|
|
+ >
|
|
|
+ <template v-if="currentPersonPay">
|
|
|
+ <el-descriptions :column="1" border size="small">
|
|
|
+ <el-descriptions-item label="批次号">
|
|
|
+ {{ currentPersonPay.batchNo || '—' }}
|
|
|
+ </el-descriptions-item>
|
|
|
+ <el-descriptions-item label="姓名">{{ currentPersonPay.name }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="身份证">
|
|
|
+ {{ maskIdCard(currentPersonPay.idCard) }}
|
|
|
+ </el-descriptions-item>
|
|
|
+ <el-descriptions-item label="街镇/村居">
|
|
|
+ {{ currentPersonPay.town }} / {{ currentPersonPay.village }}
|
|
|
+ </el-descriptions-item>
|
|
|
+ <el-descriptions-item label="银行卡">
|
|
|
+ {{ maskBankCard(currentPersonPay.bankCard) }}
|
|
|
+ </el-descriptions-item>
|
|
|
+ <el-descriptions-item label="开户行">
|
|
|
+ {{ currentPersonPay.bankName || '—' }}
|
|
|
+ </el-descriptions-item>
|
|
|
+ <el-descriptions-item
|
|
|
+ v-if="!isAdjustPayRow(currentPersonPay)"
|
|
|
+ label="月补标准(元/月)"
|
|
|
+ >
|
|
|
+ {{ formatAmount(currentPersonPay.monthlyAmount ?? currentPersonPay.standard) }}
|
|
|
+ </el-descriptions-item>
|
|
|
+ <el-descriptions-item v-if="!isAdjustPayRow(currentPersonPay)" label="一次性代发">
|
|
|
+ {{ formatAmount(currentPersonPay.oneTimeAmount) }}
|
|
|
+ </el-descriptions-item>
|
|
|
+ <el-descriptions-item label="补发金额">
|
|
|
+ {{ formatAmount(currentPersonPay.supplementAmount) }}
|
|
|
+ </el-descriptions-item>
|
|
|
+ <el-descriptions-item v-if="!isAdjustPayRow(currentPersonPay)" label="丧葬费">
|
|
|
+ {{ formatAmount(currentPersonPay.funeralAmount) }}
|
|
|
+ </el-descriptions-item>
|
|
|
+ <el-descriptions-item v-if="!isAdjustPayRow(currentPersonPay)" label="扣减金额">
|
|
|
+ {{ formatAmount(currentPersonPay.deductAmount) }}
|
|
|
+ </el-descriptions-item>
|
|
|
+ <el-descriptions-item label="应发合计">
|
|
|
+ <strong>{{ formatAmount(currentPersonPay.payableAmount) }}</strong>
|
|
|
+ <div class="base-info-view__pay-tip">
|
|
|
+ <template v-if="isAdjustPayRow(currentPersonPay)">
|
|
|
+ 本批为年调标差额补发,不展示月标;应发合计=补发差额。
|
|
|
+ </template>
|
|
|
+ <template v-else>
|
|
|
+ 应发=月补标准+一次性代发+补发+丧葬费−扣减(以后台重算为准)。
|
|
|
+ </template>
|
|
|
+ </div>
|
|
|
+ </el-descriptions-item>
|
|
|
+ <el-descriptions-item label="实发金额">
|
|
|
+ {{
|
|
|
+ currentPersonPay.actualAmount == null
|
|
|
+ ? '未回盘'
|
|
|
+ : formatAmount(currentPersonPay.actualAmount)
|
|
|
+ }}
|
|
|
+ </el-descriptions-item>
|
|
|
+ <el-descriptions-item label="回盘结果">
|
|
|
+ {{ currentPersonPay.returnResult || '—' }}
|
|
|
+ </el-descriptions-item>
|
|
|
+ <el-descriptions-item label="人员状态">
|
|
|
+ <StatusTag :status="currentPersonPay.status" />
|
|
|
+ </el-descriptions-item>
|
|
|
+ <el-descriptions-item label="备注">
|
|
|
+ {{ currentPersonPay.remark || '—' }}
|
|
|
+ </el-descriptions-item>
|
|
|
+ </el-descriptions>
|
|
|
+ </template>
|
|
|
+ </el-dialog>
|
|
|
+
|
|
|
+ <!-- 人员录入弹窗(人员表全字段) -->
|
|
|
+ <el-dialog v-model="personDialogVisible" title="单人信息录入" width="920px" destroy-on-close>
|
|
|
<p class="base-info-view__dialog-tip">
|
|
|
<template v-if="isAdmin">
|
|
|
- 请确认已选择街镇与村居({{ villageScopeLabel }})。姓名、身份证号、银行卡号为必填;开户行、社会保险登记码、备注为选填。
|
|
|
+ 请确认已选择街镇与村居({{ villageScopeLabel }})。姓名、身份证号、手机号、银行卡号为必填;月补标准由街镇标准自动带出,无需手填。灰色项为系统只读。
|
|
|
</template>
|
|
|
<template v-else>
|
|
|
- 仅可录入本村({{ villageScopeLabel }})人员。姓名、身份证号、银行卡号为必填;开户行、社会保险登记码、备注为选填。社会保险登记码用于后续居保状态匹配,建议一并填写。
|
|
|
+ 仅可录入本村({{ villageScopeLabel }})人员。姓名、身份证号、手机号、银行卡号为必填;收款人/继承人等选填,有值时证件与手机将校验格式。
|
|
|
</template>
|
|
|
</p>
|
|
|
- <el-form :model="personForm" label-width="120px" size="small">
|
|
|
- <el-row :gutter="12">
|
|
|
- <el-col :span="12">
|
|
|
- <el-form-item label="所属村居">
|
|
|
- <el-input :model-value="villageScopeLabel" disabled />
|
|
|
- </el-form-item>
|
|
|
- </el-col>
|
|
|
- <el-col :span="12">
|
|
|
- <el-form-item label="姓名" required>
|
|
|
- <el-input v-model="personForm.name" class="is-editable" placeholder="请输入姓名" />
|
|
|
- </el-form-item>
|
|
|
- </el-col>
|
|
|
- <el-col :span="12">
|
|
|
- <el-form-item label="身份证号" required>
|
|
|
- <el-input
|
|
|
- v-model="personForm.idCard"
|
|
|
- class="is-editable"
|
|
|
- placeholder="请输入18位身份证号"
|
|
|
- />
|
|
|
- </el-form-item>
|
|
|
- </el-col>
|
|
|
- <el-col :span="12">
|
|
|
- <el-form-item label="银行卡号" required>
|
|
|
- <el-input
|
|
|
- v-model="personForm.bankCard"
|
|
|
- class="is-editable"
|
|
|
- placeholder="请输入银行卡号"
|
|
|
- />
|
|
|
- </el-form-item>
|
|
|
- </el-col>
|
|
|
- <el-col :span="12">
|
|
|
- <el-form-item label="开户行">
|
|
|
- <el-input
|
|
|
- v-model="personForm.bankName"
|
|
|
- class="is-editable"
|
|
|
- placeholder="选填,如工商银行叶榭支行"
|
|
|
- />
|
|
|
- </el-form-item>
|
|
|
- </el-col>
|
|
|
- <el-col :span="12">
|
|
|
- <el-form-item label="社会保险登记码">
|
|
|
- <el-input
|
|
|
- v-model="personForm.socialInsuranceCode"
|
|
|
- class="is-editable"
|
|
|
- placeholder="选填,建议填写以便居保匹配"
|
|
|
- />
|
|
|
- </el-form-item>
|
|
|
- </el-col>
|
|
|
- <el-col :span="24">
|
|
|
- <el-form-item label="备注">
|
|
|
- <el-input
|
|
|
- v-model="personForm.remark"
|
|
|
- type="textarea"
|
|
|
- :rows="2"
|
|
|
- class="is-editable"
|
|
|
- placeholder="选填,可注明材料或特殊说明"
|
|
|
- />
|
|
|
- </el-form-item>
|
|
|
- </el-col>
|
|
|
- </el-row>
|
|
|
+ <el-form
|
|
|
+ ref="personFormRef"
|
|
|
+ :model="personForm"
|
|
|
+ :rules="personFormRules"
|
|
|
+ label-width="140px"
|
|
|
+ size="small"
|
|
|
+ >
|
|
|
+ <PersonnelFormFields
|
|
|
+ :model="personForm"
|
|
|
+ mode="create"
|
|
|
+ :town-options="townOptions"
|
|
|
+ :village-options="villageOptions"
|
|
|
+ :town-disabled="true"
|
|
|
+ :village-disabled="true"
|
|
|
+ :required-keys="['name', 'idCard', 'phoneNumber', 'bankCard']"
|
|
|
+ @update:model="onPersonFormUpdate"
|
|
|
+ />
|
|
|
</el-form>
|
|
|
<template #footer>
|
|
|
<el-button @click="personDialogVisible = false">取消</el-button>
|
|
|
@@ -319,30 +593,55 @@
|
|
|
<script setup lang="ts">
|
|
|
import { computed, nextTick, onMounted, reactive, ref, watch } from 'vue'
|
|
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
|
|
-import type { TableInstance } from 'element-plus'
|
|
|
+import type { FormInstance, FormRules, TableInstance, UploadFile } from 'element-plus'
|
|
|
import PagePanel from '@/components/common/PagePanel.vue'
|
|
|
import StatusTag from '@/components/common/StatusTag.vue'
|
|
|
import TablePagination from '@/components/common/TablePagination.vue'
|
|
|
+import AlertLifecycleTimeline from '@/components/biz/AlertLifecycleTimeline.vue'
|
|
|
import BatchDetailDrawer from '@/components/biz/BatchDetailDrawer.vue'
|
|
|
+import PersonnelDetailDescriptions from '@/components/biz/PersonnelDetailDescriptions.vue'
|
|
|
+import PersonnelFormFields from '@/components/biz/PersonnelFormFields.vue'
|
|
|
import {
|
|
|
+ downloadPersonnelImportTemplate,
|
|
|
+ ensureVillageMonthBatch,
|
|
|
+ fetchAlertDetail,
|
|
|
fetchBatchMemberIds,
|
|
|
fetchPeople,
|
|
|
+ fetchPeoplePage,
|
|
|
+ fetchPersonDetail,
|
|
|
+ fetchPersonnelAlerts,
|
|
|
+ fetchPersonnelPaymentHistory,
|
|
|
fetchVillageBatches,
|
|
|
+ importPersonnelExcel,
|
|
|
savePerson,
|
|
|
submitVillageBatch,
|
|
|
updateBatchMembers,
|
|
|
} from '@/api/biz'
|
|
|
+import { emptyPersonForm } from '@/constants/personnelFields'
|
|
|
import { useClientPagination } from '@/composables/useClientPagination'
|
|
|
+import { useServerPagination } from '@/composables/useServerPagination'
|
|
|
import { useTownVillageOptions } from '@/composables/useTownVillageOptions'
|
|
|
import { useUserStore } from '@/stores/user'
|
|
|
import { filterByRoleScope } from '@/utils/buildDashboard'
|
|
|
import { maskIdCard, maskBankCard, formatAmount } from '@/utils/mask'
|
|
|
-import type { Person, VillageBatch } from '@/types/biz'
|
|
|
+import {
|
|
|
+ isValidIdCard,
|
|
|
+ isValidIdCardOptional,
|
|
|
+ isValidPhone,
|
|
|
+ isValidPhoneOptional,
|
|
|
+} from '@/utils/validators'
|
|
|
+import type {
|
|
|
+ AlertItem,
|
|
|
+ AlertLifecycleItem,
|
|
|
+ BatchMemberPerson,
|
|
|
+ Person,
|
|
|
+ VillageBatch,
|
|
|
+} from '@/types/biz'
|
|
|
|
|
|
/** 本村月度批次默认每页条数 */
|
|
|
const BATCH_PAGE_SIZE = 5
|
|
|
const pageSizes = [5, 10, 20]
|
|
|
-/** 本村发放人历史记录默认每页条数(不含 5) */
|
|
|
+/** 本村人员列表默认每页条数 */
|
|
|
const PEOPLE_PAGE_SIZE = 10
|
|
|
const peoplePageSizes = [10, 20]
|
|
|
|
|
|
@@ -359,10 +658,212 @@ const {
|
|
|
} = useTownVillageOptions()
|
|
|
|
|
|
const batches = ref<VillageBatch[]>([])
|
|
|
-const people = ref<Person[]>([])
|
|
|
const batchLoading = ref(false)
|
|
|
-const peopleLoading = ref(false)
|
|
|
+const personPayVisible = ref(false)
|
|
|
+const currentPersonPay = ref<BatchMemberPerson | null>(null)
|
|
|
+const personPayTitle = computed(() =>
|
|
|
+ currentPersonPay.value
|
|
|
+ ? `${currentPersonPay.value.name} · 批次 ${currentPersonPay.value.batchNo || '—'} 发放详情`
|
|
|
+ : '发放详情',
|
|
|
+)
|
|
|
+
|
|
|
+const rosterFilter = reactive({
|
|
|
+ name: '',
|
|
|
+ idCard: '',
|
|
|
+ phone: '',
|
|
|
+ bizStatus: '',
|
|
|
+ source: '',
|
|
|
+})
|
|
|
+const rosterStatusOptions = [
|
|
|
+ { label: '正常在库', value: 'NORMAL' },
|
|
|
+ { label: '草稿', value: 'DRAFT' },
|
|
|
+ { label: '待镇初审', value: 'PENDING_TOWN_APPROVE' },
|
|
|
+ { label: '待居保匹配', value: 'PENDING_INSURANCE_MATCH' },
|
|
|
+ { label: '暂停', value: 'PAUSED' },
|
|
|
+ { label: '停止', value: 'STOPPED' },
|
|
|
+ { label: '死亡丧葬', value: 'FUNERAL' },
|
|
|
+ { label: '异常待处理', value: 'ABNORMAL' },
|
|
|
+ { label: '已归档', value: 'ARCHIVED' },
|
|
|
+]
|
|
|
+const rosterSourceOptions = ['村级录入', 'Excel导入', '历史台账', '死亡名单']
|
|
|
+
|
|
|
+const personDetailVisible = ref(false)
|
|
|
+const personDetailLoading = ref(false)
|
|
|
+const personDetail = ref<Person | null>(null)
|
|
|
+const personAlertVisible = ref(false)
|
|
|
+const personAlertLoading = ref(false)
|
|
|
+const personAlertTarget = ref<Person | null>(null)
|
|
|
+const personAlerts = ref<AlertItem[]>([])
|
|
|
+const personHistoryVisible = ref(false)
|
|
|
+const personHistoryLoading = ref(false)
|
|
|
+const personHistoryTarget = ref<Person | null>(null)
|
|
|
+const personHistoryRows = ref<BatchMemberPerson[]>([])
|
|
|
+const alertLifecycleVisible = ref(false)
|
|
|
+const alertLifecycleLoading = ref(false)
|
|
|
+const alertLifecycleItems = ref<AlertLifecycleItem[]>([])
|
|
|
+const alertLifecycleTitle = ref('处理流程')
|
|
|
+const templateDownloading = ref(false)
|
|
|
+const importing = ref(false)
|
|
|
+
|
|
|
+function isAdjustPayRow(row?: BatchMemberPerson | null) {
|
|
|
+ if (!row) return false
|
|
|
+ const no = String(row.batchNo || '').replace(/-/g, '')
|
|
|
+ if (no.length >= 6 && no.endsWith('99')) return true
|
|
|
+ const remark = String(row.remark || '')
|
|
|
+ return remark.includes('调标') || remark.includes('YEARLY_ADJUST')
|
|
|
+}
|
|
|
+
|
|
|
+function openPersonPay(row: BatchMemberPerson) {
|
|
|
+ currentPersonPay.value = row
|
|
|
+ personPayVisible.value = true
|
|
|
+}
|
|
|
+
|
|
|
+async function openPersonDetail(row: Person) {
|
|
|
+ personDetail.value = row
|
|
|
+ personDetailVisible.value = true
|
|
|
+ personDetailLoading.value = true
|
|
|
+ try {
|
|
|
+ const full = await fetchPersonDetail(String(row.id))
|
|
|
+ if (full) personDetail.value = full
|
|
|
+ } finally {
|
|
|
+ personDetailLoading.value = false
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+async function openPersonAlerts(row: Person) {
|
|
|
+ personAlertTarget.value = row
|
|
|
+ personAlertVisible.value = true
|
|
|
+ personAlertLoading.value = true
|
|
|
+ personAlerts.value = []
|
|
|
+ try {
|
|
|
+ personAlerts.value = await fetchPersonnelAlerts({
|
|
|
+ personnelId: String(row.id),
|
|
|
+ personName: row.name,
|
|
|
+ town: row.town,
|
|
|
+ village: row.village,
|
|
|
+ })
|
|
|
+ } finally {
|
|
|
+ personAlertLoading.value = false
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+function onPersonAlertClosed() {
|
|
|
+ personAlertTarget.value = null
|
|
|
+ personAlerts.value = []
|
|
|
+}
|
|
|
+
|
|
|
+async function openAlertLifecycle(row: AlertItem) {
|
|
|
+ alertLifecycleTitle.value = `处理流程 · ${row.type || row.id}`
|
|
|
+ alertLifecycleVisible.value = true
|
|
|
+ alertLifecycleLoading.value = true
|
|
|
+ alertLifecycleItems.value = row.lifecycle?.length ? [...row.lifecycle] : []
|
|
|
+ try {
|
|
|
+ const detail = await fetchAlertDetail(String(row.id))
|
|
|
+ if (detail?.lifecycle?.length) {
|
|
|
+ alertLifecycleItems.value = detail.lifecycle
|
|
|
+ } else if (!alertLifecycleItems.value.length && detail) {
|
|
|
+ alertLifecycleItems.value = [
|
|
|
+ {
|
|
|
+ actionLabel: '产生告警',
|
|
|
+ status: detail.status,
|
|
|
+ operator: '系统',
|
|
|
+ result: detail.content,
|
|
|
+ operateTime: detail.createdAt,
|
|
|
+ },
|
|
|
+ ]
|
|
|
+ }
|
|
|
+ } finally {
|
|
|
+ alertLifecycleLoading.value = false
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+function onAlertLifecycleClosed() {
|
|
|
+ alertLifecycleItems.value = []
|
|
|
+}
|
|
|
+
|
|
|
+async function openPersonHistory(row: Person) {
|
|
|
+ personHistoryTarget.value = row
|
|
|
+ personHistoryVisible.value = true
|
|
|
+ personHistoryLoading.value = true
|
|
|
+ personHistoryRows.value = []
|
|
|
+ try {
|
|
|
+ personHistoryRows.value = await fetchPersonnelPaymentHistory(String(row.id))
|
|
|
+ } finally {
|
|
|
+ personHistoryLoading.value = false
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+function onPersonHistoryClosed() {
|
|
|
+ personHistoryTarget.value = null
|
|
|
+ personHistoryRows.value = []
|
|
|
+}
|
|
|
+
|
|
|
+function applyRosterFilter() {
|
|
|
+ void peoplePager.resetAndLoad()
|
|
|
+}
|
|
|
+
|
|
|
+function resetRosterFilter() {
|
|
|
+ rosterFilter.name = ''
|
|
|
+ rosterFilter.idCard = ''
|
|
|
+ rosterFilter.phone = ''
|
|
|
+ rosterFilter.bizStatus = ''
|
|
|
+ rosterFilter.source = ''
|
|
|
+ void peoplePager.resetAndLoad()
|
|
|
+}
|
|
|
+
|
|
|
+async function onDownloadTemplate() {
|
|
|
+ if (!hasVillageScope.value) {
|
|
|
+ ElMessage.warning(
|
|
|
+ isAdmin.value ? '请先选择街镇与村居后再下载模板' : '当前账号未绑定村居,无法下载模板',
|
|
|
+ )
|
|
|
+ return
|
|
|
+ }
|
|
|
+ const role = userStore.roleInfo
|
|
|
+ const town = isAdmin.value ? filterTown.value : role?.town
|
|
|
+ const village = isAdmin.value ? filterVillage.value : role?.village
|
|
|
+ templateDownloading.value = true
|
|
|
+ try {
|
|
|
+ await downloadPersonnelImportTemplate(town, village)
|
|
|
+ ElMessage.success('模板已开始下载')
|
|
|
+ } finally {
|
|
|
+ templateDownloading.value = false
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+async function onImportFileChange(uploadFile: UploadFile) {
|
|
|
+ const file = uploadFile.raw
|
|
|
+ if (!file) return
|
|
|
+ if (!hasVillageScope.value) {
|
|
|
+ ElMessage.warning(
|
|
|
+ isAdmin.value ? '请先选择街镇与村居后再导入' : '当前账号未绑定村居,无法导入',
|
|
|
+ )
|
|
|
+ return
|
|
|
+ }
|
|
|
+ const role = userStore.roleInfo
|
|
|
+ const town = isAdmin.value ? filterTown.value : role?.town
|
|
|
+ const village = isAdmin.value ? filterVillage.value : role?.village
|
|
|
+ importing.value = true
|
|
|
+ try {
|
|
|
+ const res = (await importPersonnelExcel(file, town, village)) as {
|
|
|
+ successCount?: number
|
|
|
+ failCount?: number
|
|
|
+ message?: string
|
|
|
+ }
|
|
|
+ const ok = Number(res?.successCount ?? 0)
|
|
|
+ const fail = Number(res?.failCount ?? 0)
|
|
|
+ if (fail > 0) {
|
|
|
+ ElMessage.warning(res?.message || `导入完成:成功 ${ok} 条,失败 ${fail} 条`)
|
|
|
+ } else {
|
|
|
+ ElMessage.success(res?.message || `导入成功 ${ok} 条`)
|
|
|
+ }
|
|
|
+ await loadPeople()
|
|
|
+ } finally {
|
|
|
+ importing.value = false
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
const personDialogVisible = ref(false)
|
|
|
+const personFormRef = ref<FormInstance>()
|
|
|
const saving = ref(false)
|
|
|
const submittingId = ref('')
|
|
|
const batchDrawerVisible = ref(false)
|
|
|
@@ -402,13 +903,48 @@ const {
|
|
|
resetPage: resetBatchPage,
|
|
|
} = useClientPagination(batches, { pageSize: BATCH_PAGE_SIZE })
|
|
|
|
|
|
+const peoplePager = useServerPagination<Person>(
|
|
|
+ async (pageNum, pageSize) => {
|
|
|
+ const role = userStore.roleInfo
|
|
|
+ if (isAdmin.value) {
|
|
|
+ if (!filterTown.value || !filterVillage.value) {
|
|
|
+ return { list: [], total: 0 }
|
|
|
+ }
|
|
|
+ return fetchPeoplePage({
|
|
|
+ town: filterTown.value,
|
|
|
+ village: filterVillage.value,
|
|
|
+ name: rosterFilter.name.trim() || undefined,
|
|
|
+ idCard: rosterFilter.idCard.trim().replace(/\s/g, '') || undefined,
|
|
|
+ phone: rosterFilter.phone.trim() || undefined,
|
|
|
+ bizStatus: rosterFilter.bizStatus || undefined,
|
|
|
+ source: rosterFilter.source.trim() || undefined,
|
|
|
+ pageNum,
|
|
|
+ pageSize,
|
|
|
+ })
|
|
|
+ }
|
|
|
+ if (!role?.village) return { list: [], total: 0 }
|
|
|
+ return fetchPeoplePage({
|
|
|
+ town: role.town,
|
|
|
+ village: role.village,
|
|
|
+ name: rosterFilter.name.trim() || undefined,
|
|
|
+ idCard: rosterFilter.idCard.trim().replace(/\s/g, '') || undefined,
|
|
|
+ phone: rosterFilter.phone.trim() || undefined,
|
|
|
+ bizStatus: rosterFilter.bizStatus || undefined,
|
|
|
+ source: rosterFilter.source.trim() || undefined,
|
|
|
+ pageNum,
|
|
|
+ pageSize,
|
|
|
+ })
|
|
|
+ },
|
|
|
+ { pageSize: PEOPLE_PAGE_SIZE },
|
|
|
+)
|
|
|
const {
|
|
|
page: peoplePage,
|
|
|
pageSize: peoplePageSize,
|
|
|
total: peopleTotal,
|
|
|
- pagedList: pagedPeople,
|
|
|
- resetPage: resetPeoplePage,
|
|
|
-} = useClientPagination(people, { pageSize: PEOPLE_PAGE_SIZE })
|
|
|
+ list: pagedPeople,
|
|
|
+ loading: peopleLoading,
|
|
|
+ resetAndLoad: loadPeople,
|
|
|
+} = peoplePager
|
|
|
|
|
|
/** 数据范围文案 */
|
|
|
const villageScopeLabel = computed(() => {
|
|
|
@@ -429,16 +965,54 @@ const hasVillageScope = computed(() => {
|
|
|
return Boolean(userStore.roleInfo?.village)
|
|
|
})
|
|
|
|
|
|
-const personForm = reactive({
|
|
|
- name: '',
|
|
|
- idCard: '',
|
|
|
- bankCard: '',
|
|
|
- bankName: '',
|
|
|
- socialInsuranceCode: '',
|
|
|
- standard: 1280,
|
|
|
- startMonth: '',
|
|
|
- remark: '',
|
|
|
-})
|
|
|
+const personForm = ref(emptyPersonForm())
|
|
|
+
|
|
|
+function onPersonFormUpdate(v: Person) {
|
|
|
+ personForm.value = v
|
|
|
+}
|
|
|
+
|
|
|
+const personFormRules: FormRules = {
|
|
|
+ name: [{ required: true, message: '请输入姓名', trigger: 'blur' }],
|
|
|
+ idCard: [
|
|
|
+ { required: true, message: '请输入身份证号', trigger: 'blur' },
|
|
|
+ {
|
|
|
+ validator: (_r, v, cb) => {
|
|
|
+ if (!isValidIdCard(String(v || ''))) cb(new Error('身份证号须为18位合法号码'))
|
|
|
+ else cb()
|
|
|
+ },
|
|
|
+ trigger: 'blur',
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ phoneNumber: [
|
|
|
+ { required: true, message: '请输入手机号', trigger: 'blur' },
|
|
|
+ {
|
|
|
+ validator: (_r, v, cb) => {
|
|
|
+ if (!isValidPhone(String(v || ''))) cb(new Error('手机号格式不正确'))
|
|
|
+ else cb()
|
|
|
+ },
|
|
|
+ trigger: 'blur',
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ bankCard: [{ required: true, message: '请输入银行卡号', trigger: 'blur' }],
|
|
|
+ heirIdNumber: [
|
|
|
+ {
|
|
|
+ validator: (_r, v, cb) => {
|
|
|
+ if (!isValidIdCardOptional(String(v || ''))) cb(new Error('继承人身份证号格式不正确'))
|
|
|
+ else cb()
|
|
|
+ },
|
|
|
+ trigger: 'blur',
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ heirPhone: [
|
|
|
+ {
|
|
|
+ validator: (_r, v, cb) => {
|
|
|
+ if (!isValidPhoneOptional(String(v || ''))) cb(new Error('继承人手机号格式不正确'))
|
|
|
+ else cb()
|
|
|
+ },
|
|
|
+ trigger: 'blur',
|
|
|
+ },
|
|
|
+ ],
|
|
|
+}
|
|
|
|
|
|
/** 当前发放周期 yyyyMM */
|
|
|
const currentPayMonth = computed(() =>
|
|
|
@@ -515,6 +1089,15 @@ function openPersonDialog() {
|
|
|
)
|
|
|
return
|
|
|
}
|
|
|
+ const role = userStore.roleInfo
|
|
|
+ const town = isAdmin.value ? filterTown.value : role?.town || ''
|
|
|
+ const village = isAdmin.value ? filterVillage.value : role?.village || ''
|
|
|
+ personForm.value = emptyPersonForm({
|
|
|
+ town,
|
|
|
+ village,
|
|
|
+ source: '村级录入',
|
|
|
+ payeeName: '',
|
|
|
+ })
|
|
|
personDialogVisible.value = true
|
|
|
}
|
|
|
|
|
|
@@ -538,6 +1121,8 @@ async function openMemberDialog(row: VillageBatch) {
|
|
|
memberLoading.value = true
|
|
|
memberSelectedIds.value = []
|
|
|
try {
|
|
|
+ // ensure 可编辑村批时会纠正误标「待镇初审」的脏人员状态
|
|
|
+ await ensureVillageMonthBatch(row.town, row.village, row.batchNo)
|
|
|
const [allPeople, pickedIds] = await Promise.all([
|
|
|
fetchPeople({ town: row.town, village: row.village }),
|
|
|
fetchBatchMemberIds(row.id),
|
|
|
@@ -545,6 +1130,12 @@ async function openMemberDialog(row: VillageBatch) {
|
|
|
// 候选:本村人员(归档除外);暂停/停止/死亡仍列出但默认不勾选
|
|
|
memberCandidates.value = allPeople.filter((p) => p.status !== '已归档')
|
|
|
const picked = new Set(pickedIds.map(String))
|
|
|
+ const defaultPick = (s?: string) =>
|
|
|
+ s === '正常在库' ||
|
|
|
+ s === '草稿' ||
|
|
|
+ s === '资格已核验' ||
|
|
|
+ s === '待镇初审' ||
|
|
|
+ s === '待镇审批'
|
|
|
await nextTick()
|
|
|
const table = memberTableRef.value
|
|
|
if (!table) return
|
|
|
@@ -558,7 +1149,7 @@ async function openMemberDialog(row: VillageBatch) {
|
|
|
})
|
|
|
} else {
|
|
|
memberCandidates.value.forEach((p) => {
|
|
|
- if (!isExcludedMemberStatus(p.status) && (p.status === '正常在库' || p.status === '草稿')) {
|
|
|
+ if (!isExcludedMemberStatus(p.status) && defaultPick(p.status)) {
|
|
|
table.toggleRowSelection(p, true)
|
|
|
}
|
|
|
})
|
|
|
@@ -664,19 +1255,32 @@ async function onSavePerson() {
|
|
|
ElMessage.warning(isAdmin.value ? '请先选择街镇与村居' : '当前账号未绑定村居,无法保存')
|
|
|
return
|
|
|
}
|
|
|
- if (!personForm.name || !personForm.idCard) {
|
|
|
- ElMessage.warning('请填写姓名和身份证号')
|
|
|
- return
|
|
|
- }
|
|
|
+ const valid = await personFormRef.value?.validate().catch(() => false)
|
|
|
+ if (!valid) return
|
|
|
+ const f = personForm.value
|
|
|
const role = userStore.roleInfo!
|
|
|
const town = isAdmin.value ? filterTown.value : role.town
|
|
|
const village = isAdmin.value ? filterVillage.value : role.village
|
|
|
saving.value = true
|
|
|
try {
|
|
|
await savePerson({
|
|
|
- ...personForm,
|
|
|
+ ...f,
|
|
|
+ id: undefined,
|
|
|
town,
|
|
|
village,
|
|
|
+ name: f.name.trim(),
|
|
|
+ idCard: f.idCard.trim(),
|
|
|
+ phoneNumber: (f.phoneNumber || '').trim(),
|
|
|
+ bankCard: f.bankCard.trim(),
|
|
|
+ payeeName: (f.payeeName || f.name).trim(),
|
|
|
+ bankName: (f.bankName || '').trim(),
|
|
|
+ socialInsuranceCode: (f.socialInsuranceCode || '').trim(),
|
|
|
+ heirName: (f.heirName || '').trim(),
|
|
|
+ heirIdNumber: (f.heirIdNumber || '').trim(),
|
|
|
+ heirBankCard: (f.heirBankCard || '').trim(),
|
|
|
+ heirPhone: (f.heirPhone || '').trim(),
|
|
|
+ remark: (f.remark || '').trim(),
|
|
|
+ source: f.source || '村级录入',
|
|
|
})
|
|
|
ElMessage.success('人员保存成功')
|
|
|
personDialogVisible.value = false
|
|
|
@@ -711,32 +1315,9 @@ async function loadBatches() {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-/** 加载人员:同上 */
|
|
|
-async function loadPeople() {
|
|
|
- peopleLoading.value = true
|
|
|
- try {
|
|
|
- const all = await fetchPeople()
|
|
|
- const role = userStore.roleInfo
|
|
|
- if (isAdmin.value) {
|
|
|
- people.value = all.filter((p) => {
|
|
|
- if (filterTown.value && p.town !== filterTown.value) return false
|
|
|
- if (filterVillage.value && p.village !== filterVillage.value) return false
|
|
|
- return true
|
|
|
- })
|
|
|
- } else if (!role?.village) {
|
|
|
- people.value = []
|
|
|
- } else {
|
|
|
- people.value = filterByRoleScope(all, role)
|
|
|
- }
|
|
|
- resetPeoplePage()
|
|
|
- } finally {
|
|
|
- peopleLoading.value = false
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
onMounted(() => {
|
|
|
loadBatches()
|
|
|
- loadPeople()
|
|
|
+ void loadPeople()
|
|
|
})
|
|
|
|
|
|
// 切换模拟角色时重新按范围加载
|
|
|
@@ -841,6 +1422,31 @@ watch(
|
|
|
overflow: hidden;
|
|
|
}
|
|
|
|
|
|
+ &__history-tip {
|
|
|
+ flex-shrink: 0;
|
|
|
+ margin: 0 0 8px;
|
|
|
+ font-size: 12px;
|
|
|
+ color: @text-secondary;
|
|
|
+ line-height: 1.5;
|
|
|
+ }
|
|
|
+
|
|
|
+ &__roster-filter {
|
|
|
+ flex-shrink: 0;
|
|
|
+ margin-bottom: 4px;
|
|
|
+ }
|
|
|
+
|
|
|
+ &__import-upload {
|
|
|
+ display: inline-flex;
|
|
|
+ }
|
|
|
+
|
|
|
+ &__pay-tip {
|
|
|
+ margin-top: 4px;
|
|
|
+ font-size: 12px;
|
|
|
+ color: @text-secondary;
|
|
|
+ font-weight: 400;
|
|
|
+ line-height: 1.5;
|
|
|
+ }
|
|
|
+
|
|
|
&__dialog-tip {
|
|
|
margin: 0 0 12px;
|
|
|
font-size: 12px;
|