Files
BMS/bms-front/src/views/price/set/components/detail/SetVersionTable.vue
T
2026-11-09 11:45:23 +08:00

234 lines
7.9 KiB
Vue

<template>
<set-section-card :title="title" tone="green" :count="filteredVersions.length">
<template #action>
<el-button
type="primary"
plain
size="small"
:disabled="hasDraft"
v-hasPermi="addPerm"
@click="emit('create-version')"
>新建价格版本</el-button>
</template>
<el-tabs v-model="statusTab" class="ver-tabs">
<el-tab-pane :label="tabLabel('all')" name="all" />
<el-tab-pane :label="tabLabel('pending')" name="pending" />
<el-tab-pane :label="tabLabel('1')" name="1" />
<el-tab-pane :label="tabLabel('2')" name="2" />
<el-tab-pane :label="tabLabel('del')" name="del" />
</el-tabs>
<el-table :data="filteredVersions" border empty-text="暂无版本">
<el-table-column :label="colVer" width="100" align="center">
<template #default="{ row }">{{ row.versionNo || '-' }}</template>
</el-table-column>
<el-table-column :label="colEff" min-width="170">
<template #default="{ row }">
<el-button link type="primary" @click="openDetails(row)">{{ row.effectiveTime || '未设置' }}</el-button>
</template>
</el-table-column>
<el-table-column :label="colExp" min-width="170">
<template #default="{ row }">{{ row.expireTime || '-' }}</template>
</el-table-column>
<el-table-column :label="colStatus" width="100" align="center">
<template #default="{ row }">
<el-tag :type="isDeletedVersion(row) ? 'danger' : versionStatusTag(row.status)" size="small">{{ statusText(row) }}</el-tag>
</template>
</el-table-column>
<el-table-column :label="colCreate" min-width="170" align="center">
<template #default="{ row }">{{ row.createTime || '-' }}</template>
</el-table-column>
<el-table-column :label="colRows" width="100" align="center">
<template #default="{ row }">{{ detailCount(row) }}</template>
</el-table-column>
<el-table-column :label="colAction" width="540" align="center" fixed="right">
<template #default="{ row }">
<el-button link type="primary" @click="openDetails(row)">查看详情</el-button>
<el-button link type="primary" @click="emit('view-import-attach', row)">查看附件</el-button>
<el-button
link
type="primary"
v-hasPermi="['monitor:priceLog:list']"
@click="emit('view-price-log', row)"
>变更记录</el-button>
<el-button
v-if="!isDeletedVersion(row) && showEditButton(row)"
link
type="primary"
@click="emit('edit', row)"
>编辑</el-button>
<el-button
v-if="!isDeletedVersion(row) && isDraftVersion(row)"
link
type="success"
@click="emit('activate', row)"
v-hasPermi="editPerm"
>启动</el-button>
<el-button
v-if="!isDeletedVersion(row) && isPendingVersion(row)"
link
type="primary"
@click="emit('reschedule', row)"
v-hasPermi="editPerm"
>修改生效时间</el-button>
<el-button
v-if="!isDeletedVersion(row) && (isDraftVersion(row) || isPendingVersion(row) || isEffectVersion(row))"
link
type="danger"
:loading="isRemoving(row)"
:disabled="removingPsvId != null"
@click="emit('remove', row)"
v-hasPermi="editPerm"
>删除</el-button>
</template>
</el-table-column>
</el-table>
</set-section-card>
</template>
<script setup name="SetVersionTable">
import SetSectionCard from './SetSectionCard.vue'
import { versionStatusLabel, versionStatusTag, isDraftVersion, isPendingVersion, isEffectVersion, isPriceEditableVersion } from '../../utils/priceSet'
import { checkPermi } from '@/utils/permission'
import { usePriceSetModule, usePriceSetPerm } from '../../composables/usePriceSetModule'
const mod = usePriceSetModule()
const addPerm = usePriceSetPerm('add')
const editPerm = usePriceSetPerm('edit')
const props = defineProps({
versions: { type: Array, default: () => [] },
modelValue: { type: [Number, String], default: undefined },
removingPsvId: { type: [Number, String], default: null }
})
const emit = defineEmits(['update:modelValue', 'create-version', 'open-details', 'view-import-attach', 'view-price-log', 'activate', 'reschedule', 'edit', 'remove'])
const title = '套餐版本'
const colVer = '版本号'
const colCreate = '创建时间'
const colEff = '生效时间'
const colExp = '失效时间'
const colStatus = '状态'
const colRows = '明细行数'
const colAction = '操作'
const statusTab = ref('all')
function isDeletedVersion(row) {
return !!row && String(row.isDel) === '1'
}
function statusText(row) {
if (isDeletedVersion(row)) return '已删除'
return versionStatusLabel(row.status)
}
const activeVersions = computed(() => (props.versions || []).filter(v => !isDeletedVersion(v)))
const deletedVersions = computed(() => (props.versions || []).filter(v => isDeletedVersion(v)))
const counts = computed(() => {
const map = { all: 0, pending: 0, '1': 0, '2': 0, del: 0 }
activeVersions.value.forEach(v => {
map.all++
const s = String(v.status == null ? '' : v.status)
if (s === '0' || s === '3') map.pending++
else if (map[s] != null) map[s]++
})
map.del = deletedVersions.value.length
return map
})
function tabLabel(key) {
const labels = {
all: '全部',
pending: '待生效',
'1': '已生效',
'2': '已失效',
del: '已删除'
}
return labels[key] + ' (' + (counts.value[key] || 0) + ')'
}
const hasDraft = computed(() => activeVersions.value.some(v => isDraftVersion(v)))
function toEffMs(v) {
if (!v || !v.effectiveTime) return NaN
const t = new Date(String(v.effectiveTime).replace(/-/g, '/')).getTime()
return Number.isNaN(t) ? NaN : t
}
/** draft / pending first, then effect, then expired; within group by effective time desc. */
function statusRank(v) {
const s = String(v && v.status != null ? v.status : '')
if (s === '0') return 0
if (s === '3') return 1
if (s === '1') return 2
if (s === '2') return 3
return 9
}
function sortVersions(list) {
return (list || []).slice().sort((a, b) => {
const ra = statusRank(a)
const rb = statusRank(b)
if (ra !== rb) return ra - rb
const ta = toEffMs(a)
const tb = toEffMs(b)
const aNull = Number.isNaN(ta)
const bNull = Number.isNaN(tb)
if (aNull && bNull) return (Number(b.versionSeq) || 0) - (Number(a.versionSeq) || 0)
if (aNull) return 1
if (bNull) return -1
if (tb !== ta) return tb - ta
return (Number(b.versionSeq) || 0) - (Number(a.versionSeq) || 0)
})
}
const filteredVersions = computed(() => {
if (statusTab.value === 'del') {
return sortVersions(deletedVersions.value)
}
const list = activeVersions.value
let filtered
if (statusTab.value === 'all') {
filtered = list
} else if (statusTab.value === 'pending') {
filtered = list.filter(v => {
const s = String(v.status)
return s === '0' || s === '3'
})
} else {
filtered = list.filter(v => String(v.status) === statusTab.value)
}
return sortVersions(filtered)
})
function detailCount(row) {
if (row && row.detailCount != null) return row.detailCount
return (row && row.details || []).length
}
function showEditButton(row) {
if (checkPermi([mod.permPrefix + ':forceEdit'])) return true
return isPriceEditableVersion(row) && checkPermi([mod.permPrefix + ':edit'])
}
function isRemoving(row) {
return props.removingPsvId != null && row && String(props.removingPsvId) === String(row.psvId)
}
function openDetails(row) {
if (!row || row.psvId == null) return
emit('update:modelValue', row.psvId)
emit('open-details', row)
}
</script>
<style scoped>
.ver-tabs {
margin-bottom: 8px;
}
.ver-tabs :deep(.el-tabs__header) {
margin-bottom: 8px;
}
</style>