466 lines
12 KiB
Vue
466 lines
12 KiB
Vue
<template>
|
|
<div class="step2-container">
|
|
<div class="main-row">
|
|
<div class="step-indicator">
|
|
<div :class="['circle', currentStep === 1 ? 'active' : '']">1</div>
|
|
<div class="line"></div>
|
|
<div :class="['circle', currentStep === 2 ? 'active' : '']">2</div>
|
|
</div>
|
|
<div class="right-content">
|
|
<div v-if="currentStep === 1">
|
|
<div class="step-title">第一步:选择车型</div>
|
|
<el-form label-width="120px" class="model-form">
|
|
<el-form-item label="车型列表" required>
|
|
<el-select
|
|
v-model="selectedCarTypeProxy"
|
|
placeholder="请选择车型"
|
|
filterable
|
|
@change="handleCarTypeChange"
|
|
>
|
|
<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>
|
|
</el-form-item>
|
|
<el-form-item label="计划购买数量" required>
|
|
<el-input-number v-model="planCount" :min="1" :max="999" />
|
|
</el-form-item>
|
|
<el-form-item label="计划交付时间" required>
|
|
<el-date-picker
|
|
v-model="deliveryTime"
|
|
type="date"
|
|
placeholder="选择日期"
|
|
format="YYYY-MM-DD"
|
|
value-format="YYYY-MM-DD"
|
|
/>
|
|
</el-form-item>
|
|
<el-form-item label="计划交付方式" required>
|
|
<el-radio-group v-model="deliveryMethod">
|
|
<el-radio label="self">自行提货</el-radio>
|
|
<el-radio label="logistics">物流配送</el-radio>
|
|
</el-radio-group>
|
|
</el-form-item>
|
|
|
|
<!-- 其他车型的额外表单 -->
|
|
<template v-if="selectedCarTypeProxy === '其他'">
|
|
<el-form-item label="可参考车型列表">
|
|
<el-select
|
|
v-model="referenceCarType"
|
|
placeholder="请选择参考车型"
|
|
filterable
|
|
@change="handleReferenceCarTypeChange"
|
|
>
|
|
<el-option-group
|
|
v-for="group in referenceCarTypeOptions"
|
|
: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>
|
|
</el-form-item>
|
|
<el-form-item label="车型输入">
|
|
<el-input v-model="modelInput" placeholder="请输入车型" />
|
|
</el-form-item>
|
|
<el-form-item label="车型描述">
|
|
<el-input
|
|
type="textarea"
|
|
:rows="5"
|
|
v-model="modelDesc"
|
|
placeholder="请输入车型描述"
|
|
/>
|
|
</el-form-item>
|
|
</template>
|
|
</el-form>
|
|
</div>
|
|
<div v-else>
|
|
<div class="step">
|
|
<div class="step-left">
|
|
<div class="step-title">
|
|
第一步:车型{{
|
|
selectedCarTypeProxy === "其他"
|
|
? "其他"
|
|
: selectedCarTypeProxy
|
|
}}
|
|
<div
|
|
v-if="selectedCarTypeProxy === '其他'"
|
|
class="reference-model"
|
|
>
|
|
参考车型:{{ referenceCarType || "未选择" }}
|
|
</div>
|
|
</div>
|
|
<div
|
|
class="step-title"
|
|
:style="{
|
|
marginTop:
|
|
selectedCarTypeProxy === '其他' ? '165px' : '192px',
|
|
}"
|
|
>
|
|
第二步:选择轮对商品
|
|
</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="tree-table-row">
|
|
<el-tree
|
|
:data="treeData"
|
|
:props="treeProps"
|
|
node-key="id"
|
|
highlight-current
|
|
@current-change="handleTreeSelect"
|
|
class="tree-box"
|
|
default-expand-all
|
|
/>
|
|
<el-table
|
|
:data="selectedPartInfo ? [selectedPartInfo] : []"
|
|
border
|
|
class="part-table"
|
|
style="width: 600px; margin-left: 24px"
|
|
>
|
|
<el-table-column prop="name" label="部件名称">
|
|
<template #default="scope">
|
|
<el-input v-model="scope.row.name" />
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column prop="spec" label="规格">
|
|
<template #default="scope">
|
|
<el-input v-model="scope.row.spec" />
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column prop="desc" label="描述">
|
|
<template #default="scope">
|
|
<el-input v-model="scope.row.desc" />
|
|
</template>
|
|
</el-table-column>
|
|
</el-table>
|
|
</div>
|
|
</div>
|
|
<div class="btn-row">
|
|
<el-button @click="handlePrevStep">上一步</el-button>
|
|
<el-button type="primary" @click="handleNextStep">下一步</el-button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 车型信息弹窗 -->
|
|
<car-model-dialog
|
|
v-model="carModelDialogVisible"
|
|
:car-model="selectedCarModel"
|
|
/>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { ref, computed, watch } from "vue";
|
|
import { carTypeOptions, treeData, partInfoMap } from "@/data/stepMockData";
|
|
import CarModelDialog from "@/components/CarModelDialog.vue";
|
|
|
|
const props = defineProps({
|
|
form: Object,
|
|
selectedCarType: String,
|
|
});
|
|
|
|
const emit = defineEmits(["update:form", "prev-step", "next-step"]);
|
|
|
|
const currentStep = ref(1);
|
|
const carModelDialogVisible = ref(false);
|
|
const selectedCarModel = ref(null);
|
|
const referenceCarType = ref("");
|
|
const deliveryTime = ref("");
|
|
const deliveryMethod = ref("");
|
|
|
|
// 计算属性:参考车型选项(排除"其他"选项)
|
|
const referenceCarTypeOptions = computed(() => {
|
|
return carTypeOptions.map((group) => ({
|
|
...group,
|
|
options: group.options.filter((option) => option.value !== "其他"),
|
|
}));
|
|
});
|
|
|
|
// 彻底双向绑定
|
|
const selectedCarTypeProxy = computed({
|
|
get() {
|
|
return props.form?.selectedCarType ?? "其他";
|
|
},
|
|
set(val) {
|
|
if (props.form) {
|
|
props.form.selectedCarType = val;
|
|
emit("update:form", props.form);
|
|
}
|
|
},
|
|
});
|
|
|
|
const planCount = computed({
|
|
get() {
|
|
return props.form?.planCount ?? "";
|
|
},
|
|
set(val) {
|
|
if (props.form) {
|
|
props.form.planCount = val;
|
|
emit("update:form", props.form);
|
|
}
|
|
},
|
|
});
|
|
|
|
const modelInput = computed({
|
|
get() {
|
|
return props.form?.modelInput ?? "";
|
|
},
|
|
set(val) {
|
|
if (props.form) {
|
|
props.form.modelInput = val;
|
|
emit("update:form", props.form);
|
|
}
|
|
},
|
|
});
|
|
|
|
const modelDesc = computed({
|
|
get() {
|
|
return props.form?.modelDesc ?? "";
|
|
},
|
|
set(val) {
|
|
if (props.form) {
|
|
props.form.modelDesc = val;
|
|
emit("update:form", props.form);
|
|
}
|
|
},
|
|
});
|
|
|
|
const selectedPartInfo = computed({
|
|
get() {
|
|
return props.form?.selectedPartInfo ?? null;
|
|
},
|
|
set(val) {
|
|
if (props.form) {
|
|
props.form.selectedPartInfo = val;
|
|
emit("update:form", props.form);
|
|
}
|
|
},
|
|
});
|
|
|
|
// 监听车型变化,清空表单
|
|
watch(selectedCarTypeProxy, (newVal, oldVal) => {
|
|
if (newVal !== oldVal) {
|
|
// 清空所有相关字段
|
|
if (props.form) {
|
|
props.form.planCount = "";
|
|
props.form.modelInput = "";
|
|
props.form.modelDesc = "";
|
|
props.form.selectedPartInfo = null;
|
|
emit("update:form", props.form);
|
|
}
|
|
}
|
|
});
|
|
|
|
// 模拟树形结构数据
|
|
const treeProps = { children: "children", label: "label" };
|
|
|
|
function handleTreeSelect(node) {
|
|
if (node && node.id && partInfoMap[node.id]) {
|
|
selectedPartInfo.value = { ...partInfoMap[node.id] };
|
|
} else {
|
|
selectedPartInfo.value = null;
|
|
}
|
|
}
|
|
|
|
function handleCarTypeChange(value) {
|
|
// 只有非"其他"选项才显示车型信息弹窗
|
|
if (value !== "其他") {
|
|
const selectedOption = carTypeOptions
|
|
.flatMap((group) => group.options)
|
|
.find((option) => option.value === value);
|
|
|
|
if (selectedOption) {
|
|
selectedCarModel.value = {
|
|
name: selectedOption.label,
|
|
description: selectedOption.description || "",
|
|
specifications: selectedOption.specifications || {},
|
|
image: selectedOption.image || "",
|
|
};
|
|
carModelDialogVisible.value = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
function handleReferenceCarTypeChange(value) {
|
|
// 显示参考车型信息弹窗
|
|
const selectedOption = referenceCarTypeOptions.value
|
|
.flatMap((group) => group.options)
|
|
.find((option) => option.value === value);
|
|
|
|
if (selectedOption) {
|
|
selectedCarModel.value = {
|
|
name: selectedOption.label,
|
|
description: selectedOption.description || "",
|
|
specifications: selectedOption.specifications || {},
|
|
image: selectedOption.image || "",
|
|
};
|
|
carModelDialogVisible.value = true;
|
|
}
|
|
}
|
|
|
|
function handlePrevStep() {
|
|
if (currentStep.value === 2) {
|
|
currentStep.value = 1;
|
|
} else {
|
|
emit("prev-step");
|
|
}
|
|
}
|
|
|
|
function handleNextStep() {
|
|
if (currentStep.value === 1) {
|
|
currentStep.value = 2;
|
|
} else {
|
|
emit("next-step");
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style scoped lang="scss">
|
|
.step2-container {
|
|
background: #fff;
|
|
padding: 24px;
|
|
border-radius: 8px;
|
|
}
|
|
.main-row {
|
|
display: flex;
|
|
align-items: flex-start;
|
|
}
|
|
.step-indicator {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
margin-right: 24px;
|
|
margin-top: 10px;
|
|
}
|
|
.circle {
|
|
width: 32px;
|
|
height: 32px;
|
|
border-radius: 50%;
|
|
background: #ccc;
|
|
color: #fff;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
font-size: 20px;
|
|
margin-bottom: 8px;
|
|
}
|
|
.circle.active {
|
|
background: #2156f3;
|
|
}
|
|
.line {
|
|
width: 4px;
|
|
height: 170px;
|
|
background: #ccc;
|
|
margin-bottom: 8px;
|
|
}
|
|
.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;
|
|
align-items: flex-start;
|
|
border: 1px solid #bbb;
|
|
border-radius: 4px;
|
|
padding: 12px;
|
|
background: #fff;
|
|
}
|
|
.tree-box {
|
|
min-width: 340px;
|
|
max-width: 400px;
|
|
margin-right: 24px;
|
|
background: #fff;
|
|
}
|
|
.part-table {
|
|
min-width: 350px;
|
|
max-width: 600px;
|
|
}
|
|
.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;
|
|
}
|
|
</style> |