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

882 lines
20 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>
<!-- 查询条件区车型车轴齿轮箱车轮制动盘 -->
<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
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-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);
// 彻底双向绑定
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("");
onMounted(() => {
// 只有已选车型才自动查询,否则等待用户选择
if (props.form.selectedCarType) {
onQuery();
}
});
function selectCarType(val) {
selectedCarTypeProxy.value = val;
}
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 === "不限") return true;
return item.feature.includes(paramAxle.value);
}
// 齿轮箱
if (item.type === "齿轮箱") {
if (paramGearbox.value === "不限") return true;
return item.feature.includes(paramGearbox.value);
}
// 车轮
if (item.type === "车轮") {
if (paramWheel.value === "不限") return true;
return item.feature.includes(paramWheel.value);
}
// 制动盘
if (item.type === "制动盘") {
if (paramBrake.value === "不限") return true;
return item.feature.includes(paramBrake.value);
}
// 其它类型不显示
return false;
})
.map((item) => ({ ...item, selected: false }));
}
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;
}
.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;
}
.btn-row {
display: flex;
justify-content: flex-end;
gap: 16px;
margin-top: 20px;
}
.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;
}
.param-form-row {
display: flex;
flex-wrap: wrap;
align-items: flex-start;
gap: 24px 32px;
margin-bottom: 0;
position: relative;
}
.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: 120px;
}
.param-form-btns {
display: flex;
flex-direction: row;
align-items: center;
gap: 12px;
margin-left: auto;
min-width: 200px;
}
.param-btn {
min-width: 90px;
}
.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-dialog-cards {
display: flex;
gap: 32px;
margin-bottom: 24px;
}
.compare-card {
width: 220px;
text-align: center;
box-sizing: border-box;
}
.compare-card-img {
width: 100%;
height: 120px;
object-fit: contain;
border-radius: 4px;
border: 1px solid #eee;
margin-bottom: 12px;
}
.compare-card-info {
text-align: center;
}
.compare-card-name {
font-size: 15px;
color: #333;
font-weight: 500;
margin-bottom: 4px;
}
.compare-card-code {
font-size: 13px;
color: #888;
}
.compare-table-wrap {
margin-top: 16px;
}
.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;
}
.result-card-checkbox .el-checkbox__input.is-checked .el-checkbox__inner {
border-color: #2156f3 !important;
background-color: #2156f3 !important;
}
// 参数对比表格第一列背景色与表头一致
:deep(.el-table .param-col),
:deep(.el-table th.param-col) {
background: #f5f7fa !important;
}
</style>