Files
deShanXiao/frontEnd/src/pages/funeralRetail/publicComponents/serviceItemSelect.vue

354 lines
8.2 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="service-selection">
<!-- 标题 -->
<h2 class="title">服务项目选择</h2>
<!-- 搜索框 -->
<el-form>
<el-row :gutter="15" style="margin-top: 15px">
<el-col :span="8">
<el-form-item label="商品名称">
<el-input
v-model="searchKeyword"
placeholder="请输入商品名称"
clearable />
</el-form-item>
</el-col>
<el-col :span="16">
<el-form-item label="价格">
<el-input-number
v-model="price"
:min="0"
clearable
placeholder="请输入价格"
type="number"></el-input-number>
</el-form-item>
</el-col>
</el-row>
</el-form>
<el-divider style="margin: 8px 0; margin-bottom: 30px" />
<!-- 两列布局 -->
<div
class="content"
v-loading="lodaing"
element-loading-text="数据加载中....">
<!-- 左侧树结构 -->
<div class="left">
<el-tree
ref="tree"
:data="categoryTree"
:props="treeProps"
show-checkbox
check-strictly
@check="getCheckedKeys" />
</div>
<!-- 右侧商品列表 -->
<div class="right">
<div
v-for="(item, index) in categoryServiceList"
:key="index"
class="item">
<!-- 商品信息 -->
<div class="item-info">
<div class="item-name">商品名称{{ item.name }}</div>
<div class="item-group">
所属分类{{
categoryTreeAll.find((fitem) => fitem.id === item.parentId)
?.name || "无"
}}
</div>
<div class="item-price">
{{ item.price }} / {{ item.unit || "(暂无单位)" }}
</div>
</div>
<!-- 添加/删除按钮 -->
<div class="item-actions">
<!-- <el-button
v-if="!item.quantity || item.quantity === 0"
type="primary"
@click="addItem(item)">
<span style="color: #fff"> 添加</span>
</el-button>
<div v-else class="quantity-control">
<el-button type="danger" @click="removeItem(item)"></el-button>
<span class="quantity">{{ item.quantity }}</span>
<el-button type="primary" @click="addItem(item)"> </el-button>
</div> -->
<el-input-number
style="margin-top: 5px"
v-model="item.quantity"
:min="0"
@change="methods.quantityChange(item)" />
</div>
</div>
</div>
</div>
<!-- 底部悬浮框 -->
<div class="footer">
<div class="footer-item">
<span>总金额</span>
<span
><span class="red-font">{{ totalAmount }} </span></span
>
</div>
<!-- <div class="footer-item">
<span>减免金额</span>
<span
><span class="green-font">
{{ discountAmount }}
</span>
</span
>
</div>
<div class="footer-item">
<span>实收金额</span>
<span
><span class="red-font">
{{ actualAmount }}
</span>
</span
>
</div> -->
</div>
</div>
</template>
<script setup lang="ts">
import { ref, computed, onMounted, watch } from "vue";
import api from "@/lib/request";
const emits = defineEmits(["selectedProduct"]);
const products = defineModel<Product[]>();
// 搜索关键词
const searchKeyword = ref("");
const price = ref(0);
const lodaing = ref(false);
// 分类树数据
const categoryTree = ref([]);
const categoryTreeAll = ref([]);
const categoryServiceAll = ref<Product[]>([]);
const selectedCategory = ref([]);
const tree = ref();
const categoryServiceList = computed(() => {
let resData: Product[] = [];
selectedCategory.value.forEach((item) => {
let findData = categoryServiceAll.value.filter(
(fitem) => fitem.parentId === item.id
);
if (findData) {
resData = [...resData, ...findData];
}
});
let res = selectedCategory.value.length ? resData : categoryServiceAll.value;
if (searchKeyword.value) {
res = res.filter((item) => item.name.includes(searchKeyword.value));
let includeCategory = categoryTreeAll.value.filter((category) =>
category.name.includes(searchKeyword.value)
);
categoryServiceAll.value.forEach((item) => {
includeCategory.forEach((iitem) => {
if (
item.parentId === iitem.id &&
!res.find((fitem) => fitem.name === item.name)
) {
res.unshift(item);
}
});
});
}
if (price.value && price.value !== 0) {
res = res.filter((fitem) => Number(fitem.price) === price.value);
}
return res;
});
// 树结构配置
const treeProps = {
children: "children",
label: "name",
};
const syncProductsWithCategoryServiceAll = () => {
if (!products.value) return; // 防止 products 为空时报错
categoryServiceAll.value.forEach((item) => {
const findItem = products.value?.find(
(newItem) => newItem.name === item.name
);
if (findItem) {
item.quantity = findItem.quantity;
} else {
item.quantity = 0;
}
});
};
watch(
() => products.value,
(newVal) => {
syncProductsWithCategoryServiceAll();
},
{
deep: true,
immediate: true,
}
);
// 总金额
const totalAmount = computed(() => {
return categoryServiceAll.value.reduce(
(sum, item) => sum + item.price * (item.quantity || 0),
0
);
});
const methods = {
quantityChange(item: any) {
let findData = products.value?.find(
(findItem) => findItem.name === item.name
);
if (!findData) {
products.value?.push(item);
} else {
findData.quantity = item.quantity;
}
},
};
const getCheckedKeys = (data: any) => {
selectedCategory.value = tree.value.getCheckedNodes();
};
onMounted(async () => {
lodaing.value = true;
let treeList = await api().get("/service-category/list");
let allTreeList = await api().get("/service-category/list", {
params: { all: true },
});
let allService = await api().get("/service-item/list", {
params: { all: true },
});
categoryTree.value = treeList.data.list;
categoryServiceAll.value = allService.data.list;
categoryTreeAll.value = allTreeList.data.list;
syncProductsWithCategoryServiceAll();
lodaing.value = false;
});
</script>
<style lang="scss" scoped>
.service-selection {
position: relative;
overflow: hidden;
.title {
font-size: 18px;
font-weight: bold;
}
.search-box {
margin-bottom: 20px;
width: 50%;
}
.left {
width: 200px;
margin-right: 20px;
}
.right {
flex: 1;
overflow-y: auto;
padding-bottom: 30px;
max-height: calc(100vh - 150px);
flex-wrap: wrap;
display: flex;
width: 100%;
justify-content: space-between;
align-items: flex-start;
}
.content {
display: flex;
justify-content: space-between;
.item {
display: flex;
flex-wrap: wrap;
width: calc(50% - 10px);
align-items: center;
margin-bottom: 10px;
padding: 10px;
border: 1px solid #eee;
border-radius: 4px;
flex-direction: column;
// .item-image {
// width: 100px;
// height: 100px;
// margin-right: 10px;
// }
.item-info {
flex: 1;
}
.item-name {
font-weight: bold;
}
.item-category,
.item-group,
.item-price {
font-size: 12px;
color: #666;
}
.item-actions {
margin-left: 10px;
}
}
}
}
.quantity-control {
display: flex;
align-items: center;
}
.quantity {
margin: 0 10px;
}
.footer {
position: fixed;
bottom: 0;
right: 0;
width: 50%;
z-index: 10;
background: #fff;
padding: 10px 20px;
border-top: 1px solid #eee;
display: flex;
justify-content: space-around;
box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.1);
.footer-item {
font-size: 14px;
}
.red-font,
.green-font {
font-size: 18px;
color: #f04b22;
font-weight: bold;
padding-right: 8px;
}
.green-font {
color: #67c23a;
}
}
</style>