| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599 |
- <template>
- <div class="page-fill status-maintenance-view">
- <PagePanel title="补助标准管理">
- <template #extra>
- <el-button
- v-if="canDistrictAdjust"
- type="warning"
- size="small"
- @click="openDistrictAdjust"
- >
- 全区调标
- </el-button>
- <el-button
- v-if="canMutateStandard"
- type="primary"
- size="small"
- :disabled="!canOpenCreate"
- :title="createBlockedTip || undefined"
- @click="openCreateDialog"
- >
- 新增标准
- </el-button>
- </template>
- <el-alert
- type="info"
- :closable="false"
- show-icon
- class="page-alert"
- :title="standardAlertTitle"
- />
- <el-alert
- v-if="pendingTip"
- type="warning"
- :closable="false"
- show-icon
- class="page-alert"
- :title="pendingTip"
- />
- <div class="page-fill__table">
- <el-table v-loading="standardLoading" :data="pagedStandards" border stripe size="small" height="100%">
- <el-table-column prop="year" label="年度" width="80" align="center" />
- <el-table-column label="月补助(元)" width="110" align="right">
- <template #default="{ row }">{{ formatAmount(row.monthly) }}</template>
- </el-table-column>
- <el-table-column label="丧葬补助(元)" width="120" align="right">
- <template #default="{ row }">{{ formatAmount(row.funeral) }}</template>
- </el-table-column>
- <el-table-column prop="date" label="生效日期" width="110" align="center" />
- <el-table-column prop="reason" label="调整原因" min-width="160" show-overflow-tooltip />
- <el-table-column label="当前" width="72" align="center">
- <template #default="{ row }">
- <el-tag v-if="isCurrentStandard(row as SubsidyStandard)" size="small" type="success">
- 有效
- </el-tag>
- <span v-else class="muted">历史</span>
- </template>
- </el-table-column>
- <el-table-column label="操作" width="200" align="center" fixed="right">
- <template #default="{ row }">
- <template v-if="isCurrentStandard(row as SubsidyStandard)">
- <el-button
- v-if="canMutateStandard"
- link
- type="primary"
- size="small"
- @click="openEditDialog(row as SubsidyStandard)"
- >
- 调整标准
- </el-button>
- <span v-else class="muted">现行标准</span>
- </template>
- <el-button
- v-else
- link
- type="primary"
- size="small"
- @click="openViewDialog(row as SubsidyStandard)"
- >
- 查看
- </el-button>
- <el-button
- v-if="isAdmin"
- link
- type="danger"
- size="small"
- @click="onDeleteStandard(row as SubsidyStandard)"
- >
- 删除
- </el-button>
- </template>
- </el-table-column>
- </el-table>
- </div>
- <TablePagination
- v-model:page="standardPage"
- v-model:page-size="standardPageSize"
- :total="standardTotal"
- />
- </PagePanel>
- <el-dialog v-model="viewDialogVisible" title="查看补助标准" width="480px" destroy-on-close>
- <el-descriptions v-if="currentStandard" :column="1" border size="small">
- <el-descriptions-item label="适用范围">全区统一</el-descriptions-item>
- <el-descriptions-item label="年度">{{ currentStandard.year }}</el-descriptions-item>
- <el-descriptions-item label="月补助">
- {{ formatAmount(currentStandard.monthly) }} 元
- </el-descriptions-item>
- <el-descriptions-item label="丧葬补助">
- {{ formatAmount(currentStandard.funeral) }} 元
- </el-descriptions-item>
- <el-descriptions-item label="生效日期">{{ currentStandard.date }}</el-descriptions-item>
- <el-descriptions-item label="调整原因">{{ currentStandard.reason }}</el-descriptions-item>
- </el-descriptions>
- <template #footer>
- <el-button type="primary" @click="viewDialogVisible = false">关闭</el-button>
- </template>
- </el-dialog>
- <el-dialog
- v-model="formDialogVisible"
- :title="formMode === 'create' ? '新增补助标准' : '调整补助标准'"
- width="520px"
- destroy-on-close
- @closed="onFormDialogClosed"
- >
- <el-form ref="formRef" :model="form" :rules="formRules" label-width="110px" size="small">
- <el-form-item label="年度" prop="year">
- <el-date-picker
- v-model="form.year"
- type="year"
- value-format="YYYY"
- placeholder="请选择年度"
- :disabled="formMode === 'edit' || formMode === 'create'"
- :disabled-date="disableNonCurrentYear"
- class="is-editable"
- style="width: 100%"
- />
- <p v-if="formMode === 'create'" class="status-maintenance-view__field-tip">
- 仅允许新增本年度({{ currentYear }})全区标准;本年度已有标准时请使用「调整标准」。
- </p>
- </el-form-item>
- <el-form-item label="月补助(元)" prop="monthly">
- <el-input-number
- v-model="form.monthly"
- :min="0"
- :precision="0"
- :controls="false"
- class="is-editable"
- style="width: 100%"
- placeholder="请输入月补助"
- />
- </el-form-item>
- <el-form-item label="丧葬补助(元)" prop="funeral">
- <el-input-number
- v-model="form.funeral"
- :min="0"
- :precision="0"
- :controls="false"
- class="is-editable"
- style="width: 100%"
- placeholder="请输入丧葬补助"
- />
- </el-form-item>
- <el-form-item label="生效日期" prop="date">
- <el-date-picker
- v-model="form.date"
- type="date"
- value-format="YYYY-MM-DD"
- placeholder="请选择生效日期"
- class="is-editable"
- style="width: 100%"
- />
- </el-form-item>
- <el-form-item label="调整原因" prop="reason">
- <el-input
- v-model="form.reason"
- type="textarea"
- :rows="2"
- class="is-editable"
- placeholder="请填写调整原因"
- />
- </el-form-item>
- </el-form>
- <template #footer>
- <el-button @click="formDialogVisible = false">取消</el-button>
- <el-button type="primary" :loading="submitting" @click="submitForm">确定</el-button>
- </template>
- </el-dialog>
- <el-dialog
- v-model="districtVisible"
- title="全区调标"
- width="520px"
- destroy-on-close
- @closed="onDistrictClosed"
- >
- <el-alert
- type="warning"
- :closable="false"
- show-icon
- class="page-alert"
- title="将按全区统一新标准落库,并按各镇生成调标补发批次。街镇登录时提示已调标。"
- />
- <el-form ref="districtFormRef" :model="districtForm" :rules="districtRules" label-width="120px" size="small">
- <el-form-item label="新月补助(元)" prop="monthly">
- <el-input-number
- v-model="districtForm.monthly"
- :min="0"
- :precision="0"
- :controls="false"
- class="is-editable"
- style="width: 100%"
- />
- </el-form-item>
- <el-form-item label="新丧葬补助(元)" prop="funeral">
- <el-input-number
- v-model="districtForm.funeral"
- :min="0"
- :precision="0"
- :controls="false"
- class="is-editable"
- style="width: 100%"
- />
- </el-form-item>
- <el-form-item label="生效月份" prop="effectiveMonth">
- <el-date-picker
- v-model="districtForm.effectiveMonth"
- type="month"
- value-format="YYYYMM"
- placeholder="选择生效月份"
- class="is-editable"
- style="width: 100%"
- />
- </el-form-item>
- <el-form-item label="调整原因" prop="reason">
- <el-input
- v-model="districtForm.reason"
- type="textarea"
- :rows="2"
- class="is-editable"
- placeholder="请填写调标原因"
- />
- </el-form-item>
- </el-form>
- <template #footer>
- <el-button @click="districtVisible = false">取消</el-button>
- <el-button type="warning" :loading="districtSubmitting" @click="submitDistrictAdjust">确认全区调标</el-button>
- </template>
- </el-dialog>
- </div>
- </template>
- <script setup lang="ts">
- import { computed, onMounted, reactive, ref, watch } from 'vue'
- import type { FormInstance, FormRules } from 'element-plus'
- import { ElMessage, ElMessageBox } from 'element-plus'
- import PagePanel from '@/components/common/PagePanel.vue'
- import TablePagination from '@/components/common/TablePagination.vue'
- import {
- createStandard,
- deleteStandard,
- fetchStandardPendingTip,
- fetchStandards,
- submitDistrictYearlyAdjust,
- updateStandard,
- } from '@/api/biz'
- import { useClientPagination } from '@/composables/useClientPagination'
- import { guardBusy, runSubmit } from '@/composables/useSubmitLock'
- import { useUserStore } from '@/stores/user'
- import { formatAmount } from '@/utils/mask'
- import { isPositiveAmount } from '@/utils/validators'
- import type { SubsidyStandard } from '@/types/biz'
- const userStore = useUserStore()
- const isAdmin = computed(() => userStore.roleInfo?.key === 'admin')
- const isHr = computed(() => userStore.roleInfo?.key === 'hr')
- const canMutateStandard = computed(() => isAdmin.value)
- const canDistrictAdjust = computed(() => isHr.value || isAdmin.value)
- const standardAlertTitle = computed(() => {
- if (isHr.value) {
- return '补助标准全区统一,不按街镇分别维护。调标仅由区人社办理;确认后更新全区标准并按各镇生成补发批次。'
- }
- if (!isAdmin.value) {
- return '补助标准全区统一,由区人社调标。本页只可查看,不发起调标或补发。'
- }
- return '补助标准全区统一。标准变更默认需分管领导审批后生效;全区调标由区人社办理。'
- })
- const standards = ref<SubsidyStandard[]>([])
- const standardLoading = ref(false)
- const submitting = ref(false)
- const pendingTip = ref('')
- const viewDialogVisible = ref(false)
- const formDialogVisible = ref(false)
- const districtVisible = ref(false)
- const districtSubmitting = ref(false)
- const districtFormRef = ref<FormInstance>()
- const districtForm = reactive({
- monthly: undefined as number | undefined,
- funeral: undefined as number | undefined,
- effectiveMonth: '',
- reason: '',
- })
- function positiveMoneyRule(label: string) {
- return {
- validator: (_r: unknown, v: unknown, cb: (e?: Error) => void) => {
- if (!isPositiveAmount(v)) cb(new Error(`${label}须为大于 0 的金额,不能为 0、负数或中文`))
- else cb()
- },
- trigger: 'blur' as const,
- }
- }
- const districtRules: FormRules = {
- monthly: [positiveMoneyRule('新月补助')],
- funeral: [positiveMoneyRule('新丧葬补助')],
- effectiveMonth: [{ required: true, message: '请选择生效月份', trigger: 'change' }],
- reason: [{ required: true, message: '请填写调标原因', trigger: 'blur' }],
- }
- const formMode = ref<'create' | 'edit'>('create')
- const currentStandard = ref<SubsidyStandard | null>(null)
- const editingId = ref<number | null>(null)
- const formRef = ref<FormInstance>()
- /** 业务本年度:优先系统发放月,否则取自然年 */
- const currentYear = computed(() => {
- const m = String(userStore.currentMonth || '')
- if (/^\d{4}/.test(m)) return Number(m.slice(0, 4))
- return new Date().getFullYear()
- })
- function isCurrentStandard(row: SubsidyStandard) {
- return row.current === true
- }
- function hasCurrentYearStandard() {
- return standards.value.some((s) => Number(s.year) === currentYear.value)
- }
- const createBlockedTip = computed(() => {
- if (hasCurrentYearStandard()) {
- return `${currentYear.value} 年度已有全区标准,禁止新增;请使用「调整标准」`
- }
- return ''
- })
- const canOpenCreate = computed(() => canMutateStandard.value && !hasCurrentYearStandard())
- function disableNonCurrentYear(date: Date) {
- return date.getFullYear() !== currentYear.value
- }
- const form = reactive({
- town: '',
- year: '' as string,
- monthly: undefined as number | undefined,
- funeral: undefined as number | undefined,
- date: '' as string,
- reason: '',
- })
- const formRules: FormRules = {
- year: [{ required: true, message: '请选择年度', trigger: 'change' }],
- monthly: [positiveMoneyRule('月补助')],
- funeral: [positiveMoneyRule('丧葬补助')],
- date: [{ required: true, message: '请选择生效日期', trigger: 'change' }],
- reason: [{ required: true, message: '请填写调整原因', trigger: 'blur' }],
- }
- const {
- page: standardPage,
- pageSize: standardPageSize,
- total: standardTotal,
- pagedList: pagedStandards,
- resetPage: resetStandardPage,
- } = useClientPagination(standards)
- function resetForm() {
- form.town = '全区'
- form.year = String(currentYear.value)
- form.monthly = undefined
- form.funeral = undefined
- form.date = ''
- form.reason = ''
- editingId.value = null
- }
- function openViewDialog(row: SubsidyStandard) {
- currentStandard.value = row
- viewDialogVisible.value = true
- }
- function openCreateDialog() {
- if (hasCurrentYearStandard()) {
- ElMessage.warning(`${currentYear.value} 年度已有全区补助标准,请使用「调整标准」`)
- return
- }
- formMode.value = 'create'
- resetForm()
- form.year = String(currentYear.value)
- formDialogVisible.value = true
- }
- function openEditDialog(row: SubsidyStandard) {
- formMode.value = 'edit'
- editingId.value = row.id
- form.town = row.town
- form.year = String(row.year)
- form.monthly = row.monthly
- form.funeral = row.funeral
- form.date = row.date
- form.reason = row.reason
- formDialogVisible.value = true
- }
- function onFormDialogClosed() {
- formRef.value?.resetFields()
- resetForm()
- }
- async function submitForm() {
- const valid = await formRef.value?.validate().catch(() => false)
- if (!valid) return
- const year = Number(form.year)
- const monthly = form.monthly ?? 0
- const funeral = form.funeral ?? 0
- if (formMode.value === 'create') {
- if (year !== currentYear.value) {
- ElMessage.warning(`仅允许新增本年度(${currentYear.value})标准,不可新增历史或未来年度`)
- form.year = String(currentYear.value)
- return
- }
- if (hasCurrentYearStandard()) {
- ElMessage.warning(`${currentYear.value} 年度已有全区补助标准,请使用「调整标准」`)
- return
- }
- }
- if (guardBusy(submitting)) return
- submitting.value = true
- try {
- if (formMode.value === 'create') {
- const res = await createStandard({
- town: '全区',
- year: currentYear.value,
- monthly,
- funeral,
- date: form.date,
- reason: form.reason.trim(),
- })
- if (!res.ok) {
- ElMessage.warning(res.message || '新增失败')
- return
- }
- const tip = 'pendingTip' in res ? res.pendingTip : undefined
- if (tip) pendingTip.value = tip
- ElMessage.success(tip || '新增标准成功')
- } else if (editingId.value != null) {
- const editing = standards.value.find((s) => s.id === editingId.value)
- const res = await updateStandard({
- id: editingId.value,
- rawId: editing?.rawId,
- town: '全区',
- monthly,
- funeral,
- date: form.date,
- reason: form.reason.trim(),
- })
- if (!res.ok) {
- ElMessage.warning(res.message || '调整失败')
- return
- }
- const tip = 'pendingTip' in res ? res.pendingTip : undefined
- if (tip) pendingTip.value = tip
- ElMessage.success(tip || '调整标准成功')
- }
- formDialogVisible.value = false
- await loadStandards()
- } finally {
- submitting.value = false
- }
- }
- async function onDeleteStandard(row: SubsidyStandard) {
- const tip = isCurrentStandard(row)
- ? `月补 ${row.monthly} 元是当前全区有效标准,删除后将恢复上一档。确认删除?`
- : `确认删除 ${row.year} 年全区标准(月补 ${row.monthly})?`
- try {
- await ElMessageBox.confirm(tip, '删除补助标准', {
- type: 'warning',
- confirmButtonText: '确认删除',
- cancelButtonText: '取消',
- })
- } catch {
- return
- }
- await runSubmit(submitting, async () => {
- const res = await deleteStandard({ id: row.id, rawId: row.rawId })
- if (!res.ok) {
- ElMessage.warning(res.message || '删除失败')
- return
- }
- ElMessage.success(res.message || '已删除')
- await loadStandards()
- })
- }
- function openDistrictAdjust() {
- districtForm.monthly = undefined
- districtForm.funeral = undefined
- const m = String(userStore.currentMonth || '').replace('-', '')
- districtForm.effectiveMonth = /^\d{6}$/.test(m) ? m : ''
- districtForm.reason = ''
- districtVisible.value = true
- }
- function onDistrictClosed() {
- districtFormRef.value?.resetFields()
- }
- async function submitDistrictAdjust() {
- const valid = await districtFormRef.value?.validate().catch(() => false)
- if (!valid) return
- if (guardBusy(districtSubmitting)) return
- districtSubmitting.value = true
- try {
- await ElMessageBox.confirm(
- '确认按新标准完成全区统一调标?将更新全区一份标准,并按各镇生成补发批次。',
- '确认全区调标',
- { type: 'warning', confirmButtonText: '确认调标', cancelButtonText: '取消' },
- )
- const res = await submitDistrictYearlyAdjust({
- monthly: districtForm.monthly ?? 0,
- funeral: districtForm.funeral ?? 0,
- effectiveMonth: districtForm.effectiveMonth,
- reason: districtForm.reason.trim(),
- })
- const tip = String((res as { tip?: string })?.tip || '全区调标已完成,已生成各镇补发批次')
- ElMessage.success(tip)
- districtVisible.value = false
- await loadStandards()
- } catch (e) {
- if (e === 'cancel' || e === 'close') return
- ElMessage.error(e instanceof Error ? e.message : '全区调标失败')
- } finally {
- districtSubmitting.value = false
- }
- }
- async function loadStandards() {
- standardLoading.value = true
- try {
- standards.value = await fetchStandards()
- pendingTip.value = await fetchStandardPendingTip()
- resetStandardPage()
- } finally {
- standardLoading.value = false
- }
- }
- onMounted(loadStandards)
- watch(
- () => userStore.currentRole,
- () => {
- loadStandards()
- },
- )
- </script>
- <style scoped lang="less">
- .status-maintenance-view {
- :deep(.el-input-number .el-input__inner) {
- text-align: left;
- }
- &__filter {
- margin-bottom: 10px;
- padding: 8px 12px 0;
- }
- &__field-tip {
- margin: 4px 0 0;
- font-size: 12px;
- color: @text-secondary;
- line-height: 1.4;
- }
- }
- .muted {
- color: @text-secondary;
- font-size: 12px;
- }
- </style>
|