scanner in CustomerSelector.vue

This commit is contained in:
Timi
2026-09-09 17:49:37 +08:00
parent 809bfa8520
commit 8e77b114f9
17 changed files with 804 additions and 410 deletions
+71 -2
View File
@@ -6,7 +6,21 @@
class="search"
placeholder="搜索编码、姓名(拼音)、电话"
borderless
/>
>
<template #suffix-icon>
<t-button
size="extra-small"
theme="primary"
variant="outline"
@click.stop="toggleScanner"
>
<template #icon>
<t-icon name="scan" />
</template>
<span v-text="scannerVisible ? '关闭扫码' : '扫码'" />
</t-button>
</template>
</t-input>
<t-button
v-if="canReadTag"
class="tag-filter"
@@ -20,6 +34,15 @@
<span v-text="tagFilterText" />
</t-button>
</div>
<div v-if="scannerVisible" class="scanner-wrapper">
<code-scanner
:visible="scannerVisible"
:status-text="scanStatus"
active-text="正在识别客户编码,请将条码或二维码移入取景框"
@decode="handleDecodedText"
@status-change="handleScanStatusChange"
/>
</div>
<div v-if="canReadTag && selectedTagList.length" class="selected-tags bg-white">
<t-tag
v-for="item in selectedTagList"
@@ -89,10 +112,12 @@ import { Message } from "@/utils/message";
import { Time, Toolkit, WebToolkit } from "timi-web";
import { type Page, type PageResult, useUserStore } from "timi-web-api";
import CustomerAPI from "@/api/CustomerAPI";
import CodeScanner from "@/components/CodeScanner.vue";
import CustomerRemoveDialog from "@/pages/customer/component/CustomerRemoveDialog.vue";
import TagSelector from "@/components/TagSelector.vue";
import { useCurrentStoreStore } from "@/store/currentStoreStore";
import { useNavBarStore } from "@/store/navBarStore";
import { useSettingStore } from "@/store/settingStore";
import type { Customer } from "@/types/Customer";
import type { NavBarAction } from "@/types/NavBarAction";
import type { Tag } from "@/types/Tag";
@@ -105,6 +130,7 @@ defineOptions({
const router = useRouter();
const userStore = useUserStore();
const currentStoreStore = useCurrentStoreStore();
const settingStore = useSettingStore();
const navBarStore = useNavBarStore();
const isRefreshing = ref(false);
const isExporting = ref(false);
@@ -118,6 +144,42 @@ const canCreateCustomer = computed(() => userStore.hasPermissions(permissionCode
const canDeleteCustomer = computed(() => userStore.hasPermissions(permissionCodes.customer.delete));
const canExportCustomer = computed(() => userStore.hasPermissions(permissionCodes.customer.export));
const canShowActionMenu = computed(() => canCreateCustomer.value || canExportCustomer.value);
const scannerVisible = ref(false);
const scanStatus = ref("点击扫码开始识别");
let latestDecodedText = "";
async function toggleScanner(): Promise<void> {
scannerVisible.value = !scannerVisible.value;
if (scannerVisible.value) {
latestDecodedText = "";
scanStatus.value = "正在启动扫码预览";
}
}
async function handleDecodedText(rawText: string): Promise<void> {
const normalizedText = rawText.trim();
if (!normalizedText || normalizedText === latestDecodedText) {
return;
}
latestDecodedText = normalizedText;
await settingStore.playScanSuccessSound();
try {
const customer = await CustomerAPI.findByCode(normalizedText, currentStoreStore.currentStoreId || undefined);
if (!customer?.id) {
scanStatus.value = `客户 ${normalizedText} 不存在,请先建档。`;
return;
}
keyword.value = customer.code || normalizedText;
scannerVisible.value = false;
scanStatus.value = `已识别 ${customer.code || normalizedText}`;
} catch {
scanStatus.value = "客户查询失败,请重试。";
}
}
function handleScanStatusChange(value: string): void {
scanStatus.value = value;
}
const actionList = computed<NavBarAction[]>(() => {
const list: NavBarAction[] = [];
@@ -232,6 +294,7 @@ watch(keyword, async () => {
onUnmounted(() => {
navBarStore.clearRight();
window.clearTimeout(keywordTimer);
scannerVisible.value = false;
});
// ---------- 导航 ----------
@@ -315,9 +378,11 @@ async function doExport(): Promise<void> {
align-items: center;
.search {
--td-input-vertical-padding: .25rem 1rem;
width: 100%;
border: 1px solid var(--td-component-border);
padding: .5rem 1rem;
padding-right: .3rem;
border-radius: 1rem;
}
@@ -328,6 +393,10 @@ async function doExport(): Promise<void> {
}
}
.scanner-wrapper {
padding: .5rem 1rem 0;
}
.selected-tags {
gap: .5rem;
z-index: 9;