feat(buyer): 增强商品详情与支付逻辑

- 引入 buyerLogin 工具,优化商品详情页的链接跳转逻辑,确保未登录用户能正确重定向到登录页面
- 更新商品 SKU 详情 API,支持未登录状态下的访问
- 修改支付 API,新增 skipMessage 参数以控制支付提示信息
- 改进商品列表与支付页面的用户体验,确保更流畅的操作流程
- 规范化商品类型与库存提示,提升用户体验
This commit is contained in:
田香琪
2026-08-05 14:17:39 +08:00
parent c0b00999c5
commit d4cd6e0936
24 changed files with 700 additions and 511 deletions

View File

@@ -92,22 +92,19 @@ function normalizeCardKeyFulfillLine(source) {
};
}
export function collectCardKeyFulfillLines(orderItems = [], giftSummaries = []) {
/** 汇总当前订单 orderItems 中的卡密履约行(不含满赠子单;赠品卡密在赠品子单详情单独查看) */
export function collectCardKeyFulfillLines(orderItems = []) {
const lines = [];
(orderItems || []).forEach((item) => {
if (!isCardKeyFulfillApplicable(item)) return;
lines.push(normalizeCardKeyFulfillLine(item));
});
(giftSummaries || []).forEach((gift) => {
lines.push(normalizeCardKeyFulfillLine({ ...gift, isGift: true }));
});
return lines;
}
export function orderHasCardKeySection({ orderItems, giftSummaries, isECouponOrder } = {}) {
export function orderHasCardKeySection({ orderItems, isECouponOrder } = {}) {
if (isECouponOrder) return true;
if (Array.isArray(giftSummaries) && giftSummaries.length > 0) return true;
if (collectCardKeyFulfillLines(orderItems, []).length > 0) return true;
if (collectCardKeyFulfillLines(orderItems).length > 0) return true;
return (orderItems || []).some(
(item) =>
item.goodsType === E_COUPON_GOODS_TYPE ||
@@ -126,13 +123,13 @@ export function flattenCardKeyFulfillLines(lines, withGoodsName = false) {
return rows;
}
/** 订单是否含已交付卡密(含满赠子单 */
export function orderHasCardKeyContent(orderItems, giftSummaries) {
return flattenCardKeyFulfillLines(collectCardKeyFulfillLines(orderItems, giftSummaries)).length > 0;
/** 当前订单是否含已交付卡密(仅 orderItems */
export function orderHasCardKeyContent(orderItems) {
return flattenCardKeyFulfillLines(collectCardKeyFulfillLines(orderItems)).length > 0;
}
export function flattenOrderCardKeys(orderItems, withGoodsName = false, giftSummaries) {
const lines = collectCardKeyFulfillLines(orderItems, giftSummaries);
export function flattenOrderCardKeys(orderItems, withGoodsName = false) {
const lines = collectCardKeyFulfillLines(orderItems);
if (lines.some((line) => line.status)) {
return flattenCardKeyFulfillLines(lines, withGoodsName);
}

View File

@@ -5,11 +5,29 @@
<template>
<div class="search card-key-pool">
<el-card>
<div v-if="goodsName" class="pool-header">
<div v-if="goodsName || skuOptions.length" class="pool-header">
<el-button @click="goBack">返回</el-button>
<span class="pool-meta">
<span v-if="goodsName" class="pool-meta">
<strong>商品</strong>{{ goodsName }}
</span>
<div v-if="skuOptions.length" class="pool-sku-select">
<span class="pool-sku-label">规格 SKU</span>
<el-select
v-model="skuId"
:loading="skuLoading"
placeholder="请选择规格"
filterable
style="width: 320px"
@change="onSkuChange"
>
<el-option
v-for="item in skuOptions"
:key="item.id"
:label="formatSkuOptionLabel(item)"
:value="item.id"
/>
</el-select>
</div>
</div>
<el-alert
@@ -167,9 +185,17 @@
max-height="320"
border
>
<el-table-column prop="row" label="行号" width="80" />
<el-table-column prop="cardNo" label="卡号" min-width="120" />
<el-table-column prop="reason" label="失败原因" min-width="200" />
<el-table-column label="行号" width="80" align="center">
<template #default="{ row: failItem }">
<span v-if="failItem">{{ failItem.row }}</span>
</template>
</el-table-column>
<el-table-column label="卡号" min-width="120">
<template #default="{ row: failItem }">
<span v-if="failItem">{{ failItem.cardNo || "—" }}</span>
</template>
</el-table-column>
<el-table-column prop="reason" label="失败原因" min-width="200" show-overflow-tooltip />
</el-table>
<template #footer>
<el-button type="primary" @click="importResultVisible = false">确定</el-button>
@@ -204,6 +230,7 @@ import {
exportCardKey,
downloadImportTemplate,
} from "@/api/cardKey";
import { getQueryGoodsIdGoodsList } from "@/api/goods";
import {
formatCardKeyStatus,
CARD_KEY_STATUS_TAG,
@@ -223,6 +250,8 @@ export default {
skuId: "",
goodsId: "",
goodsName: "",
skuOptions: [],
skuLoading: false,
statusTab: "ALL",
stats: null,
poolLocked: false,
@@ -271,6 +300,20 @@ export default {
];
},
},
watch: {
"$route.query": {
handler() {
const prevSkuId = this.skuId;
this.initFromRoute();
if (this.skuId && this.skuId !== prevSkuId) {
this.resetPoolViewState();
this.loadStats();
this.getList();
}
},
deep: true,
},
},
methods: {
formatStatus: formatCardKeyStatus,
statusTag(status) {
@@ -285,6 +328,65 @@ export default {
this.goodsId = q.goodsId || "";
this.goodsName = q.goodsName || "";
},
formatSkuOptionLabel(sku) {
if (!sku) return "";
const spec = sku.simpleSpecs || sku.sn || sku.id;
const qty =
sku.poolStock != null && sku.poolStock !== ""
? sku.poolStock
: sku.quantity != null
? sku.quantity
: "—";
return `${spec}(库存 ${qty}`;
},
resetPoolViewState() {
this.statusTab = "ALL";
this.searchForm.pageNumber = 1;
this.searchForm.cardNo = "";
this.importRange = [];
this.poolLocked = false;
this.stats = null;
this.data = [];
this.total = 0;
},
/** 卡池变更后刷新 SKU 选择器中的可售库存(后端 syncSkuStock → quantity */
refreshSkuOptionStock() {
if (!this.goodsId) return Promise.resolve();
return this.loadSkuOptions();
},
async loadSkuOptions() {
if (!this.goodsId) {
this.skuOptions = [];
return;
}
this.skuLoading = true;
try {
const res = await getQueryGoodsIdGoodsList(this.goodsId);
if (res.success) {
this.skuOptions = (res.result || []).filter((s) => !s.deleteFlag);
}
} catch {
this.skuOptions = [];
} finally {
this.skuLoading = false;
}
},
onSkuChange(newSkuId) {
if (!newSkuId) return;
this.skuId = newSkuId;
this.resetPoolViewState();
this.$router.replace({
path: this.$route.path,
query: {
...this.$route.query,
skuId: newSkuId,
goodsId: this.goodsId,
goodsName: this.goodsName,
},
});
this.loadStats();
this.getList();
},
onStatusTabClick(tab) {
this.statusTab = tab.paneName;
this.searchForm.pageNumber = 1;
@@ -370,6 +472,7 @@ export default {
this.importResultVisible = true;
this.loadStats();
this.getList();
this.refreshSkuOptionStock();
} else {
this.checkPoolLocked(res);
}
@@ -407,6 +510,7 @@ export default {
this.addVisible = false;
this.loadStats();
this.getList();
this.refreshSkuOptionStock();
} else {
this.checkPoolLocked(res);
}
@@ -426,6 +530,7 @@ export default {
this.$Message.success("作废成功");
this.loadStats();
this.getList();
this.refreshSkuOptionStock();
}
});
},
@@ -445,8 +550,15 @@ export default {
this.exportLoading = false;
});
},
init() {
async init() {
this.initFromRoute();
if (this.goodsId) {
await this.loadSkuOptions();
}
if (!this.skuId && this.skuOptions.length) {
this.onSkuChange(this.skuOptions[0].id);
return;
}
if (this.skuId) {
this.loadStats();
this.getList();
@@ -475,6 +587,18 @@ export default {
font-size: 14px;
}
.pool-sku-select {
display: flex;
align-items: center;
gap: 8px;
}
.pool-sku-label {
color: #606266;
font-size: 14px;
white-space: nowrap;
}
.goods-tab {
:deep(.el-tabs__item) {
font-size: 14px;

View File

@@ -1,58 +1,31 @@
<template>
<div class="goods-operation">
<div class="step-list">
<el-steps :active="activestep" align-center style="height: 60px; margin-top: 10px">
<el-step title="选择商品品类" />
<el-step title="填写商品详情" />
<el-step title="商品发布成功" />
</el-steps>
</div>
<!-- 第一步 选择分类 -->
<first-step ref='first' v-show="activestep === 0" @change="getFirstData"></first-step>
<!-- 第二步 商品详细信息 -->
<second-step ref='second' :firstData="firstData" v-if="activestep === 1"></second-step>
<!-- 第三步 发布完成 -->
<third-step ref='third' v-if="activestep === 2"></third-step>
<!-- 填写商品详细信息含商品类型商品分类 -->
<second-step ref="second" :firstData="firstData" v-if="activestep === 1"></second-step>
<!-- 发布完成 -->
<third-step ref="third" v-if="activestep === 2"></third-step>
</div>
</template>
<script>
import firstStep from './goodsOperationFirst'
import secondStep from './goodsOperationSec'
import thirdStep from './goodsOperationThird'
import secondStep from "./goodsOperationSec";
import thirdStep from "./goodsOperationThird";
export default {
name: "addGoods",
components: {
firstStep,
secondStep,
thirdStep
thirdStep,
},
data() {
return {
/** 当前激活步骤*/
activestep: 0,
firstData: {}, // 第一步传递的数据
/** 当前激活步骤1 填写详情2 发布成功 */
activestep: 1,
firstData: {},
};
},
methods: {
// 选择商品分类回调
getFirstData (item) {
this.firstData = item;
this.activestep = 1;
}
},
mounted() {
// 编辑商品、模板、复制商品
if (this.$route.query.id || this.$route.query.draftId || this.$route.query.copyId) {
this.activestep = 1;
} else {
this.activestep = 0
this.$refs.first.selectGoodsType = true;
}
}
window.scrollTo(0, 0);
},
};
</script>
<style lang="scss" scoped>

View File

@@ -1,249 +1,9 @@
<template>
<div>
<!-- 选择商品类型 -->
<el-dialog v-model="selectGoodsType" width="550px" :show-close="false">
<div class="goods-type-list">
<div
class="goods-type-item"
:class="{ 'active-goods-type': item.check }"
@click="handleClickGoodsType(item)"
v-for="(item, index) in goodsTypeWay"
:key="index"
>
<img :src="item.img" />
<div>
<h2>{{ item.title }}</h2>
<p>{{ item.desc }}</p>
</div>
</div>
</div>
<template #footer>
<div class="goods-type-actions">
<el-button @click="cancelGoodsType">取消</el-button>
<el-button type="primary" @click="confirmGoodsType">确认</el-button>
</div>
</template>
</el-dialog>
<!-- 商品分类 -->
<div class="content-goods-publish">
<div class="goods-category">
<ul v-if="categoryListLevel1.length > 0">
<li
v-for="(item, index) in categoryListLevel1"
:class="{ activeClass: category[0].name === item.name }"
@click="handleSelectCategory(item, index, 1)"
:key="index"
>
<span>{{ item.name }}</span>
<span>&gt;</span>
</li>
</ul>
<ul v-if="categoryListLevel2.length > 0">
<li
v-for="(item, index) in categoryListLevel2"
:class="{ activeClass: category[1].name === item.name }"
@click="handleSelectCategory(item, index, 2)"
:key="index"
>
<span>{{ item.name }}</span>
<span>&gt;</span>
</li>
</ul>
<ul v-if="categoryListLevel3.length > 0">
<li
v-for="(item, index) in categoryListLevel3"
:class="{ activeClass: category[2].name === item.name }"
@click="handleSelectCategory(item, index, 3)"
:key="index"
>
<span>{{ item.name }}</span>
</li>
</ul>
</div>
<p class="current-goods-category">
您当前选择的商品类别是
<span>{{ category[0].name }}</span>
<span v-show="category[1].name">> {{ category[1].name }}</span>
<span v-show="category[2].name">> {{ category[2].name }}</span>
</p>
</div>
<!-- 底部按钮 -->
<div class="footer">
<div class="footer-btns">
<el-button type="primary" @click="openGoodsTypeDialog">商品类型</el-button>
<el-button type="primary" @click="next">下一步</el-button>
</div>
</div>
</div>
<div></div>
</template>
<script>
import * as API_GOODS from "@/api/goods";
import goodsType1Img from "@/assets/goodsType1.png";
import goodsType2Img from "@/assets/goodsType2.png";
/** 商品类型与分类已迁移至 goodsOperationSec.vue 基本信息区 */
export default {
data() {
return {
selectGoodsType: false, // 展示选择商品分类modal
/** 商品类型选项E_COUPON 为卡密商品(与 VIRTUAL_GOODS 核销型区分,见 FR-S-01 */
goodsTypeWay: [
{
title: "实物商品",
img: goodsType1Img,
desc: "零售批发,物流配送",
type: "PHYSICAL_GOODS",
check: false,
},
{
title: "虚拟商品",
img: goodsType2Img,
desc: "虚拟核验,无需物流",
type: "VIRTUAL_GOODS",
check: false,
},
{
title: "电子卡券",
img: goodsType2Img,
desc: "卡密自动发卡,无需物流",
type: "E_COUPON", // goodsType库存由卡池同步非手动填写
check: false,
},
],
// 商品分类选择数组
category: [
{ name: "", id: "" },
{ name: "", id: "" },
{ name: "", id: "" },
],
// 商品类型
goodsType: "",
pendingGoodsType: "",
/** 1级分类列表*/
categoryListLevel1: [],
/** 2级分类列表*/
categoryListLevel2: [],
/** 3级分类列表*/
categoryListLevel3: [],
};
},
watch: {
selectGoodsType(val) {
if (val) {
this.syncGoodsTypeSelection();
}
},
},
methods: {
syncGoodsTypeSelection() {
this.pendingGoodsType = this.goodsType;
this.goodsTypeWay.forEach((item) => {
item.check = item.type === this.goodsType;
});
},
openGoodsTypeDialog() {
this.selectGoodsType = true;
},
// 点击商品类型(仅临时选中,确认后才生效)
handleClickGoodsType(val) {
this.goodsTypeWay.forEach((item) => {
item.check = item.type === val.type;
});
this.pendingGoodsType = val.type;
},
cancelGoodsType() {
this.syncGoodsTypeSelection();
this.selectGoodsType = false;
},
confirmGoodsType() {
if (!this.pendingGoodsType) {
this.$Message.error("请选择商品类型");
return;
}
this.goodsType = this.pendingGoodsType;
this.selectGoodsType = false;
},
/** 选择商城商品分类 */
handleSelectCategory(row, index, level) {
if (level === 1) {
this.category.forEach((cate) => {
(cate.name = ""), (cate.id = "");
});
this.category[0].name = row.name;
this.category[0].id = row.id;
this.categoryListLevel2 = this.categoryListLevel1[index].children;
this.categoryListLevel3 = [];
} else if (level === 2) {
this.category[1].name = row.name;
this.category[1].id = row.id;
this.category[2].name = "";
this.category[2].id = "";
this.categoryListLevel3 = this.categoryListLevel2[index].children;
} else {
this.category[2].name = row.name;
this.category[2].id = row.id;
}
},
/** 查询下一级 商城商品分类*/
GET_NextLevelCategory(row) {
const _id = row && row.id !== 0 ? row.id : 0;
API_GOODS.getGoodsCategoryAll().then((res) => {
if (res.success && res.result) {
this.categoryListLevel1 = res.result;
}
});
},
// 下一步
next() {
window.scrollTo(0, 0);
if (!this.goodsType) {
this.$Message.error("请选择商品类型");
return;
}
if (!this.category[0].name) {
this.$Message.error("请选择商品分类");
return;
} else if (!this.category[2].name) {
this.$Message.error("必须选择到三级分类");
return;
} else if (this.category[2].name) {
let params = {
category: this.category,
goodsType: this.goodsType,
};
this.$emit("change", params);
}
},
},
mounted() {
this.GET_NextLevelCategory();
},
name: "goodsOperationFirst",
};
</script>
<style lang="scss" scoped>
@import "./addGoods.scss";
.footer {
display: flex;
justify-content: center;
}
.footer-btns {
display: flex;
gap: 12px;
justify-content: center;
}
.goods-type-actions {
display: flex;
justify-content: flex-end;
gap: 12px;
width: 100%;
}
.content-goods-publish {
.goods-category li.activeClass {
background-color: #409eff;
border-color: #409eff;
color: #fff;
}
}
</style>

View File

@@ -8,12 +8,37 @@
<div class="base-info-item">
<h4>基本信息</h4>
<div class="form-item-view">
<el-form-item label="商品类">
<span class="goods-category-name">{{
baseInfoForm.categoryName[0]
}}</span>
<span> &gt; {{ baseInfoForm.categoryName[1] }}</span>
<span> &gt; {{ baseInfoForm.categoryName[2] }}</span>
<el-form-item label="商品类型" prop="goodsType">
<el-select
v-model="baseInfoForm.goodsType"
placeholder="请选择商品类型"
style="width: 260px"
popper-class="goods-type-select-popper"
@change="handleGoodsTypeChange"
>
<el-option
v-for="item in goodsTypeOptions"
:key="item.value"
:label="item.label"
:value="item.value"
>
<div class="goods-type-option">
<span class="goods-type-option-label">{{ item.label }}</span>
<span class="goods-type-option-desc">{{ item.desc }}</span>
</div>
</el-option>
</el-select>
<div v-if="currentGoodsTypeDesc" class="form-tip">{{ currentGoodsTypeDesc }}</div>
</el-form-item>
<el-form-item label="商品分类" prop="categoryPath">
<el-cascader
v-model="selectedCategory"
:options="categoryList"
placeholder="请选择商品分类"
clearable
style="width: 260px"
@change="handleCategoryChange"
/>
</el-form-item>
<el-form-item label="商品名称" prop="goodsName">
<el-input v-model="baseInfoForm.goodsName" clearable placeholder="商品名称" style="width: 260px" type="text" />
@@ -494,7 +519,6 @@
<!-- 底部按钮 -->
<div class="footer">
<div class="footer-btns">
<el-button type="primary" @click="pre">上一步</el-button>
<el-button :loading="submitLoading" type="primary" @click="save">
{{ $route.query.id ? "保存" : "保存商品" }}
</el-button>
@@ -575,6 +599,12 @@ export default {
needsLogistics() {
return this.baseInfoForm.goodsType === "PHYSICAL_GOODS";
},
currentGoodsTypeDesc() {
const matched = this.goodsTypeOptions.find(
(item) => item.value === this.baseInfoForm.goodsType
);
return matched ? matched.desc : "";
},
},
data() {
// 表单验证项,商品价格
@@ -608,6 +638,16 @@ export default {
accessToken: "", //令牌token
goodsParams: [],
categoryId: "", // 商品分类第三级id
/** 商品类型选项 */
goodsTypeOptions: [
{ label: "实物商品", value: "PHYSICAL_GOODS", desc: "零售批发物流配送" },
{ label: "虚拟商品", value: "VIRTUAL_GOODS", desc: "虚拟核验无需物流" },
{ label: "电子卡券", value: "E_COUPON", desc: "卡密自动发卡无需物流" },
],
/** 商城分类级联数据 */
categoryList: [],
/** 商城分类选中值 [一级id, 二级id, 三级id] */
selectedCategory: [],
//提交状态
submitLoading: false,
//上传图片路径
@@ -729,6 +769,8 @@ export default {
/** 存储未通过校验的单元格位置 */
validateError: [],
baseInfoFormRule: {
goodsType: [{ required: true, message: "请选择商品类型", trigger: "change" }],
categoryPath: [{ required: true, message: "请选择商品分类", trigger: "change" }],
goodsName: [regular.REQUIRED, regular.WHITE_SPACE, regular.VARCHAR60],
price: [regular.REQUIRED, { validator: checkPrice }],
sellingPoint: [regular.REQUIRED, regular.VARCHAR60],
@@ -986,9 +1028,79 @@ export default {
});
}
},
pre() {
// 上一步
this.$parent.activestep--;
/** 加载商城商品分类树 */
loadCategoryList() {
return API_GOODS.getGoodsCategoryAll().then((res) => {
if (res.success && res.result) {
this.categoryList = this.formatCategoryTree(res.result);
this.syncSelectedCategoryFromPath();
}
});
},
formatCategoryTree(list) {
return (list || []).map((item) => ({
value: String(item.id),
label: item.name,
children: (item.children || []).map((child) => ({
value: String(child.id),
label: child.name,
children: (child.children || []).map((grandson) => ({
value: String(grandson.id),
label: grandson.name,
})),
})),
}));
},
syncSelectedCategoryFromPath() {
const path = this.baseInfoForm.categoryPath;
if (!path) {
return;
}
const ids = path.split(",").filter(Boolean).map(String);
if (ids.length >= 3) {
this.selectedCategory = ids;
this.categoryId = ids[2];
}
},
getCategoryNames(ids) {
const names = [];
let options = this.categoryList;
ids.forEach((id) => {
const found = (options || []).find((item) => item.value === id);
if (found) {
names.push(found.label);
options = found.children || [];
}
});
return names;
},
handleGoodsTypeChange() {
if (this.isVirtualLikeGoods) {
this.baseInfoForm.salesModel = "RETAIL";
}
if (this.isECouponGoods) {
this.baseInfoForm.templateId = 0;
}
this.renderTableData(this.skuTableData);
},
handleCategoryChange(val) {
if (!val || val.length < 3) {
this.selectedCategory = val || [];
this.categoryId = "";
this.baseInfoForm.categoryPath = "";
this.baseInfoForm.categoryName = [];
this.baseInfoForm.brandId = 0;
this.brandList = [];
this.goodsParams = [];
this.params_panel = [];
return;
}
this.categoryId = val[2];
this.baseInfoForm.categoryPath = val.join(",");
this.baseInfoForm.categoryName = this.getCategoryNames(val);
this.baseInfoForm.brandId = 0;
this.getGoodsBrandList();
this.GET_GoodsParams();
},
// 预览图片
handleView(url) {
@@ -1265,6 +1377,7 @@ export default {
this.baseInfoForm = { ...this.baseInfoForm, ...response.result };
this.baseInfoForm.release = 1; //即使是被放入仓库,修改的时候也会显示会立即发布
this.categoryId = response.result.categoryPath.split(",")[2];
this.syncSelectedCategoryFromPath();
// 如果是复制商品需要清除ID确保提交时作为新商品
if (this.$route.query.copyId) {
@@ -1301,19 +1414,6 @@ export default {
this.GET_ShopGoodsLabel();
this.GET_GoodsUnit();
if (this.firstData.category) {
const cateId = [];
this.baseInfoForm.categoryName = [];
this.firstData.category.forEach((cate) => {
this.baseInfoForm.categoryName.push(cate.name);
cateId.push(cate.id);
});
this.categoryId = cateId[2];
this.baseInfoForm.categoryPath = cateId.toString();
}
this.firstData.goodsType &&
(this.baseInfoForm.goodsType = this.firstData.goodsType);
/** 查询商品参数 */
this.GET_GoodsParams();
},
@@ -2204,6 +2304,10 @@ export default {
this.$Message.error(`以下参数为必填项:${missingParams.join('、')}`);
return;
}
if (!this.selectedCategory || this.selectedCategory.length < 3) {
this.$Message.error("必须选择到三级分类");
return;
}
this.submitLoading = true;
this.$refs["baseInfoForm"].validate((valid) => {
if (valid) {
@@ -2362,37 +2466,20 @@ export default {
accessToken: this.getStore("accessToken"),
};
this.GET_ShipTemplate()
this.loadCategoryList();
if (this.$route.query.id || this.$route.query.draftId) {
// 编辑商品、模板
this.GET_GoodData(this.$route.query.id, this.$route.query.draftId);
} else if (this.$route.query.copyId) {
// 复制商品
this.GET_GoodData(this.$route.query.copyId);
} else if (this.firstData.tempId) {
// 选择模板
this.GET_GoodData("", this.firstData.tempId);
} else {
// 新增商品、模板
if (this.firstData.tempId) {
// 选择模板
this.GET_GoodData("", this.firstData.tempId);
} else {
const cateId = [];
this.firstData.category.forEach((cate) => {
this.baseInfoForm.categoryName.push(cate.name);
cateId.push(cate.id);
});
this.categoryId = cateId[2];
this.baseInfoForm.categoryPath = cateId.toString();
this.baseInfoForm.goodsType = this.firstData.goodsType;
/** 获取该商城分类下 商品参数信息 */
this.GET_GoodsParams();
/** 查询品牌列表 */
this.getGoodsBrandList();
// 获取商品单位
this.GET_GoodsUnit();
// 获取当前店铺分类
this.GET_ShopGoodsLabel();
}
// 新增商品
this.GET_GoodsUnit();
this.GET_ShopGoodsLabel();
}
},
};
@@ -2505,4 +2592,58 @@ export default {
.refresh-icon {
margin-left: 10px;
}
.form-tip {
color: #666;
font-size: 12px;
line-height: 1.5;
margin-top: 4px;
max-width: 260px;
}
.goods-type-option {
display: flex;
flex-direction: column;
line-height: 1.4;
padding: 2px 0;
}
.goods-type-option-label {
color: #303133;
}
.goods-type-option-desc {
color: #909399;
font-size: 12px;
}
</style>
<style lang="scss">
.goods-type-select-popper {
.el-select-dropdown__item {
height: auto;
min-height: 58px;
padding: 10px 12px;
line-height: 1.5;
white-space: normal;
}
.goods-type-option {
display: flex;
flex-direction: column;
gap: 4px;
}
.goods-type-option-label {
color: #303133;
font-size: 14px;
line-height: 1.4;
}
.goods-type-option-desc {
color: #909399;
font-size: 12px;
line-height: 1.5;
white-space: normal;
}
}
</style>

View File

@@ -2,9 +2,6 @@
<div class="content-goods-publish">
<div class="success" style="text-align: left">
<h1>恭喜您商品发布成功!</h1>
<div class="goToGoodsList" @click="gotoGoodsList">
<a>去店铺查看商品列表>></a>
</div>
<div class="operation">
<h3>您还可以</h3>
<div>
@@ -34,5 +31,17 @@ export default {
}
</script>
<style lang="scss" scoped>
@import "./addGoods.scss";
@import "./addGoods.scss";
.success {
a {
color: #409eff;
cursor: pointer;
text-decoration: none;
&:hover {
color: #66b1ff;
}
}
}
</style>

View File

@@ -61,8 +61,8 @@
</div>
<div class="wap-content-desc-bottom">
<div>{{ $filters.unitPrice(item.price) }}</div>
<div v-if="item.goodsType === 'E_COUPON'" class="pool-stock-hint">
库存 {{ item.quantity != null ? item.quantity : "" }}卡池
<div v-if="showStockHint(item)" class="stock-hint">
库存 {{ item.quantity != null ? item.quantity : "" }}<template v-if="item.goodsType === 'E_COUPON'">卡池</template>
</div>
</div>
</div>
@@ -142,6 +142,9 @@ export default {
methods: {
goodsTypeLabel,
goodsTypeTagType,
showStockHint(item) {
return ["PHYSICAL_GOODS", "VIRTUAL_GOODS", "E_COUPON"].includes(item.goodsType);
},
onSearchGoods() {
this.goodsData = [];
this.goodsParams.pageNumber = 1;
@@ -261,16 +264,41 @@ export default {
position: relative;
flex-wrap: wrap;
height: 340px;
align-content: flex-start;
overflow-y: auto;
}
.wap-content-item {
width: 210px;
position: relative;
width: calc((100% - 56px) / 4);
box-sizing: border-box;
margin: 10px 7px;
padding: 6px 0;
}
.active {
background: url("../../assets/selected.png") no-repeat;
background-position: right;
background-size: 10%;
padding: 6px 8px;
background: #fff;
border-radius: 4px;
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.08);
min-width: 0;
:deep(.wap-content-desc) {
width: auto;
flex: 1;
min-width: 0;
}
&.active {
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.12);
&::after {
content: "";
position: absolute;
top: 0;
right: 0;
width: 20px;
height: 20px;
background: url("../../assets/selected.png") no-repeat center / contain;
z-index: 2;
pointer-events: none;
}
}
}
.loading-mask {
position: absolute;
@@ -280,10 +308,15 @@ export default {
margin-top: 12px;
justify-content: flex-end;
}
.pool-stock-hint {
font-size: 12px;
color: #999;
margin-top: 4px;
.wap-content-desc-bottom {
align-items: center;
.stock-hint {
font-size: 12px;
color: #999;
white-space: nowrap;
flex-shrink: 0;
}
}
</style>
<style lang="scss">

View File

@@ -1,7 +1,7 @@
<template>
<el-dialog
v-model="flag"
width="1160px"
width="1200px"
top="120px"
:z-index="10000"
:close-on-click-modal="false"

View File

@@ -257,7 +257,7 @@
</el-table-column>
</el-table>
<!-- 卡密信息E_COUPON 主单或满赠子单 cardKeyFulfillStatus 为准 -->
<!-- 卡密信息当前订单 orderItems满赠赠品卡密在赠品子单详情查看 -->
<div v-if="hasCardKeySection" class="ecoupon-card-keys mt_10">
<h4>卡密信息</h4>
<div
@@ -265,9 +265,6 @@
:key="line.key"
class="ecoupon-fulfill-block"
>
<div v-if="cardKeyFulfillLines.length > 1" class="ecoupon-fulfill-title mb_10">
{{ line.goodsName || (line.isGift ? "满赠电子卡券" : "电子卡券") }}
</div>
<el-alert
v-if="line.status !== 'DELIVERED'"
:type="cardKeyFulfillAlertType(line.status)"
@@ -284,13 +281,6 @@
class="mb_10"
>
<el-table-column type="index" label="序号" width="60" align="center" />
<el-table-column
v-if="cardKeyFulfillLines.length > 1"
prop="goodsName"
label="商品"
min-width="140"
show-overflow-tooltip
/>
<el-table-column prop="cardNo" label="卡号" min-width="140" show-overflow-tooltip />
<el-table-column prop="cardSecret" label="卡密" min-width="120" show-overflow-tooltip />
<el-table-column prop="allocatedTime" label="发卡时间" width="170" />
@@ -889,15 +879,14 @@ export default {
hasCardKeySection() {
return orderHasCardKeySection({
orderItems: this.data,
giftSummaries: this.orderInfo.giftECouponOrders,
isECouponOrder: this.isECouponOrder,
});
},
cardKeyFulfillLines() {
return collectCardKeyFulfillLines(this.data, this.orderInfo.giftECouponOrders);
return collectCardKeyFulfillLines(this.data);
},
ecouponCardKeyRows() {
return flattenOrderCardKeys(this.data, true, this.orderInfo.giftECouponOrders);
return flattenOrderCardKeys(this.data, true);
},
canPartDelivery() {
if (!this.allowOperation.ship) return false;
@@ -934,10 +923,7 @@ export default {
cardKeyFulfillAlertType,
resolveCardKeyFulfillMessage,
lineCardKeyRows(line) {
return (line.cardKeys || []).map((ck) => ({
...ck,
goodsName: line.goodsName,
}));
return line.cardKeys || [];
},
copyCardKey(row) {
const text = `卡号:${row.cardNo || ""}\n卡密${row.cardSecret || ""}`;

View File

@@ -113,24 +113,26 @@
</el-select>
</el-form-item>
<el-form-item v-if="form.giftFlag" label="赠品" prop="giftId">
<p class="describe">电子卡券赠品支付后自动发卡卡池不足时仅取消赠品子单不影响主订单</p>
<el-select
v-model="form.giftId"
filterable
remote
:remote-method="getGiftList"
placeholder="输入赠品名称搜索"
:disabled="fieldDisabled"
:loading="giftLoading"
style="width: 260px"
>
<el-option
v-for="item in giftList"
:value="item.id"
:key="item.id"
:label="giftOptionLabel(item)"
/>
</el-select>
<div class="gift-field">
<el-select
v-model="form.giftId"
filterable
remote
:remote-method="getGiftList"
placeholder="输入赠品名称搜索"
:disabled="fieldDisabled"
:loading="giftLoading"
style="width: 260px"
>
<el-option
v-for="item in giftList"
:value="item.id"
:key="item.id"
:label="giftOptionLabel(item)"
/>
</el-select>
<p class="describe describe-block">电子卡券赠品支付后自动发卡卡池不足时仅取消赠品子单不影响主订单</p>
</div>
</el-form-item>
<el-form-item v-if="form.pointFlag" label="赠积分" prop="point">
<el-input
@@ -513,6 +515,17 @@ h4 {
margin-left: 10px;
color: #999;
}
.describe-block {
margin-left: 0;
margin-top: 4px;
line-height: 1.5;
max-width: 260px;
}
.gift-field {
display: flex;
flex-direction: column;
align-items: flex-start;
}
.link-text {
color: #409eff;
cursor: pointer;