feat(goods): 完善商品定时上下架功能

- 新增定时下架支持,商品列表同时展示上架和下架计划
- 添加定时计划的编辑和删除操作入口
- 新增取消定时上下架API接口
- 商品选择对话框支持商品模式,适配SPU维度选择
- 商品选择对话框增加高级筛选折叠和品牌联动逻辑
- 优化商品列表筛选布局为弹性自适应宽度
- 管理端定时计划页面增加定时下架标签页
- 商家端定时计划页面重构为完整CRUD抽屉交互
- 修改楼层列表新建逻辑改为路由跳转装修页
This commit is contained in:
pikachu1995@126.com
2026-11-04 11:07:37 +08:00
parent f6f106e12b
commit cbaf89c835
11 changed files with 366 additions and 90 deletions

View File

@@ -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)

View File

@@ -70,6 +70,7 @@
</el-card>
<el-card>
<slot v-if="scheduledOnly" name="scheduled-controls" />
<div v-if="!scheduledOnly" class="goods-tab">
<el-tabs v-model="currentStatus" @tab-click="onStatusTabClick">
<el-tab-pane
@@ -142,6 +143,7 @@
</template>
</el-table-column>
<el-table-column v-if="currentStatus === 'SCHEDULED_UPPER'" prop="scheduledUpperTime" label="计划上架时间" width="190" />
<el-table-column v-if="currentStatus === 'SCHEDULED_DOWN'" prop="scheduledDownTime" label="计划下架时间" width="190" />
<el-table-column label="价格" width="200">
<template #default="{ row }">
<span :style="{ color: $mainColor }">{{ unitPrice(row.price, '¥') }}</span>
@@ -154,7 +156,7 @@
<span v-if="row">{{ goodsTypeText(row.goodsType) }}</span>
</template>
</el-table-column>
<el-table-column label="状态" width="150">
<el-table-column v-if="!scheduledOnly" label="状态" width="150">
<template #default="{ row }">
<span v-if="row">{{ marketEnableText(row.marketEnable) }}</span>
</template>
@@ -165,7 +167,12 @@
</template>
</el-table-column>
<el-table-column prop="storeName" label="店铺名称" width="200" show-overflow-tooltip />
<el-table-column v-if="!readOnly" label="操作" width="200" align="center" fixed="right">
<el-table-column v-if="scheduledOnly" label="操作" width="100" align="center" fixed="right">
<template #default="{ row }">
<a class="link-text" @click="emit('delete-schedule', row)">删除</a>
</template>
</el-table-column>
<el-table-column v-else-if="!readOnly" label="操作" width="200" align="center" fixed="right">
<template #default="{ row }">
<template v-if="row.authFlag === 'TOBEAUDITED'">
<a class="link-text" @click="openAuditModal(row)">审核</a>
@@ -299,7 +306,9 @@ import { ElMessage, ElMessageBox } from "element-plus";
import { unitPrice, customRouterPush } from "@/utils/filters";
defineOptions({ name: "goods" });
const emit = defineEmits(["delete-schedule"]);
const props = defineProps({
scheduledStatus: { type: String, default: "SCHEDULED_UPPER" },
scheduledOnly: { type: Boolean, default: false },
readOnly: { type: Boolean, default: false },
});
@@ -320,7 +329,7 @@ const auditModalVisible = ref(false);
const auditSubmitLoading = ref(false);
const currentAuditGoods = ref(null);
const submitLoading = ref(false);
const currentStatus = ref(props.scheduledOnly ? "SCHEDULED_UPPER" : "ALL");
const currentStatus = ref(props.scheduledOnly ? props.scheduledStatus : "ALL");
const { loading, data, total, searchForm, getDataList, handleSearch, resetSearch } = useTable(
getGoodsListData,
@@ -338,11 +347,11 @@ const { loading, data, total, searchForm, getDataList, handleSearch, resetSearch
},
beforeRequest: (p) => {
const params = { ...p };
const status = props.scheduledOnly ? "SCHEDULED_UPPER" : currentStatus.value;
const status = props.scheduledOnly ? props.scheduledStatus : currentStatus.value;
if (status && status !== "ALL") {
params.goodsStatus = status;
if (status === "SCHEDULED_UPPER") {
params.sort = "scheduled_upper_time";
if (["SCHEDULED_UPPER", "SCHEDULED_DOWN"].includes(status)) {
params.sort = status === "SCHEDULED_UPPER" ? "scheduled_upper_time" : "scheduled_down_time";
params.order = "asc";
}
} else {
@@ -357,7 +366,7 @@ const { loading, data, total, searchForm, getDataList, handleSearch, resetSearch
);
function handleReset() {
currentStatus.value = props.scheduledOnly ? "SCHEDULED_UPPER" : "ALL";
currentStatus.value = props.scheduledOnly ? props.scheduledStatus : "ALL";
resetSearch();
}
const goodsNumerData = ref({});

View File

@@ -1,5 +1,12 @@
<template>
<goods-list scheduled-only read-only />
<goods-list :key="`${activeStatus}-${refreshKey}`" scheduled-only read-only :scheduled-status="activeStatus" @delete-schedule="deleteSchedule">
<template #scheduled-controls>
<el-tabs v-model="activeStatus">
<el-tab-pane label="定时上架" name="SCHEDULED_UPPER" />
<el-tab-pane label="定时下架" name="SCHEDULED_DOWN" />
</el-tabs>
</template>
</goods-list>
</template>
<script setup>
@@ -9,6 +16,30 @@
* @since 2026-11-03
*/
import GoodsList from "@/views/goods/goods-info/goods.vue";
import { ref } from "vue";
import { ElMessage, ElMessageBox } from "element-plus";
import { cancelGoodsMarketSchedule } from "@/api/goods";
defineOptions({ name: "goodsScheduledUpper" });
const activeStatus = ref("SCHEDULED_UPPER");
const refreshKey = ref(0);
async function deleteSchedule(row) {
const status = activeStatus.value === "SCHEDULED_UPPER" ? "UPPER" : "DOWN";
try {
await ElMessageBox.confirm(`确认删除“${row.goodsName}”的定时${status === 'UPPER' ? '上架' : '下架'}计划?`, "删除定时计划", { confirmButtonText: "确定", cancelButtonText: "取消", type: "warning" });
} catch {
return;
}
try {
const res = await cancelGoodsMarketSchedule(row.id, status);
if (res.success) {
ElMessage.success("定时计划已删除");
refreshKey.value++;
}
} catch {
// 接口错误由统一拦截器提示。
}
}
</script>