feat(material): add global floating helper to track latest specification codes with smart grouping

This commit is contained in:
DXC
2026-04-13 08:28:27 +08:00
parent e23e8c6a9e
commit c7ac092be4
5 changed files with 298 additions and 1 deletions

View File

@ -69,4 +69,12 @@ export function batchSetInspection(data: { ids: number[], isInspectionRequired:
method: 'post',
data
})
}
// 7. 获取智能分组规格最大连号
export function getLatestSpecs() {
return request({
url: '/inbound/base/spec-latest',
method: 'get'
})
}

View File

@ -0,0 +1,200 @@
<template>
<div class="spec-helper" :class="{ expanded }">
<!-- 触发按钮 -->
<div class="trigger-btn" @click="toggle">
<span class="arrow">{{ expanded ? '>' : '<' }}</span>
</div>
<!-- 面板内容 -->
<div class="panel">
<div class="panel-header">
<span class="title">规格连号助手</span>
<el-input
v-model="filterText"
placeholder="搜索分组或规格..."
clearable
size="small"
class="search-input"
>
<template #prefix>
<el-icon><Search /></el-icon>
</template>
</el-input>
</div>
<el-scrollbar class="data-list">
<div
v-for="item in filteredData"
:key="item.group"
class="data-item"
>
<span class="group-tag">{{ item.group }}</span>
<span class="latest-spec">{{ item.latest }}</span>
</div>
<el-empty v-if="filteredData.length === 0" description="暂无数据" :image-size="60" />
</el-scrollbar>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, computed, onMounted } from 'vue'
import { Search } from '@element-plus/icons-vue'
import { getLatestSpecs } from '@/api/material_base'
import { ElMessage } from 'element-plus'
interface SpecItem {
group: string
latest: string
}
const expanded = ref(false)
const filterText = ref('')
const specData = ref<SpecItem[]([])
const toggle = () => {
expanded.value = !expanded.value
if (expanded.value && specData.value.length === 0) {
fetchData()
}
}
const fetchData = async () => {
try {
const res = await getLatestSpecs()
if (res.data.code === 200) {
specData.value = res.data.data || []
} else {
ElMessage.error(res.data.msg || '获取规格数据失败')
}
} catch (error) {
console.error('获取规格数据失败:', error)
ElMessage.error('获取规格数据失败')
}
}
const filteredData = computed(() => {
if (!filterText.value) {
return specData.value
}
const keyword = filterText.value.toLowerCase()
return specData.value.filter(
item =>
item.group.toLowerCase().includes(keyword) ||
item.latest.toLowerCase().includes(keyword)
)
})
onMounted(() => {
// 默认不加载,展开时再加载
})
</script>
<style scoped>
.spec-helper {
position: fixed;
right: 0;
top: 50%;
transform: translateY(-50%);
z-index: 9999;
display: flex;
align-items: center;
transition: transform 0.3s ease;
}
.spec-helper:not(.expanded) {
transform: translateY(-50%) translateX(calc(100% - 24px));
}
.trigger-btn {
width: 24px;
height: 60px;
background: #409eff;
color: white;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
border-radius: 4px 0 0 4px;
flex-shrink: 0;
}
.trigger-btn:hover {
background: #66b1ff;
}
.arrow {
font-size: 14px;
font-weight: bold;
}
.panel {
width: 280px;
height: 400px;
background: white;
border-radius: 4px 0 0 4px;
box-shadow: -2px 0 8px rgba(0, 0, 0, 0.15);
display: flex;
flex-direction: column;
overflow: hidden;
}
.panel-header {
padding: 12px;
border-bottom: 1px solid #ebeef5;
display: flex;
flex-direction: column;
gap: 8px;
}
.title {
font-size: 14px;
font-weight: 600;
color: #303133;
}
.search-input {
width: 100%;
}
.data-list {
flex: 1;
overflow-y: auto;
}
.data-item {
padding: 8px 12px;
display: flex;
align-items: center;
justify-content: space-between;
border-bottom: 1px solid #f5f7fa;
cursor: default;
}
.data-item:hover {
background: #f5f7fa;
}
.group-tag {
font-size: 12px;
font-weight: 500;
color: #409eff;
background: #ecf5ff;
padding: 2px 6px;
border-radius: 3px;
max-width: 80px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.latest-spec {
font-size: 13px;
color: #303133;
font-family: monospace;
max-width: 140px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
</style>

View File

@ -5,12 +5,16 @@
<div class="main-container">
<AppMain />
</div>
<!-- 全局规格连号助手 -->
<SpecHelper />
</div>
</template>
<script setup lang="ts">
import Sidebar from './components/Sidebar/index.vue'
import AppMain from './components/AppMain.vue'
import SpecHelper from '@/components/SpecHelper/index.vue'
</script>
<style scoped>