优化供应商搜索下拉与卡片操作
供应商画像选择器改为支持输入筛选的动态下拉框,输入关键词实时匹配并刷新画像;删除暂未实现的收藏方案按钮,保留已接入操作。
This commit is contained in:
parent
69a1433a86
commit
d05be62e02
@ -403,14 +403,6 @@ function generateReport() {
|
|||||||
>
|
>
|
||||||
查看标准详情
|
查看标准详情
|
||||||
</button>
|
</button>
|
||||||
<button
|
|
||||||
v-if="card.type === 'solution'"
|
|
||||||
type="button"
|
|
||||||
disabled
|
|
||||||
title="功能开发中"
|
|
||||||
>
|
|
||||||
📌 收藏方案
|
|
||||||
</button>
|
|
||||||
<button
|
<button
|
||||||
v-if="card.type === 'solution'"
|
v-if="card.type === 'solution'"
|
||||||
type="button"
|
type="button"
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, onMounted, ref, watch } from "vue";
|
import { computed, onBeforeUnmount, onMounted, ref, watch } from "vue";
|
||||||
import { useRoute, useRouter } from "vue-router";
|
import { useRoute, useRouter } from "vue-router";
|
||||||
|
|
||||||
import { fetchSupplierOptions } from "@/api/suppliers";
|
import { fetchSupplierOptions } from "@/api/suppliers";
|
||||||
@ -10,6 +10,11 @@ const router = useRouter();
|
|||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const supplierNames = ref<string[]>([]);
|
const supplierNames = ref<string[]>([]);
|
||||||
const selected = ref("");
|
const selected = ref("");
|
||||||
|
const supplierQuery = ref("");
|
||||||
|
const supplierResults = ref<string[]>([]);
|
||||||
|
const supplierDropdownOpen = ref(false);
|
||||||
|
const supplierSearching = ref(false);
|
||||||
|
let supplierSearchTimer: ReturnType<typeof setTimeout> | undefined;
|
||||||
const profile = ref<SupplierProfile | null>(null);
|
const profile = ref<SupplierProfile | null>(null);
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
const error = ref("");
|
const error = ref("");
|
||||||
@ -89,11 +94,64 @@ async function loadOptions() {
|
|||||||
typeof route.query.supplier === "string"
|
typeof route.query.supplier === "string"
|
||||||
? route.query.supplier
|
? route.query.supplier
|
||||||
: (response.data.data?.defaultSupplier ?? supplierNames.value[0] ?? "");
|
: (response.data.data?.defaultSupplier ?? supplierNames.value[0] ?? "");
|
||||||
|
supplierQuery.value = selected.value;
|
||||||
|
supplierResults.value = supplierNames.value;
|
||||||
if (selected.value) await loadProfile();
|
if (selected.value) await loadProfile();
|
||||||
} catch {
|
} catch {
|
||||||
error.value = "供应商列表加载失败,请重试。";
|
error.value = "供应商列表加载失败,请重试。";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function openSupplierDropdown() {
|
||||||
|
supplierDropdownOpen.value = true;
|
||||||
|
if (!supplierQuery.value || supplierQuery.value === selected.value) {
|
||||||
|
supplierResults.value = supplierNames.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function searchSuppliers(value: string) {
|
||||||
|
if (supplierSearchTimer) clearTimeout(supplierSearchTimer);
|
||||||
|
const keyword = value.trim();
|
||||||
|
if (!keyword) {
|
||||||
|
supplierResults.value = supplierNames.value;
|
||||||
|
supplierSearching.value = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const local = supplierNames.value.filter((name) => name.includes(keyword));
|
||||||
|
supplierResults.value = local;
|
||||||
|
supplierSearching.value = true;
|
||||||
|
supplierSearchTimer = setTimeout(async () => {
|
||||||
|
try {
|
||||||
|
const response = await fetchSupplierOptions(keyword);
|
||||||
|
supplierResults.value =
|
||||||
|
response.data.data?.items.map((item) => item.name) ?? [];
|
||||||
|
} catch {
|
||||||
|
supplierResults.value = local;
|
||||||
|
} finally {
|
||||||
|
supplierSearching.value = false;
|
||||||
|
}
|
||||||
|
}, 180);
|
||||||
|
}
|
||||||
|
|
||||||
|
function onSupplierInput(event: Event) {
|
||||||
|
supplierQuery.value = (event.target as HTMLInputElement).value;
|
||||||
|
supplierDropdownOpen.value = true;
|
||||||
|
searchSuppliers(supplierQuery.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
function selectSupplier(name: string) {
|
||||||
|
selected.value = name;
|
||||||
|
supplierQuery.value = name;
|
||||||
|
supplierDropdownOpen.value = false;
|
||||||
|
chooseSupplier();
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeSupplierDropdown() {
|
||||||
|
window.setTimeout(() => {
|
||||||
|
supplierDropdownOpen.value = false;
|
||||||
|
if (selected.value) supplierQuery.value = selected.value;
|
||||||
|
}, 120);
|
||||||
|
}
|
||||||
async function loadProfile() {
|
async function loadProfile() {
|
||||||
if (!selected.value) return;
|
if (!selected.value) return;
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
@ -116,11 +174,15 @@ function chooseSupplier() {
|
|||||||
void loadProfile();
|
void loadProfile();
|
||||||
}
|
}
|
||||||
onMounted(() => void loadOptions());
|
onMounted(() => void loadOptions());
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
if (supplierSearchTimer) clearTimeout(supplierSearchTimer);
|
||||||
|
});
|
||||||
watch(
|
watch(
|
||||||
() => route.query.supplier,
|
() => route.query.supplier,
|
||||||
(value) => {
|
(value) => {
|
||||||
if (typeof value === "string" && value !== selected.value) {
|
if (typeof value === "string" && value !== selected.value) {
|
||||||
selected.value = value;
|
selected.value = value;
|
||||||
|
supplierQuery.value = value;
|
||||||
void loadProfile();
|
void loadProfile();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -132,16 +194,46 @@ watch(
|
|||||||
<div class="supplier-inner">
|
<div class="supplier-inner">
|
||||||
<header class="supplier-head">
|
<header class="supplier-head">
|
||||||
<h1>供应商深度画像</h1>
|
<h1>供应商深度画像</h1>
|
||||||
<select
|
<div class="supplier-combobox" @focusout="closeSupplierDropdown">
|
||||||
v-model="selected"
|
<input
|
||||||
class="supplier-select"
|
:value="supplierQuery"
|
||||||
aria-label="选择供应商"
|
class="supplier-select"
|
||||||
@change="chooseSupplier"
|
type="text"
|
||||||
>
|
role="combobox"
|
||||||
<option v-for="name in supplierNames" :key="name" :value="name">
|
aria-label="搜索并选择供应商"
|
||||||
{{ name }}
|
aria-autocomplete="list"
|
||||||
</option>
|
:aria-expanded="supplierDropdownOpen"
|
||||||
</select>
|
placeholder="输入供应商名称"
|
||||||
|
@focus="openSupplierDropdown"
|
||||||
|
@input="onSupplierInput"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
v-if="supplierDropdownOpen"
|
||||||
|
class="supplier-options"
|
||||||
|
role="listbox"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
v-for="name in supplierResults"
|
||||||
|
:key="name"
|
||||||
|
type="button"
|
||||||
|
class="supplier-option"
|
||||||
|
:class="{ active: name === selected }"
|
||||||
|
role="option"
|
||||||
|
:aria-selected="name === selected"
|
||||||
|
@mousedown.prevent="selectSupplier(name)"
|
||||||
|
>
|
||||||
|
{{ name }}
|
||||||
|
</button>
|
||||||
|
<span v-if="supplierSearching" class="supplier-option-state"
|
||||||
|
>搜索中…</span
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
v-else-if="!supplierResults.length"
|
||||||
|
class="supplier-option-state"
|
||||||
|
>未找到匹配供应商</span
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<span v-if="profile" class="tier-badge" :class="tierClass">{{
|
<span v-if="profile" class="tier-badge" :class="tierClass">{{
|
||||||
tierLabel
|
tierLabel
|
||||||
}}</span>
|
}}</span>
|
||||||
@ -418,6 +510,8 @@ watch(
|
|||||||
font-weight: 650;
|
font-weight: 650;
|
||||||
}
|
}
|
||||||
.supplier-select {
|
.supplier-select {
|
||||||
|
width: 180px;
|
||||||
|
min-height: 34px;
|
||||||
padding: 7px 14px;
|
padding: 7px 14px;
|
||||||
border: 1px solid #e4e4e2;
|
border: 1px solid #e4e4e2;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
@ -428,6 +522,47 @@ watch(
|
|||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
.supplier-combobox {
|
||||||
|
position: relative;
|
||||||
|
z-index: 5;
|
||||||
|
}
|
||||||
|
.supplier-combobox .supplier-select {
|
||||||
|
cursor: text;
|
||||||
|
}
|
||||||
|
.supplier-options {
|
||||||
|
position: absolute;
|
||||||
|
top: calc(100% + 4px);
|
||||||
|
right: 0;
|
||||||
|
left: 0;
|
||||||
|
max-height: 260px;
|
||||||
|
overflow-y: auto;
|
||||||
|
border: 1px solid var(--color-border-strong);
|
||||||
|
border-radius: 5px;
|
||||||
|
padding: 4px 0;
|
||||||
|
background: #fff;
|
||||||
|
box-shadow: var(--shadow-lg);
|
||||||
|
}
|
||||||
|
.supplier-option {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
border: 0;
|
||||||
|
padding: 8px 12px;
|
||||||
|
background: #fff;
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
font-size: 12px;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
.supplier-option:hover,
|
||||||
|
.supplier-option.active {
|
||||||
|
background: var(--color-accent-subtle);
|
||||||
|
color: var(--color-accent);
|
||||||
|
}
|
||||||
|
.supplier-option-state {
|
||||||
|
display: block;
|
||||||
|
padding: 9px 12px;
|
||||||
|
color: var(--color-text-faint);
|
||||||
|
font-size: 11px;
|
||||||
|
}
|
||||||
.version-note {
|
.version-note {
|
||||||
color: #999;
|
color: #999;
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
@ -700,6 +835,13 @@ watch(
|
|||||||
order: 3;
|
order: 3;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
.supplier-combobox {
|
||||||
|
order: 3;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.supplier-combobox .supplier-select {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
.version-note {
|
.version-note {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user