mirror of
https://gitee.com/beijing_hongye_huicheng/lilishop-ui.git
synced 2026-09-22 12:52:03 +08:00
feat(buyer): 添加电子卡券(E_COUPON)支持
- 新增 E_COUPON 商品类型,支持电子卡券的展示与购买逻辑 - 优化商品详情页,调整促销、库存和购买按钮逻辑以适应电子卡券 - 增加卡密信息展示与管理功能,支持商家端卡池管理 - 更新相关页面以处理电子卡券的特殊逻辑,如免地址、隐藏优惠券等
This commit is contained in:
@@ -53,6 +53,8 @@
|
||||
>
|
||||
<el-option label="实物商品" value="PHYSICAL_GOODS" />
|
||||
<el-option label="虚拟商品" value="VIRTUAL_GOODS" />
|
||||
<!-- E_COUPON:卡密商品,可跳转卡池管理 -->
|
||||
<el-option label="电子卡券" value="E_COUPON" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
@@ -144,7 +146,7 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="storeName" label="店铺名称" width="200" show-overflow-tooltip />
|
||||
<el-table-column label="操作" width="200" align="center" fixed="right">
|
||||
<el-table-column label="操作" width="260" align="center" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<template v-if="row.marketEnable === 'DOWN'">
|
||||
<a class="link-text" @click="upper(row)">上架</a>
|
||||
@@ -156,6 +158,11 @@
|
||||
<span class="op-split">|</span>
|
||||
<a class="link-text" @click="editGoods(row)">编辑</a>
|
||||
</template>
|
||||
<!-- E_COUPON 专属:卡池管理入口(原型 P-03) -->
|
||||
<template v-if="row.goodsType === 'E_COUPON'">
|
||||
<span class="op-split">|</span>
|
||||
<a class="link-text" @click="goCardKeyPool(row)">卡池管理</a>
|
||||
</template>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
@@ -192,6 +199,7 @@
|
||||
import {
|
||||
getGoodsListData,
|
||||
getGoodsNumerData,
|
||||
getQueryGoodsIdGoodsList,
|
||||
upGoods,
|
||||
lowGoods,
|
||||
} from "@/api/goods";
|
||||
@@ -248,7 +256,34 @@ export default {
|
||||
goodsTypeText(v) {
|
||||
if (v === "PHYSICAL_GOODS") return "实物商品";
|
||||
if (v === "VIRTUAL_GOODS") return "虚拟商品";
|
||||
return "电子卡券";
|
||||
if (v === "E_COUPON") return "电子卡券"; // 卡密商品
|
||||
return v || "—";
|
||||
},
|
||||
/** 跳转卡池管理;单 SKU 商品默认取第一个 SKU(card-key-pool 需 skuId) */
|
||||
async goCardKeyPool(row) {
|
||||
let skuId = row.skuId;
|
||||
if (!skuId) {
|
||||
try {
|
||||
const res = await getQueryGoodsIdGoodsList(row.id);
|
||||
if (res.success && res.result?.length) {
|
||||
skuId = res.result[0].id;
|
||||
} else {
|
||||
this.$message.warning("该商品暂无 SKU,请先完善商品规格");
|
||||
return;
|
||||
}
|
||||
} catch {
|
||||
this.$message.error("获取商品规格失败");
|
||||
return;
|
||||
}
|
||||
}
|
||||
this.$router.push({
|
||||
path: "/card-key-pool",
|
||||
query: {
|
||||
skuId,
|
||||
goodsId: row.id,
|
||||
goodsName: row.goodsName,
|
||||
},
|
||||
});
|
||||
},
|
||||
marketEnableText(v) {
|
||||
if (v === "DOWN") return "下架";
|
||||
|
||||
@@ -84,6 +84,7 @@ export default {
|
||||
data() {
|
||||
return {
|
||||
selectGoodsType: false, // 展示选择商品分类modal
|
||||
/** 商品类型选项;E_COUPON 为卡密商品(与 VIRTUAL_GOODS 核销型区分,见 FR-S-01) */
|
||||
goodsTypeWay: [
|
||||
{
|
||||
title: "实物商品",
|
||||
@@ -99,6 +100,13 @@ export default {
|
||||
type: "VIRTUAL_GOODS",
|
||||
check: false,
|
||||
},
|
||||
{
|
||||
title: "电子卡券",
|
||||
img: goodsType2Img,
|
||||
desc: "卡密自动发卡,无需物流",
|
||||
type: "E_COUPON", // goodsType;库存由卡池同步,非手动填写
|
||||
check: false,
|
||||
},
|
||||
],
|
||||
// 商品分类选择数组
|
||||
category: [
|
||||
@@ -230,4 +238,12 @@ export default {
|
||||
gap: 12px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.content-goods-publish {
|
||||
.goods-category li.activeClass {
|
||||
background-color: #409eff;
|
||||
border-color: #409eff;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
</el-form-item>
|
||||
<el-form-item class="form-item-view-el" label="销售模式" prop="salesModel">
|
||||
<el-radio-group
|
||||
v-if="baseInfoForm.goodsType != 'VIRTUAL_GOODS'"
|
||||
v-if="!isVirtualLikeGoods"
|
||||
v-model="baseInfoForm.salesModel"
|
||||
@change="handleSalesModeChange"
|
||||
>
|
||||
@@ -52,7 +52,7 @@
|
||||
<el-radio-button value="WHOLESALE">批发型</el-radio-button>
|
||||
</el-radio-group>
|
||||
<el-radio-group v-else v-model="baseInfoForm.salesModel">
|
||||
<el-radio-button value="RETAIL">虚拟型</el-radio-button>
|
||||
<el-radio-button value="RETAIL">{{ isECouponGoods ? "电子卡券" : "虚拟型" }}</el-radio-button>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="baseInfoForm.salesModel == 'WHOLESALE'" class="form-item-view-el" label="销售规则"
|
||||
@@ -336,7 +336,7 @@
|
||||
@change="updateSkuTable(row, 'sn', $index)"
|
||||
/>
|
||||
<el-input
|
||||
v-else-if="col.slot === 'weight' && baseInfoForm.goodsType !== 'VIRTUAL_GOODS'"
|
||||
v-else-if="col.slot === 'weight' && needsLogistics && baseInfoForm.salesModel !== 'WHOLESALE'"
|
||||
v-model="row.weight"
|
||||
clearable
|
||||
placeholder="请输入重量"
|
||||
@@ -344,6 +344,14 @@
|
||||
>
|
||||
<template #append>kg</template>
|
||||
</el-input>
|
||||
<el-input
|
||||
v-else-if="col.slot === 'quantity' && isECouponGoods"
|
||||
:model-value="resolvePoolStockDisplay(row)"
|
||||
disabled
|
||||
placeholder="卡池可用数"
|
||||
>
|
||||
<template #append>{{ baseInfoForm.goodsUnit || "" }}</template>
|
||||
</el-input>
|
||||
<el-input
|
||||
v-else-if="col.slot === 'quantity'"
|
||||
v-model="row.quantity"
|
||||
@@ -444,7 +452,7 @@
|
||||
</div>
|
||||
</el-form-item>
|
||||
</div>
|
||||
<div v-if="baseInfoForm.goodsType != 'VIRTUAL_GOODS'">
|
||||
<div v-if="needsLogistics">
|
||||
<h4>商品物流信息</h4>
|
||||
<div class="form-item-view">
|
||||
<el-form-item class="form-item-view-el" label="物流模板" prop="templateId">
|
||||
@@ -513,7 +521,7 @@
|
||||
@selected="(list) => { selectedImage = list }"
|
||||
/>
|
||||
<template #footer>
|
||||
<el-button @click="picModelFlag = false">取消</el-button>
|
||||
<el-button @click="picModelFlag = false; selectedImage = []">取消</el-button>
|
||||
<el-button type="primary" @click="confirmUrls">确定</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
@@ -552,6 +560,22 @@ export default {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
/** 卡密商品:库存只读展示 poolStock,提交时 quantity 传 0(FR-S-01 / P-02) */
|
||||
isECouponGoods() {
|
||||
return this.baseInfoForm.goodsType === "E_COUPON";
|
||||
},
|
||||
isVirtualGoods() {
|
||||
return this.baseInfoForm.goodsType === "VIRTUAL_GOODS";
|
||||
},
|
||||
isVirtualLikeGoods() {
|
||||
return this.isVirtualGoods || this.isECouponGoods;
|
||||
},
|
||||
/** 仅实物需要运费模板;E_COUPON 强制 templateId=0(§6.1) */
|
||||
needsLogistics() {
|
||||
return this.baseInfoForm.goodsType === "PHYSICAL_GOODS";
|
||||
},
|
||||
},
|
||||
data() {
|
||||
// 表单验证项,商品价格
|
||||
const checkPrice = (rule, value, callback) => {
|
||||
@@ -759,11 +783,32 @@ export default {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
/** E_COUPON 库存列只读展示,无卡密时显示 0 */
|
||||
resolvePoolStockDisplay(row) {
|
||||
if (row.poolStock != null && row.poolStock !== "") {
|
||||
return row.poolStock;
|
||||
}
|
||||
if (row.quantity != null && row.quantity !== "") {
|
||||
return row.quantity;
|
||||
}
|
||||
return 0;
|
||||
},
|
||||
/** E_COUPON 提交固定传 0,真实库存由卡池同步 */
|
||||
resolveSubmitQuantity(sku) {
|
||||
if (this.isECouponGoods) {
|
||||
return 0;
|
||||
}
|
||||
return sku.quantity;
|
||||
},
|
||||
defaultSkuQuantity() {
|
||||
return this.isECouponGoods ? 0 : "";
|
||||
},
|
||||
draggableItemKey(item) {
|
||||
return item;
|
||||
},
|
||||
// 选择图片modal
|
||||
handleCLickImg(val, index) {
|
||||
this.selectedImage = [];
|
||||
this.picModelFlag = true;
|
||||
this.selectedFormBtnName = val;
|
||||
this.$nextTick(() => {
|
||||
@@ -793,22 +838,60 @@ export default {
|
||||
desc: "视频大小不能超过10MB",
|
||||
});
|
||||
},
|
||||
parseOssSelectionUrl(item) {
|
||||
if (!item) return "";
|
||||
if (typeof item === "string") {
|
||||
const index = item.indexOf(",");
|
||||
return index >= 0 ? item.slice(index + 1) : item;
|
||||
}
|
||||
return item.url || "";
|
||||
},
|
||||
// 图片选择后回调
|
||||
callbackSelected(val) {
|
||||
this.picModelFlag = false;
|
||||
if (val && this.selectedFormBtnName == 'selectedSkuImages') {
|
||||
this.selectedSku.images.push(val);
|
||||
if (!val?.url) {
|
||||
return;
|
||||
}
|
||||
if (this.selectedFormBtnName === "selectedSkuImages") {
|
||||
if (!this.selectedSku.images) {
|
||||
this.selectedSku.images = [];
|
||||
}
|
||||
this.selectedSku.images.push(val.url);
|
||||
} else {
|
||||
this.baseInfoForm[this.selectedFormBtnName].push(val.url);
|
||||
}
|
||||
|
||||
},
|
||||
confirmUrls() {
|
||||
if (this.selectedImage && this.selectedFormBtnName == 'selectedSkuImages') {
|
||||
this.selectedSku.images = [...this.selectedSku.images, ...this.selectedImage];
|
||||
} else {
|
||||
this.baseInfoForm[this.selectedFormBtnName] = [...this.baseInfoForm[this.selectedFormBtnName], ...this.selectedImage];
|
||||
const urls = (this.selectedImage || [])
|
||||
.map((item) => this.parseOssSelectionUrl(item))
|
||||
.filter(Boolean);
|
||||
if (!urls.length) {
|
||||
this.$Message.warning("请选择图片");
|
||||
return;
|
||||
}
|
||||
if (this.selectedFormBtnName === "selectedSkuImages") {
|
||||
if (!this.selectedSku.images) {
|
||||
this.selectedSku.images = [];
|
||||
}
|
||||
urls.forEach((url) => {
|
||||
if (this.selectedSku.images.length < 5) {
|
||||
this.selectedSku.images.push(url);
|
||||
}
|
||||
});
|
||||
} else if (this.selectedFormBtnName === "goodsGalleryFiles") {
|
||||
urls.forEach((url) => {
|
||||
if (this.baseInfoForm.goodsGalleryFiles.length < 5) {
|
||||
this.baseInfoForm.goodsGalleryFiles.push(url);
|
||||
}
|
||||
});
|
||||
} else if (this.selectedFormBtnName) {
|
||||
const target = this.baseInfoForm[this.selectedFormBtnName];
|
||||
if (Array.isArray(target)) {
|
||||
urls.forEach((url) => target.push(url));
|
||||
}
|
||||
}
|
||||
this.selectedImage = [];
|
||||
this.picModelFlag = false;
|
||||
},
|
||||
// 局部刷新
|
||||
refresh(v) {
|
||||
@@ -1255,6 +1338,7 @@ export default {
|
||||
price: e.price,
|
||||
// cost: e.cost,
|
||||
quantity: e.quantity,
|
||||
poolStock: e.poolStock,
|
||||
// alertQuantity: e.alertQuantity,
|
||||
weight: e.weight,
|
||||
};
|
||||
@@ -1724,14 +1808,20 @@ export default {
|
||||
...combination,
|
||||
id: existingCombination.id || "",
|
||||
sn: existingCombination.sn || "",
|
||||
quantity: existingCombination.quantity || "",
|
||||
quantity: this.isECouponGoods
|
||||
? this.resolvePoolStockDisplay(existingCombination)
|
||||
: (existingCombination.quantity || ""),
|
||||
poolStock: existingCombination.poolStock,
|
||||
cost: existingCombination.cost || "",
|
||||
price: existingCombination.price || "",
|
||||
weight: existingCombination.weight || ""
|
||||
};
|
||||
} else {
|
||||
// 新组合使用默认值
|
||||
return combination;
|
||||
return {
|
||||
...combination,
|
||||
quantity: this.defaultSkuQuantity(),
|
||||
};
|
||||
}
|
||||
});
|
||||
this.baseInfoForm.regeneratorSkuFlag = true;
|
||||
@@ -1779,14 +1869,20 @@ export default {
|
||||
...combination,
|
||||
id: existingCombination.id || "",
|
||||
sn: existingCombination.sn || "",
|
||||
quantity: existingCombination.quantity || "",
|
||||
quantity: this.isECouponGoods
|
||||
? this.resolvePoolStockDisplay(existingCombination)
|
||||
: (existingCombination.quantity || ""),
|
||||
poolStock: existingCombination.poolStock,
|
||||
cost: existingCombination.cost || "",
|
||||
price: existingCombination.price || "",
|
||||
weight: existingCombination.weight || ""
|
||||
};
|
||||
} else {
|
||||
// 新组合使用默认值
|
||||
return combination;
|
||||
return {
|
||||
...combination,
|
||||
quantity: this.defaultSkuQuantity(),
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1833,7 +1929,7 @@ export default {
|
||||
|
||||
// 有重量的情况
|
||||
if (
|
||||
this.baseInfoForm.goodsType !== "VIRTUAL_GOODS" &&
|
||||
this.needsLogistics &&
|
||||
this.baseInfoForm.salesModel !== "WHOLESALE"
|
||||
) {
|
||||
pushData.push({
|
||||
@@ -1843,7 +1939,7 @@ export default {
|
||||
}
|
||||
pushData.push(
|
||||
{
|
||||
title: "库存",
|
||||
title: this.isECouponGoods ? "卡池可用数" : "库存",
|
||||
slot: "quantity",
|
||||
},
|
||||
// {
|
||||
@@ -1878,7 +1974,10 @@ export default {
|
||||
...combination,
|
||||
id: existingCombination.id || "",
|
||||
sn: existingCombination.sn || "",
|
||||
quantity: existingCombination.quantity || "",
|
||||
quantity: this.isECouponGoods
|
||||
? this.resolvePoolStockDisplay(existingCombination)
|
||||
: (existingCombination.quantity || ""),
|
||||
poolStock: existingCombination.poolStock,
|
||||
cost: existingCombination.cost || "",
|
||||
price: existingCombination.price || (this.baseInfoForm.salesModel === 'WHOLESALE' && this.wholesaleData.length > 0 ? this.wholesaleData[0].price : ""),
|
||||
weight: existingCombination.weight || ""
|
||||
@@ -1889,7 +1988,7 @@ export default {
|
||||
...combination,
|
||||
id: "",
|
||||
sn: "",
|
||||
quantity: "",
|
||||
quantity: this.defaultSkuQuantity(),
|
||||
cost: "",
|
||||
price: this.baseInfoForm.salesModel === 'WHOLESALE' && this.wholesaleData.length > 0 ? this.wholesaleData[0].price : "",
|
||||
weight: ""
|
||||
@@ -2153,6 +2252,10 @@ export default {
|
||||
return;
|
||||
}
|
||||
if (submit.templateId === "") submit.templateId = 0;
|
||||
if (this.isECouponGoods) {
|
||||
submit.templateId = 0;
|
||||
submit.salesModel = "RETAIL";
|
||||
}
|
||||
let flag = false;
|
||||
let paramValue = "";
|
||||
|
||||
@@ -2175,7 +2278,7 @@ export default {
|
||||
let skuCopy = {
|
||||
cost: 1,
|
||||
price: sku.price,
|
||||
quantity: sku.quantity,
|
||||
quantity: this.resolveSubmitQuantity(sku),
|
||||
// alertQuantity: sku.alertQuantity,
|
||||
sn: sku.sn,
|
||||
images: [],
|
||||
|
||||
Reference in New Issue
Block a user