| | |
| | | <script lang="ts" setup> |
| | | import { onMounted, ref } from 'vue'; |
| | | import { computed, onMounted, ref } from 'vue'; |
| | | |
| | | import { CountTo } from '@vben/common-ui'; |
| | | import { IconifyIcon } from '@vben/icons'; |
| | |
| | | import { getMaterialStockPage } from '#/api/mes/wm/materialstock'; |
| | | import { getTaskTodoPage } from '#/api/bpm/task'; |
| | | |
| | | import { useHomeAccess } from '#/utils/use-home-access'; |
| | | |
| | | defineOptions({ name: 'DashboardBizCards' }); |
| | | |
| | | const emit = defineEmits<{ |
| | | navigate: [name: string]; |
| | | }>(); |
| | | |
| | | const { canQuery, canSeeMenu } = useHomeAccess(); |
| | | |
| | | const canViewPurchase = computed( |
| | | () => canQuery('erp:purchase-order:query') && canSeeMenu('ErpPurchaseOrder'), |
| | | ); |
| | | const canViewSale = computed( |
| | | () => canQuery('erp:sale-order:query') && canSeeMenu('ErpSaleOrder'), |
| | | ); |
| | | const canViewStock = computed( |
| | | () => canQuery('mes:wm-material-stock:query') && canSeeMenu('MesWmMaterialStock'), |
| | | ); |
| | | const canViewBpmTodo = computed( |
| | | () => canQuery('bpm:task:query') && canSeeMenu('BpmTodoTask'), |
| | | ); |
| | | |
| | | /** 可见卡片数量决定列宽,避免只剩一两张时挤在左上角 */ |
| | | const colSpan = computed(() => { |
| | | const count = [ |
| | | canViewPurchase.value, |
| | | canViewSale.value, |
| | | canViewStock.value, |
| | | canViewBpmTodo.value, |
| | | ].filter(Boolean).length; |
| | | if (count >= 4) return 6; |
| | | if (count === 3) return 8; |
| | | if (count === 2) return 12; |
| | | return 24; |
| | | }); |
| | | |
| | | const purchaseCount = ref(0); |
| | | const saleCount = ref(0); |
| | |
| | | const bpmTodoCount = ref(0); |
| | | |
| | | async function loadPurchase() { |
| | | if (!canViewPurchase.value) return; |
| | | try { |
| | | const result = await getPurchaseOrderPage({ pageNo: 1, pageSize: 1 }); |
| | | purchaseCount.value = result.total ?? 0; |
| | |
| | | } |
| | | |
| | | async function loadSale() { |
| | | if (!canViewSale.value) return; |
| | | try { |
| | | const result = await getSaleOrderPage({ pageNo: 1, pageSize: 1 }); |
| | | saleCount.value = result.total ?? 0; |
| | |
| | | } |
| | | |
| | | async function loadStock() { |
| | | if (!canViewStock.value) return; |
| | | try { |
| | | const result = await getMaterialStockPage({ pageNo: 1, pageSize: 1 }); |
| | | stockCount.value = result.total ?? 0; |
| | |
| | | } |
| | | |
| | | async function loadBpmTodo() { |
| | | if (!canViewBpmTodo.value) return; |
| | | try { |
| | | const result = await getTaskTodoPage({ pageNo: 1, pageSize: 1 }); |
| | | bpmTodoCount.value = result.total ?? 0; |
| | |
| | | <template> |
| | | <Row :gutter="16"> |
| | | <!-- 采购订单 --> |
| | | <Col :lg="6" :md="12" :sm="12" :xl="6" :xs="24" class="mb-4"> |
| | | <Col |
| | | v-if="canViewPurchase" |
| | | :lg="colSpan" |
| | | :md="colSpan" |
| | | :sm="colSpan" |
| | | :xl="colSpan" |
| | | :xs="24" |
| | | class="mb-4" |
| | | > |
| | | <div |
| | | class="glass-tilt group cursor-pointer overflow-hidden rounded-2xl border-0" |
| | | :style="tiltStyle('purchase')" |
| | |
| | | </Col> |
| | | |
| | | <!-- 销售订单 --> |
| | | <Col :lg="6" :md="12" :sm="12" :xl="6" :xs="24" class="mb-4"> |
| | | <Col |
| | | v-if="canViewSale" |
| | | :lg="colSpan" |
| | | :md="colSpan" |
| | | :sm="colSpan" |
| | | :xl="colSpan" |
| | | :xs="24" |
| | | class="mb-4" |
| | | > |
| | | <div |
| | | class="glass-tilt group cursor-pointer overflow-hidden rounded-2xl border-0" |
| | | :style="tiltStyle('sale')" |
| | |
| | | </Col> |
| | | |
| | | <!-- 物料库存 --> |
| | | <Col :lg="6" :md="12" :sm="12" :xl="6" :xs="24" class="mb-4"> |
| | | <Col |
| | | v-if="canViewStock" |
| | | :lg="colSpan" |
| | | :md="colSpan" |
| | | :sm="colSpan" |
| | | :xl="colSpan" |
| | | :xs="24" |
| | | class="mb-4" |
| | | > |
| | | <div |
| | | class="glass-tilt group cursor-pointer overflow-hidden rounded-2xl border-0" |
| | | :style="tiltStyle('stock')" |
| | |
| | | </Col> |
| | | |
| | | <!-- BPM 待办 --> |
| | | <Col :lg="6" :md="12" :sm="12" :xl="6" :xs="24" class="mb-4"> |
| | | <Col |
| | | v-if="canViewBpmTodo" |
| | | :lg="colSpan" |
| | | :md="colSpan" |
| | | :sm="colSpan" |
| | | :xl="colSpan" |
| | | :xs="24" |
| | | class="mb-4" |
| | | > |
| | | <div |
| | | class="glass-tilt group cursor-pointer overflow-hidden rounded-2xl border-0" |
| | | :style="tiltStyle('bpm')" |
| | | @mousemove="(e: MouseEvent) => onMouseMove(e, 'bpm')" |
| | | @mouseleave="onMouseLeave('bpm')" |
| | | @click="emit('navigate', 'BpmTaskTodo')" |
| | | @click="emit('navigate', 'BpmTodoTask')" |
| | | > |
| | | <div class="relative z-10 flex items-center gap-4 p-5"> |
| | | <div class="flex size-12 flex-shrink-0 items-center justify-center rounded-xl bg-gradient-to-br from-red-500 to-rose-400 shadow-lg shadow-red-500/25 transition-transform duration-300 group-hover:scale-110"> |