From cbaf89c8354975051c821c9c8fb6db205caad02d Mon Sep 17 00:00:00 2001 From: "pikachu1995@126.com" Date: Wed, 4 Nov 2026 11:07:37 +0800 Subject: [PATCH] =?UTF-8?q?feat(goods):=20=E5=AE=8C=E5=96=84=E5=95=86?= =?UTF-8?q?=E5=93=81=E5=AE=9A=E6=97=B6=E4=B8=8A=E4=B8=8B=E6=9E=B6=E5=8A=9F?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增定时下架支持,商品列表同时展示上架和下架计划 - 添加定时计划的编辑和删除操作入口 - 新增取消定时上下架API接口 - 商品选择对话框支持商品模式,适配SPU维度选择 - 商品选择对话框增加高级筛选折叠和品牌联动逻辑 - 优化商品列表筛选布局为弹性自适应宽度 - 管理端定时计划页面增加定时下架标签页 - 商家端定时计划页面重构为完整CRUD抽屉交互 - 修改楼层列表新建逻辑改为路由跳转装修页 --- manager/src/api/goods.js | 4 + manager/src/views/goods/goods-info/goods.vue | 23 ++- .../src/views/goods/scheduled-upper/index.vue | 33 +++- seller/src/api/goods.js | 4 + seller/src/views/goods/goods-seller/goods.vue | 30 ++-- .../goods/goods-seller/goodsOperationSec.vue | 6 +- .../src/views/goods/scheduled-upper/index.vue | 159 +++++++++++++++++- seller/src/views/lili-dialog/goods-dialog.vue | 145 +++++++++------- seller/src/views/lili-dialog/index.vue | 41 ++++- seller/src/views/shop/floorList.vue | 7 +- seller/src/views/shop/pc/index.vue | 4 +- 11 files changed, 366 insertions(+), 90 deletions(-) diff --git a/manager/src/api/goods.js b/manager/src/api/goods.js index 6bebf41fc..756ed769a 100644 --- a/manager/src/api/goods.js +++ b/manager/src/api/goods.js @@ -1,6 +1,10 @@ // 统一请求路径前缀在libs/axios.js中修改 import { getRequest, postRequest, putRequest, deleteRequest} from '@/libs/axios'; +export const cancelGoodsMarketSchedule = (goodsId, status) => { + return deleteRequest(`/goods/goods/schedule/market/${goodsId}`, { status }); +}; + // 获取商品品牌分页列表 export const getManagerBrandPage = (params) => { return getRequest('/goods/brand/getByPage', params) diff --git a/manager/src/views/goods/goods-info/goods.vue b/manager/src/views/goods/goods-info/goods.vue index bf2a71544..d9f8b1436 100644 --- a/manager/src/views/goods/goods-info/goods.vue +++ b/manager/src/views/goods/goods-info/goods.vue @@ -70,6 +70,7 @@ +
+ - + @@ -165,7 +167,12 @@ - + + + + + + +
@@ -222,6 +221,9 @@ const defaultFilterForm = () => ({ }); const props = defineProps({ + productMode: { type: Boolean, default: false }, + marketEnable: { type: String, default: "UPPER" }, + authFlag: { type: String, default: "PASS" }, selectedWay: { type: Array, default: () => [], @@ -230,6 +232,7 @@ const props = defineProps({ const emit = defineEmits(["selected"]); const type = ref("multiple"); +const showAdvanced = ref(false); const selectedList = ref([]); const platformCategoryList = ref([]); const shopCategoryList = ref([]); @@ -250,13 +253,26 @@ const shopCategoryProps = { emitPath: false, }; const filterForm = ref(defaultFilterForm()); +let brandRequest = 0; +watch(() => filterForm.value.categoryPath, async (categoryId) => { + const request = ++brandRequest; + filterForm.value.brandId = ""; + brandList.value = []; + if (!categoryId) return; + try { + const res = await API_Goods.getCategoryBrandListDataSeller(categoryId); + if (request === brandRequest) brandList.value = Array.isArray(res) ? res : res?.result || []; + } catch { + // 请求失败由统一拦截器提示。 + } +}); const total = ref(0); const goodsParams = reactive({ pageNumber: 1, pageSize: 20, order: "desc", - marketEnable: "UPPER", - authFlag: "PASS", + marketEnable: props.marketEnable, + authFlag: props.authFlag, sort: "createTime", }); const goodsData = ref([]); @@ -272,6 +288,7 @@ watch( () => props.selectedWay, (val) => { selectedList.value = Array.isArray(val) ? val.slice() : []; + syncTableSelectionFromSelected(); }, { deep: true, immediate: true } ); @@ -292,6 +309,13 @@ function resetFilter() { filterForm.value = defaultFilterForm(); onSearchGoods(); } +function syncTableSelectionFromSelected() { + nextTick(() => { + const ids = new Set(selectedList.value.map(item => String(item.id || item.skuId))); + goodsData.value.forEach(item => { item.selected = ids.has(String(item.id)); }); + syncTableSelection(); + }); +} function changePageSize(v) { goodsParams.pageNumber = v; getQueryGoodsList(); @@ -330,7 +354,13 @@ function buildQueryParams() { } function getQueryGoodsList() { loading.value = true; - API_Goods.getGoodsSkuData(buildQueryParams()) + const query = props.productMode ? API_Goods.getGoodsListData : API_Goods.getGoodsSkuData; + const params = buildQueryParams(); + if (props.productMode) { + params.id = params.goodsId; + delete params.goodsId; + } + query(params) .then((res) => { initGoods(res); }) @@ -349,6 +379,7 @@ function initGoods(res) { .map(String) ); records.forEach((item) => { + if (props.productMode) item.goodsId = item.id; item.___type = "goods"; item.selected = selectedIds.has(String(item.id)); }); @@ -412,12 +443,6 @@ function formatShopCategory(list) { item.children && item.children.length ? formatShopCategory(item.children) : undefined, })); } -function goodsTypeText(value) { - if (value === "PHYSICAL_GOODS") return "实物商品"; - if (value === "VIRTUAL_GOODS") return "虚拟商品"; - if (value === "E_COUPON") return "电子卡券"; - return "-"; -} function brandText(brandId) { if (!brandId) return "-"; const brand = brandList.value.find((item) => String(item.id) === String(brandId)); @@ -433,18 +458,6 @@ function findTreeNodeLabel(tree, targetId, valueKey, labelKey) { } return ""; } -function categoryPathText(path, tree, valueKey, labelKey) { - if (!path) return "-"; - const ids = String(path).split(",").filter(Boolean); - if (!ids.length) return "-"; - const labels = ids - .map((id) => findTreeNodeLabel(tree, id, valueKey, labelKey)) - .filter(Boolean); - return labels.length ? labels.join(" / ") : "-"; -} -function storeCategoryText(path) { - return categoryPathText(path, shopCategoryList.value, "value", "label"); -} function platformCategoryText(path) { if (!path) return "-"; const ids = String(path).split(",").filter(Boolean); @@ -469,13 +482,10 @@ function init() { } }) .catch(() => {}); - API_Goods.getBrandListData() - .then((res) => { - brandList.value = Array.isArray(res) ? res : res?.result || []; - }) - .catch(() => {}); } +watch(showAdvanced, calcTableHeight); + onMounted(() => { init(); nextTick(calcTableHeight); @@ -555,6 +565,7 @@ defineExpose({ type }); .filter-row { display: flex; + width: 100%; flex-wrap: nowrap; align-items: center; gap: 16px; @@ -563,12 +574,14 @@ defineExpose({ type }); .filter-row-field { display: inline-flex; align-items: center; - flex: 0 0 244px; - min-width: 244px; + flex: 1 1 0; + min-width: 0; } .filter-control { - width: 160px; + flex: 1; + width: 100%; + min-width: 0; } .filter-row-label { @@ -585,6 +598,7 @@ defineExpose({ type }); .range-filter-row { display: flex; + width: 100%; flex-wrap: nowrap; align-items: center; gap: 16px; @@ -593,16 +607,20 @@ defineExpose({ type }); .range-group { display: inline-flex; align-items: center; - flex: 0 0 244px; - min-width: 244px; + flex: 1 1 0; + min-width: 0; } .range-group-inputs { display: inline-flex; align-items: center; + flex: 1; + min-width: 0; :deep(.el-input) { - width: 72px; + flex: 1; + width: 100%; + min-width: 0; } } @@ -620,7 +638,15 @@ defineExpose({ type }); white-space: normal; word-break: break-word; line-height: 1.5; + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + overflow: hidden; } +.goods-info { display: flex; align-items: center; gap: 12px; padding: 8px 0; } +.goods-info-copy { min-width: 0; } +.goods-code { margin-top: 6px; font-size: 12px; color: #909399; overflow-wrap: anywhere; } +.no-image { display: flex; height: 100%; align-items: center; justify-content: center; font-size: 11px; color: #a8abb2; background: #f5f7fa; } :deep(.goods-name-column .cell) { white-space: normal; @@ -633,6 +659,9 @@ defineExpose({ type }); height: 48px; object-fit: contain; vertical-align: middle; + flex-shrink: 0; + border-radius: 6px; + border: 1px solid #ebeef5; } .goods-price { diff --git a/seller/src/views/lili-dialog/index.vue b/seller/src/views/lili-dialog/index.vue index c40edb825..fa482dce7 100755 --- a/seller/src/views/lili-dialog/index.vue +++ b/seller/src/views/lili-dialog/index.vue @@ -1,19 +1,27 @@