admin-vue3/src/views/mes/wm/salesnotice/components/WmSalesNoticeSelect.vue

178 lines
5.2 KiB
Vue
Raw Blame History

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.

<!--
MES 发货通知单选择器只读输入框 + 点击弹窗选择
交互显示为只读 el-input点击打开弹窗单选模式进行选择
Props:
modelValue 绑定的发货通知单 IDv-model
status 固定状态筛选透传给 Dialog限制可选范围
disabled 是否禁用
clearable 是否允许清空鼠标悬停时显示清除图标
placeholder 占位文字
Events:
update:modelValue v-model 更新
change(item) 选中发货通知单变化时触发传递完整 WmSalesNoticeVO清空时为 undefined
-->
<template>
<div
v-bind="attrs"
class="w-full"
:class="disabled ? 'cursor-not-allowed' : 'cursor-pointer'"
@click="handleClick"
@mouseenter="hovering = true"
@mouseleave="hovering = false"
>
<el-tooltip :disabled="!selectedItem" placement="top" :show-after="500">
<template #content>
<div v-if="selectedItem" class="leading-6">
<div>编号:{{ selectedItem.code }}</div>
<div>名称:{{ selectedItem.name || '-' }}</div>
<div>客户:{{ selectedItem.clientName || '-' }}</div>
<div>销售订单:{{ selectedItem.salesOrderCode || '-' }}</div>
</div>
</template>
<el-input
:model-value="displayLabel"
:placeholder="placeholder"
:disabled="disabled"
readonly
:suffix-icon="suffixIcon"
:class="disabled ? 'is-select-disabled' : 'is-select-clickable'"
/>
</el-tooltip>
</div>
<!-- 弹窗必须放在 div 外部,否则弹窗内的点击事件会冒泡到 div 触发 handleClick -->
<WmSalesNoticeSelectDialog
ref="dialogRef"
:multiple="false"
:status="status"
@selected="handleSelected"
/>
</template>
<script setup lang="ts">
import { WmSalesNoticeApi, WmSalesNoticeVO } from '@/api/mes/wm/salesnotice'
import { Search, CircleClose } from '@element-plus/icons-vue'
import WmSalesNoticeSelectDialog from './WmSalesNoticeSelectDialog.vue'
// div + DialogVue attrs
// div class / style
const attrs = useAttrs()
defineOptions({ name: 'WmSalesNoticeSelect', inheritAttrs: false })
const props = withDefaults(
defineProps<{
modelValue?: number // ID
status?: number //
disabled?: boolean //
clearable?: boolean //
placeholder?: string //
}>(),
{
disabled: false,
clearable: true,
placeholder: '请选择发货通知单'
}
)
const emit = defineEmits<{
'update:modelValue': [value: number | undefined]
change: [item: WmSalesNoticeVO | undefined]
}>()
const dialogRef = ref() // 弹窗 Ref
const hovering = ref(false) // 鼠标是否悬停
// ==================== 名称回显 ====================
const selectedItem = ref<WmSalesNoticeVO | undefined>() // 当前选中的发货通知单对象
/** 输入框显示文本:展示通知单名称,保持简洁 */
const displayLabel = computed(() => {
return selectedItem.value?.name ?? ''
})
/** 是否显示清除图标 */
const showClear = computed(() => {
return props.clearable && !props.disabled && hovering.value && props.modelValue != null
})
/** 后缀图标:悬停且有值时显示清除,否则显示搜索 */
const suffixIcon = computed(() => {
return showClear.value ? CircleClose : Search
})
/** 根据 ID 单条查询发货通知单信息(用于编辑回显) */
const resolveItemById = async (id: number | undefined) => {
if (id == null) {
selectedItem.value = undefined
return
}
if (selectedItem.value?.id === id) {
return
}
try {
selectedItem.value = await WmSalesNoticeApi.getSalesNotice(id)
} catch (e) {
console.error('[WmSalesNoticeSelect] resolveItemById failed:', e)
}
}
/** 监听 modelValue 变化,触发回显 */
watch(
() => props.modelValue,
(val) => {
resolveItemById(val)
},
{ immediate: true }
)
// ==================== 点击交互 ====================
/** 点击组件:清除或打开弹窗 */
const handleClick = (e: MouseEvent) => {
if (props.disabled) {
return
}
// 点击清除图标:清空选中
const target = e.target as HTMLElement
if (showClear.value && target.closest('.el-input__suffix')) {
e.stopPropagation()
selectedItem.value = undefined
emit('update:modelValue', undefined)
emit('change', undefined)
return
}
// 打开弹窗,传入当前选中 ID 用于预选高亮
const selectedIds = props.modelValue != null ? [props.modelValue] : []
dialogRef.value.open(selectedIds)
}
/** 弹窗选中回调 */
const handleSelected = (rows: WmSalesNoticeVO[]) => {
if (!rows || rows.length === 0) {
return
}
const item = rows[0]
selectedItem.value = item
emit('update:modelValue', item.id)
emit('change', item)
}
</script>
<style lang="scss" scoped>
/* :deep 用于穿透 el-input 内部元素的 cursor 样式UnoCSS 无法直接处理组件内部 DOM */
.is-select-clickable {
:deep(.el-input__wrapper),
:deep(.el-input__inner) {
cursor: pointer;
}
}
.is-select-disabled {
:deep(.el-input__wrapper),
:deep(.el-input__inner) {
cursor: not-allowed;
}
}
</style>