You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

283 lines
7.0 KiB

<script lang="ts" setup>
import { Card, Dropdown, Menu, Popconfirm } from 'ant-design-vue'
import type { PropType } from 'vue'
import { ref, watch } from 'vue'
import type { ModelItem } from './data'
import CreateModel from './CreateModel.vue'
import Icon from '@/components/Icon/index'
import { modelCardListApi, modelDeleteApi } from '@/api/alert/model/models'
import type { ModelQueryParams } from '@/api/alert/model/model/models'
import { useGo } from '@/hooks/web/usePage'
import { useDrawer } from '@/components/Drawer'
import { useMessage } from '@/hooks/web/useMessage'
const props = defineProps({
loading: {
type: Boolean,
},
selectData: {
type: Object as PropType<Record<string, any> | null | undefined>,
},
systemId: {
type: Number,
},
})
const [registerDraw, { openDrawer }] = useDrawer()
const { createMessage } = useMessage()
// 点击模型卡片跳转训练页面
const go = useGo()
function changeModel(id, version?) {
const versionParam = version ? `?version=${encodeURIComponent(version)}` : ''
go(`/model/train/${id}${versionParam}`)
}
const modelCardList = ref<Array<ModelItem>>([])
const lastQuery = ref<ModelQueryParams | null>(null)
const colors = ['#8dc63f', '#dbb09e']
const statusStr = ['未下装', '已下装']
const statusIcons = ['material-symbols:lock-open', 'material-symbols:lock']
function colorToBg(hex: string, alpha = 0.5) {
const raw = (hex || '').replace('#', '')
if (raw.length !== 6)
return 'rgba(0,0,0,0.5)'
const r = Number.parseInt(raw.slice(0, 2), 16)
const g = Number.parseInt(raw.slice(2, 4), 16)
const b = Number.parseInt(raw.slice(4, 6), 16)
return `rgba(${r}, ${g}, ${b}, ${alpha})`
}
function buildQuery(value: any): ModelQueryParams {
return {
unitId: value?.unit,
typeId: value?.type,
systemId: value?.system,
name: value?.name,
}
}
async function loadModelList(value: any) {
const queryParams = buildQuery(value)
lastQuery.value = queryParams
const modelList = await modelCardListApi(queryParams)
if (!modelList) {
modelCardList.value = []
return
}
const cardList: ModelItem[] = []
for (const modelCard of modelList) {
const statusIndex = Number(modelCard.status) || 0
const card: ModelItem = {
id: modelCard.id,
title: modelCard.name,
version: modelCard.version,
icon: statusIcons[statusIndex],
value: 1,
total: 1,
color: colors[statusIndex],
status: statusStr[statusIndex],
creator: modelCard.creator,
createTime: modelCard.createTime,
description: modelCard.name,
headStyle: {},
bodyStyle: {},
statusColor: colors[statusIndex],
cardBg: colorToBg(colors[statusIndex]),
}
cardList.push(card)
}
modelCardList.value = cardList
}
watch(
() => props.selectData,
async (value) => {
await loadModelList(value)
},
{ immediate: true },
)
async function confirmDelete(id: number | string) {
await modelDeleteApi(id)
createMessage.success('删除成功')
await loadModelList(props.selectData)
}
</script>
<template>
<div class="enter-y">
<div class="card-grid">
<template v-for="item in modelCardList" :key="item.title">
<Dropdown :trigger="['contextmenu']">
<Card
size="small"
:loading="loading"
:hoverable="true"
class="model-card"
:style="{ backgroundColor: item.cardBg }"
@click="changeModel(item.id, item.version)"
>
<div class="card-top">
<div class="card-title">
{{ item.title }}
</div>
<div class="card-tags">
<span class="status-icon" :style="{ color: item.statusColor }">
<Icon :icon="item.icon" :size="20" />
</span>
</div>
</div>
<div class="card-meta">
<span class="meta-item">
<Icon icon="ic:baseline-person" :size="16" class="meta-icon" />
{{ item.creator || '未知' }}
</span>
<span class="meta-item">
<Icon icon="solar:calendar-bold" :size="16" class="meta-icon" />
{{ item.createTime || '--' }}
</span>
</div>
<div class="card-footer">
<span class="footer-text">版本</span>
<span class="footer-text strong">{{ item.version || 'v-test' }}</span>
</div>
</Card>
<template #overlay>
<Menu>
<Menu.Item key="delete">
<Popconfirm
title="确认删除该模型?"
ok-text="删除"
ok-type="danger"
@confirm="() => confirmDelete(item.id)"
>
<span>删除</span>
</Popconfirm>
</Menu.Item>
</Menu>
</template>
</Dropdown>
</template>
<Card v-show="systemId != null" size="small" class="icon-card" :hoverable="true" @click="openDrawer(true)">
<Icon icon="ic:sharp-add" :size="80" color="#a7a9a7" />
</Card>
</div>
<CreateModel :system-id="systemId" @register="registerDraw" />
</div>
</template>
<style scoped>
.card-grid {
display: grid;
grid-template-columns: repeat(4, minmax(0, 1fr));
gap: 20px;
}
.model-card {
padding: 12px;
border: 1px solid rgb(0 0 0 / 2%);
border-radius: 12px;
box-shadow: 0 6px 18px rgb(0 0 0 / 6%);
transition: transform 0.2s ease, box-shadow 0.2s ease;
}
.model-card:hover {
box-shadow: 0 12px 28px rgb(0 0 0 / 10%);
transform: translateY(-2px);
}
.card-top {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 8px;
}
.card-title {
padding-right: 8px;
overflow: hidden;
font-size: 18px;
font-weight: 600;
line-height: 1.4;
color: #1f2937;
text-overflow: ellipsis;
word-break: keep-all;
white-space: nowrap;
}
.card-tags {
display: inline-flex;
gap: 8px;
align-items: center;
}
.status-icon {
display: inline-flex;
align-items: center;
justify-content: center;
width: 32px;
height: 32px;
background-color: rgb(0 0 0 / 3%);
border-radius: 8px;
}
.card-meta {
display: flex;
gap: 8px;
align-items: center;
justify-content: space-between;
margin-bottom: 12px;
font-size: 12px;
color: #6b7280;
}
.meta-item {
display: inline-flex;
gap: 6px;
align-items: center;
min-width: 0;
}
.meta-icon {
color: #9ca3af;
}
.card-footer {
display: flex;
gap: 8px;
align-items: center;
padding-top: 10px;
font-size: 13px;
color: #4b5563;
border-top: 1px dashed #e5e7eb;
}
.footer-text.strong {
font-weight: 600;
}
.footer-divider {
flex: 1;
height: 1px;
border-top: 1px dashed #e5e7eb;
}
.icon-card {
display: flex;
align-items: center;
justify-content: center;
min-height: 180px;
background-color: #f3f4f6;
border: 1px dashed #d1d5db;
border-radius: 12px;
transition: border-color 0.2s ease, background 0.2s ease;
}
.icon-card:hover {
background-color: #eef2ff;
border-color: #4c7af0;
}
</style>