272 lines
6.5 KiB
Vue
272 lines
6.5 KiB
Vue
<template>
|
|
<div class="search-area">
|
|
<div class="iccid-search">
|
|
<div class="iccid-input-wrapper">
|
|
<ElInput
|
|
v-model="searchIccid"
|
|
placeholder="请输入虚拟号、ICCID、IMEI、SN或MSISDN"
|
|
clearable
|
|
:prefix-icon="Search"
|
|
class="asset-search-input"
|
|
@focus="iccidInputFocused = true"
|
|
@blur="iccidInputFocused = false"
|
|
@keyup.enter="handleSearch"
|
|
/>
|
|
<!-- 放大显示区域 -->
|
|
<Transition name="zoom-fade">
|
|
<div v-if="iccidInputFocused && searchIccid" class="iccid-magnifier">
|
|
<div class="magnifier-arrow"></div>
|
|
<div class="magnifier-content">
|
|
{{ formatIccidWithDashes(searchIccid) }}
|
|
</div>
|
|
</div>
|
|
</Transition>
|
|
</div>
|
|
<ElButton class="search-action-button" type="primary" @click="handleSearch" :icon="Search">
|
|
查询
|
|
</ElButton>
|
|
|
|
<!-- 操作按钮组 -->
|
|
<div v-if="hasCardInfo" class="operation-button-group">
|
|
<ElButton
|
|
v-permission="'asset_info:sync'"
|
|
@click="handleRefresh"
|
|
type="primary"
|
|
:icon="Refresh"
|
|
:disabled="refreshDisabled"
|
|
>
|
|
同步
|
|
</ElButton>
|
|
<ElButton type="default" plain :icon="Back" @click="handleGoBack"> 返回上次搜索 </ElButton>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { ref } from 'vue'
|
|
import { useRoute } from 'vue-router'
|
|
import { ElInput, ElButton } from 'element-plus'
|
|
import { Back, Search, Refresh } from '@element-plus/icons-vue'
|
|
import { useAssetFormatters } from '../composables/useAssetFormatters'
|
|
|
|
// Use formatters
|
|
const { formatIccidWithDashes } = useAssetFormatters()
|
|
|
|
const route = useRoute()
|
|
|
|
// Props
|
|
interface Props {
|
|
hasCardInfo?: boolean
|
|
refreshDisabled?: boolean
|
|
}
|
|
|
|
withDefaults(defineProps<Props>(), {
|
|
hasCardInfo: false,
|
|
refreshDisabled: false
|
|
})
|
|
|
|
// Emits
|
|
const emit = defineEmits<{
|
|
search: [payload: { identifier: string }]
|
|
refresh: []
|
|
goBack: []
|
|
}>()
|
|
|
|
// State
|
|
const searchIccid = ref('')
|
|
const iccidInputFocused = ref(false)
|
|
const previousSearchValue = ref('')
|
|
|
|
// 从URL参数中获取初始值
|
|
const initializeFromQuery = () => {
|
|
const iccid = route.query.iccid as string
|
|
const virtualNo = route.query.virtual_no as string
|
|
const deviceNo = route.query.device_no as string
|
|
|
|
const identifier = iccid || virtualNo || deviceNo
|
|
if (identifier) {
|
|
searchIccid.value = identifier
|
|
}
|
|
}
|
|
|
|
// 初始化时从URL参数加载
|
|
initializeFromQuery()
|
|
|
|
// 暴露方法供父组件调用,用于更新搜索框值
|
|
const updateSearchValue = (value: string) => {
|
|
if (value && value !== searchIccid.value) {
|
|
previousSearchValue.value = searchIccid.value
|
|
}
|
|
searchIccid.value = value
|
|
}
|
|
|
|
// Methods
|
|
const handleSearch = () => {
|
|
if (!searchIccid.value.trim()) {
|
|
return
|
|
}
|
|
previousSearchValue.value = searchIccid.value.trim()
|
|
emit('search', { identifier: searchIccid.value.trim() })
|
|
}
|
|
|
|
const handleRefresh = () => {
|
|
emit('refresh')
|
|
}
|
|
|
|
const handleGoBack = () => {
|
|
if (previousSearchValue.value) {
|
|
const temp = searchIccid.value
|
|
searchIccid.value = previousSearchValue.value
|
|
previousSearchValue.value = temp
|
|
emit('search', { identifier: searchIccid.value.trim() })
|
|
}
|
|
}
|
|
|
|
// 暴露给父组件
|
|
defineExpose({
|
|
updateSearchValue,
|
|
handleGoBack
|
|
})
|
|
</script>
|
|
|
|
<style scoped lang="scss">
|
|
.search-area {
|
|
position: relative;
|
|
z-index: 20;
|
|
margin-bottom: 20px;
|
|
overflow: visible;
|
|
}
|
|
|
|
.iccid-search {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 12px;
|
|
align-items: flex-start;
|
|
overflow: visible;
|
|
|
|
.iccid-input-wrapper {
|
|
position: relative;
|
|
flex: 1 1 360px;
|
|
min-width: 240px;
|
|
|
|
.asset-search-input {
|
|
width: 100%;
|
|
|
|
:deep(.el-input__prefix) {
|
|
color: var(--el-text-color-secondary);
|
|
}
|
|
}
|
|
|
|
.iccid-magnifier {
|
|
position: absolute;
|
|
top: calc(100% + 12px);
|
|
left: 0;
|
|
z-index: 2000;
|
|
white-space: nowrap;
|
|
|
|
.magnifier-arrow {
|
|
position: absolute;
|
|
top: -9px;
|
|
left: 80px;
|
|
width: 16px;
|
|
height: 16px;
|
|
background-color: var(--el-color-primary-light-9);
|
|
border-top: 2px solid var(--el-color-primary);
|
|
border-left: 2px solid var(--el-color-primary);
|
|
transform: rotate(45deg);
|
|
}
|
|
|
|
.magnifier-content {
|
|
display: inline-block;
|
|
width: fit-content;
|
|
max-width: min(720px, calc(100vw - 48px));
|
|
padding: 14px 22px;
|
|
font-family: 'Courier New', Consolas, monospace;
|
|
font-size: 24px;
|
|
font-weight: 600;
|
|
line-height: 1.4;
|
|
color: var(--el-color-primary);
|
|
text-align: center;
|
|
letter-spacing: 1.5px;
|
|
overflow-wrap: anywhere;
|
|
white-space: normal;
|
|
background-color: var(--el-color-primary-light-9);
|
|
border: 2px solid var(--el-color-primary);
|
|
border-radius: 8px;
|
|
box-shadow: 0 8px 18px rgb(15 23 42 / 12%);
|
|
}
|
|
}
|
|
}
|
|
|
|
.operation-button-group {
|
|
display: flex;
|
|
gap: 12px;
|
|
margin-left: auto;
|
|
}
|
|
}
|
|
|
|
.zoom-fade-enter-active,
|
|
.zoom-fade-leave-active {
|
|
transition: all 0.2s ease;
|
|
}
|
|
|
|
.zoom-fade-enter-from,
|
|
.zoom-fade-leave-to {
|
|
opacity: 0;
|
|
transform: scale(0.8) translateY(-10px);
|
|
}
|
|
|
|
.zoom-fade-enter-to,
|
|
.zoom-fade-leave-from {
|
|
opacity: 1;
|
|
transform: scale(1) translateY(0);
|
|
}
|
|
|
|
@media (width <= 768px) {
|
|
.search-area {
|
|
margin-bottom: 16px;
|
|
}
|
|
|
|
.iccid-search {
|
|
flex-direction: column;
|
|
align-items: stretch;
|
|
|
|
.iccid-input-wrapper {
|
|
flex-basis: auto;
|
|
width: 100%;
|
|
min-width: 0;
|
|
|
|
.iccid-magnifier {
|
|
right: 0;
|
|
left: 0;
|
|
white-space: normal;
|
|
|
|
.magnifier-arrow {
|
|
left: 24px;
|
|
}
|
|
|
|
.magnifier-content {
|
|
display: block;
|
|
width: 100%;
|
|
padding: 12px 14px;
|
|
font-size: 18px;
|
|
letter-spacing: 1px;
|
|
}
|
|
}
|
|
}
|
|
|
|
.search-action-button,
|
|
.operation-button-group,
|
|
.operation-button-group .el-button {
|
|
width: 100%;
|
|
margin-left: 0;
|
|
}
|
|
|
|
.operation-button-group {
|
|
margin-left: 0;
|
|
}
|
|
}
|
|
}
|
|
</style>
|