This commit is contained in:
zlwu committed 2026-06-26 00:50:04 +08:00
1 parent 0b50eff3d8
commit ee364e24aa
323 files changed
+57731 -35766

No files matched your search

@@ -0,0 +1,67 @@
<template>
<div class="qc-param-select-editor">
<div class="qc-param-select-editor__head">
单选选项
<span class="form-label-tip">多个选项用逗号分隔,第一个为默认选项</span>
</div>
<el-input
v-model="param._optionText"
placeholder="例如:阴性,阳性,待复核"
@input="syncOptions"
/>
</div>
</template>
<script setup lang="ts">
import { watch } from 'vue'
import type { QcItemParam } from '@/api/qc-item-param'
import {
formatQcParamOptionText,
parseQcParamOptionText
} from '@/utils/qc-item-param-options'
const param = defineModel<QcItemParam>('param', { required: true })
function ensureOptionText() {
if (param.value._optionText === undefined) {
param.value._optionText = formatQcParamOptionText(
param.value.options,
param.value.defaultValue
)
}
}
function syncOptions() {
const list = parseQcParamOptionText(param.value._optionText)
param.value.options = list.length ? JSON.stringify(list) : undefined
param.value.defaultValue = list[0] || undefined
}
watch(
() => param.value.paramType,
() => {
if (param.value.paramType === 'select') {
ensureOptionText()
}
},
{ immediate: true }
)
</script>
<style scoped lang="scss">
.qc-param-select-editor {
padding: 8px 0 4px;
display: flex;
flex-direction: column;
gap: 8px;
}
.qc-param-select-editor__head {
font-size: 12px;
color: var(--mint-text-muted, #64748b);
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 4px;
}
</style>
@@ -110,6 +110,7 @@
qcTaskConclusionLabel
} from '@/utils/qc-labels'
import { downloadAttachmentFile } from '@/utils/download-attachment'
import { fetchQcInspectionBatches, loadQcInspectionBatches } from '@/utils/qc-inspection-batch'
const props = defineProps<{
visible: boolean
@@ -140,6 +141,8 @@
const loading = ref(false)
const viewDetail = ref<QcTaskDetail | null>(null)
const viewMaterialUsages = ref<ViewMaterialUsageRow[]>([])
const viewBatchName = ref('')
const viewBatchNo = ref('')
const minioBaseUrl = (import.meta.env.VITE_MINIO_BASE_URL || '').replace(/\/$/, '')
function materialRowsFromApi(items: QcTaskMaterialUsageItem[]): ViewMaterialUsageRow[] {
@@ -162,11 +165,28 @@
if (!vis || !id) {
viewDetail.value = null
viewMaterialUsages.value = []
viewBatchName.value = ''
viewBatchNo.value = ''
return
}
loading.value = true
try {
const detail = await fetchQcTaskDetail(id)
viewBatchName.value = ''
viewBatchNo.value = ''
const batchId = (detail.batchId || '').trim()
const workOrderId = (detail.workOrderId || '').trim()
if (batchId && workOrderId) {
try {
const batches = await loadQcInspectionBatches(workOrderId)
const batch = batches.find((b) => b.id === batchId)
viewBatchName.value = batch?.batchName || ''
viewBatchNo.value = batch?.batchNo || ''
} catch {
viewBatchName.value = ''
viewBatchNo.value = ''
}
}
try {
const results = await fetchQcTaskResults(id)
;(detail as QcTaskDetail).results = results || []
@@ -188,6 +208,8 @@
} catch {
viewDetail.value = null
viewMaterialUsages.value = []
viewBatchName.value = ''
viewBatchNo.value = ''
} finally {
loading.value = false
}
@@ -360,6 +382,14 @@
tagType: qcBizTypeTagType(d.qcBizType || '')
},
{ label: '工单号', value: d.workOrderNo || '—' },
...(viewBatchName.value || viewBatchNo.value
? [
{
label: '检测批次',
value: [viewBatchName.value, viewBatchNo.value].filter(Boolean).join(' · ')
}
]
: []),
{
label: '工单状态',
value: d.workOrderStatus ? workOrderStatusLabel(d.workOrderStatus) : '—',
@@ -0,0 +1,314 @@
<!-- 样本查看 — 质检信息按检测批次只读展示 -->
<template>
<div class="sample-qc-batch-readonly" v-loading="loading">
<template v-if="batchSections.length">
<el-tabs
v-model="activeBatchKey"
class="panel-pill-tabs panel-pill-tabs--start sample-qc-batch-readonly__tabs"
>
<el-tab-pane
v-for="section in batchSections"
:key="section.key"
:name="section.key"
>
<template #label>
<span class="sample-qc-batch-readonly__tab-label">{{ section.batchName }}</span>
<span class="sample-qc-batch-readonly__tab-count">
{{ section.completedTaskCount }}/{{ section.taskCount }}
</span>
</template>
<div
v-if="section.batchNo || section.assigneeUserNames"
class="sample-qc-batch-readonly__meta"
>
<span v-if="section.batchNo" class="sample-qc-batch-readonly__meta-item">{{
section.batchNo
}}</span>
<span v-if="section.assigneeUserNames" class="sample-qc-batch-readonly__meta-item">
负责人:{{ section.assigneeUserNames }}
</span>
</div>
<div class="qc-detail-cards qc-detail-cards--tasks">
<div
v-for="task in section.tasks"
:key="task.id || `${section.key}-${task.taskNo}`"
class="qc-detail-card qc-detail-card--task"
>
<div class="qc-detail-card__header">
<span class="qc-detail-card__name">{{ task.itemName || '—' }}</span>
<el-tag :type="taskTypeTagType(task.taskType || '')" size="small">{{
task.taskTypeLabel || taskTypeLabel(task.taskType || '')
}}</el-tag>
</div>
<div class="qc-detail-card__body">
<div class="qc-kv">
<span class="qc-kv__label">任务号</span>
<span class="qc-kv__value">{{ task.taskNo || '—' }}</span>
</div>
<div class="qc-kv">
<span class="qc-kv__label">任务状态</span>
<span class="qc-kv__value">
<el-tag :type="taskStatusTagType(task.taskStatus)" size="small">
{{ task.taskStatusLabel || taskStatusLabel(task.taskStatus || '') }}
</el-tag>
</span>
</div>
<div class="qc-kv">
<span class="qc-kv__label">检测结论</span>
<span class="qc-kv__value">
<el-tag
v-if="task.conclusion"
:type="qcTaskConclusionTagType(task.conclusion)"
size="small"
>
{{ task.conclusionLabel || qcTaskConclusionLabel(task.conclusion) }}
</el-tag>
<span v-else class="qc-kv__muted">—</span>
</span>
</div>
</div>
<div v-if="task.id" class="qc-detail-card__foot">
<button type="button" class="qc-detail-card__link" @click.stop="emit('view-task', task)">
点击查看详情
</button>
</div>
</div>
</div>
</el-tab-pane>
</el-tabs>
</template>
<div v-else-if="!loading" class="sample-qc-batch-readonly__empty hint-muted">
暂无检测批次或任务
</div>
</div>
</template>
<script setup lang="ts">
import { ref, watch } from 'vue'
import {
taskTypeLabel,
taskTypeTagType,
taskStatusLabel,
qcTaskConclusionLabel,
qcTaskConclusionTagType
} from '@/utils/qc-labels'
import { taskStatusTagType } from '@/utils/status-tag-registry'
import { loadQcInspectionBatches, resolveBatchSections,
type QcBatchTaskSection,
type QcInspectionBatch
} from '@/utils/qc-inspection-batch'
export type SampleQcBatchTask = {
id?: string
taskNo?: string
taskType?: string
taskTypeLabel?: string
itemName?: string
taskStatus?: string
taskStatusLabel?: string
conclusion?: string
conclusionLabel?: string
batchId?: string
}
defineOptions({ name: 'SampleQcBatchReadonly' })
const props = defineProps<{
workOrderId?: string
tasks?: SampleQcBatchTask[]
inspectionBatches?: QcInspectionBatch[]
}>()
const emit = defineEmits<{
'view-task': [task: SampleQcBatchTask]
}>()
const loading = ref(false)
const batches = ref<QcInspectionBatch[]>([])
const batchSections = ref<QcBatchTaskSection<SampleQcBatchTask>[]>([])
const activeBatchKey = ref('')
function ensureActiveBatchKey(sections: QcBatchTaskSection<SampleQcBatchTask>[]) {
if (!sections.length) {
activeBatchKey.value = ''
return
}
const keys = new Set(sections.map((s) => s.key))
if (!keys.has(activeBatchKey.value)) {
activeBatchKey.value = sections[0].key
}
}
function syncSections() {
batchSections.value = resolveBatchSections(props.inspectionBatches || batches.value, props.tasks)
ensureActiveBatchKey(batchSections.value)
}
async function loadBatches() {
const woId = (props.workOrderId || '').trim()
if (props.inspectionBatches?.length) {
batches.value = []
syncSections()
return
}
if (!woId) {
batches.value = []
syncSections()
return
}
loading.value = true
try {
batches.value = await loadQcInspectionBatches(woId)
} catch {
batches.value = []
} finally {
loading.value = false
syncSections()
}
}
watch(
() => [props.workOrderId, props.tasks, props.inspectionBatches] as const,
() => {
if (props.inspectionBatches?.length) {
batches.value = []
syncSections()
return
}
if (props.workOrderId) {
loadBatches()
return
}
syncSections()
},
{ immediate: true, deep: true }
)
</script>
<style scoped lang="scss">
.sample-qc-batch-readonly__tabs {
&.panel-pill-tabs.el-tabs > :deep(.el-tabs__header) {
margin: 0 0 10px;
}
&.panel-pill-tabs.el-tabs :deep(.el-tabs__nav-scroll) {
justify-content: flex-start;
}
&.panel-pill-tabs.el-tabs :deep(.el-tabs__item) {
min-width: 96px;
padding: 6px 14px !important;
}
}
.sample-qc-batch-readonly__tab-label {
margin-right: 6px;
}
.sample-qc-batch-readonly__tab-count {
font-size: 11px;
font-weight: 500;
opacity: 0.85;
}
.sample-qc-batch-readonly__meta {
display: flex;
flex-wrap: wrap;
gap: 8px 16px;
margin-bottom: 8px;
font-size: 12px;
color: var(--mint-text-muted, #64748b);
}
.sample-qc-batch-readonly__empty {
padding: 8px 0;
font-size: 13px;
}
.qc-detail-cards {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
gap: 10px;
&--tasks {
margin-bottom: 0;
}
}
.qc-detail-card {
border: 1px solid #e2e8f0;
border-radius: 8px;
background: #fff;
overflow: hidden;
}
.qc-detail-card__header {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 8px;
padding: 10px 12px;
border-bottom: 1px solid #f1f5f9;
}
.qc-detail-card__name {
font-size: 13px;
font-weight: 600;
color: #1e293b;
text-align: left;
flex: 1;
word-break: break-word;
}
.qc-detail-card__body {
padding: 10px 12px;
display: flex;
flex-direction: column;
gap: 8px;
}
.qc-detail-card__foot {
padding: 2px 12px 8px;
text-align: right;
border-top: 1px dashed #f1f5f9;
}
.qc-detail-card__link {
padding: 0;
border: none;
background: none;
font-size: 11px;
line-height: 1.4;
color: #047857;
cursor: pointer;
&:hover {
color: #065f46;
text-decoration: underline;
}
}
.qc-kv__muted {
color: var(--mint-text-muted, #334155);
font-weight: 400;
}
.qc-kv {
display: flex;
align-items: flex-start;
gap: 8px;
font-size: 12px;
}
.qc-kv__label {
color: var(--mint-text-muted, #334155);
flex-shrink: 0;
min-width: 56px;
}
.qc-kv__value {
color: #1e293b;
font-weight: 500;
word-break: break-word;
}
</style>
@@ -32,56 +32,20 @@
</div>
</div>
<div v-if="qcInfo.workOrderStatus === 'dpz' && !qcInfo.tasks?.length" class="sample-qc-hint">
<div
v-if="qcInfo.workOrderStatus === 'dpz' && !hasTaskContent"
class="sample-qc-hint"
>
工单待配置检测项目
</div>
<div v-if="qcInfo.tasks?.length" class="qc-detail-cards qc-detail-cards--tasks">
<div
v-for="task in qcInfo.tasks"
:key="task.id"
class="qc-detail-card qc-detail-card--task"
>
<div class="qc-detail-card__header">
<span class="qc-detail-card__name">{{ task.itemName || '—' }}</span>
<el-tag :type="taskTypeTagType(task.taskType || '')" size="small">{{
task.taskTypeLabel || taskTypeLabel(task.taskType || '')
}}</el-tag>
</div>
<div class="qc-detail-card__body">
<div class="qc-kv">
<span class="qc-kv__label">任务号</span>
<span class="qc-kv__value">{{ task.taskNo || '—' }}</span>
</div>
<div class="qc-kv">
<span class="qc-kv__label">任务状态</span>
<span class="qc-kv__value">
<el-tag :type="taskStatusTagType(task.taskStatus)" size="small">
{{ task.taskStatusLabel || taskStatusLabel(task.taskStatus || '') }}
</el-tag>
</span>
</div>
<div class="qc-kv">
<span class="qc-kv__label">检测结论</span>
<span class="qc-kv__value">
<el-tag
v-if="task.conclusion"
:type="qcTaskConclusionTagType(task.conclusion)"
size="small"
>
{{ task.conclusionLabel || qcTaskConclusionLabel(task.conclusion) }}
</el-tag>
<span v-else class="qc-kv__muted">—</span>
</span>
</div>
</div>
<div class="qc-detail-card__foot">
<button type="button" class="qc-detail-card__link" @click.stop="openTaskView(task)">
点击查看详情
</button>
</div>
</div>
</div>
<SampleQcBatchReadonly
v-if="hasTaskContent"
:work-order-id="qcInfo.id"
:tasks="qcInfo.tasks"
:inspection-batches="qcInfo.inspectionBatches"
@view-task="openTaskView"
/>
<div v-else-if="qcInfo.workOrderStatus !== 'dpz'" class="sample-qc-hint"> 暂无检测任务 </div>
</template>
@@ -98,17 +62,11 @@
<script setup lang="ts">
import { computed, ref } from 'vue'
import {
qcBizTypeLabel,
workOrderStatusLabel,
taskTypeLabel,
taskTypeTagType,
taskStatusLabel,
qcTaskConclusionLabel,
qcTaskConclusionTagType
} from '@/utils/qc-labels'
import { workOrderStatusTagType, taskStatusTagType } from '@/utils/status-tag-registry'
import { qcBizTypeLabel, workOrderStatusLabel } from '@/utils/qc-labels'
import { workOrderStatusTagType } from '@/utils/status-tag-registry'
import type { QcInspectionBatch } from '@/utils/qc-inspection-batch'
import QcTaskViewDialog from '@/components/business/qc-task-view-dialog/index.vue'
import SampleQcBatchReadonly from '@/components/business/sample-qc-batch-readonly/index.vue'
export type SampleQcTask = {
id?: string
@@ -124,9 +82,11 @@
conclusionLabel?: string
completedTime?: string
remark?: string
batchId?: string
}
export type SampleQcInfo = {
id?: string
workOrderNo?: string
workOrderStatus?: string
workOrderStatusLabel?: string
@@ -147,6 +107,7 @@
itemName?: string
companyName?: string
}>
inspectionBatches?: QcInspectionBatch[]
tasks?: SampleQcTask[]
}
@@ -172,6 +133,11 @@
const taskViewVisible = ref(false)
const taskViewId = ref('')
const hasTaskContent = computed(
() =>
!!(props.qcInfo?.inspectionBatches?.length || props.qcInfo?.tasks?.length)
)
const emptyHint = computed(() => {
const rec = props.sampleReception
if (!rec) {
@@ -236,74 +202,6 @@
min-width: 140px;
}
.qc-detail-cards {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
gap: 10px;
&--tasks {
margin-bottom: 0;
}
}
.qc-detail-card {
border: 1px solid #e2e8f0;
border-radius: 8px;
background: #fff;
overflow: hidden;
}
.qc-detail-card__header {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 8px;
padding: 10px 12px;
border-bottom: 1px solid #f1f5f9;
}
.qc-detail-card__name {
font-size: 13px;
font-weight: 600;
color: #1e293b;
text-align: left;
flex: 1;
word-break: break-word;
}
.qc-detail-card__body {
padding: 10px 12px;
display: flex;
flex-direction: column;
gap: 8px;
}
.qc-detail-card__foot {
padding: 2px 12px 8px;
text-align: right;
border-top: 1px dashed #f1f5f9;
}
.qc-detail-card__link {
padding: 0;
border: none;
background: none;
font-size: 11px;
line-height: 1.4;
color: #047857;
cursor: pointer;
&:hover {
color: #065f46;
text-decoration: underline;
}
}
.qc-kv__muted {
color: var(--mint-text-muted, #334155);
font-weight: 400;
}
.qc-kv {
display: flex;
align-items: flex-start;
@@ -4,8 +4,12 @@
<ArtSvgIcon :icon="item.meta.icon" :color="theme?.iconColor" class="mr-1 text-lg" />
<span class="text-md">{{ formatMenuTitle(item.meta.title) }}</span>
<div v-if="item.meta.showBadge" class="art-badge art-badge-horizontal" />
<div v-if="item.meta.showTextBadge" class="art-text-badge">
{{ item.meta.showTextBadge }}
<div
v-if="resolveMenuTextBadge(item)"
class="art-text-badge"
:class="menuTextBadgeClass(item)"
>
{{ resolveMenuTextBadge(item) }}
</div>
</template>
@@ -38,8 +42,8 @@
class="art-badge"
:style="{ right: level === 0 ? '10px' : '20px' }"
/>
<div v-if="item.meta.showTextBadge && level !== 0" class="art-text-badge">
{{ item.meta.showTextBadge }}
<div v-if="resolveMenuTextBadge(item)" class="art-text-badge" :class="menuTextBadgeClass(item)">
{{ resolveMenuTextBadge(item) }}
</div>
</ElMenuItem>
</template>
@@ -49,6 +53,12 @@
import { AppRouteRecord } from '@/types/router'
import { handleMenuJump } from '@/utils/navigation'
import { formatMenuTitle } from '@/utils/router'
import { storeToRefs } from 'pinia'
import { useQcMenuBadgeStore } from '@/store/modules/qc-menu-badge'
import { useProductionMenuBadgeStore } from '@/store/modules/production-menu-badge'
import { resolveQcMenuBadgeCode } from '@/utils/qc-menu-badge'
import { resolveProductionMenuBadgeCode } from '@/utils/production-menu-badge'
import { formatQcWorkOrderTabBadgeCount } from '@/api/qc-work-order'
const props = defineProps({
item: {
@@ -67,6 +77,41 @@
})
const emit = defineEmits(['close'])
const qcMenuBadgeStore = useQcMenuBadgeStore()
const productionMenuBadgeStore = useProductionMenuBadgeStore()
const { loadedMenuCodes, badgeByMenuCode } = storeToRefs(qcMenuBadgeStore)
const { loadedMenuCodes: productionLoadedMenuCodes } = storeToRefs(productionMenuBadgeStore)
function resolveMenuTextBadge(item: AppRouteRecord): string {
const productionMenuCode = resolveProductionMenuBadgeCode(item)
if (productionMenuCode && productionLoadedMenuCodes.value.includes(productionMenuCode)) {
return productionMenuBadgeStore.getBadgeText(productionMenuCode)
}
const menuCode = resolveQcMenuBadgeCode(item)
if (menuCode && loadedMenuCodes.value.includes(menuCode)) {
const count = badgeByMenuCode.value[menuCode] ?? 0
return formatQcWorkOrderTabBadgeCount(count)
}
if (item.meta.showTextBadge && props.level !== 0) {
return item.meta.showTextBadge
}
return ''
}
function menuTextBadgeClass(item: AppRouteRecord): Record<string, boolean> {
const productionMenuCode = resolveProductionMenuBadgeCode(item)
if (productionMenuCode && productionLoadedMenuCodes.value.includes(productionMenuCode)) {
return {
'art-text-badge--zero': productionMenuBadgeStore.isBadgeZero(productionMenuCode),
'art-text-badge--dual': true
}
}
const menuCode = resolveQcMenuBadgeCode(item)
if (menuCode && loadedMenuCodes.value.includes(menuCode)) {
return { 'art-text-badge--zero': (badgeByMenuCode.value[menuCode] ?? 0) === 0 }
}
return {}
}
// 过滤后的子菜单项(不包含隐藏的)
const filteredChildren = computed(() => {
@@ -157,6 +157,7 @@ $popup-menu-radius: 6px;
// 菜单高度
.el-sub-menu__title,
.el-menu-item {
position: relative;
display: flex !important;
align-items: center;
min-width: 0;
@@ -165,6 +166,15 @@ $popup-menu-radius: 6px;
line-height: $menu-height !important;
overflow: hidden !important;
.menu-title-wrap {
position: relative;
display: flex;
flex: 1;
align-items: center;
min-width: 0;
padding-right: 28px;
}
.menu-name {
flex-shrink: 1;
min-width: 0;
@@ -54,12 +54,18 @@
/>
<template #title>
<span class="menu-name">
{{ formatMenuTitle(item.meta.title) }}
</span>
<div v-if="item.meta.showBadge" class="art-badge" />
<div v-if="item.meta.showTextBadge && (level > 0 || menuOpen)" class="art-text-badge">
{{ item.meta.showTextBadge }}
<div class="menu-title-wrap">
<span class="menu-name">
{{ formatMenuTitle(item.meta.title) }}
</span>
<div v-if="item.meta.showBadge" class="art-badge" />
<div
v-if="resolveMenuTextBadge(item)"
class="art-text-badge"
:class="menuTextBadgeClass(item)"
>
{{ resolveMenuTextBadge(item) }}
</div>
</div>
</template>
</ElMenuItem>
@@ -73,6 +79,11 @@
import { filterSidebarRoutes } from '@/utils/router/sidebar-menu-filter'
import { handleMenuJump } from '@/utils/navigation'
import { useSettingStore } from '@/store/modules/setting'
import { useQcMenuBadgeStore } from '@/store/modules/qc-menu-badge'
import { useProductionMenuBadgeStore } from '@/store/modules/production-menu-badge'
import { resolveQcMenuBadgeCode } from '@/utils/qc-menu-badge'
import { resolveProductionMenuBadgeCode } from '@/utils/production-menu-badge'
import { formatQcWorkOrderTabBadgeCount } from '@/api/qc-work-order'
interface MenuTheme {
iconColor?: string
@@ -107,8 +118,12 @@
const emit = defineEmits<Emits>()
const settingStore = useSettingStore()
const qcMenuBadgeStore = useQcMenuBadgeStore()
const productionMenuBadgeStore = useProductionMenuBadgeStore()
const { menuOpen } = storeToRefs(settingStore)
const { loadedMenuCodes, badgeByMenuCode } = storeToRefs(qcMenuBadgeStore)
const { loadedMenuCodes: productionLoadedMenuCodes } = storeToRefs(productionMenuBadgeStore)
/**
* 过滤后的菜单项列表
@@ -116,6 +131,38 @@
*/
const filteredMenuItems = computed(() => filterSidebarRoutes(props.list || []))
function resolveMenuTextBadge(item: AppRouteRecord): string {
const productionMenuCode = resolveProductionMenuBadgeCode(item)
if (productionMenuCode && productionLoadedMenuCodes.value.includes(productionMenuCode)) {
return productionMenuBadgeStore.getBadgeText(productionMenuCode)
}
const menuCode = resolveQcMenuBadgeCode(item)
if (menuCode && loadedMenuCodes.value.includes(menuCode)) {
const count = badgeByMenuCode.value[menuCode] ?? 0
return formatQcWorkOrderTabBadgeCount(count)
}
const staticBadge = item.meta.showTextBadge
if (staticBadge && (props.level > 0 || menuOpen.value)) {
return staticBadge
}
return ''
}
function menuTextBadgeClass(item: AppRouteRecord): Record<string, boolean> {
const productionMenuCode = resolveProductionMenuBadgeCode(item)
if (productionMenuCode && productionLoadedMenuCodes.value.includes(productionMenuCode)) {
return {
'art-text-badge--zero': productionMenuBadgeStore.isBadgeZero(productionMenuCode),
'art-text-badge--dual': true
}
}
const menuCode = resolveQcMenuBadgeCode(item)
if (menuCode && loadedMenuCodes.value.includes(menuCode)) {
return { 'art-text-badge--zero': (badgeByMenuCode.value[menuCode] ?? 0) === 0 }
}
return {}
}
/**
* 跳转到指定页面
* @param item 菜单项数据