新增自动触发中文公式编辑/测试、查价运输方式 Tab 分组选择,并完善销售产品字段预览与菜单展示。 Co-authored-by: Cursor <cursoragent@cursor.com>
279 lines
6.0 KiB
Vue
279 lines
6.0 KiB
Vue
<script lang="ts" setup>
|
|
import { computed, ref, watch } from 'vue'
|
|
import { IconDown, IconSearch } from '@arco-design/web-vue/es/icon'
|
|
import type { PriceCheckShippingGroup } from '@/api/order'
|
|
|
|
const props = defineProps<{
|
|
modelValue: string
|
|
groups: PriceCheckShippingGroup[]
|
|
loading?: boolean
|
|
placeholder?: string
|
|
disabled?: boolean
|
|
}>()
|
|
|
|
const emit = defineEmits<{
|
|
'update:modelValue': [string]
|
|
}>()
|
|
|
|
const popupVisible = ref(false)
|
|
const activeGroupCode = ref('')
|
|
const keyword = ref('')
|
|
|
|
const flatOptions = computed(() =>
|
|
props.groups.flatMap((group) =>
|
|
group.products.map((product) => ({
|
|
...product,
|
|
groupCode: group.groupCode,
|
|
groupName: group.groupName,
|
|
}))
|
|
)
|
|
)
|
|
|
|
const selectedLabel = computed(() => {
|
|
const hit = flatOptions.value.find((item) => item.productCode === props.modelValue)
|
|
return hit?.label ?? ''
|
|
})
|
|
|
|
const filteredGroups = computed(() => {
|
|
const kw = keyword.value.trim().toLowerCase()
|
|
if (!kw) return props.groups
|
|
return props.groups
|
|
.map((group) => ({
|
|
...group,
|
|
products: group.products.filter((product) => {
|
|
const text = `${product.label} ${product.productCode} ${product.productName}`.toLowerCase()
|
|
return text.includes(kw)
|
|
}),
|
|
}))
|
|
.filter((group) => group.products.length > 0)
|
|
})
|
|
|
|
watch(
|
|
() => props.groups,
|
|
(groups) => {
|
|
if (!groups.length) {
|
|
activeGroupCode.value = ''
|
|
return
|
|
}
|
|
if (!groups.some((group) => group.groupCode === activeGroupCode.value)) {
|
|
activeGroupCode.value = groups[0].groupCode
|
|
}
|
|
},
|
|
{ immediate: true }
|
|
)
|
|
|
|
watch(filteredGroups, (groups) => {
|
|
if (!groups.length) {
|
|
activeGroupCode.value = ''
|
|
return
|
|
}
|
|
if (!groups.some((group) => group.groupCode === activeGroupCode.value)) {
|
|
activeGroupCode.value = groups[0].groupCode
|
|
}
|
|
})
|
|
|
|
function openPopup() {
|
|
if (props.disabled || props.loading) return
|
|
popupVisible.value = true
|
|
}
|
|
|
|
function closePopup() {
|
|
popupVisible.value = false
|
|
keyword.value = ''
|
|
}
|
|
|
|
function selectProduct(productCode: string) {
|
|
emit('update:modelValue', productCode)
|
|
closePopup()
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<a-dropdown
|
|
v-model:popup-visible="popupVisible"
|
|
trigger="click"
|
|
position="bl"
|
|
:disabled="disabled || loading"
|
|
>
|
|
<div
|
|
class="shipping-tab-select-trigger"
|
|
:class="{ disabled: disabled || loading, open: popupVisible }"
|
|
@click="openPopup"
|
|
>
|
|
<span class="trigger-text" :class="{ placeholder: !selectedLabel }">
|
|
{{ selectedLabel || placeholder || '请选择运输方式' }}
|
|
</span>
|
|
<icon-down class="trigger-icon" />
|
|
</div>
|
|
<template #content>
|
|
<div class="shipping-tab-select-panel" @click.stop>
|
|
<div class="panel-search">
|
|
<a-input
|
|
v-model="keyword"
|
|
allow-clear
|
|
size="small"
|
|
:placeholder="placeholder || '搜索产品名称或代码'"
|
|
>
|
|
<template #prefix>
|
|
<icon-search />
|
|
</template>
|
|
</a-input>
|
|
</div>
|
|
<a-tabs
|
|
v-if="filteredGroups.length"
|
|
v-model:active-key="activeGroupCode"
|
|
type="rounded"
|
|
size="small"
|
|
class="group-tabs"
|
|
>
|
|
<a-tab-pane
|
|
v-for="group in filteredGroups"
|
|
:key="group.groupCode"
|
|
:title="group.groupName"
|
|
>
|
|
<div class="product-list">
|
|
<button
|
|
v-for="product in group.products"
|
|
:key="product.productCode"
|
|
type="button"
|
|
class="product-item"
|
|
:class="{ active: modelValue === product.productCode }"
|
|
@click="selectProduct(product.productCode)"
|
|
>
|
|
<span class="product-name">{{ product.productName || product.productCode }}</span>
|
|
<span class="product-code">{{ product.productCode }}</span>
|
|
</button>
|
|
</div>
|
|
</a-tab-pane>
|
|
</a-tabs>
|
|
<a-empty v-else description="暂无运输方式" />
|
|
</div>
|
|
</template>
|
|
</a-dropdown>
|
|
</template>
|
|
|
|
<style lang="less" scoped>
|
|
.shipping-tab-select-trigger {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
width: 100%;
|
|
min-height: 32px;
|
|
padding: 4px 12px;
|
|
border: 1px solid #e5e6eb;
|
|
border-radius: 4px;
|
|
background: #fff;
|
|
cursor: pointer;
|
|
transition: border-color 0.2s, box-shadow 0.2s;
|
|
|
|
&:hover:not(.disabled) {
|
|
border-color: #c9cdd4;
|
|
}
|
|
|
|
&.open:not(.disabled) {
|
|
border-color: var(--app-primary-color, #2d8c8c);
|
|
box-shadow: 0 0 0 2px rgba(45, 140, 140, 0.12);
|
|
}
|
|
|
|
&.disabled {
|
|
cursor: not-allowed;
|
|
background: #f7f8fa;
|
|
color: #c9cdd4;
|
|
}
|
|
}
|
|
|
|
.trigger-text {
|
|
flex: 1;
|
|
min-width: 0;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
white-space: nowrap;
|
|
font-size: 14px;
|
|
color: #1d2129;
|
|
|
|
&.placeholder {
|
|
color: #c9cdd4;
|
|
}
|
|
}
|
|
|
|
.trigger-icon {
|
|
flex-shrink: 0;
|
|
margin-left: 8px;
|
|
color: #86909c;
|
|
font-size: 12px;
|
|
}
|
|
|
|
.shipping-tab-select-panel {
|
|
width: 420px;
|
|
max-width: 90vw;
|
|
padding: 8px 8px 4px;
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
.panel-search {
|
|
margin-bottom: 8px;
|
|
}
|
|
|
|
.group-tabs {
|
|
:deep(.arco-tabs-nav) {
|
|
margin-bottom: 0;
|
|
}
|
|
|
|
:deep(.arco-tabs-content) {
|
|
padding-top: 8px;
|
|
}
|
|
}
|
|
|
|
.product-list {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 4px;
|
|
max-height: 260px;
|
|
overflow-y: auto;
|
|
}
|
|
|
|
.product-item {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
gap: 12px;
|
|
width: 100%;
|
|
padding: 8px 10px;
|
|
border: none;
|
|
border-radius: 4px;
|
|
background: transparent;
|
|
text-align: left;
|
|
cursor: pointer;
|
|
transition: background-color 0.15s;
|
|
|
|
&:hover {
|
|
background: rgba(45, 140, 140, 0.06);
|
|
}
|
|
|
|
&.active {
|
|
background: rgba(45, 140, 140, 0.12);
|
|
|
|
.product-name {
|
|
color: var(--app-primary-color, #2d8c8c);
|
|
font-weight: 600;
|
|
}
|
|
}
|
|
}
|
|
|
|
.product-name {
|
|
flex: 1;
|
|
min-width: 0;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
white-space: nowrap;
|
|
font-size: 13px;
|
|
color: #1d2129;
|
|
}
|
|
|
|
.product-code {
|
|
flex-shrink: 0;
|
|
font-size: 12px;
|
|
color: #86909c;
|
|
}
|
|
</style>
|