| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511 |
- <template>
- <div class="status-maintenance-view">
- <PagePanel title="补助标准管理">
- <template #extra>
- <el-button
- type="primary"
- size="small"
- :disabled="!canOpenCreate"
- :title="createBlockedTip || undefined"
- @click="openCreateDialog"
- >
- 新增标准
- </el-button>
- </template>
- <!-- 系统管理员:按街镇筛选全区标准 -->
- <div v-if="isAdmin" class="query-panel status-maintenance-view__filter">
- <el-form :inline="true" size="small">
- <el-form-item label="街镇">
- <el-select
- v-model="filterTown"
- placeholder="全部街镇"
- clearable
- style="width: 160px"
- @change="loadStandards"
- >
- <el-option v-for="t in townOptions" :key="t" :label="t" :value="t" />
- </el-select>
- </el-form-item>
- <el-form-item>
- <el-button type="primary" @click="loadStandards">查询</el-button>
- <el-button @click="resetTownFilter">重置</el-button>
- </el-form-item>
- </el-form>
- </div>
- <el-table v-loading="standardLoading" :data="pagedStandards" border stripe size="small">
- <el-table-column prop="town" label="街镇" width="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
- link
- type="primary"
- size="small"
- @click="openEditDialog(row as SubsidyStandard)"
- >
- 调整标准
- </el-button>
- </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>
- <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="街镇">{{ currentStandard.town }}</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="town">
- <el-select
- v-if="isAdmin && formMode === 'create'"
- v-model="form.town"
- placeholder="请选择街镇"
- class="is-editable"
- style="width: 100%"
- @change="onCreateTownChange"
- >
- <el-option v-for="t in townOptions" :key="t" :label="t" :value="t" />
- </el-select>
- <el-input v-else :model-value="form.town" disabled />
- </el-form-item>
- <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>
- </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, fetchStandards, updateStandard } from '@/api/biz'
- import { useClientPagination } from '@/composables/useClientPagination'
- import { useDynamicRegionOptions } from '@/composables/useTownVillageOptions'
- import { useUserStore } from '@/stores/user'
- import { formatAmount } from '@/utils/mask'
- import type { SubsidyStandard } from '@/types/biz'
- const userStore = useUserStore()
- const isAdmin = computed(() => userStore.roleInfo?.key === 'admin')
- const { townOptions } = useDynamicRegionOptions()
- const filterTown = ref('')
- const standards = ref<SubsidyStandard[]>([])
- const standardLoading = ref(false)
- const submitting = ref(false)
- const viewDialogVisible = ref(false)
- const formDialogVisible = ref(false)
- const formMode = ref<'create' | 'edit'>('create')
- const currentStandard = ref<SubsidyStandard | null>(null)
- const editingId = ref<number | null>(null)
- const formRef = ref<FormInstance>()
- const currentTown = computed(() => userStore.roleInfo?.town || '')
- /** 业务本年度:优先系统发放月,否则取自然年 */
- const currentYear = computed(() => {
- const m = String(userStore.currentMonth || '')
- if (/^\d{4}/.test(m)) return Number(m.slice(0, 4))
- return new Date().getFullYear()
- })
- /**
- * 是否当前有效标准:只认后端 current 标志。
- * 业务约定:每个街镇同一时间只应有一条有效标准(不按「日期最新」猜)。
- */
- function isCurrentStandard(row: SubsidyStandard) {
- return row.current === true
- }
- /** 某街镇是否已有本年度标准(有则禁止再新增) */
- function townHasCurrentYearStandard(town: string) {
- if (!town) return false
- const y = currentYear.value
- return standards.value.some((s) => s.town === town && Number(s.year) === y)
- }
- const createBlockedTip = computed(() => {
- if (isAdmin.value && !filterTown.value && !form.town) {
- return '请先选择街镇后再新增本年度标准'
- }
- const town = isAdmin.value ? filterTown.value || form.town : currentTown.value
- if (town && townHasCurrentYearStandard(town)) {
- return `「${town}」${currentYear.value} 年度已有标准,禁止新增;请使用「调整标准」`
- }
- return ''
- })
- /** 列表页「新增标准」按钮:管理员需先选镇,且本年度尚无标准 */
- const canOpenCreate = computed(() => {
- if (isAdmin.value) {
- if (!filterTown.value) return false
- return !townHasCurrentYearStandard(filterTown.value)
- }
- return Boolean(currentTown.value) && !townHasCurrentYearStandard(currentTown.value)
- })
- 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 = {
- town: [{ required: true, message: '请选择街镇', trigger: 'change' }],
- year: [{ required: true, message: '请选择年度', trigger: 'change' }],
- monthly: [{ required: true, message: '请输入月补助', trigger: 'blur' }],
- funeral: [{ required: true, message: '请输入丧葬补助', trigger: 'blur' }],
- 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 = isAdmin.value ? filterTown.value || '' : currentTown.value
- 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() {
- const town = isAdmin.value ? filterTown.value : currentTown.value
- if (isAdmin.value && !town) {
- ElMessage.warning('请先在上方选择街镇后再新增')
- return
- }
- if (!town) {
- ElMessage.warning('当前账号未绑定街镇,无法新增标准')
- return
- }
- if (townHasCurrentYearStandard(town)) {
- ElMessage.warning(
- `「${town}」${currentYear.value} 年度已有补助标准,禁止重复新增;如需变更请点击「调整标准」`,
- )
- return
- }
- formMode.value = 'create'
- resetForm()
- form.year = String(currentYear.value)
- formDialogVisible.value = true
- }
- function onCreateTownChange(town: string) {
- if (formMode.value !== 'create') return
- form.year = String(currentYear.value)
- if (town && townHasCurrentYearStandard(town)) {
- ElMessage.warning(
- `「${town}」${currentYear.value} 年度已有补助标准,禁止新增;请关闭后使用「调整标准」`,
- )
- formDialogVisible.value = false
- }
- }
- 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()
- }
- function resetTownFilter() {
- filterTown.value = ''
- loadStandards()
- }
- 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 (!form.town) {
- ElMessage.warning('请选择街镇')
- return
- }
- if (townHasCurrentYearStandard(form.town)) {
- ElMessage.warning(
- `「${form.town}」${currentYear.value} 年度已有补助标准,禁止重复新增;请使用「调整标准」`,
- )
- return
- }
- }
- submitting.value = true
- try {
- if (formMode.value === 'create') {
- const res = await createStandard({
- town: form.town,
- year: currentYear.value,
- monthly,
- funeral,
- date: form.date,
- reason: form.reason.trim(),
- })
- if (!res.ok) {
- ElMessage.warning(res.message || '新增失败')
- return
- }
- ElMessage.success('新增标准成功')
- } 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,
- monthly,
- funeral,
- date: form.date,
- reason: form.reason.trim(),
- })
- if (!res.ok) {
- ElMessage.warning(res.message || '调整失败')
- return
- }
- ElMessage.success('调整标准成功')
- }
- formDialogVisible.value = false
- await loadStandards()
- } finally {
- submitting.value = false
- }
- }
- async function onDeleteStandard(row: SubsidyStandard) {
- const tip = isCurrentStandard(row)
- ? `「${row.town}」${row.monthly} 元是当前有效标准,删除后将自动恢复同镇上一档为有效。确认删除?`
- : `确认删除「${row.town}」${row.year} 年标准(月补 ${row.monthly})?删除后列表不再展示。`
- try {
- await ElMessageBox.confirm(tip, '删除补助标准', {
- type: 'warning',
- confirmButtonText: '确认删除',
- cancelButtonText: '取消',
- })
- } catch {
- return
- }
- const res = await deleteStandard({ id: row.id, rawId: row.rawId })
- if (!res.ok) {
- ElMessage.warning(res.message || '删除失败')
- return
- }
- ElMessage.success(res.message || '已删除')
- await loadStandards()
- }
- async function loadStandards() {
- standardLoading.value = true
- try {
- if (isAdmin.value) {
- const all = await fetchStandards()
- standards.value = filterTown.value
- ? all.filter((s) => s.town === filterTown.value)
- : all
- } else {
- standards.value = await fetchStandards(currentTown.value || undefined)
- }
- resetStandardPage()
- } finally {
- standardLoading.value = false
- }
- }
- onMounted(loadStandards)
- watch(
- () => userStore.currentRole,
- () => {
- filterTown.value = ''
- 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>
|