99 lines
3.7 KiB
Vue
99 lines
3.7 KiB
Vue
<template>
|
|
<view class="page">
|
|
<!-- 扫码大按钮 -->
|
|
<view class="scan-btn" @tap="handleScanCode">
|
|
<text class="scan-icon">📷</text>
|
|
<text class="scan-text">点击扫码</text>
|
|
<text class="scan-hint">扫描二维码 / 条码查询产品</text>
|
|
</view>
|
|
|
|
<!-- 手动输入 -->
|
|
<view class="manual-input">
|
|
<input v-model="serialNumber" class="input" type="text" maxlength="16"
|
|
placeholder="手动输入16位身份证" @confirm="handleSearch" />
|
|
<button class="search-btn" @tap="handleSearch" :disabled="loading">
|
|
{{ loading ? '查询中' : '查询' }}
|
|
</button>
|
|
</view>
|
|
|
|
<!-- 最近扫描 -->
|
|
<view v-if="lastScanned" class="last-scan">最近扫描: <text class="sn-text">{{ lastScanned }}</text></view>
|
|
<view v-if="loading" class="loading">查询中...</view>
|
|
<view v-if="error" class="error-box">{{ error }}</view>
|
|
|
|
<!-- 空状态 -->
|
|
<view v-if="!loading && !error" class="empty">
|
|
<text class="empty-icon">📱</text>
|
|
<text class="empty-text">扫码或手动输入身份证查询产品进度</text>
|
|
</view>
|
|
</view>
|
|
</template>
|
|
|
|
<script>
|
|
import { get } from "../../utils/request";
|
|
|
|
export default {
|
|
data() {
|
|
return {
|
|
serialNumber: "",
|
|
lastScanned: "",
|
|
loading: false,
|
|
error: "",
|
|
};
|
|
},
|
|
methods: {
|
|
async doQuery(sn) {
|
|
if (!sn || sn.length < 8) { this.error = "身份证至少需要 8 位"; return; }
|
|
this.serialNumber = sn;
|
|
this.lastScanned = sn;
|
|
this.loading = true;
|
|
this.error = "";
|
|
try {
|
|
await get(`/products/scan/${sn}`);
|
|
uni.navigateTo({ url: `/pages/scan/detail?serial=${sn}` });
|
|
} catch (e) {
|
|
this.error = e?.data?.detail || "未找到该产品";
|
|
} finally {
|
|
this.loading = false;
|
|
}
|
|
},
|
|
handleSearch() { this.doQuery(this.serialNumber.trim()); },
|
|
handleScanCode() {
|
|
uni.scanCode({
|
|
onlyFromCamera: true, scanType: ["qrCode", "barCode"],
|
|
success: (res) => {
|
|
const sn = (res.result || "").replace(/[^a-zA-Z0-9]/g, "").slice(0, 16);
|
|
this.doQuery(sn);
|
|
},
|
|
fail: (err) => {
|
|
if (!err.errMsg || !err.errMsg.includes("cancel")) {
|
|
uni.showToast({ title: "扫码失败,请重试", icon: "none" });
|
|
}
|
|
},
|
|
});
|
|
},
|
|
},
|
|
};
|
|
</script>
|
|
|
|
<style scoped>
|
|
.page { min-height: 100vh; padding: 40px 16px 16px; }
|
|
.scan-btn { display: flex; flex-direction: column; align-items: center; justify-content: center;
|
|
height: 180px; background: linear-gradient(135deg, #2563EB, #3B82F6);
|
|
border-radius: 16px; color: #fff; box-shadow: 0 4px 16px rgba(37,99,235,0.3); }
|
|
.scan-icon { font-size: 52px; margin-bottom: 8px; }
|
|
.scan-text { font-size: 20px; font-weight: 700; }
|
|
.scan-hint { font-size: 13px; opacity: 0.8; margin-top: 4px; }
|
|
.manual-input { display: flex; gap: 8px; margin-top: 16px; }
|
|
.input { flex: 1; height: 44px; padding: 0 12px; border: 1px solid #e5e7eb; border-radius: 10px; font-size: 14px; background: #fff; }
|
|
.search-btn { height: 44px; padding: 0 20px; background: #2563EB; color: #fff; border: none; border-radius: 10px; font-size: 14px; font-weight: 600; line-height: 44px; }
|
|
.search-btn[disabled] { opacity: 0.6; }
|
|
.last-scan { font-size: 12px; color: #9ca3af; margin-top: 16px; }
|
|
.sn-text { font-family: monospace; color: #4b5563; }
|
|
.loading { text-align: center; padding: 24px 0; color: #6b7280; }
|
|
.error-box { padding: 12px; border-radius: 10px; background: #fef2f2; color: #dc2626; font-size: 13px; border: 1px solid #fecaca; margin-top: 12px; }
|
|
.empty { display: flex; flex-direction: column; align-items: center; padding-top: 40px; color: #9ca3af; }
|
|
.empty-icon { font-size: 48px; margin-bottom: 8px; }
|
|
.empty-text { font-size: 14px; }
|
|
</style>
|