510 lines
23 KiB
Vue
510 lines
23 KiB
Vue
<script setup lang="ts">
|
||
import '@/styles/adminDensity.css'
|
||
import { computed, onMounted, reactive, ref, watch } from 'vue'
|
||
import { ElMessage, type FormInstance, type FormRules } from 'element-plus'
|
||
import { Plus, Save } from '@lucide/vue'
|
||
import { useAccessPreviewStore } from '@/core/access/accessPreview'
|
||
import { useUnsavedWorkRegistration } from '@/core/unsaved-work/useUnsavedWorkRegistration'
|
||
import {
|
||
changePlatformFeeItemStatus,
|
||
createPlatformFeeItem,
|
||
deletePlatformFeeItem,
|
||
getPlatformFeeItem,
|
||
listPlatformFeeItems,
|
||
updatePlatformFeeItem,
|
||
type FeeItemStatus,
|
||
type FeeItemTradeScope,
|
||
type PlatformFeeItemResource,
|
||
type SavePlatformFeeItemRequest,
|
||
} from '@/modules/fee-items/api/platformFeeItems'
|
||
import { categoryLabels, type SupplierCategory } from '@/modules/suppliers'
|
||
import { getApiErrorDetails, type ApiErrorDetails } from '@/shared/api/http'
|
||
import ApiErrorMeta from '@/shared/components/ApiErrorMeta.vue'
|
||
import AdminPage from '@/shared/components/AdminPage.vue'
|
||
import AppDialog from '@/shared/components/AppDialog.vue'
|
||
import AppDrawer from '@/shared/components/AppDrawer.vue'
|
||
import DataTable, { type DataTableColumn } from '@/shared/components/DataTable.vue'
|
||
import FormSection from '@/shared/components/FormSection.vue'
|
||
import ListPageLayout from '@/shared/components/ListPageLayout.vue'
|
||
import LoadErrorState from '@/shared/components/LoadErrorState.vue'
|
||
import QueryToolbar, { type QueryField, type QueryModel } from '@/shared/components/QueryToolbar.vue'
|
||
|
||
type FeeItemFilters = QueryModel & {
|
||
keyword: string
|
||
category: string
|
||
tradeScope: FeeItemTradeScope | ''
|
||
status: FeeItemStatus | ''
|
||
}
|
||
|
||
type FeeItemForm = SavePlatformFeeItemRequest & { id: string }
|
||
|
||
const access = useAccessPreviewStore()
|
||
const rows = ref<PlatformFeeItemResource[]>([])
|
||
const loading = ref(false)
|
||
const loadError = ref<ApiErrorDetails | null>(null)
|
||
const page = ref(1)
|
||
const pageSize = ref(20)
|
||
const total = ref(0)
|
||
const filters = ref<FeeItemFilters>(emptyFilters())
|
||
const appliedFilters = ref<FeeItemFilters>(emptyFilters())
|
||
const editorOpen = ref(false)
|
||
const editorLoading = ref(false)
|
||
const saving = ref(false)
|
||
const formRef = ref<FormInstance>()
|
||
const statusLoadingIds = ref(new Set<string>())
|
||
const statusOpen = ref(false)
|
||
const statusTarget = ref<PlatformFeeItemResource | null>(null)
|
||
const statusSaving = ref(false)
|
||
const deleteOpen = ref(false)
|
||
const deleteTarget = ref<PlatformFeeItemResource | null>(null)
|
||
const deleting = ref(false)
|
||
let loadSequence = 0
|
||
let editorSequence = 0
|
||
|
||
function emptyFilters(): FeeItemFilters {
|
||
return { keyword: '', category: '', tradeScope: '', status: '' }
|
||
}
|
||
|
||
function emptyForm(): FeeItemForm {
|
||
return {
|
||
id: '',
|
||
category: '',
|
||
tradeScope: 'DOMESTIC',
|
||
feeTypeName: '',
|
||
supplierCategory: null,
|
||
pickupPayable: false,
|
||
pickupReceivable: false,
|
||
warehousePayable: false,
|
||
warehouseReceivable: false,
|
||
allocationFee: false,
|
||
description: null,
|
||
}
|
||
}
|
||
|
||
const form = reactive<FeeItemForm>(emptyForm())
|
||
const rules: FormRules<FeeItemForm> = {
|
||
category: [{ required: true, message: '请输入费用分类', trigger: 'blur' }],
|
||
tradeScope: [{ required: true, message: '请选择贸易范围', trigger: 'change' }],
|
||
feeTypeName: [{ required: true, message: '请输入费用名称', trigger: 'blur' }],
|
||
}
|
||
const hasPayableDirection = computed(() => form.pickupPayable || form.warehousePayable)
|
||
|
||
const canView = computed(() => access.can('firstleg:fee-item:view'))
|
||
const editorTitle = computed(() => form.id ? '编辑费用项目' : '新增费用项目')
|
||
const editorPermission = computed(() => form.id ? 'firstleg:fee-item:edit' : 'firstleg:fee-item:create')
|
||
|
||
const { captureBaseline } = useUnsavedWorkRegistration({
|
||
key: 'fee-item-editor',
|
||
label: editorTitle,
|
||
isOpen: editorOpen,
|
||
model: form,
|
||
requiredPermission: editorPermission,
|
||
})
|
||
|
||
const queryFields = computed<QueryField[]>(() => [
|
||
{ key: 'keyword', type: 'input', width: 210, label: '费用名称', placeholder: '请输入费用名称', props: { 'aria-label': '费用名称', maxlength: 128 } },
|
||
{ key: 'category', type: 'input', width: 190, label: '费用分类', placeholder: '请输入费用分类', props: { 'aria-label': '费用分类', maxlength: 64 } },
|
||
{
|
||
key: 'tradeScope', type: 'select', width: 160, label: '贸易范围', placeholder: '全部范围', props: { 'aria-label': '贸易范围' },
|
||
options: [{ label: '国内', value: 'DOMESTIC' }, { label: '国际', value: 'INTERNATIONAL' }],
|
||
},
|
||
{
|
||
key: 'status', type: 'select', width: 150, label: '状态', placeholder: '全部状态', props: { 'aria-label': '状态' },
|
||
options: [{ label: '启用', value: 'ACTIVE' }, { label: '停用', value: 'DISABLED' }],
|
||
},
|
||
])
|
||
|
||
const columns: DataTableColumn[] = [
|
||
{ key: 'actions', label: '操作', width: 250, fixed: 'left', slotName: 'actions' },
|
||
{ key: 'feeTypeName', label: '费用项目', minWidth: 210, slotName: 'feeItem' },
|
||
{ key: 'category', prop: 'category', label: '费用分类', minWidth: 150 },
|
||
{ key: 'tradeScope', label: '贸易范围', width: 110, slotName: 'tradeScope' },
|
||
{ key: 'pickup', label: '提货', minWidth: 160, slotName: 'pickup' },
|
||
{ key: 'warehouse', label: '仓库', minWidth: 160, slotName: 'warehouse' },
|
||
{ key: 'supplierCategory', label: '供应商类别', width: 120, slotName: 'supplierCategory' },
|
||
{ key: 'allocationFee', label: '分摊费', width: 100, slotName: 'allocationFee' },
|
||
{ key: 'description', label: '说明', minWidth: 220, slotName: 'description', defaultVisible: false },
|
||
{ key: 'createdByName', label: '创建人', minWidth: 120, formatter: (row: PlatformFeeItemResource) => row.createdByName || '—' },
|
||
{ key: 'createdAt', label: '创建日期', minWidth: 155, formatter: (row: PlatformFeeItemResource) => formatTime(row.createdAt) },
|
||
{ key: 'updatedByName', label: '更新人', minWidth: 120, formatter: (row: PlatformFeeItemResource) => row.updatedByName || '—' },
|
||
{ key: 'updatedAt', label: '更新日期', minWidth: 155, formatter: (row: PlatformFeeItemResource) => formatTime(row.updatedAt) },
|
||
{ key: 'status', label: '状态', width: 100, slotName: 'status' },
|
||
]
|
||
|
||
function optionalText(value: string | null) {
|
||
return value?.trim() || null
|
||
}
|
||
|
||
function formatTime(value?: string | null) {
|
||
return value?.replace('T', ' ').slice(0, 16) || '—'
|
||
}
|
||
|
||
function directionText(payable: boolean, receivable: boolean) {
|
||
if (payable && receivable) return '应付、应收'
|
||
if (payable) return '应付'
|
||
if (receivable) return '应收'
|
||
return '不适用'
|
||
}
|
||
|
||
async function loadFeeItems() {
|
||
if (!canView.value) {
|
||
rows.value = []
|
||
total.value = 0
|
||
loadError.value = null
|
||
return
|
||
}
|
||
const sequence = ++loadSequence
|
||
loading.value = true
|
||
loadError.value = null
|
||
try {
|
||
const result = await listPlatformFeeItems({
|
||
keyword: appliedFilters.value.keyword,
|
||
category: appliedFilters.value.category,
|
||
tradeScope: appliedFilters.value.tradeScope,
|
||
status: appliedFilters.value.status,
|
||
page: page.value,
|
||
size: pageSize.value,
|
||
})
|
||
if (sequence !== loadSequence) return
|
||
rows.value = result.records
|
||
total.value = result.total
|
||
} catch (error) {
|
||
if (sequence !== loadSequence) return
|
||
rows.value = []
|
||
total.value = 0
|
||
loadError.value = getApiErrorDetails(error, '费用项目加载失败,请稍后重试')
|
||
} finally {
|
||
if (sequence === loadSequence) loading.value = false
|
||
}
|
||
}
|
||
|
||
function applyFilters(model: QueryModel) {
|
||
appliedFilters.value = {
|
||
keyword: String(model.keyword || ''),
|
||
category: String(model.category || ''),
|
||
tradeScope: (model.tradeScope || '') as FeeItemTradeScope | '',
|
||
status: (model.status || '') as FeeItemStatus | '',
|
||
}
|
||
if (page.value === 1) void loadFeeItems()
|
||
else page.value = 1
|
||
}
|
||
|
||
function openCreate() {
|
||
editorSequence += 1
|
||
Object.assign(form, emptyForm())
|
||
captureBaseline()
|
||
editorOpen.value = true
|
||
void formRef.value?.clearValidate()
|
||
}
|
||
|
||
function assignForm(item: PlatformFeeItemResource) {
|
||
Object.assign(form, {
|
||
id: String(item.id),
|
||
category: item.category,
|
||
tradeScope: item.tradeScope,
|
||
feeTypeName: item.feeTypeName,
|
||
supplierCategory: item.supplierCategory,
|
||
pickupPayable: item.pickupPayable,
|
||
pickupReceivable: item.pickupReceivable,
|
||
warehousePayable: item.warehousePayable,
|
||
warehouseReceivable: item.warehouseReceivable,
|
||
allocationFee: item.allocationFee,
|
||
description: item.description,
|
||
})
|
||
captureBaseline()
|
||
editorOpen.value = true
|
||
void formRef.value?.clearValidate()
|
||
}
|
||
|
||
async function openEdit(item: PlatformFeeItemResource) {
|
||
const sequence = ++editorSequence
|
||
editorLoading.value = true
|
||
try {
|
||
const detail = await getPlatformFeeItem(item.id)
|
||
if (sequence === editorSequence) assignForm(detail)
|
||
} catch (error) {
|
||
if (sequence !== editorSequence) return
|
||
const details = getApiErrorDetails(error, '费用项目详情加载失败,请稍后重试')
|
||
ElMessage.error(details.requestId ? `${details.message}(requestId: ${details.requestId})` : details.message)
|
||
} finally {
|
||
if (sequence === editorSequence) editorLoading.value = false
|
||
}
|
||
}
|
||
|
||
function buildPayload(): SavePlatformFeeItemRequest {
|
||
return {
|
||
category: form.category.trim(),
|
||
tradeScope: form.tradeScope,
|
||
feeTypeName: form.feeTypeName.trim(),
|
||
supplierCategory: hasPayableDirection.value ? form.supplierCategory : null,
|
||
pickupPayable: form.pickupPayable,
|
||
pickupReceivable: form.pickupReceivable,
|
||
warehousePayable: form.warehousePayable,
|
||
warehouseReceivable: form.warehouseReceivable,
|
||
allocationFee: form.allocationFee,
|
||
description: optionalText(form.description),
|
||
}
|
||
}
|
||
|
||
async function saveFeeItem() {
|
||
const valid = await formRef.value?.validate().catch(() => false)
|
||
if (!valid) return
|
||
if (hasPayableDirection.value && !form.supplierCategory) {
|
||
ElMessage.warning('启用应付方向时必须选择供应商类别')
|
||
return
|
||
}
|
||
saving.value = true
|
||
try {
|
||
if (form.id) await updatePlatformFeeItem(form.id, buildPayload())
|
||
else await createPlatformFeeItem(buildPayload())
|
||
editorOpen.value = false
|
||
ElMessage.success(form.id ? '费用项目已更新' : '费用项目已创建')
|
||
await loadFeeItems()
|
||
} catch (error) {
|
||
const details = getApiErrorDetails(error, '费用项目保存失败,请检查后重试')
|
||
ElMessage.error(details.requestId ? `${details.message}(requestId: ${details.requestId})` : details.message)
|
||
} finally {
|
||
saving.value = false
|
||
}
|
||
}
|
||
|
||
function requestStatusChange(item: PlatformFeeItemResource) {
|
||
statusTarget.value = item
|
||
statusOpen.value = true
|
||
}
|
||
|
||
async function confirmStatusChange() {
|
||
const item = statusTarget.value
|
||
if (!item) return
|
||
const nextStatus: FeeItemStatus = item.status === 'ACTIVE' ? 'DISABLED' : 'ACTIVE'
|
||
const action = nextStatus === 'ACTIVE' ? '启用' : '停用'
|
||
const key = String(item.id)
|
||
statusLoadingIds.value = new Set(statusLoadingIds.value).add(key)
|
||
statusSaving.value = true
|
||
try {
|
||
await changePlatformFeeItemStatus(item.id, nextStatus)
|
||
ElMessage.success(`已${action}“${item.feeTypeName}”`)
|
||
statusOpen.value = false
|
||
statusTarget.value = null
|
||
await loadFeeItems()
|
||
} catch (error) {
|
||
const details = getApiErrorDetails(error, `${action}费用项目失败`)
|
||
ElMessage.error(details.requestId ? `${details.message}(requestId: ${details.requestId})` : details.message)
|
||
} finally {
|
||
const pending = new Set(statusLoadingIds.value)
|
||
pending.delete(key)
|
||
statusLoadingIds.value = pending
|
||
statusSaving.value = false
|
||
}
|
||
}
|
||
|
||
function requestDelete(item: PlatformFeeItemResource) {
|
||
deleteTarget.value = item
|
||
deleteOpen.value = true
|
||
}
|
||
|
||
async function confirmDelete() {
|
||
if (!deleteTarget.value) return
|
||
deleting.value = true
|
||
try {
|
||
await deletePlatformFeeItem(deleteTarget.value.id)
|
||
ElMessage.success(`已删除“${deleteTarget.value.feeTypeName}”`)
|
||
deleteOpen.value = false
|
||
deleteTarget.value = null
|
||
if (rows.value.length === 1 && page.value > 1) page.value -= 1
|
||
else await loadFeeItems()
|
||
} catch (error) {
|
||
const details = getApiErrorDetails(error, '费用项目删除失败;已被业务使用的项目不能删除')
|
||
ElMessage.error(details.requestId ? `${details.message}(requestId: ${details.requestId})` : details.message)
|
||
} finally {
|
||
deleting.value = false
|
||
}
|
||
}
|
||
|
||
watch([page, pageSize], () => void loadFeeItems())
|
||
watch(
|
||
() => access.can(editorPermission.value),
|
||
(allowed) => {
|
||
if (!allowed && editorOpen.value) editorOpen.value = false
|
||
},
|
||
{ flush: 'sync' },
|
||
)
|
||
|
||
onMounted(() => {
|
||
if (canView.value) void loadFeeItems()
|
||
})
|
||
</script>
|
||
|
||
<template>
|
||
<AdminPage as="div" class="fee-items-page admin-density" spacing="normal">
|
||
<el-result v-if="!canView" icon="warning" title="无权查看费用项目" sub-title="请联系管理员开通费用项目查看权限" />
|
||
|
||
<ListPageLayout v-else aria-label="费用项目管理">
|
||
|
||
|
||
|
||
|
||
<section><DataTable appearance="integrated" :stripe="false" column-settings-mode="show" :results-visible="!loadError"
|
||
v-model:page="page"
|
||
v-model:page-size="pageSize"
|
||
:data="rows"
|
||
:columns="columns"
|
||
:loading="loading"
|
||
:total="total"
|
||
:page-sizes="[20, 50, 100]"
|
||
border
|
||
preference-key="fee-items-table"
|
||
aria-label="费用项目列表"
|
||
row-key="id"
|
||
empty-text="暂无符合条件的费用项目"
|
||
>
|
||
<template #toolbar-start><h1>费用项目</h1></template>
|
||
<template #filters>
|
||
<QueryToolbar
|
||
v-model="filters"
|
||
ariaLabel="费用项目筛选"
|
||
:fields="queryFields"
|
||
:loading="loading"
|
||
preference-key="fee-items-query"
|
||
@search="applyFilters"
|
||
/>
|
||
</template>
|
||
<template #notices><template v-if="loadError">
|
||
<LoadErrorState
|
||
title="费用项目加载失败"
|
||
:message="loadError.message"
|
||
:loading="loading"
|
||
@retry="loadFeeItems"
|
||
>
|
||
<template #details><ApiErrorMeta :details="loadError" /></template>
|
||
</LoadErrorState>
|
||
</template></template>
|
||
|
||
|
||
<template #toolbar-actions>
|
||
<el-button
|
||
v-if="access.can('firstleg:fee-item:create')"
|
||
type="primary"
|
||
:disabled="loading"
|
||
@click="openCreate"
|
||
><Plus :size="16" />新增费用项目</el-button>
|
||
</template>
|
||
<template #actions="{ row }"><div class="row-actions density-actions admin-text-actions">
|
||
<div v-if="access.can('firstleg:fee-item:edit') || access.can('firstleg:fee-item:delete')" class="table-actions density-actions">
|
||
<el-button data-tone="neutral" class="density-action" v-if="access.can('firstleg:fee-item:edit')" size="small" :disabled="loading || editorLoading" @click="openEdit(row as PlatformFeeItemResource)">编辑</el-button>
|
||
<el-button class="density-action"
|
||
v-if="access.can('firstleg:fee-item:edit')"
|
||
size="small"
|
||
|
||
:data-tone="(row as PlatformFeeItemResource).status === 'ACTIVE' ? 'danger' : 'success'"
|
||
:loading="statusLoadingIds.has(String((row as PlatformFeeItemResource).id))"
|
||
:disabled="loading"
|
||
@click="requestStatusChange(row as PlatformFeeItemResource)"
|
||
>{{ (row as PlatformFeeItemResource).status === 'ACTIVE' ? '停用' : '启用' }}</el-button>
|
||
<el-button data-tone="danger" class="density-action" v-if="access.can('firstleg:fee-item:delete')" size="small" :disabled="loading" @click="requestDelete(row as PlatformFeeItemResource)">删除</el-button>
|
||
</div>
|
||
<span v-else class="muted-value">仅查看</span>
|
||
</div></template>
|
||
<template #feeItem="{ row }"><div class="fee-item-name"><strong>{{ (row as PlatformFeeItemResource).feeTypeName }}</strong>
|
||
<!-- <small>{{ (row as PlatformFeeItemResource).feeCode || '未分配编码' }}</small> -->
|
||
</div></template>
|
||
<template #tradeScope="{ row }"><el-tag effect="plain">{{ (row as PlatformFeeItemResource).tradeScope === 'DOMESTIC' ? '国内' : '国际' }}</el-tag></template>
|
||
<template #pickup="{ row }">{{ directionText((row as PlatformFeeItemResource).pickupPayable, (row as PlatformFeeItemResource).pickupReceivable) }}</template>
|
||
<template #warehouse="{ row }">{{ directionText((row as PlatformFeeItemResource).warehousePayable, (row as PlatformFeeItemResource).warehouseReceivable) }}</template>
|
||
<template #supplierCategory="{ row }">{{ (row as PlatformFeeItemResource).supplierCategory ? categoryLabels[(row as PlatformFeeItemResource).supplierCategory as SupplierCategory] : '未配置' }}</template>
|
||
<template #allocationFee="{ row }">{{ (row as PlatformFeeItemResource).allocationFee ? '是' : '否' }}</template>
|
||
<template #description="{ row }"><span class="description-cell">{{ (row as PlatformFeeItemResource).description || '—' }}</span></template>
|
||
<template #status="{ row }"><el-tag :type="(row as PlatformFeeItemResource).status === 'ACTIVE' ? 'success' : 'info'">{{ (row as PlatformFeeItemResource).status === 'ACTIVE' ? '启用' : '停用' }}</el-tag></template>
|
||
<template #empty>
|
||
<el-empty v-if="!loading" description="暂无符合条件的费用项目">
|
||
<el-button v-if="access.can('firstleg:fee-item:create')" type="primary" @click="openCreate"><Plus :size="16" />新增第一项费用</el-button>
|
||
</el-empty>
|
||
</template>
|
||
<template #pagination-left>平台公共主数据 · 共 {{ total }} 项</template>
|
||
</DataTable></section>
|
||
</ListPageLayout>
|
||
|
||
<AppDrawer class="admin-density admin-density-drawer"
|
||
v-model="editorOpen"
|
||
:title="editorTitle"
|
||
size="min(620px, 94vw)"
|
||
:loading="editorLoading"
|
||
:confirm-loading="saving"
|
||
>
|
||
<el-form class="density-form" ref="formRef" :model="form" :rules="rules" label-position="top" :disabled="saving || editorLoading">
|
||
<FormSection class="density-form-section" title="基本信息" description="费用项目为平台公共主数据,不按客户单独维护。">
|
||
<div class="form-grid two-cols">
|
||
<el-form-item label="费用名称" prop="feeTypeName" required><el-input v-model="form.feeTypeName" maxlength="128" placeholder="请输入费用名称" /></el-form-item>
|
||
<el-form-item label="费用分类" prop="category" required><el-input v-model="form.category" maxlength="64" placeholder="例如 仓储成本" /></el-form-item>
|
||
</div>
|
||
<el-form-item label="贸易范围" prop="tradeScope" required>
|
||
<el-segmented v-model="form.tradeScope" :options="[{ label: '国内', value: 'DOMESTIC' }, { label: '国际', value: 'INTERNATIONAL' }]" />
|
||
</el-form-item>
|
||
</FormSection>
|
||
<FormSection class="density-form-section" title="费用方向" description="选择费用在提货和仓库环节的应收、应付属性。">
|
||
<div class="direction-grid">
|
||
<fieldset><legend>提货环节</legend><el-checkbox v-model="form.pickupPayable">应付</el-checkbox><el-checkbox v-model="form.pickupReceivable">应收</el-checkbox></fieldset>
|
||
<fieldset><legend>仓库环节</legend><el-checkbox v-model="form.warehousePayable">应付</el-checkbox><el-checkbox v-model="form.warehouseReceivable">应收</el-checkbox></fieldset>
|
||
</div>
|
||
<el-checkbox v-model="form.allocationFee">作为分摊费用</el-checkbox>
|
||
<el-form-item v-if="hasPayableDirection" label="供应商类别" required>
|
||
<el-select v-model="form.supplierCategory" placeholder="请选择 Warehouse 供应商类别">
|
||
<el-option v-for="(label, value) in categoryLabels" :key="value" :label="label" :value="value" />
|
||
</el-select>
|
||
</el-form-item>
|
||
</FormSection>
|
||
<FormSection class="density-form-section" title="适用说明" :bordered="false">
|
||
<el-form-item label="说明"><el-input v-model="form.description" type="textarea" :rows="4" maxlength="500" show-word-limit placeholder="请输入适用场景或核算说明" /></el-form-item>
|
||
</FormSection>
|
||
</el-form>
|
||
<template #footer>
|
||
<div class="drawer-footer">
|
||
<el-button :disabled="saving" @click="editorOpen = false">取消</el-button>
|
||
<el-button type="primary" :loading="saving" :disabled="editorLoading" @click="saveFeeItem"><Save :size="16" />保存费用项目</el-button>
|
||
</div>
|
||
</template>
|
||
</AppDrawer>
|
||
|
||
<AppDialog class="admin-density-dialog"
|
||
v-model="statusOpen"
|
||
:title="statusTarget?.status === 'ACTIVE' ? '停用费用项目' : '启用费用项目'"
|
||
:confirm-text="statusTarget?.status === 'ACTIVE' ? '确认停用' : '确认启用'"
|
||
:confirm-type="statusTarget?.status === 'ACTIVE' ? 'warning' : 'success'"
|
||
:confirm-loading="statusSaving"
|
||
@confirm="confirmStatusChange"
|
||
>
|
||
<p class="delete-message">
|
||
确定{{ statusTarget?.status === 'ACTIVE' ? '停用' : '启用' }}“<strong>{{ statusTarget?.feeTypeName }}</strong>”吗?
|
||
<template v-if="statusTarget?.status === 'ACTIVE'">停用后该项目不能再用于新的费用记录。</template>
|
||
</p>
|
||
</AppDialog>
|
||
|
||
<AppDialog class="admin-density-dialog"
|
||
v-model="deleteOpen"
|
||
title="删除费用项目"
|
||
confirm-text="确认删除"
|
||
confirm-type="danger"
|
||
:confirm-loading="deleting"
|
||
@confirm="confirmDelete"
|
||
>
|
||
<p class="delete-message">确定删除“<strong>{{ deleteTarget?.feeTypeName }}</strong>”吗?删除后无法恢复;已被业务单据使用的项目将由服务端拒绝删除。</p>
|
||
</AppDialog>
|
||
</AdminPage>
|
||
</template>
|
||
|
||
<style scoped>
|
||
.fee-items-page { min-width: 0; }
|
||
.fee-item-name { display: flex; min-width: 0; flex-direction: column; gap: var(--admin-space-1); }
|
||
.fee-item-name strong { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||
.fee-item-name small { color: var(--cc-muted); font-size: var(--admin-font-caption); }
|
||
.description-cell { display: -webkit-box; overflow: hidden; line-height: 1.5; -webkit-box-orient: vertical; -webkit-line-clamp: 2; }
|
||
.direction-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--admin-space-5); margin-bottom: var(--admin-space-5); }
|
||
.direction-grid fieldset { min-width: 0; margin: 0; padding: var(--admin-space-5); border: 1px solid var(--el-border-color); border-radius: var(--admin-radius); }
|
||
.direction-grid legend { padding: 0 5px; color: var(--el-text-color-secondary); font-size: var(--admin-font-body); }
|
||
.delete-message { margin: 0; color: var(--el-text-color-regular); line-height: 1.7; overflow-wrap: anywhere; }
|
||
@media (max-width: 720px) {
|
||
.direction-grid, .form-grid.two-cols { grid-template-columns: 1fr; }
|
||
.table-actions { flex-wrap: wrap; }
|
||
}
|
||
</style>
|