This commit is contained in:
2026-09-15 09:58:15 +08:00
parent 3f998bdb88
commit 4bf66d9571
90 changed files with 1599 additions and 1388 deletions
@@ -100,17 +100,14 @@ describe('FeeItemsView public master data and access', () => {
})
it('keeps all three row actions visible without a premature more menu', () => {
expect(source).toContain('size="small" plain type="primary"')
expect(source).toContain('size="small"\n plain\n :type=')
expect(source).toContain('size="small" plain type="danger"')
expect(source).toContain('<Pencil :size="15" />编辑')
expect(source).toContain('<Power :size="15" />')
expect(source).toContain('<Trash2 :size="15" />删除')
expect(source).not.toContain('MoreHorizontal')
expect(source).not.toContain("access.can('firstleg:fee-item:edit')\" link")
expect(source).not.toContain("access.can('firstleg:fee-item:delete')\" link")
const actions = source.slice(source.indexOf('<template #actions='), source.indexOf('</template>', source.indexOf('<template #actions=')))
expect(actions.match(/class="density-action"/g)).toHaveLength(3)
expect(actions).toContain('>编辑</el-button>')
expect(actions).toContain('>删除</el-button>')
expect(actions).toContain('data-tone="danger"')
expect(actions).toContain(':data-tone=')
expect(actions).not.toMatch(/<(?:el-dropdown|Pencil|Power|Eye|Trash2|Download|CircleX)\b/)
})
it('keeps the prototype table treatment and mobile form fallback without inventing a pricing mode', () => {
expect(source).toContain(':page-sizes="[20, 50, 100]"\n border\n stripe')
expect(source).toContain('@media (max-width: 720px)')
+22 -21
View File
@@ -1,7 +1,8 @@
<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 { Pencil, Plus, Power, Save, Trash2 } from '@lucide/vue'
import { Plus, Save } from '@lucide/vue'
import { useAccessPreviewStore } from '@/core/access/accessPreview'
import { useUnsavedWorkRegistration } from '@/core/unsaved-work/useUnsavedWorkRegistration'
import {
@@ -335,7 +336,7 @@ onMounted(() => {
</script>
<template>
<AdminPage as="div" class="fee-items-page" spacing="normal">
<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="费用项目管理">
@@ -387,18 +388,18 @@ onMounted(() => {
><Plus :size="16" />新增费用项目</el-button>
</template>
<template #actions="{ row }">
<div v-if="access.can('firstleg:fee-item:edit') || access.can('firstleg:fee-item:delete')" class="table-actions">
<el-button v-if="access.can('firstleg:fee-item:edit')" size="small" plain type="primary" :disabled="loading || editorLoading" @click="openEdit(row as PlatformFeeItemResource)"><Pencil :size="15" />编辑</el-button>
<el-button
<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"
plain
:type="(row as PlatformFeeItemResource).status === 'ACTIVE' ? 'warning' : 'success'"
:data-tone="(row as PlatformFeeItemResource).status === 'ACTIVE' ? 'danger' : 'success'"
:loading="statusLoadingIds.has(String((row as PlatformFeeItemResource).id))"
:disabled="loading"
@click="requestStatusChange(row as PlatformFeeItemResource)"
><Power :size="15" />{{ (row as PlatformFeeItemResource).status === 'ACTIVE' ? '停用' : '启用' }}</el-button>
<el-button v-if="access.can('firstleg:fee-item:delete')" size="small" plain type="danger" :disabled="loading" @click="requestDelete(row as PlatformFeeItemResource)"><Trash2 :size="15" />删除</el-button>
>{{ (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>
</template>
@@ -420,15 +421,15 @@ onMounted(() => {
</section>
</ListPageLayout>
<AppDrawer
<AppDrawer class="admin-density admin-density-drawer"
v-model="editorOpen"
:title="editorTitle"
size="min(620px, 94vw)"
:loading="editorLoading"
:confirm-loading="saving"
>
<el-form ref="formRef" :model="form" :rules="rules" label-position="top" :disabled="saving || editorLoading">
<FormSection title="基本信息" description="费用项目为平台公共主数据,不按客户单独维护。">
<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>
@@ -437,7 +438,7 @@ onMounted(() => {
<el-segmented v-model="form.tradeScope" :options="[{ label: '国内', value: 'DOMESTIC' }, { label: '国际', value: 'INTERNATIONAL' }]" />
</el-form-item>
</FormSection>
<FormSection title="费用方向" description="选择费用在提货和仓库环节的应收、应付属性。">
<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>
@@ -449,7 +450,7 @@ onMounted(() => {
</el-select>
</el-form-item>
</FormSection>
<FormSection title="适用说明" :bordered="false">
<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>
@@ -461,7 +462,7 @@ onMounted(() => {
</template>
</AppDrawer>
<AppDialog
<AppDialog class="admin-density-dialog"
v-model="statusOpen"
:title="statusTarget?.status === 'ACTIVE' ? '停用费用项目' : '启用费用项目'"
:confirm-text="statusTarget?.status === 'ACTIVE' ? '确认停用' : '确认启用'"
@@ -475,7 +476,7 @@ onMounted(() => {
</p>
</AppDialog>
<AppDialog
<AppDialog class="admin-density-dialog"
v-model="deleteOpen"
title="删除费用项目"
confirm-text="确认删除"
@@ -490,13 +491,13 @@ onMounted(() => {
<style scoped>
.fee-items-page { min-width: 0; }
.fee-item-name { display: flex; min-width: 0; flex-direction: column; gap: 4px; }
.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: 11px; }
.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: 12px; margin-bottom: 12px; }
.direction-grid fieldset { min-width: 0; margin: 0; padding: 12px; border: 1px solid var(--el-border-color); border-radius: 6px; }
.direction-grid legend { padding: 0 5px; color: var(--el-text-color-secondary); font-size: 13px; }
.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; }