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.

375 lines
7.4 KiB
Vue

<template>
<view class="page-container">
<NavBar title="关键件查询" subTitle="请选择查询方式" />
<view class="content-section">
<view class="scan-section">
<view class="scan-area" @click="startScan">
<view class="scan-icon">
<text class="icon-text">📷</text>
</view>
<text class="scan-title">自动扫描</text>
<text class="scan-desc">点击启动扫描关键件编码</text>
</view>
<view v-if="isScanning" class="scanning-overlay">
<view class="scanning-animation">
<view class="scan-line"></view>
<view class="scan-corners">
<view class="corner corner-tl"></view>
<view class="corner corner-tr"></view>
<view class="corner corner-bl"></view>
<view class="corner corner-br"></view>
</view>
</view>
<text class="scanning-text">正在打开扫码...</text>
</view>
</view>
<view class="divider">
<view class="divider-line"></view>
<text class="divider-text">或</text>
<view class="divider-line"></view>
</view>
<view class="input-section">
<view class="input-label">手动输入关键件ID</view>
<view class="input-wrapper">
<input
v-model="keypartCode"
class="code-input"
type="text"
placeholder="请输入关键件ID"
placeholder-class="input-placeholder"
/>
</view>
<view class="confirm-btn" :class="{ active: keypartCode.length > 0 }" @click="confirmInput">
<text class="btn-text"></text>
</view>
</view>
</view>
</view>
</template>
<script setup>
import { ref } from 'vue';
import NavBar from '@/components/common/NavBar.vue'
const keypartCode = ref('');
const isScanning = ref(false);
const SCAN_TYPE = 'KEY_PART';
const MODULE_NAME = '关键件';
function parseScanResult(res) {
const raw = String(res?.result || '').trim();
if (!raw) return {};
const splitIndex = raw.indexOf('-');
if (splitIndex <= 0 || splitIndex >= raw.length - 1) return {};
return {
type: raw.slice(0, splitIndex),
id: raw.slice(splitIndex + 1)
};
}
function startScan() {
if (isScanning.value) return;
isScanning.value = true;
const finish = () => {
isScanning.value = false;
}
uni.scanCode({
onlyFromCamera: true,
scanType: ['qrCode', 'barCode'],
success: (res) => {
const { type, id } = parseScanResult(res);
if (!type || !id) {
uni.showToast({ title: '未获取到扫码结果', icon: 'none' })
return
}
if (type !== SCAN_TYPE) {
uni.showToast({ title: `二维码类型不匹配,请扫描${MODULE_NAME}二维码`, icon: 'none' })
return
}
navigateToDetail(id)
},
fail: (err) => {
const msg = String(err?.errMsg || '')
if (msg.includes('cancel')) {
uni.showToast({ title: '已取消扫码', icon: 'none' })
return
}
if (msg.toLowerCase().includes('not support') || msg.toLowerCase().includes('not supported')) {
uni.showToast({ title: '当前平台不支持扫码', icon: 'none' })
return
}
uni.showToast({ title: '扫码失败', icon: 'none' })
},
complete: finish
})
}
function confirmInput() {
if (keypartCode.value.trim().length === 0) {
uni.showToast({
title: '请输入关键件ID',
icon: 'none'
});
return;
}
navigateToDetail(keypartCode.value.trim(), 'input');
}
function navigateToDetail(id, type = 'scan') {
let url
if (type === 'scan') {
url = `/pages_function/pages/keypart/detail?id=${encodeURIComponent(id)}&type=${type}`
} else {
url = `/pages_function/pages/keypart/detail?code=${id}&type=${type}`
}
uni.navigateTo({
url: url
});
}
</script>
<style lang="scss" scoped>
.page-container {
min-height: 100vh;
background-color: #f0f2f5;
}
.content-section {
padding: 40rpx 30rpx;
}
.scan-section {
position: relative;
background: #ffffff;
border-radius: 24rpx;
padding: 60rpx 40rpx;
margin-bottom: 30rpx;
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.05);
}
.scan-area {
display: flex;
flex-direction: column;
align-items: center;
&:active {
opacity: 0.8;
}
}
.scan-icon {
width: 160rpx;
height: 160rpx;
background: linear-gradient(135deg, #1a3a5c 0%, #3d7ab5 100%);
border-radius: 32rpx;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 30rpx;
.icon-text {
font-size: 72rpx;
}
}
.scan-title {
font-size: 34rpx;
font-weight: 600;
color: #1a3a5c;
margin-bottom: 12rpx;
}
.scan-desc {
font-size: 26rpx;
color: #999999;
}
.scanning-overlay {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(26, 58, 92, 0.95);
border-radius: 24rpx;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
z-index: 10;
}
.scanning-animation {
width: 300rpx;
height: 300rpx;
position: relative;
margin-bottom: 40rpx;
}
.scan-line {
position: absolute;
top: 0;
left: 20rpx;
right: 20rpx;
height: 4rpx;
background: linear-gradient(90deg, transparent, #ff8c00, transparent);
animation: scanMove 1.5s ease-in-out infinite;
}
@keyframes scanMove {
0% {
top: 20rpx;
}
50% {
top: 260rpx;
}
100% {
top: 20rpx;
}
}
.scan-corners {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
.corner {
position: absolute;
width: 40rpx;
height: 40rpx;
border-color: #ff8c00;
border-style: solid;
border-width: 0;
}
.corner-tl {
top: 0;
left: 0;
border-top-width: 6rpx;
border-left-width: 6rpx;
border-top-left-radius: 12rpx;
}
.corner-tr {
top: 0;
right: 0;
border-top-width: 6rpx;
border-right-width: 6rpx;
border-top-right-radius: 12rpx;
}
.corner-bl {
bottom: 0;
left: 0;
border-bottom-width: 6rpx;
border-left-width: 6rpx;
border-bottom-left-radius: 12rpx;
}
.corner-br {
bottom: 0;
right: 0;
border-bottom-width: 6rpx;
border-right-width: 6rpx;
border-bottom-right-radius: 12rpx;
}
.scanning-text {
font-size: 32rpx;
color: #ffffff;
margin-bottom: 30rpx;
}
.divider {
display: flex;
align-items: center;
margin-bottom: 30rpx;
.divider-line {
flex: 1;
height: 2rpx;
background: #e8eaed;
}
.divider-text {
padding: 0 30rpx;
font-size: 28rpx;
color: #999999;
}
}
.input-section {
background: #ffffff;
border-radius: 24rpx;
padding: 40rpx;
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.05);
}
.input-label {
font-size: 30rpx;
font-weight: 600;
color: #1a3a5c;
margin-bottom: 24rpx;
}
.input-wrapper {
margin-bottom: 30rpx;
}
.code-input {
width: 100%;
height: 96rpx;
background: #f5f7fa;
border-radius: 16rpx;
padding: 0 30rpx;
font-size: 30rpx;
color: #333333;
border: 2rpx solid transparent;
&:focus {
border-color: #1a3a5c;
background: #ffffff;
}
}
.input-placeholder {
color: #c0c4cc;
}
.confirm-btn {
height: 96rpx;
background: #c0c4cc;
border-radius: 16rpx;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.3s ease;
&.active {
background: linear-gradient(135deg, #1a3a5c 0%, #2d5a87 100%);
&:active {
opacity: 0.8;
transform: scale(0.98);
}
}
.btn-text {
font-size: 32rpx;
font-weight: 600;
color: #ffffff;
}
}
</style>