<script lang="ts" setup>
|
import type { MesMdWorkstationApi } from '#/api/mes/md/workstation';
|
|
import { computed, ref } from 'vue';
|
|
import { IconifyIcon } from '@vben/icons';
|
|
import { Input, Modal, Spin } from 'ant-design-vue';
|
|
import { getWorkstationPage } from '#/api/mes/md/workstation';
|
|
defineOptions({ name: 'WorkbenchWorkstationSelectDialog' });
|
|
const emit = defineEmits<{
|
select: [workstation: MesMdWorkstationApi.Workstation];
|
}>();
|
|
const visible = ref(false);
|
const loading = ref(false);
|
const workstations = ref<MesMdWorkstationApi.Workstation[]>([]);
|
const searchKeyword = ref('');
|
|
/** 打开弹窗 */
|
async function open() {
|
visible.value = true;
|
searchKeyword.value = '';
|
await loadWorkstations();
|
}
|
|
/** 加载工作站列表 */
|
async function loadWorkstations() {
|
loading.value = true;
|
try {
|
const result = await getWorkstationPage({ pageNo: 1, pageSize: 200 });
|
workstations.value = result.list ?? [];
|
} finally {
|
loading.value = false;
|
}
|
}
|
|
/** 过滤后的工作站 */
|
const filtered = computed(() => {
|
const kw = searchKeyword.value.trim().toLowerCase();
|
if (!kw) return workstations.value;
|
return workstations.value.filter((ws) => {
|
const fields = [ws.code, ws.name, ws.processName, ws.workshopName, ws.address];
|
return fields.some((f) => String(f ?? '').toLowerCase().includes(kw));
|
});
|
});
|
|
/** 选择工作站 */
|
function handleSelect(ws: MesMdWorkstationApi.Workstation) {
|
emit('select', ws);
|
visible.value = false;
|
}
|
|
defineExpose({ open });
|
</script>
|
|
<template>
|
<Modal
|
v-model:open="visible"
|
title="选择工作站"
|
:footer="null"
|
width="900px"
|
:body-style="{ padding: '16px 20px', maxHeight: '70vh', overflow: 'auto' }"
|
centered
|
destroy-on-close
|
>
|
<!-- 搜索框 -->
|
<div class="ws-select__search">
|
<Input
|
v-model:value="searchKeyword"
|
placeholder="搜索工作站编码/名称/工序/车间"
|
allow-clear
|
>
|
<template #prefix>
|
<IconifyIcon icon="lucide:search" class="text-gray-400" />
|
</template>
|
</Input>
|
</div>
|
|
<Spin :spinning="loading">
|
<!-- 空状态 -->
|
<div v-if="!filtered.length && !loading" class="ws-select__empty">
|
<IconifyIcon icon="lucide:inbox" class="ws-select__empty-icon" />
|
<p>暂无工作站数据</p>
|
</div>
|
|
<!-- 卡片网格 -->
|
<div v-else class="ws-select__grid">
|
<button
|
v-for="ws in filtered"
|
:key="ws.id"
|
type="button"
|
class="ws-card"
|
@click="handleSelect(ws)"
|
>
|
<div class="ws-card__head">
|
<span class="ws-card__code">{{ ws.code }}</span>
|
<span v-if="ws.processName" class="ws-card__process">
|
{{ ws.processName }}
|
</span>
|
</div>
|
<div class="ws-card__name">{{ ws.name }}</div>
|
<div class="ws-card__info">
|
<div v-if="ws.workshopName" class="ws-card__info-row">
|
<IconifyIcon icon="lucide:building-2" class="ws-card__info-icon" />
|
<span>{{ ws.workshopName }}</span>
|
</div>
|
<div v-if="ws.address" class="ws-card__info-row">
|
<IconifyIcon icon="lucide:map-pin" class="ws-card__info-icon" />
|
<span>{{ ws.address }}</span>
|
</div>
|
</div>
|
<div class="ws-card__action">
|
<IconifyIcon icon="lucide:pointer" />
|
点击选择
|
</div>
|
</button>
|
</div>
|
</Spin>
|
</Modal>
|
</template>
|
|
<style lang="scss" scoped>
|
.ws-select__search {
|
margin-bottom: 16px;
|
}
|
|
.ws-select__empty {
|
display: flex;
|
flex-direction: column;
|
align-items: center;
|
gap: 8px;
|
padding: 48px 0;
|
color: #8c8c8c;
|
|
p {
|
margin: 0;
|
font-size: 15px;
|
}
|
}
|
|
.ws-select__empty-icon {
|
font-size: 40px;
|
opacity: 0.5;
|
}
|
|
.ws-select__grid {
|
display: grid;
|
grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
|
gap: 16px;
|
}
|
|
.ws-card {
|
display: flex;
|
flex-direction: column;
|
gap: 10px;
|
padding: 16px;
|
border: 2px solid #e8e8e8;
|
border-radius: 12px;
|
text-align: left;
|
cursor: pointer;
|
background: linear-gradient(
|
145deg,
|
#eff6ff 0%,
|
#dbeafe 52%,
|
#bfdbfe 100%
|
);
|
transition:
|
transform 0.18s ease,
|
box-shadow 0.18s ease,
|
border-color 0.18s ease;
|
|
&:hover {
|
transform: translateY(-2px);
|
border-color: #3b82f6;
|
box-shadow: 0 8px 24px rgb(59 130 246 / 20%);
|
}
|
|
&:active {
|
transform: translateY(0);
|
}
|
}
|
|
.ws-card__head {
|
display: flex;
|
align-items: center;
|
justify-content: space-between;
|
gap: 8px;
|
}
|
|
.ws-card__code {
|
font-size: 22px;
|
font-weight: 800;
|
letter-spacing: 0.03em;
|
color: #1e40af;
|
line-height: 1.1;
|
}
|
|
.ws-card__process {
|
flex-shrink: 0;
|
padding: 4px 10px;
|
border-radius: 999px;
|
font-size: 12px;
|
font-weight: 700;
|
background: #3b82f6;
|
color: #fff;
|
}
|
|
.ws-card__name {
|
font-size: 15px;
|
font-weight: 600;
|
color: #1e3a5f;
|
line-height: 1.3;
|
}
|
|
.ws-card__info {
|
display: flex;
|
flex-direction: column;
|
gap: 4px;
|
margin-top: auto;
|
}
|
|
.ws-card__info-row {
|
display: flex;
|
align-items: center;
|
gap: 6px;
|
font-size: 13px;
|
color: #475569;
|
}
|
|
.ws-card__info-icon {
|
font-size: 14px;
|
color: #64748b;
|
flex-shrink: 0;
|
}
|
|
.ws-card__action {
|
display: flex;
|
align-items: center;
|
justify-content: center;
|
gap: 6px;
|
padding: 8px;
|
border-radius: 8px;
|
background: rgb(59 130 246 / 12%);
|
border: 1px solid rgb(59 130 246 / 20%);
|
font-size: 14px;
|
font-weight: 600;
|
color: #2563eb;
|
transition: all 0.2s;
|
|
.ws-card:hover & {
|
background: rgb(59 130 246 / 20%);
|
}
|
}
|
</style>
|