zhiqi/src/views/order/intention/Step2 copy.vue

1002 lines
24 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="step2-container">
<div class="main-row">
<div class="right-content">
<div>
<!-- 查询条件区Tab切换 -->
<el-tabs
v-model="activeTab"
class="query-tabs"
@tab-change="handleTabChange"
>
<!-- 商品查询Tab -->
<el-tab-pane label="商品查询" name="product">
<div class="query-container">
<div class="param-form-row">
<div class="param-form-item">
<span class="param-label">车型:</span>
<el-select
v-model="selectedCarTypeProxy"
placeholder="请选择车型"
filterable
class="param-input"
@change="onQuery"
>
<el-option-group
v-for="group in carTypeOptions"
:key="group.label"
:label="group.label"
>
<el-option
v-for="option in group.options"
:key="option.value"
:label="option.label"
:value="option.value"
/>
</el-option-group>
</el-select>
</div>
<div class="param-form-item">
<span class="param-label">车轴:</span>
<el-autocomplete
v-model="paramAxle"
:fetch-suggestions="queryAxleSuggestions"
placeholder="不限"
class="param-input"
/>
</div>
<div class="param-form-item">
<span class="param-label">齿轮箱:</span>
<el-autocomplete
v-model="paramGearbox"
:fetch-suggestions="queryGearboxSuggestions"
placeholder="不限"
class="param-input"
/>
</div>
<div class="param-form-item">
<span class="param-label">车轮:</span>
<el-autocomplete
v-model="paramWheel"
:fetch-suggestions="queryWheelSuggestions"
placeholder="不限"
class="param-input"
/>
</div>
<div class="param-form-item">
<span class="param-label">制动盘:</span>
<el-autocomplete
v-model="paramBrake"
:fetch-suggestions="queryBrakeSuggestions"
placeholder="不限"
class="param-input"
/>
</div>
<div class="param-form-btns">
<el-button type="primary" class="param-btn" @click="onQuery"
>查询</el-button
>
</div>
</div>
</div>
</el-tab-pane>
<!-- 语义查询Tab -->
<el-tab-pane label="语义查询" name="semantic">
<div class="query-container">
<el-input
v-model="semanticQuery"
type="textarea"
:rows="4"
placeholder="请描述您的具体需求(比如用途、偏好等),我们将为您筛选最合适的商品"
class="semantic-input"
/>
<div class="semantic-query-btn">
<el-button type="primary" @click="onSemanticQuery"
>查询</el-button
>
</div>
</div>
</el-tab-pane>
</el-tabs>
<!-- 查询结果卡片区 -->
<div
v-if="queryResultList.length"
class="result-card-list-wrap"
style="position: relative"
>
<el-button
v-if="selectedCompareList.length > 0"
type="warning"
class="compare-btn-float"
@click="onCompare"
circle
>
参数<br />对比
</el-button>
<div class="result-card-list">
<el-card
v-for="item in queryResultList"
:key="item.code"
class="result-card"
:class="{ active: item.selected }"
@click="toggleSelect(item)"
>
<div class="result-card-checkbox-wrap" @click.stop>
<el-checkbox
v-model="item.selected"
class="result-card-checkbox"
/>
</div>
<img
:src="item.image"
class="result-card-img"
@click.stop="toggleSelect(item)"
/>
<div class="result-card-info">
<div class="result-card-name">{{ item.label }}</div>
<div class="result-card-code">{{ item.code }}</div>
</div>
</el-card>
</div>
</div>
<!-- 参数对比弹窗 -->
<el-dialog
v-model="compareDialogVisible"
title="参数对比"
width="60%"
top="40px"
:close-on-click-modal="false"
>
<!-- 表格对比区,图片放在表头下方 -->
<div class="compare-table-table-wrap">
<el-table :data="compareTableData" border style="width: 100%">
<el-table-column
prop="param"
label="差异参数"
width="180"
class-name="param-col"
align="center"
header-align="center"
/>
<el-table-column
v-for="item in selectedCompareList"
:key="item.code"
:label="item.code"
:prop="item.code"
align="center"
header-align="center"
>
<template #header="{ column }">
<div
style="
display: flex;
flex-direction: column;
align-items: center;
"
>
<img
v-if="
selectedCompareList.find(
(i) => i.code === column.label
)?.image
"
:src="
selectedCompareList.find(
(i) => i.code === column.label
).image
"
style="
width: 60px;
height: 40px;
object-fit: contain;
margin-top: 4px;
border-radius: 4px;
border: 1px solid #eee;
"
/>
<span>{{ column.label }}</span>
</div>
</template>
</el-table-column>
</el-table>
</div>
</el-dialog>
</div>
<div class="btn-row">
<el-button @click="emit('prev-step')">上一步</el-button>
<el-button type="primary" @click="onNextStep">下一步</el-button>
</div>
</div>
</div>
<!-- 车型信息弹窗 -->
<car-model-dialog
v-model="carModelDialogVisible"
:car-model="selectedCarModel"
/>
</div>
</template>
<script setup>
import { ref, computed, watch, onMounted } from "vue";
import {
carTypeOptions,
allParts,
compareTableData,
} from "@/data/stepMockData";
import CarModelDialog from "@/components/CarModelDialog.vue";
import { ElMessage } from "element-plus";
const props = defineProps({
form: Object,
selectedCarType: String,
});
const emit = defineEmits(["update:form", "prev-step", "next-step"]);
const carModelDialogVisible = ref(false);
const selectedCarModel = ref(null);
// Tab切换相关
const activeTab = ref("product"); // 默认显示商品查询
// 彻底双向绑定
const selectedCarTypeProxy = computed({
get() {
return props.form?.selectedCarType ?? "";
},
set(val) {
if (props.form) {
props.form.selectedCarType = val;
emit("update:form", props.form);
}
},
});
// 商品查询参数
const paramAxle = ref("");
const paramGearbox = ref("");
const paramWheel = ref("");
const paramBrake = ref("");
// 语义查询参数
const semanticQuery = ref("");
onMounted(() => {
// 只有已选车型才自动查询,否则等待用户选择
if (props.form.selectedCarType) {
onQuery();
}
});
function selectCarType(val) {
selectedCarTypeProxy.value = val;
}
// 处理Tab切换
function handleTabChange(tabName) {
// 可以在这里添加Tab切换时的逻辑
console.log("切换到:", tabName);
}
const queryResultList = ref([]);
// 商品查询
function onQuery() {
queryResultList.value = allParts
.filter((item) => {
// 车型过滤
if (
selectedCarTypeProxy.value &&
item.carType !== selectedCarTypeProxy.value
)
return false;
// 车轴
if (item.type === "车轴") {
if (paramAxle.value === "不限" || !paramAxle.value) return true;
return item.feature.includes(paramAxle.value);
}
// 齿轮箱
if (item.type === "齿轮箱") {
if (paramGearbox.value === "不限" || !paramGearbox.value) return true;
return item.feature.includes(paramGearbox.value);
}
// 车轮
if (item.type === "车轮") {
if (paramWheel.value === "不限" || !paramWheel.value) return true;
return item.feature.includes(paramWheel.value);
}
// 制动盘
if (item.type === "制动盘") {
if (paramBrake.value === "不限" || !paramBrake.value) return true;
return item.feature.includes(paramBrake.value);
}
// 其它类型不显示
return false;
})
.map((item) => ({ ...item, selected: false }));
}
// 语义查询
function onSemanticQuery() {
if (!semanticQuery.value.trim()) {
ElMessage.warning("请输入您的查询需求");
return;
}
// 这里只是模拟语义查询实际项目中应该调用后端API
// 简单匹配包含查询关键词的结果
const queryWords = semanticQuery.value.trim().toLowerCase();
queryResultList.value = allParts
.filter((item) => {
// 简单匹配逻辑:匹配名称、特征或类型中包含查询词
const matchName = item.label.toLowerCase().includes(queryWords);
const matchFeature = item.feature.toLowerCase().includes(queryWords);
const matchType = item.type.toLowerCase().includes(queryWords);
// 如果选择了车型,还要匹配车型
const matchCarType =
!selectedCarTypeProxy.value ||
item.carType === selectedCarTypeProxy.value;
return (matchName || matchFeature || matchType) && matchCarType;
})
.map((item) => ({ ...item, selected: false }));
if (queryResultList.value.length === 0) {
ElMessage.info("没有找到匹配的结果,请尝试其他关键词");
}
}
const compareDialogVisible = ref(false);
const selectedCompareList = computed(() =>
queryResultList.value.filter((i) => i.selected)
);
// compareTableData 直接从stepMockData.js导入无需本地定义
function onCompare() {
if (selectedCompareList.value.length === 0) {
ElMessage.warning("请先选择要对比的卡片");
return;
}
compareDialogVisible.value = true;
}
function toggleSelect(item) {
// 统计已选中的数量
const selectedCount = queryResultList.value.filter((i) => i.selected).length;
if (!item.selected && selectedCount >= 3) {
ElMessage.warning("最多只能选择3个卡片进行对比");
return;
}
item.selected = !item.selected;
}
function onNextStep() {
const selected = queryResultList.value.filter((i) => i.selected);
if (selected.length !== 1) {
ElMessage.warning("请且只能选择一个卡片进入下一步");
return;
}
// 写入form
if (props.form) {
props.form.selectedCarType = selectedCarTypeProxy.value;
props.form.selectedPartInfo = {
code: selected[0].code,
label: selected[0].label,
image: selected[0].image,
type: selected[0].type,
feature: selected[0].feature,
};
emit("update:form", props.form);
}
emit("next-step");
}
// 智能模糊建议方法
function queryAxleSuggestions(queryString, cb) {
const results = allParts
.filter(
(item) =>
item.type === "车轴" &&
item.carType === selectedCarTypeProxy.value &&
item.feature.includes(queryString)
)
.map((item) => ({ value: item.feature }));
// 增加'不限'选项
cb([
{ value: "不限" },
...results.filter(
(r, i, arr) => arr.findIndex((x) => x.value === r.value) === i
),
]);
}
function queryGearboxSuggestions(queryString, cb) {
const results = allParts
.filter(
(item) =>
item.type === "齿轮箱" &&
item.carType === selectedCarTypeProxy.value &&
item.feature.includes(queryString)
)
.map((item) => ({ value: item.feature }));
cb([
{ value: "不限" },
...results.filter(
(r, i, arr) => arr.findIndex((x) => x.value === r.value) === i
),
]);
}
function queryWheelSuggestions(queryString, cb) {
const results = allParts
.filter(
(item) =>
item.type === "车轮" &&
item.carType === selectedCarTypeProxy.value &&
item.feature.includes(queryString)
)
.map((item) => ({ value: item.feature }));
cb([
{ value: "不限" },
...results.filter(
(r, i, arr) => arr.findIndex((x) => x.value === r.value) === i
),
]);
}
function queryBrakeSuggestions(queryString, cb) {
const results = allParts
.filter(
(item) =>
item.type === "制动盘" &&
item.carType === selectedCarTypeProxy.value &&
item.feature.includes(queryString)
)
.map((item) => ({ value: item.feature }));
cb([
{ value: "不限" },
...results.filter(
(r, i, arr) => arr.findIndex((x) => x.value === r.value) === i
),
]);
}
</script>
<style scoped lang="scss">
.step2-container {
background: #fff;
padding: 24px;
border-radius: 8px;
}
.main-row {
display: flex;
align-items: flex-start;
}
.right-content {
flex: 1;
}
// Tab查询相关样式
.query-tabs {
margin-bottom: 20px;
}
:deep(.el-tabs__header) {
margin: 0;
}
// 统一的查询容器样式 - 确保两个Tab内容框大小一致
.query-container {
background-color: #ffffff;
border: 1px solid #e5e7eb;
border-radius: 8px;
padding: 20px;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
transition: all 0.2s ease;
min-height: 180px; /* 固定最小高度确保两个框大小一致 */
box-sizing: border-box; /* 确保padding不影响整体尺寸 */
}
.query-container:hover {
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
}
// 商品查询表单样式
.param-form-row {
display: flex;
flex-wrap: wrap;
align-items: center; /* 垂直居中对齐 */
gap: 24px 32px;
margin-bottom: 0;
position: relative;
height: 100%; /* 占满容器高度 */
}
.param-form-item {
display: flex;
align-items: center;
gap: 8px;
margin-bottom: 0;
}
.param-label {
font-size: 14px;
color: #666;
min-width: 60px;
text-align: right;
}
.param-input {
width: 180px;
}
.param-form-btns {
display: flex;
flex-direction: row;
align-items: center;
gap: 12px;
margin-left: auto;
min-width: 200px;
}
.param-btn {
min-width: 90px;
}
// 语义查询样式 - 移除输入框边框
.semantic-input {
width: 100%;
margin-bottom: 16px;
font-size: 14px;
line-height: 1.5;
min-height: 120px;
resize: none; /* 禁止调整大小 */
}
// 关键移除语义查询输入框的边框和阴影
:deep(.semantic-input .el-textarea__inner) {
border: none !important;
box-shadow: none !important;
padding: 10px 0; /* 调整内边距 */
line-height: 1.6;
}
// 聚焦时也不显示边框
:deep(.semantic-input .el-textarea__inner:focus) {
border: none !important;
box-shadow: none !important;
}
.semantic-query-btn {
text-align: right;
margin-top: 16px; /* 增加距离确保整体高度一致 */
}
// 结果卡片样式
.result-card-list {
display: flex;
flex-wrap: wrap;
gap: 24px;
margin-top: 24px;
}
.result-card {
width: 220px;
position: relative;
padding-top: 16px;
box-sizing: border-box;
transition: box-shadow 0.2s, transform 0.15s, border-color 0.2s;
cursor: pointer;
border: 1px solid #eee;
}
.result-card:hover {
box-shadow: 0 4px 16px rgba(33, 86, 243, 0.12);
transform: translateY(-4px) scale(1.03);
border-color: #2156f3;
}
.result-card.active {
box-shadow: 0 6px 20px rgba(33, 86, 243, 0.18);
border-color: #2156f3;
background: #f5f8ff;
transform: scale(1.04);
}
.result-card-checkbox-wrap {
position: absolute;
left: 20px;
top: 22px;
z-index: 2;
}
.result-card-checkbox {
position: static;
}
.result-card-img {
width: 100%;
height: 120px;
object-fit: contain;
border-radius: 4px;
border: 1px solid #eee;
margin-bottom: 12px;
transition: transform 0.2s;
}
.result-card:hover .result-card-img,
.result-card.active .result-card-img {
transform: scale(1.08);
}
.result-card-info {
text-align: left;
}
.result-card-name {
font-size: 15px;
color: #333;
font-weight: 500;
margin-bottom: 4px;
}
.result-card-code {
font-size: 13px;
color: #888;
}
// 对比按钮和弹窗样式
.compare-btn-float {
position: absolute;
right: 20px;
top: 170px;
z-index: 10;
width: 48px;
height: 48px;
display: flex;
align-items: center;
justify-content: center;
padding: 0;
border-radius: 8px;
box-shadow: 0 2px 12px rgba(33, 86, 243, 0.12);
transition: box-shadow 0.2s, background 0.2s;
font-size: 14px;
}
.compare-btn-float:hover {
background: #ffe9c6;
color: #999;
box-shadow: 0 4px 16px rgba(255, 153, 0, 0.18);
}
.result-card-list-wrap {
position: relative;
}
.compare-table-wrap {
margin-top: 16px;
}
:deep(.el-table .param-col),
:deep(.el-table th.param-col) {
background: #f5f7fa !important;
}
// 按钮行样式
.btn-row {
display: flex;
justify-content: flex-end;
gap: 16px;
margin-top: 20px;
}
// 其他样式
.step {
display: flex;
justify-content: space-between;
margin-bottom: 20px;
}
.step-left {
flex: 1;
margin-right: 10px;
}
.step-title {
font-size: 18px;
margin: 15px 0;
color: #333;
font-weight: 500;
&.active {
color: #2156f3;
}
.reference-model {
font-size: 14px;
color: #666;
margin-top: 8px;
padding-left: 20px;
position: relative;
&::before {
content: "";
position: absolute;
left: 0;
top: 50%;
transform: translateY(-50%);
width: 12px;
height: 12px;
background: #2156f3;
border-radius: 50%;
}
}
}
.image-box.right-align {
display: flex;
justify-content: flex-end;
align-items: flex-start;
margin-bottom: 16px;
}
.table-title {
font-weight: bold;
margin: 16px 0 8px 0;
color: #333;
}
.tree-table-row {
display: flex;
flex-direction: column;
border: 1px solid #bbb;
border-radius: 4px;
padding: 12px;
background: #fff;
}
.header-title {
flex: 1;
padding: 10px;
border-radius: 4px;
}
.header-code,
.header-name {
font-size: 14px;
color: #333;
margin-bottom: 5px;
}
.tab-container {
width: 100%;
}
.tab-header {
background: #f5f7fa;
border-radius: 4px;
padding: 16px;
margin-bottom: 20px;
}
.tab-header-item {
display: flex;
gap: 20px;
align-items: center;
}
.tab-header-image {
width: 200px;
height: 150px;
flex-shrink: 0;
}
.tab-header-info {
flex: 1;
display: flex;
flex-direction: column;
gap: 12px;
}
.tab-header-code,
.tab-header-name {
font-size: 14px;
color: #333;
line-height: 1.5;
}
.part-img {
width: 100%;
height: 100%;
object-fit: contain;
border-radius: 4px;
border: 1px solid #eee;
}
.model-form {
max-width: 900px;
margin-top: 16px;
}
.main-img {
width: 580px;
height: 260px;
object-fit: contain;
border-radius: 8px;
border: 1px solid #eee;
}
.custom-tab-label {
display: flex;
flex-direction: column;
align-items: center;
padding: 8px 0;
}
.tab-label-image {
width: 80px;
height: 60px;
margin-bottom: 8px;
}
.tab-img {
width: 100%;
height: 100%;
object-fit: contain;
border-radius: 4px;
border: 1px solid #eee;
}
.tab-label-info {
text-align: center;
}
.tab-label-code {
font-size: 12px;
color: #666;
margin-bottom: 4px;
}
.tab-label-name {
font-size: 14px;
color: #333;
font-weight: 500;
}
:deep(.el-tabs__item) {
height: auto !important;
padding: 0 20px !important;
}
:deep(.el-tabs__nav) {
display: flex;
gap: 20px;
}
:deep(.el-tabs__item.is-active) {
.tab-label-code,
.tab-label-name {
color: var(--el-color-primary);
}
}
.tree-node {
transition: background 0.2s;
padding: 8px 12px;
cursor: pointer;
border-radius: 4px;
margin-bottom: 4px;
display: flex;
align-items: center;
justify-content: space-between;
}
.tree-node.active {
color: #2156f3;
}
.tree-node:hover {
color: #2156f3;
}
.tab-content-flex {
display: flex;
min-height: 300px;
height: 400px;
max-height: 400px;
overflow: hidden;
}
.tab-left {
width: 260px;
border-right: 1px solid #eee;
padding-right: 16px;
overflow-y: auto;
height: 100%;
}
.tab-right {
flex: 1;
padding-left: 24px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
overflow-y: auto;
height: 100%;
}
.right-button {
margin: 15px 0 0 260px;
}
.detail-img {
width: 180px;
height: 120px;
object-fit: contain;
border: 1px solid #eee;
border-radius: 4px;
margin-bottom: 16px;
}
.replace-list {
display: flex;
flex-direction: column;
gap: 12px;
}
.replace-item {
display: flex;
align-items: center;
gap: 16px;
padding: 8px;
border: 1px solid #eee;
border-radius: 4px;
cursor: pointer;
}
.replace-item.selected {
border-color: #2156f3;
}
.replace-img {
width: 80px;
height: 60px;
object-fit: contain;
border-radius: 4px;
border: 1px solid #eee;
}
.no-data {
display: flex;
align-items: center;
justify-content: center;
min-height: 300px;
color: #999;
font-size: 18px;
}
// 新增样式
:deep(.el-collapse-item.is-disabled .el-collapse-item__header) {
cursor: default;
color: #303133;
&:hover {
background-color: transparent;
}
}
:deep(.el-collapse-item.is-disabled .el-collapse-item__arrow) {
display: none;
}
:deep(.el-collapse-item__header) {
padding: 0;
height: auto;
}
:deep(.el-collapse-item__content) {
padding: 0;
padding-left: 16px;
}
:deep(.el-collapse-item__wrap) {
border-bottom: none;
}
.search-row {
display: flex;
align-items: center;
margin-bottom: 8px;
}
.search-label {
font-size: 15px;
color: #666;
margin-right: 8px;
}
.car-type-text-list {
display: flex;
gap: 32px;
}
.car-type-text {
font-size: 16px;
color: #333;
cursor: pointer;
padding: 0 4px 4px 4px;
border-bottom: 2px solid transparent;
transition: color 0.2s, border 0.2s;
}
.car-type-text.active {
color: #2156f3;
border-bottom: 2px solid #2156f3;
font-weight: 500;
}
.search-divider {
border-bottom: 1px dashed #e5e5e5;
margin: 8px 0 16px 0;
}
.param-group-title {
font-size: 15px;
color: #333;
font-weight: 600;
margin: 12px 0 12px 0;
}
.result-card-checkbox .el-checkbox__input.is-checked .el-checkbox__inner {
border-color: #2156f3 !important;
background-color: #2156f3 !important;
}
</style>