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

354 lines
8.3 KiB
Vue

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<template>
<view class="page-container">
<NavBar :title="t('moldOperate.selectMountMold')" />
<!-- 搜索区 -->
<view class="search-bar">
<view class="search-input-wrap">
<text class="search-icon iconfont icon-search"></text>
<input class="search-input" v-model="searchText" :placeholder="t('moldOperate.searchName')" @input="onSearch" placeholder-class="search-placeholder" />
<text v-if="searchText" class="search-clear" @click="clearSearch">✕</text>
</view>
</view>
<!-- 模具列表 -->
<scroll-view scroll-y class="mold-list" v-if="filteredList.length > 0">
<view
v-for="mold in filteredList"
:key="mold.id"
class="mold-card"
:class="{ active: selectedIds.has(String(mold.id)) }"
@click="toggleMold(mold)"
>
<view class="mold-card-header">
<text class="mold-name">{{ textValue(mold.name) }}</text>
<text class="mold-check-icon" v-if="selectedIds.has(String(mold.id))">&#10003;</text>
</view>
<view class="mold-card-body">
<view class="info-row">
<text class="info-label">{{ t('moldOperate.moldCode') }}</text>
<text class="info-value">{{ textValue(mold.code) }}</text>
</view>
<view class="info-row">
<text class="info-label">产品型号</text>
<text class="info-value">{{ textValue(mold.productName) }}</text>
</view>
<view class="info-row">
<text class="info-label">{{ t('moldOperate.status') }}</text>
<view :class="['status-tag', getStatusClass(mold.status)]">{{ getStatusText(mold.status) }}</view>
</view>
</view>
</view>
</scroll-view>
<!-- 空状态 -->
<view v-else class="empty-wrap">
<text v-if="loading" class="empty-text">{{ t('functionCommon.loading') }}</text>
<text v-else class="empty-text">{{ t('moldOperate.noMoldData') }}</text>
</view>
<!-- 底部确认按钮 -->
<view class="bottom-actions">
<view class="bottom-btn confirm-btn" @click="handleConfirm">
{{ t('functionCommon.confirm') }}{{ selectedIds.size > 0 ? `(${selectedIds.size})` : '' }}
</view>
</view>
</view>
</template>
<script setup>
import { ref, computed } from 'vue'
import { onShow } from '@dcloudio/uni-app'
import { useI18n } from 'vue-i18n'
import NavBar from '@/components/common/NavBar.vue'
import { getMoldBrandPage } from '@/api/mes/mold'
const { t } = useI18n()
const moldList = ref([])
const selectedIds = ref(new Set())
const searchText = ref('')
const loading = ref(false)
function textValue(v) {
if (v === 0) return '0'
if (v == null) return '-'
const s = String(v).trim()
return s || '-'
}
const STATUS_MAP = { 0: '在机', 1: '待用', 2: '维修', 3: '报废', 4: '在库' }
function getStatusText(s) { return STATUS_MAP[s] || textValue(s) }
function getStatusClass(s) {
if (s === 0) return 'in-use-tag'
if (s === 2) return 'repairing-tag'
if (s === 3) return 'scrapped-tag'
return 'standby-tag'
}
const filteredList = computed(() => {
// 排除在机状态的模具status=0不能重复上模
const available = moldList.value.filter(m => Number(m.status) !== 0)
const keyword = searchText.value.trim().toLowerCase()
if (!keyword) return available
return available.filter((m) => {
return (m.name || '').toLowerCase().includes(keyword) ||
(m.code || '').toLowerCase().includes(keyword) ||
(m.productName || '').toLowerCase().includes(keyword)
})
})
function onSearch() {}
function clearSearch() {
searchText.value = ''
}
async function loadMolds() {
loading.value = true
try {
const res = await getMoldBrandPage({ pageNo: 1, pageSize: 100 })
const root = (res && res.data !== undefined) ? res.data : res
// 递归查找任意嵌套的 list/rows/records
function findList(obj, depth = 0) {
if (!obj || typeof obj !== 'object' || depth > 3) return []
if (Array.isArray(obj)) return obj
for (const key of ['list', 'rows', 'records', 'items', 'data']) {
if (Array.isArray(obj[key])) return obj[key]
}
for (const val of Object.values(obj)) {
const found = findList(val, depth + 1)
if (found.length) return found
}
return []
}
moldList.value = findList(root)
// 保留一行关键日志用于排查
if (!moldList.value.length) console.error('moldSelect: 未找到模具数据', root)
} catch (e) {
console.error('loadMolds error', e)
} finally {
loading.value = false
}
}
function toggleMold(mold) {
const id = String(mold.id)
const set = new Set(selectedIds.value)
if (set.has(id)) {
set.delete(id)
} else {
// 上模每次只上一个模具,限制单选
set.clear()
set.add(id)
}
selectedIds.value = set
}
function handleConfirm() {
if (selectedIds.value.size === 0) {
uni.showToast({ title: t('moldOperate.validatorMoldRequired'), icon: 'none' })
return
}
const selected = moldList.value.filter((m) => selectedIds.value.has(String(m.id)))
.map((m) => ({
id: m.id,
name: m.name,
code: m.code,
productName: m.productName || '-',
status: m.status
}))
getApp().globalData._moldSelectResult = selected
uni.navigateBack()
}
onShow(async () => {
// 从 index.vue 传来的已选模具(编辑模式回显)
const preSelected = getApp().globalData._moldSelectPreSelected
if (preSelected && Array.isArray(preSelected)) {
selectedIds.value = new Set(preSelected.map((id) => String(id)))
getApp().globalData._moldSelectPreSelected = null
} else {
selectedIds.value = new Set()
}
await loadMolds()
})
</script>
<style lang="scss" scoped>
.page-container {
min-height: 100vh;
background: #f5f6f8;
padding-bottom: 140rpx;
}
/* 搜索栏 */
.search-bar {
padding: 16rpx 24rpx;
background: #fff;
}
.search-input-wrap {
display: flex;
align-items: center;
height: 72rpx;
padding: 0 16rpx;
background: #f5f6f8;
border-radius: 36rpx;
}
.search-icon {
font-size: 32rpx;
color: #999;
margin-right: 12rpx;
}
.search-input {
flex: 1;
font-size: 28rpx;
color: #333;
}
.search-placeholder {
color: #bbb;
}
.search-clear {
font-size: 28rpx;
color: #999;
padding: 8rpx;
}
/* 模具列表 */
.mold-list {
padding: 16rpx 24rpx;
}
.mold-card {
background: #fff;
border-radius: 14rpx;
padding: 24rpx;
margin-bottom: 16rpx;
border: 2rpx solid transparent;
&.active {
border-color: #2563eb;
background: #f0f5ff;
}
}
.mold-card-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 16rpx;
}
.mold-name {
font-size: 30rpx;
font-weight: 700;
color: #1a1a1a;
}
.mold-check-icon {
font-size: 32rpx;
color: #2563eb;
font-weight: 700;
flex-shrink: 0;
width: 40rpx;
height: 40rpx;
line-height: 36rpx;
text-align: center;
border: 2rpx solid #2563eb;
border-radius: 50%;
}
.mold-card-body {
border-top: 1rpx solid #f0f0f0;
padding-top: 16rpx;
}
.info-row {
display: flex;
align-items: center;
margin-bottom: 10rpx;
&:last-child {
margin-bottom: 0;
}
}
.info-label {
width: 140rpx;
font-size: 24rpx;
color: #999;
flex-shrink: 0;
}
.info-value {
font-size: 26rpx;
color: #333;
}
/* 状态标签 */
.status-tag {
display: inline-flex;
align-items: center;
padding: 4rpx 16rpx;
border-radius: 6rpx;
font-size: 22rpx;
font-weight: 500;
&.standby-tag {
color: #2563eb; background: #eff6ff; border: 1rpx solid #bfdbfe;
}
&.in-use-tag {
color: #059669; background: #ecfdf5; border: 1rpx solid #a7f3d0;
}
&.repairing-tag {
color: #d97706; background: #fffbeb; border: 1rpx solid #fde68a;
}
&.scrapped-tag {
color: #dc2626; background: #fef2f2; border: 1rpx solid #fecaca;
}
}
/* 空状态 */
.empty-wrap {
padding: 120rpx 0;
text-align: center;
}
.empty-text {
font-size: 28rpx;
color: #999;
}
/* 底部确认按钮 */
.bottom-actions {
position: fixed;
left: 0;
right: 0;
bottom: 0;
padding: 18rpx 24rpx calc(18rpx + env(safe-area-inset-bottom));
background: #fff;
box-shadow: 0 -4rpx 20rpx rgba(0, 0, 0, 0.06);
z-index: 99;
}
.bottom-btn {
width: 100%;
height: 84rpx;
line-height: 84rpx;
text-align: center;
border-radius: 16rpx;
font-size: 30rpx;
font-weight: 600;
}
.confirm-btn {
background: #1f4b79;
color: #fff;
}
</style>