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

754 lines
17 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="step3-container">
<div class="main-row">
<div class="right-content">
<!-- 商品参数选择页面内容 -->
<div class="step">
<div class="step-left">
<div class="step-title">
车型:{{
selectedCarTypeProxy === "其他" ? "其他" : selectedCarTypeProxy
}}
<div
v-if="selectedCarTypeProxy === '其他'"
class="reference-model"
>
参考车型:{{ referenceCarType || "未选择" }}
</div>
</div>
</div>
<div class="image-box right-align">
<img src="/images/3D.png" alt="3D结构图" class="main-img" />
</div>
</div>
<div class="table-title">商品参数选择</div>
<!-- 一级菜单作为表头 -->
<div
class="header-title"
v-for="item in firstLevelItems"
:key="item.code"
>
<div class="header-code">编号:{{ item.code }}</div>
<div class="header-name">名称:{{ item.label }}</div>
</div>
<div class="tree-table-row">
<!-- 二级菜单作为tab页 -->
<div class="tab-container">
<el-tabs v-model="activeTab" @tab-click="handleTabClick">
<el-tab-pane
v-for="item in secondLevelItems"
:key="item.code"
:name="item.code"
>
<template #label>
<div class="custom-tab-label">
<div class="tab-label-image">
<img
:src="item.image || '/images/part-default.png'"
class="tab-img"
/>
</div>
<div class="tab-label-info">
<div class="tab-label-code">{{ item.code }}</div>
<div class="tab-label-name">{{ item.label }}</div>
</div>
</div>
</template>
<!-- 三级及以下树形结构 -->
<div
v-if="
activeSecondLevelItem &&
activeSecondLevelItem.children &&
activeSecondLevelItem.children.length
"
class="tab-content tab-content-flex"
>
<div class="tab-left">
<el-collapse v-model="collapseActiveNames" accordion>
<el-collapse-item
v-for="thirdLevel in activeSecondLevelItem.children"
:key="thirdLevel.code"
:name="thirdLevel.code"
:disabled="
!thirdLevel.children ||
thirdLevel.children.length === 0
"
>
<template #title>
<span
:class="[
'tree-node',
selectedNode &&
selectedNode.code === thirdLevel.code
? 'active'
: '',
]"
@click.stop="handleNodeClick(thirdLevel)"
>
{{ thirdLevel.label }}
<el-button
v-if="thirdLevel.replaceable"
type="primary"
link
@click.stop="openReplaceDialog(thirdLevel)"
>替换</el-button
>
</span>
</template>
<ul
v-if="
thirdLevel.children &&
thirdLevel.children.length > 0
"
>
<li
v-for="fourthLevel in thirdLevel.children"
:key="fourthLevel.code"
:class="[
'tree-node',
selectedNode &&
selectedNode.code === fourthLevel.code
? 'active'
: '',
]"
@click="handleNodeClick(fourthLevel)"
>
<span>{{ fourthLevel.label }}</span>
<el-button
v-if="fourthLevel.replaceable"
type="primary"
link
@click.stop="openReplaceDialog(fourthLevel)"
>替换</el-button
>
</li>
</ul>
</el-collapse-item>
</el-collapse>
</div>
<div class="tab-right" v-if="selectedNode">
<img
:src="selectedNode.image"
class="detail-img"
v-if="selectedNode.image"
/>
<div class="detail-info">
<div>编号:{{ selectedNode.code }}</div>
<div>名称:{{ selectedNode.label }}</div>
<el-button
v-if="selectedNode.replaceable"
type="primary"
class="right-button"
@click="openReplaceDialog(selectedNode)"
>替换</el-button
>
</div>
</div>
<div class="tab-right" v-else>
<span>请选择左侧商品</span>
</div>
</div>
<div v-else class="tab-content no-data">暂无数据</div>
</el-tab-pane>
</el-tabs>
</div>
</div>
<el-dialog
v-model="replaceDialogVisible"
title="特定替换部件"
width="500px"
>
<div class="replace-list">
<div
v-for="item in replaceList"
:key="item.code"
class="replace-item"
:class="{ selected: replaceSelected === item.code }"
@click="replaceSelected = item.code"
>
<img :src="item.image" class="replace-img" v-if="item.image" />
<div>
<div>编号:{{ item.code }}</div>
<div>名称:{{ item.label }}</div>
</div>
</div>
</div>
<template #footer>
<el-button @click="replaceDialogVisible = false">取消</el-button>
<el-button type="primary" @click="handleReplace">替换</el-button>
</template>
</el-dialog>
<div class="btn-row">
<el-button @click="emit('prev-step')">上一步</el-button>
<el-button type="primary" @click="emit('next-step')"
>下一步</el-button
>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref, computed, watch, onMounted } from "vue";
import {
carTypeOptions,
productTreeData,
replacePartsMap,
} from "@/data/stepMockData";
const props = defineProps({
form: Object,
selectedCarType: String,
});
const emit = defineEmits(["update:form", "prev-step", "next-step"]);
const referenceCarType = ref("");
const selectedCarTypeProxy = computed({
get() {
return props.form?.selectedCarType ?? "其他";
},
set(val) {
if (props.form) {
props.form.selectedCarType = val;
emit("update:form", props.form);
}
},
});
// 商品参数选择相关逻辑
const treeData = ref(productTreeData);
const selectedNode = ref(null);
const replaceDialogVisible = ref(false);
const replaceList = ref([]);
const replaceSelected = ref(null);
const activeTab = ref("");
const collapseActiveNames = ref([]);
// 计算一级菜单项
const firstLevelItems = computed(() => treeData.value);
// 计算二级菜单项(只取第一个一级菜单的 children 作为 tab
const secondLevelItems = computed(() => {
return treeData.value.length > 0 ? treeData.value[0].children || [] : [];
});
// 当前激活的二级菜单项
const activeSecondLevelItem = computed(() => {
return secondLevelItems.value.find((item) => item.code === activeTab.value);
});
// 处理tab切换
function handleTabClick(tab) {
activeTab.value = tab.props.name;
selectedNode.value = null;
}
// 处理节点点击
function handleNodeClick(node) {
selectedNode.value = node;
}
// 打开替换对话框
function openReplaceDialog(node) {
replaceList.value = replacePartsMap[node.code] || [];
replaceSelected.value = null;
replaceDialogVisible.value = true;
}
// 处理替换
function handleReplace() {
if (!replaceSelected.value) return;
const newPart = replaceList.value.find(
(item) => item.code === replaceSelected.value
);
if (newPart) {
selectedNode.value.code = newPart.code;
selectedNode.value.label = newPart.label;
selectedNode.value.image = newPart.image;
}
replaceDialogVisible.value = false;
}
const treeProps = {
label: "label",
children: "children",
};
function selectFirstNode() {
const thirdLevels = activeSecondLevelItem.value?.children || [];
if (thirdLevels.length > 0) {
// 默认选择第一个节点
selectedNode.value = thirdLevels[0];
// 如果第一个节点有子节点,则展开并选择第一个子节点
if (thirdLevels[0].children && thirdLevels[0].children.length > 0) {
collapseActiveNames.value = [thirdLevels[0].code];
selectedNode.value = thirdLevels[0].children[0];
} else {
collapseActiveNames.value = [];
}
} else {
selectedNode.value = null;
collapseActiveNames.value = [];
}
}
onMounted(() => {
if (secondLevelItems.value.length > 0) {
activeTab.value = secondLevelItems.value[0].code;
}
selectFirstNode();
});
watch(secondLevelItems, (newVal) => {
if (newVal.length > 0) {
activeTab.value = newVal[0].code;
}
selectFirstNode();
});
watch(activeTab, () => {
selectFirstNode();
});
</script>
<style scoped lang="scss">
.step3-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;
}
.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;
}
.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;
}
</style>