mirror of
https://gitee.com/beijing_hongye_huicheng/lilishop-ui.git
synced 2026-11-13 04:18:55 +08:00
feat: 优化商品促销页面并独立展示定时上架
- 新增平台、商家定时上架独立页面,平台列表仅供查看 - 虚拟销量支持状态筛选及右侧抽屉设置,修复编码显示问题 - 统一列表操作栏,优化商品选择器、砍价、积分及优惠券页面 - 两端新增活动优惠券入口,隐藏创建表单中的活动类型字段 - 移除限时直降、第 N 件优惠页面、路由、接口调用及缓存菜单入口 验证:26 个改动前端文件编译或语法检查通过,暂存差异格式检查通过。
This commit is contained in:
@@ -200,20 +200,7 @@ export const getCouponReceiveList = (params) => {
|
||||
return getRequest("/promotion/coupon/received", params);
|
||||
};
|
||||
|
||||
// ========== 限时直降 ==========
|
||||
export const getFlashDiscountList = (params) => getRequest('/promotion/flashDiscount', params)
|
||||
export const getFlashDiscountDetail = (id) => getRequest(`/promotion/flashDiscount/${id}`)
|
||||
export const saveFlashDiscount = (params) => postRequest('/promotion/flashDiscount', params, { 'Content-type': 'application/json' })
|
||||
export const editFlashDiscount = (params) => putRequest('/promotion/flashDiscount', params, { 'Content-type': 'application/json' })
|
||||
export const deleteFlashDiscount = (id) => deleteRequest(`/promotion/flashDiscount/${id}`)
|
||||
export const updateFlashDiscountStatus = (id, params) => putRequest(`/promotion/flashDiscount/status/${id}`, params)
|
||||
|
||||
// ========== 第N件优惠 ==========
|
||||
export const getNthItemDiscountList = (params) => getRequest('/promotion/nthItemDiscount', params)
|
||||
export const getNthItemDiscountDetail = (id) => getRequest(`/promotion/nthItemDiscount/${id}`)
|
||||
export const saveNthItemDiscount = (params) => postRequest('/promotion/nthItemDiscount', params, { 'Content-type': 'application/json' })
|
||||
export const editNthItemDiscount = (params) => putRequest('/promotion/nthItemDiscount', params, { 'Content-type': 'application/json' })
|
||||
export const updateNthItemDiscountStatus = (id, params) => putRequest(`/promotion/nthItemDiscount/status/${id}`, params)
|
||||
|
||||
// 兼容平台优惠券命名(商家端实际调用店铺优惠券接口)
|
||||
export const getPlatformCouponList = getShopCouponList
|
||||
|
||||
@@ -7,6 +7,12 @@ import { useAppStore } from "@/stores/app";
|
||||
|
||||
let util = {};
|
||||
|
||||
// 避免旧缓存中的已下线菜单重新注册路由。
|
||||
const DEPRECATED_FRONT_ROUTES = new Set([
|
||||
"promotion/flash-discount/flash-discount",
|
||||
"promotion/nth-item-discount/nth-item-discount",
|
||||
]);
|
||||
|
||||
/** 店铺主页一级菜单 */
|
||||
export const HOME_NAV_NAME = "home";
|
||||
export const HOME_NAV_ITEM = { name: HOME_NAV_NAME, title: "主页" };
|
||||
@@ -489,6 +495,9 @@ util.initMenuData = function (data) {
|
||||
|
||||
util.initRouterNode = function (routers, data) {
|
||||
for (let item of data) {
|
||||
if (DEPRECATED_FRONT_ROUTES.has(item.frontRoute)) {
|
||||
continue;
|
||||
}
|
||||
const menu = Object.assign({}, item);
|
||||
const hasChildren = item.children && item.children.length > 0;
|
||||
|
||||
|
||||
@@ -46,6 +46,12 @@ export const otherRouter = {
|
||||
name: "goods-operation",
|
||||
component: () => import("@/views/goods/goods-seller/goodsOperation.vue")
|
||||
},
|
||||
{
|
||||
path: "goods-scheduled-upper",
|
||||
title: "定时上架",
|
||||
name: "goods-scheduled-upper",
|
||||
component: () => import("@/views/goods/scheduled-upper/index.vue")
|
||||
},
|
||||
{
|
||||
path: "goods-operation-edit",
|
||||
title: "编辑商品",
|
||||
@@ -196,18 +202,6 @@ export const otherRouter = {
|
||||
name: "full-discount-detail",
|
||||
component: () => import("@/views/promotion/full-discount/full-discount-add.vue")
|
||||
},
|
||||
{
|
||||
path: "flash-discount-add",
|
||||
title: "限时直降",
|
||||
name: "flash-discount-add",
|
||||
component: () => import("@/views/promotion/flash-discount/flash-discount-add.vue")
|
||||
},
|
||||
{
|
||||
path: "nth-item-discount-add",
|
||||
title: "第N件优惠",
|
||||
name: "nth-item-discount-add",
|
||||
component: () => import("@/views/promotion/nth-item-discount/nth-item-discount-add.vue")
|
||||
},
|
||||
{
|
||||
path: "export-order-deliver",
|
||||
title: "发货",
|
||||
|
||||
@@ -111,7 +111,7 @@
|
||||
</el-card>
|
||||
|
||||
<el-card>
|
||||
<div class="goods-tab">
|
||||
<div v-if="!scheduledOnly" class="goods-tab">
|
||||
<el-tabs v-model="currentStatus" @tab-click="onStatusTabClick">
|
||||
<el-tab-pane
|
||||
v-for="(item, index) in goodsStatusWithCount"
|
||||
@@ -169,6 +169,7 @@
|
||||
>{{ row.goodsName }}</a>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column v-if="currentStatus === 'SCHEDULED_UPPER'" prop="scheduledUpperTime" label="计划上架时间" width="190" />
|
||||
<el-table-column label="价格" width="120">
|
||||
<template #default="{ row }">
|
||||
<span :style="{ color: $mainColor }">{{ $filters.unitPrice(row.price, '¥') }}</span>
|
||||
@@ -265,6 +266,8 @@ import { customRouterPush } from "@/utils/filters";
|
||||
|
||||
defineOptions({ name: "goods" });
|
||||
|
||||
const props = defineProps({ scheduledOnly: { type: Boolean, default: false } });
|
||||
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
|
||||
@@ -315,7 +318,7 @@ const underForm = ref({
|
||||
const submitLoading = ref(false); // 添加或编辑提交状态
|
||||
const data = ref([]); // 表单数据
|
||||
const total = ref(0); // 表单数据总数
|
||||
const currentStatus = ref("ALL");
|
||||
const currentStatus = ref(props.scheduledOnly ? "SCHEDULED_UPPER" : "ALL");
|
||||
const goodsNumerData = ref({});
|
||||
const selectedRows = ref([]); // 选中的行数据
|
||||
const selectAll = ref(false); // 全选状态
|
||||
@@ -433,6 +436,12 @@ function buildSearchParams(includeGoodsStatus = true) {
|
||||
|
||||
if (!includeGoodsStatus) {
|
||||
delete params.goodsStatus;
|
||||
} else if (props.scheduledOnly) {
|
||||
params.goodsStatus = "SCHEDULED_UPPER";
|
||||
}
|
||||
if (includeGoodsStatus && currentStatus.value === "SCHEDULED_UPPER") {
|
||||
params.sort = "scheduled_upper_time";
|
||||
params.order = "asc";
|
||||
}
|
||||
|
||||
if (createRange.value && createRange.value.length === 2) {
|
||||
@@ -502,6 +511,7 @@ function getDataList() {
|
||||
});
|
||||
}
|
||||
function getNumberData() {
|
||||
if (props.scheduledOnly) return;
|
||||
getGoodsNumerData(buildSearchParams(false)).then((res) => {
|
||||
if (res.success) {
|
||||
goodsNumerData.value = res.result;
|
||||
@@ -583,6 +593,7 @@ function goodsStatusClick(name) {
|
||||
}
|
||||
selectedRows.value = [];
|
||||
clearTableSelection();
|
||||
searchForm.value.pageNumber = 1;
|
||||
getDataList();
|
||||
}
|
||||
|
||||
@@ -663,7 +674,7 @@ function batchLower() {
|
||||
|
||||
onMounted(() => {
|
||||
const goodsStatus = route.query.goodsStatus;
|
||||
if (goodsStatus) {
|
||||
if (goodsStatus && !props.scheduledOnly && goodsStatus !== "SCHEDULED_UPPER") {
|
||||
currentStatus.value = goodsStatus;
|
||||
searchForm.value.goodsStatus = goodsStatus;
|
||||
}
|
||||
|
||||
14
seller/src/views/goods/scheduled-upper/index.vue
Normal file
14
seller/src/views/goods/scheduled-upper/index.vue
Normal file
@@ -0,0 +1,14 @@
|
||||
<template>
|
||||
<goods-list scheduled-only />
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
/**
|
||||
* 商家商品定时上架列表,复用统一的商品筛选和列表布局。
|
||||
* @author Bulbasaur
|
||||
* @since 2026-11-03
|
||||
*/
|
||||
import GoodsList from "@/views/goods/goods-seller/goods.vue";
|
||||
|
||||
defineOptions({ name: "goodsScheduledUpper" });
|
||||
</script>
|
||||
@@ -142,13 +142,6 @@
|
||||
style="width: 260px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="活动类型" prop="getType">
|
||||
<el-select :disabled="disabled" v-model="form.getType" style="width: 260px">
|
||||
<el-option label="免费领取" value="FREE" />
|
||||
<el-option label="活动赠送" value="ACTIVITY" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="发放数量" prop="publishNum" v-if="form.getType === 'FREE'">
|
||||
<div class="form-field-block">
|
||||
<el-input
|
||||
@@ -369,7 +362,7 @@ const form = ref({
|
||||
couponType: "PRICE",
|
||||
couponName: "",
|
||||
promotionName: "",
|
||||
getType: "FREE",
|
||||
getType: route.query.getType === "ACTIVITY" ? "ACTIVITY" : "FREE",
|
||||
promotionGoodsList: [],
|
||||
scopeIdGoods: [],
|
||||
rangeDayType: "",
|
||||
@@ -448,6 +441,7 @@ watch(
|
||||
(val) => {
|
||||
if (val == "FREE") {
|
||||
rangeTimeType.value = 1;
|
||||
formRule.rangeTime = [{ required: true, message: "请选择优惠券有效期" }];
|
||||
} else {
|
||||
rangeTimeType.value = 0;
|
||||
}
|
||||
@@ -455,7 +449,7 @@ watch(
|
||||
delete formRule.rangeTime;
|
||||
}
|
||||
},
|
||||
{ deep: true }
|
||||
{ immediate: true }
|
||||
);
|
||||
watch(
|
||||
() => route.query,
|
||||
@@ -464,7 +458,9 @@ watch(
|
||||
if (id.value) {
|
||||
getCoupon();
|
||||
} else {
|
||||
formRef.value.resetFields();
|
||||
formRef.value?.resetFields();
|
||||
modalType.value = 0;
|
||||
form.value.getType = e.getType === "ACTIVITY" ? "ACTIVITY" : "FREE";
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
@@ -55,6 +55,7 @@
|
||||
<el-card>
|
||||
<div class="operation padding-row" v-if="getType !== 'ACTIVITY'">
|
||||
<el-button type="primary" @click="add">添加优惠券</el-button>
|
||||
<el-button class="activity-coupon-button" @click="addActivityCoupon">添加活动优惠券</el-button>
|
||||
<el-button @click="delAll">批量关闭</el-button>
|
||||
<el-button type="warning" @click="receivePage()">优惠券领取记录</el-button>
|
||||
</div>
|
||||
@@ -383,6 +384,10 @@ function add() {
|
||||
router.push({ name: "add-coupon" });
|
||||
}
|
||||
|
||||
function addActivityCoupon() {
|
||||
router.push({ name: "add-coupon", query: { getType: "ACTIVITY" } });
|
||||
}
|
||||
|
||||
function changePage(v) {
|
||||
searchForm.pageNumber = v;
|
||||
getDataList();
|
||||
@@ -506,6 +511,19 @@ onMounted(() => {
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.activity-coupon-button {
|
||||
--el-button-text-color: #b35c12;
|
||||
--el-button-bg-color: #fff7ed;
|
||||
--el-button-border-color: #fed7aa;
|
||||
--el-button-hover-text-color: #9a4b0b;
|
||||
--el-button-hover-bg-color: #ffedd5;
|
||||
--el-button-hover-border-color: #fdba74;
|
||||
--el-button-active-text-color: #9a4b0b;
|
||||
--el-button-active-bg-color: #fed7aa;
|
||||
--el-button-active-border-color: #fb923c;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.link-text {
|
||||
color: #409eff;
|
||||
cursor: pointer;
|
||||
|
||||
@@ -1,151 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-card>
|
||||
<el-form ref="formRef" :model="form" label-width="120px">
|
||||
<el-form-item label="活动名称" required>
|
||||
<el-input v-model="form.promotionName" style="width: 320px" />
|
||||
</el-form-item>
|
||||
<el-form-item label="活动时间" required>
|
||||
<el-date-picker
|
||||
v-model="form.rangeTime"
|
||||
class="flash-discount-range-picker"
|
||||
type="datetimerange"
|
||||
format="YYYY-MM-DD HH:mm:ss"
|
||||
start-placeholder="开始时间"
|
||||
end-placeholder="结束时间"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="限购数量">
|
||||
<el-input-number v-model="form.limitNum" :min="0" /> <span class="tip">0 表示不限</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="活动说明">
|
||||
<el-input v-model="form.description" type="textarea" style="width: 360px" />
|
||||
</el-form-item>
|
||||
<el-form-item label="活动商品">
|
||||
<el-button type="primary" @click="openGoods">选择商品</el-button>
|
||||
<el-table
|
||||
v-if="form.promotionGoodsList.length"
|
||||
:data="form.promotionGoodsList"
|
||||
border
|
||||
class="mt_10 flash-discount-goods-table"
|
||||
>
|
||||
<el-table-column prop="goodsName" label="商品" width="180" show-overflow-tooltip />
|
||||
<el-table-column prop="originalPrice" label="原价" width="90" align="center" />
|
||||
<el-table-column label="活动价" width="150" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-input-number
|
||||
v-model="row.price"
|
||||
:min="promotionPriceMin(row.goodsType)"
|
||||
:precision="2"
|
||||
size="small"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="submit">保存</el-button>
|
||||
<el-button @click="$router.back()">返回</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
<liliDialog ref="liliDialogRef" @selectedGoodsData="selectedGoods" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onMounted, reactive, ref } from "vue";
|
||||
import { useRoute, useRouter } from "vue-router";
|
||||
import { getFlashDiscountDetail, saveFlashDiscount, editFlashDiscount } from "@/api/promotion.js";
|
||||
import { promotionPriceMin } from "@/constants/goodsType";
|
||||
import { unixToDate } from "@/utils/filters";
|
||||
import { Message } from "@/utils/message";
|
||||
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
|
||||
const form = reactive({
|
||||
promotionName: "",
|
||||
rangeTime: [],
|
||||
limitNum: 0,
|
||||
description: "",
|
||||
scopeType: "PORTION_GOODS",
|
||||
promotionGoodsList: [],
|
||||
});
|
||||
|
||||
// formRef / liliDialogRef:模板 ref 改名避免与 form 变量、liliDialog 全局组件互相覆盖
|
||||
const formRef = ref(null);
|
||||
const liliDialogRef = ref(null);
|
||||
|
||||
onMounted(() => {
|
||||
if (route.query.id) {
|
||||
getFlashDiscountDetail(route.query.id).then((res) => {
|
||||
if (res.success) {
|
||||
Object.assign(form, res.result);
|
||||
form.rangeTime = [new Date(res.result.startTime), new Date(res.result.endTime)];
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
function openGoods() {
|
||||
liliDialogRef.value.open("goods");
|
||||
}
|
||||
|
||||
function selectedGoods(list) {
|
||||
list.forEach((g) => {
|
||||
if (!form.promotionGoodsList.find((i) => i.skuId === g.id)) {
|
||||
form.promotionGoodsList.push({
|
||||
skuId: g.id,
|
||||
goodsId: g.goodsId,
|
||||
goodsName: g.goodsName,
|
||||
originalPrice: g.price,
|
||||
price: g.price,
|
||||
quantity: g.quantity,
|
||||
goodsType: g.goodsType,
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function submit() {
|
||||
const payload = { ...form };
|
||||
if (payload.rangeTime && payload.rangeTime.length === 2) {
|
||||
const start =
|
||||
payload.rangeTime[0] instanceof Date
|
||||
? payload.rangeTime[0]
|
||||
: new Date(payload.rangeTime[0]);
|
||||
const end =
|
||||
payload.rangeTime[1] instanceof Date
|
||||
? payload.rangeTime[1]
|
||||
: new Date(payload.rangeTime[1]);
|
||||
payload.startTime = unixToDate(start.getTime() / 1000);
|
||||
payload.endTime = unixToDate(end.getTime() / 1000);
|
||||
}
|
||||
delete payload.rangeTime;
|
||||
const req = payload.id ? editFlashDiscount(payload) : saveFlashDiscount(payload);
|
||||
req.then((res) => {
|
||||
if (res.success) {
|
||||
Message.success("保存成功");
|
||||
router.back();
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.tip { margin-left: 8px; color: #999; }
|
||||
.mt_10 { margin-top: 10px; }
|
||||
|
||||
:deep(.flash-discount-range-picker.el-date-editor--datetimerange) {
|
||||
width: 380px;
|
||||
max-width: 380px;
|
||||
flex-grow: 0;
|
||||
--el-date-editor-width: 380px;
|
||||
}
|
||||
|
||||
.flash-discount-goods-table {
|
||||
width: fit-content;
|
||||
max-width: 100%;
|
||||
}
|
||||
</style>
|
||||
@@ -1,100 +0,0 @@
|
||||
<template>
|
||||
<div class="search">
|
||||
<el-card>
|
||||
<el-form ref="searchFormRef" :model="searchForm" inline label-width="70px" class="search-form" @keyup.enter="handleSearch">
|
||||
<el-form-item label="活动名称" prop="promotionName">
|
||||
<el-input v-model="searchForm.promotionName" placeholder="请输入活动名称" clearable style="width: 240px" />
|
||||
</el-form-item>
|
||||
<el-form-item label="活动状态" prop="promotionStatus">
|
||||
<el-select v-model="searchForm.promotionStatus" placeholder="请选择" clearable style="width: 240px">
|
||||
<el-option label="未开始" value="NEW" />
|
||||
<el-option label="已开始" value="START" />
|
||||
<el-option label="已结束" value="END" />
|
||||
<el-option label="已关闭" value="CLOSE" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="handleSearch">搜索</el-button>
|
||||
<el-button type="primary" @click="goAdd">新建活动</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
<el-card>
|
||||
<el-table v-loading="loading" border :data="data" class="mt_10">
|
||||
<el-table-column prop="promotionName" label="活动名称" min-width="140" />
|
||||
<el-table-column prop="startTime" label="开始时间" width="170" />
|
||||
<el-table-column prop="endTime" label="结束时间" width="170" />
|
||||
<el-table-column label="状态" width="100">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="statusType(row.promotionStatus)">{{ statusText(row.promotionStatus) }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="right" label="操作" width="160" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<a class="link-text" @click="goEdit(row)">编辑</a>
|
||||
<span class="op-split">|</span>
|
||||
<a class="link-text" @click="toggleStatus(row)">关闭</a>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div class="mt_10" style="display: flex; justify-content: flex-end">
|
||||
<el-pagination
|
||||
v-model:current-page="searchForm.pageNumber"
|
||||
v-model:page-size="searchForm.pageSize"
|
||||
:total="total"
|
||||
layout="total, prev, pager, next"
|
||||
@current-change="getDataList"
|
||||
/>
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from "vue";
|
||||
import { useTable } from "@/utils/useTable";
|
||||
import { useRouter } from "vue-router";
|
||||
import { getFlashDiscountList, updateFlashDiscountStatus } from "@/api/promotion.js";
|
||||
import { Message } from "@/utils/message";
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
const {
|
||||
loading,
|
||||
data,
|
||||
total,
|
||||
searchForm,
|
||||
getDataList,
|
||||
handleSearch,
|
||||
} = useTable(getFlashDiscountList, { pageSize: 20, sort: "createTime", order: "desc" });
|
||||
|
||||
// searchFormRef:模板 ref 改名避免与顶层变量 searchForm 互相覆盖
|
||||
const searchFormRef = ref(null);
|
||||
|
||||
function statusText(s) {
|
||||
return { NEW: "未开始", START: "进行中", END: "已结束", CLOSE: "已关闭" }[s] || s;
|
||||
}
|
||||
function statusType(s) {
|
||||
return { NEW: "info", START: "success", END: "danger", CLOSE: "danger" }[s] || "info";
|
||||
}
|
||||
function goAdd() {
|
||||
router.push({ name: "flash-discount-add" });
|
||||
}
|
||||
function goEdit(row) {
|
||||
router.push({ name: "flash-discount-add", query: { id: row.id } });
|
||||
}
|
||||
function toggleStatus(row) {
|
||||
updateFlashDiscountStatus(row.id).then((res) => {
|
||||
if (res.success) {
|
||||
Message.success("操作成功");
|
||||
getDataList();
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.link-text { color: #409eff; cursor: pointer; }
|
||||
.op-split { margin: 0 8px; color: #dcdfe6; }
|
||||
.mt_10 { margin-top: 10px; }
|
||||
</style>
|
||||
@@ -1,139 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-card>
|
||||
<el-form ref="form" :model="form" label-width="120px">
|
||||
<el-form-item label="活动名称" required>
|
||||
<el-input v-model="form.promotionName" style="width: 320px" />
|
||||
</el-form-item>
|
||||
<el-form-item label="活动时间" required>
|
||||
<el-date-picker
|
||||
v-model="form.rangeTime"
|
||||
class="nth-item-range-picker"
|
||||
type="datetimerange"
|
||||
format="YYYY-MM-DD HH:mm:ss"
|
||||
start-placeholder="开始时间"
|
||||
end-placeholder="结束时间"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="第N件" required>
|
||||
<el-input-number v-model="form.nthNum" :min="2" />
|
||||
<span class="tip">例如 2 表示第 2 件享受优惠</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="优惠方式" required>
|
||||
<el-select v-model="form.discountType" style="width: 200px">
|
||||
<el-option label="半价" value="HALF" />
|
||||
<el-option label="免单" value="FREE" />
|
||||
<el-option label="打折" value="RATE" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="form.discountType === 'RATE'" label="折扣" required>
|
||||
<el-input-number v-model="form.discountValue" :min="0.1" :max="9.9" :precision="1" />
|
||||
<span class="tip">如 8 表示 8 折</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="活动说明">
|
||||
<el-input v-model="form.description" type="textarea" style="width: 360px" />
|
||||
</el-form-item>
|
||||
<el-form-item label="活动商品">
|
||||
<el-button type="primary" @click="openGoods">选择商品</el-button>
|
||||
<el-table v-if="form.promotionGoodsList.length" :data="form.promotionGoodsList" border class="mt_10">
|
||||
<el-table-column prop="goodsName" label="商品" />
|
||||
<el-table-column prop="originalPrice" label="原价" width="100" />
|
||||
</el-table>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="submit">保存</el-button>
|
||||
<el-button @click="$router.back()">返回</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
<liliDialog ref="liliDialog" @selectedGoodsData="selectedGoods" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onMounted, reactive, ref } from "vue";
|
||||
import { useRoute, useRouter } from "vue-router";
|
||||
import { getNthItemDiscountDetail, saveNthItemDiscount, editNthItemDiscount } from "@/api/promotion.js";
|
||||
import { Message } from "@/utils/message";
|
||||
import { unixToDate } from "@/utils/filters";
|
||||
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
|
||||
const liliDialog = ref(null);
|
||||
|
||||
const form = reactive({
|
||||
promotionName: "",
|
||||
rangeTime: [],
|
||||
nthNum: 2,
|
||||
discountType: "HALF",
|
||||
discountValue: 8,
|
||||
description: "",
|
||||
scopeType: "PORTION_GOODS",
|
||||
promotionGoodsList: [],
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
if (route.query.id) {
|
||||
getNthItemDiscountDetail(route.query.id).then((res) => {
|
||||
if (res.success) {
|
||||
Object.assign(form, res.result);
|
||||
form.rangeTime = [new Date(res.result.startTime), new Date(res.result.endTime)];
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
function openGoods() {
|
||||
liliDialog.value.open("goods");
|
||||
}
|
||||
function selectedGoods(list) {
|
||||
list.forEach((g) => {
|
||||
if (!form.promotionGoodsList.find((i) => i.skuId === g.id)) {
|
||||
form.promotionGoodsList.push({
|
||||
skuId: g.id,
|
||||
goodsId: g.goodsId,
|
||||
goodsName: g.goodsName,
|
||||
originalPrice: g.price,
|
||||
price: g.price,
|
||||
quantity: g.quantity,
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
function submit() {
|
||||
const payload = { ...form };
|
||||
if (payload.rangeTime && payload.rangeTime.length === 2) {
|
||||
const start =
|
||||
payload.rangeTime[0] instanceof Date
|
||||
? payload.rangeTime[0]
|
||||
: new Date(payload.rangeTime[0]);
|
||||
const end =
|
||||
payload.rangeTime[1] instanceof Date
|
||||
? payload.rangeTime[1]
|
||||
: new Date(payload.rangeTime[1]);
|
||||
payload.startTime = unixToDate(start.getTime() / 1000);
|
||||
payload.endTime = unixToDate(end.getTime() / 1000);
|
||||
}
|
||||
delete payload.rangeTime;
|
||||
const req = payload.id ? editNthItemDiscount(payload) : saveNthItemDiscount(payload);
|
||||
req.then((res) => {
|
||||
if (res.success) {
|
||||
Message.success("保存成功");
|
||||
router.back();
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.tip { margin-left: 8px; color: #999; }
|
||||
.mt_10 { margin-top: 10px; }
|
||||
|
||||
:deep(.nth-item-range-picker.el-date-editor--datetimerange) {
|
||||
width: 380px;
|
||||
max-width: 380px;
|
||||
flex-grow: 0;
|
||||
--el-date-editor-width: 380px;
|
||||
}
|
||||
</style>
|
||||
@@ -1,107 +0,0 @@
|
||||
<template>
|
||||
<div class="search">
|
||||
<el-card>
|
||||
<el-form ref="searchFormRef" :model="searchForm" inline label-width="70px" class="search-form" @keyup.enter="handleSearch">
|
||||
<el-form-item label="活动名称" prop="promotionName">
|
||||
<el-input v-model="searchForm.promotionName" placeholder="请输入活动名称" clearable style="width: 240px" />
|
||||
</el-form-item>
|
||||
<el-form-item label="活动状态" prop="promotionStatus">
|
||||
<el-select v-model="searchForm.promotionStatus" placeholder="请选择" clearable style="width: 240px">
|
||||
<el-option label="未开始" value="NEW" />
|
||||
<el-option label="已开始" value="START" />
|
||||
<el-option label="已结束" value="END" />
|
||||
<el-option label="已关闭" value="CLOSE" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="handleSearch">搜索</el-button>
|
||||
<el-button type="primary" @click="goAdd">新建活动</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
<el-card>
|
||||
<el-table v-loading="loading" border :data="data" class="mt_10">
|
||||
<el-table-column prop="promotionName" label="活动名称" min-width="140" />
|
||||
<el-table-column prop="nthNum" label="第N件" width="80" />
|
||||
<el-table-column label="优惠方式" width="100">
|
||||
<template #default="{ row }">{{ discountTypeText(row.discountType) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="startTime" label="开始时间" width="170" />
|
||||
<el-table-column prop="endTime" label="结束时间" width="170" />
|
||||
<el-table-column label="状态" width="100">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="statusType(row.promotionStatus)">{{ statusText(row.promotionStatus) }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="right" label="操作" width="160" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<a class="link-text" @click="goEdit(row)">编辑</a>
|
||||
<span class="op-split">|</span>
|
||||
<a class="link-text" @click="toggleStatus(row)">关闭</a>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div class="mt_10" style="display: flex; justify-content: flex-end">
|
||||
<el-pagination
|
||||
v-model:current-page="searchForm.pageNumber"
|
||||
v-model:page-size="searchForm.pageSize"
|
||||
:total="total"
|
||||
layout="total, prev, pager, next"
|
||||
@current-change="getDataList"
|
||||
/>
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from "vue";
|
||||
import { useTable } from "@/utils/useTable";
|
||||
import { useRouter } from "vue-router";
|
||||
import { getNthItemDiscountList, updateNthItemDiscountStatus } from "@/api/promotion.js";
|
||||
import { Message } from "@/utils/message";
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
const {
|
||||
loading,
|
||||
data,
|
||||
total,
|
||||
searchForm,
|
||||
getDataList,
|
||||
handleSearch,
|
||||
} = useTable(getNthItemDiscountList, { pageSize: 20, sort: "createTime", order: "desc" });
|
||||
|
||||
// searchFormRef:模板 ref 改名避免与顶层变量 searchForm 互相覆盖
|
||||
const searchFormRef = ref(null);
|
||||
|
||||
function statusText(s) {
|
||||
return { NEW: "未开始", START: "进行中", END: "已结束", CLOSE: "已关闭" }[s] || s;
|
||||
}
|
||||
function statusType(s) {
|
||||
return { NEW: "info", START: "success", END: "danger", CLOSE: "danger" }[s] || "info";
|
||||
}
|
||||
function discountTypeText(t) {
|
||||
return { HALF: "半价", FREE: "免单", RATE: "打折" }[t] || t;
|
||||
}
|
||||
function goAdd() {
|
||||
router.push({ name: "nth-item-discount-add" });
|
||||
}
|
||||
function goEdit(row) {
|
||||
router.push({ name: "nth-item-discount-add", query: { id: row.id } });
|
||||
}
|
||||
function toggleStatus(row) {
|
||||
updateNthItemDiscountStatus(row.id).then((res) => {
|
||||
if (res.success) {
|
||||
Message.success("操作成功");
|
||||
getDataList();
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.link-text { color: #409eff; cursor: pointer; }
|
||||
.op-split { margin: 0 8px; color: #dcdfe6; }
|
||||
.mt_10 { margin-top: 10px; }
|
||||
</style>
|
||||
Reference in New Issue
Block a user