|
|
@@ -0,0 +1,266 @@
|
|
|
+<template>
|
|
|
+ <div>
|
|
|
+ <el-dialog v-model="showDialog" title="选择商品" width="1400">
|
|
|
+ <div class="dialog-bos">
|
|
|
+ <div class="flex">
|
|
|
+ <el-tree-select
|
|
|
+ v-model="goodsClassify"
|
|
|
+ :data="categoryOptions"
|
|
|
+ :props="treeProps"
|
|
|
+ value-key="id"
|
|
|
+ placeholder="请选择商品分类"
|
|
|
+ clearable
|
|
|
+ check-strictly
|
|
|
+ @change="goodsClassifyChange"
|
|
|
+ style="width: 300px; margin-bottom: 10px"
|
|
|
+ />
|
|
|
+ <el-input class="ml-[20px]" v-model="queryParams.itemName" placeholder="请输入商品名称" clearable style="width: 300px; margin-bottom: 10px">
|
|
|
+ <template #append>
|
|
|
+ <el-button :icon="Search" @click="handleQuery" />
|
|
|
+ </template>
|
|
|
+ </el-input>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="flex">
|
|
|
+ <div class="tree-bos">
|
|
|
+ <!-- <el-tree :data="categoryOptions" :props="defaultProps" @node-click="handleNodeClick" :highlight-current="true" /> -->
|
|
|
+ </div>
|
|
|
+ <el-table height="600" ref="multipleTableRef" v-loading="loading" :data="tableData" border @selection-change="handleSelectionChange">
|
|
|
+ <el-table-column type="selection" width="55" />
|
|
|
+ <el-table-column label="商品图片" align="center" prop="productImage" width="100">
|
|
|
+ <template #default="scope">
|
|
|
+ <image-preview :src="scope.row.productImage" :width="60" :height="60" />
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="商品信息" align="center" minWidth="250" show-overflow-tooltip>
|
|
|
+ <template #default="scope">
|
|
|
+ <div class="text-left">
|
|
|
+ <div>{{ scope.row.itemName }}</div>
|
|
|
+ <div class="text-gray-500" style="font-size: 12px">品牌: {{ scope.row.brandName || '-' }}</div>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="SKU价格" align="center" width="180">
|
|
|
+ <template #default="scope">
|
|
|
+ <div class="text-left" style="font-size: 12px">
|
|
|
+ <div>
|
|
|
+ <span class="text-gray-500">市场价:</span>
|
|
|
+ <span class="text-red-500">¥{{ scope.row.marketPrice || '0.00' }}</span>
|
|
|
+ </div>
|
|
|
+ <div>
|
|
|
+ <span class="text-gray-500">会员价:</span>
|
|
|
+ <span class="text-red-500">¥{{ scope.row.memberPrice || '0.00' }}</span>
|
|
|
+ </div>
|
|
|
+ <div>
|
|
|
+ <span class="text-gray-500">最低价:</span>
|
|
|
+ <span class="text-red-500">¥{{ scope.row.minSellingPrice || '0.00' }}</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="成本情况" align="center" width="150">
|
|
|
+ <template #default="scope">
|
|
|
+ <div class="text-left" style="font-size: 12px">
|
|
|
+ <div>
|
|
|
+ <span class="text-gray-500">采购价:</span>
|
|
|
+ <span>¥{{ scope.row.purchasingPrice || '0.00' }}</span>
|
|
|
+ </div>
|
|
|
+ <div>
|
|
|
+ <span class="text-gray-500">暂估毛利率:</span>
|
|
|
+ <span>{{ scope.row.tempGrossMargin || '0.0000' }}%</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ </el-table>
|
|
|
+ </div>
|
|
|
+ <pagination v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" :total="total" @pagination="getList">
|
|
|
+ <template #slotDiv>
|
|
|
+ <div class="selected">已选择 {{ multipleSelection.length }} 个</div>
|
|
|
+ </template>
|
|
|
+ </pagination>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <template #footer>
|
|
|
+ <span class="dialog-footer">
|
|
|
+ <el-button @click="showDialog = false">取消</el-button>
|
|
|
+ <el-button type="primary" @click="onConfirm">确认</el-button>
|
|
|
+ </span>
|
|
|
+ </template>
|
|
|
+ </el-dialog>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script setup lang="ts">
|
|
|
+import { Search } from '@element-plus/icons-vue';
|
|
|
+import { listBase } from '@/api/pmsProduct/base';
|
|
|
+import useDiyStore from '@/store/modules/diy';
|
|
|
+import type { TableInstance } from 'element-plus';
|
|
|
+import { getCustomerProductPage } from '@/api/diy/index';
|
|
|
+import { el } from 'element-plus/es/locale/index.mjs';
|
|
|
+const goodsClassify = ref<any>('');
|
|
|
+const diyStore: any = useDiyStore();
|
|
|
+const showDialog = ref(false);
|
|
|
+const loading = ref(false);
|
|
|
+const tableData = ref<any[]>([]);
|
|
|
+const resultList = ref<any>([]); //单页之前被选中的数据
|
|
|
+const multipleSelection: any = ref([]); // 选中数据
|
|
|
+const multipleTableRef = ref<TableInstance>();
|
|
|
+const total = ref(0);
|
|
|
+const queryParams = reactive({
|
|
|
+ pageNum: 1,
|
|
|
+ pageSize: 10,
|
|
|
+ itemName: '',
|
|
|
+ topCategoryId: '',
|
|
|
+ mediumCategoryId: '',
|
|
|
+ bottomCategoryId: '',
|
|
|
+ customerId: ''
|
|
|
+});
|
|
|
+const treeProps = {
|
|
|
+ value: 'id',
|
|
|
+ label: 'label',
|
|
|
+ children: 'children'
|
|
|
+};
|
|
|
+const props = defineProps<{
|
|
|
+ categoryOptions?: any;
|
|
|
+ navIndex?: any;
|
|
|
+ clientId?: any;
|
|
|
+ type?: any;
|
|
|
+}>();
|
|
|
+const onOpen = () => {
|
|
|
+ console.log('打开弹窗');
|
|
|
+ showDialog.value = true;
|
|
|
+ getList();
|
|
|
+};
|
|
|
+
|
|
|
+// 监听表格单行选中
|
|
|
+const handleSelectionChange = (val: []) => {
|
|
|
+ multipleSelection.value = val;
|
|
|
+};
|
|
|
+
|
|
|
+/** 搜索 */
|
|
|
+const handleQuery = () => {
|
|
|
+ queryParams.pageNum = 1;
|
|
|
+ getList();
|
|
|
+};
|
|
|
+
|
|
|
+/** 获取列表 */
|
|
|
+const getList = async () => {
|
|
|
+ loading.value = true;
|
|
|
+ try {
|
|
|
+ if (props.clientId && props.clientId != 'undefined') {
|
|
|
+ queryParams.customerId = props.clientId;
|
|
|
+ } else {
|
|
|
+ delete queryParams.customerId;
|
|
|
+ }
|
|
|
+ const res =
|
|
|
+ props.clientId && props.clientId != 'undefined'
|
|
|
+ ? await getCustomerProductPage(queryParams)
|
|
|
+ : await listBase({ productStatus: 1, ...queryParams });
|
|
|
+ tableData.value = res.rows || [];
|
|
|
+ let result = [];
|
|
|
+ if (props.navIndex || props.navIndex == 0) {
|
|
|
+ if (props.type == 'editManyGoodsList') {
|
|
|
+ result = tableData.value.filter((item: any) => diyStore.editComponent.list[props.navIndex].goods_ids.includes(item.id));
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ result = tableData.value.filter((item: any) => diyStore.editComponent.goodsIds.includes(item.id));
|
|
|
+ }
|
|
|
+ resultList.value = result;
|
|
|
+ nextTick(() => {
|
|
|
+ result.forEach((item: any) => {
|
|
|
+ multipleTableRef.value?.toggleRowSelection(item, true);
|
|
|
+ });
|
|
|
+ });
|
|
|
+ total.value = res.total || 0;
|
|
|
+ } finally {
|
|
|
+ loading.value = false;
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+//确定
|
|
|
+const onConfirm = () => {
|
|
|
+ if (props.navIndex || props.navIndex == 0) {
|
|
|
+ if (props.type == 'editManyGoodsList') {
|
|
|
+ const newIds = calculateNewIds(diyStore.editComponent.list[props.navIndex].goods_ids, tableData.value, multipleSelection.value);
|
|
|
+ // console.log(newIds,'?????????????')
|
|
|
+ // return
|
|
|
+ diyStore.editComponent.list[props.navIndex].goods_ids = newIds;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ const newIds = calculateNewIds(diyStore.editComponent.goodsIds, tableData.value, multipleSelection.value);
|
|
|
+ diyStore.editComponent.goodsIds = newIds;
|
|
|
+ }
|
|
|
+ showDialog.value = false;
|
|
|
+};
|
|
|
+
|
|
|
+const calculateNewIds = (cacheIds: any, allPageItems: any, selectedItems: any) => {
|
|
|
+ // 1. 获取当前页所有存在的 ID 集合 (用于识别哪些旧数据属于当前页)
|
|
|
+ const currentPageIdSet = new Set(allPageItems.map((item) => item.id));
|
|
|
+ // 2. 获取最终选中项的 ID 集合
|
|
|
+ const selectedIdSet = new Set(selectedItems.map((item) => item.id));
|
|
|
+ // 3. 过滤旧的缓存 IDs
|
|
|
+ const retainedOldIds = cacheIds.filter((id) => {
|
|
|
+ // 情况 A: 该 ID 不在当前页数据中 (说明是其他页的数据,必须无条件保留)
|
|
|
+ if (!currentPageIdSet.has(id)) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ // 情况 B: 该 ID 在当前页数据中,且也在最终选中列表中 (说明用户保持了选中)
|
|
|
+ if (selectedIdSet.has(id)) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ // 情况 C: 该 ID 在当前页数据中,但不在最终选中列表中 (说明用户取消了选中,如 ID 4)
|
|
|
+ // 返回 false,将其剔除
|
|
|
+ return false;
|
|
|
+ });
|
|
|
+ // 4. 合并:保留的旧数据 + 当前页新选中的数据
|
|
|
+ // 使用 Set 去重,虽然逻辑上 retainedOldIds 和 selectedIdSet 不会有交集,但以防万一
|
|
|
+ const newIdsSet = new Set([...retainedOldIds, ...selectedIdSet]);
|
|
|
+ // 转回数组 (如果需要保持原有顺序或特定排序,可以在此处调整)
|
|
|
+ // 这里简单转为数组,通常建议按数字大小排序以便阅读,或者保持插入顺序
|
|
|
+ return Array.from(newIdsSet).sort((a, b) => a - b);
|
|
|
+};
|
|
|
+
|
|
|
+//选择商品分类
|
|
|
+const goodsClassifyChange = (res: any) => {
|
|
|
+ const foundNode = findNodeByKey(props.categoryOptions, res);
|
|
|
+ goodsClassify.value = res;
|
|
|
+ queryParams.topCategoryId = '';
|
|
|
+ queryParams.mediumCategoryId = '';
|
|
|
+ queryParams.bottomCategoryId = '';
|
|
|
+ if (foundNode.parentId == 0) {
|
|
|
+ queryParams.topCategoryId = foundNode.id;
|
|
|
+ } else if (foundNode.children) {
|
|
|
+ queryParams.mediumCategoryId = foundNode.id;
|
|
|
+ } else {
|
|
|
+ queryParams.bottomCategoryId = foundNode.id;
|
|
|
+ }
|
|
|
+ handleQuery();
|
|
|
+};
|
|
|
+
|
|
|
+// 递归查找节点的辅助函数
|
|
|
+const findNodeByKey = (nodes: any, key: any) => {
|
|
|
+ for (const node of nodes) {
|
|
|
+ if (node.id === key) {
|
|
|
+ return node;
|
|
|
+ }
|
|
|
+ if (node.children) {
|
|
|
+ const found = findNodeByKey(node.children, key);
|
|
|
+ if (found) return found;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+};
|
|
|
+
|
|
|
+defineExpose({
|
|
|
+ onOpen
|
|
|
+});
|
|
|
+</script>
|
|
|
+
|
|
|
+<style lang="scss">
|
|
|
+.selected {
|
|
|
+ line-height: 32px;
|
|
|
+ position: absolute;
|
|
|
+ left: 0px;
|
|
|
+}
|
|
|
+</style>
|