mirror of
https://gitee.com/beijing_hongye_huicheng/lilishop-ui.git
synced 2026-08-06 02:47:29 +08:00
feat(buyer): 添加电子卡券(E_COUPON)支持
- 新增 E_COUPON 商品类型,支持电子卡券的展示与购买逻辑 - 优化商品详情页,调整促销、库存和购买按钮逻辑以适应电子卡券 - 增加卡密信息展示与管理功能,支持商家端卡池管理 - 更新相关页面以处理电子卡券的特殊逻辑,如免地址、隐藏优惠券等
This commit is contained in:
65
seller/src/api/cardKey.js
Normal file
65
seller/src/api/cardKey.js
Normal file
@@ -0,0 +1,65 @@
|
||||
/**
|
||||
* 卡密商品(E_COUPON)— 商家端卡池 HTTP 封装
|
||||
*
|
||||
* Base Path:/store/goods/card-key(axios 已带 /store 前缀)
|
||||
* 对应 card-key-goods-api.md API-S-01a ~ S-07;导出为同步文件流(S-08)。
|
||||
*
|
||||
* @author Mike
|
||||
* @date 2026-07-31
|
||||
*/
|
||||
import {
|
||||
getRequest,
|
||||
postRequestWithNoForm,
|
||||
putRequest,
|
||||
uploadFileRequest,
|
||||
} from "@/libs/axios";
|
||||
import { downloadBlob } from "@/utils/downloadBlob";
|
||||
|
||||
/** API-S-01a 下载卡密导入模板(同步文件流) */
|
||||
export const downloadImportTemplateBlob = () => {
|
||||
return getRequest("/goods/card-key/import/template", {}, "blob");
|
||||
};
|
||||
|
||||
export const downloadImportTemplate = async () => {
|
||||
const blob = await downloadImportTemplateBlob();
|
||||
downloadBlob(blob, "card-key-import-template.xlsx");
|
||||
};
|
||||
|
||||
/** API-S-01 批量导入卡密 */
|
||||
export const importCardKey = (skuId, file) => {
|
||||
const formData = new FormData();
|
||||
formData.append("skuId", skuId);
|
||||
formData.append("file", file);
|
||||
return uploadFileRequest("/goods/card-key/import", formData);
|
||||
};
|
||||
|
||||
/** API-S-02 单条新增卡密(后端 @RequestBody,须 application/json) */
|
||||
export const addCardKey = (data) => {
|
||||
return postRequestWithNoForm("/goods/card-key/add", data);
|
||||
};
|
||||
|
||||
/** API-S-03 卡池分页列表 */
|
||||
export const getCardKeyList = (params) => {
|
||||
return getRequest("/goods/card-key/list", params);
|
||||
};
|
||||
|
||||
/** API-S-04 作废卡密 */
|
||||
export const voidCardKey = (id) => {
|
||||
return putRequest(`/goods/card-key/void/${id}`);
|
||||
};
|
||||
|
||||
/** API-S-05 卡池状态统计 */
|
||||
export const getCardKeyStats = (skuId) => {
|
||||
return getRequest(`/goods/card-key/stats/${skuId}`);
|
||||
};
|
||||
|
||||
/** API-S-07 卡池导出(同步文件流) */
|
||||
export const exportCardKeyBlob = (params) => {
|
||||
return getRequest("/goods/card-key/export", params, "blob");
|
||||
};
|
||||
|
||||
export const exportCardKey = async (params, skuId) => {
|
||||
const blob = await exportCardKeyBlob(params);
|
||||
const ts = new Date().toISOString().replace(/[-:T]/g, "").slice(0, 14);
|
||||
downloadBlob(blob, `card-key-${skuId || "export"}-${ts}.xlsx`);
|
||||
};
|
||||
41
seller/src/constants/cardKey.js
Normal file
41
seller/src/constants/cardKey.js
Normal file
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* 卡密商品(E_COUPON)— 商家端常量
|
||||
*
|
||||
* 卡池状态 UNUSED / ALLOCATED / VOIDED;Tab 与列表筛选、标签色映射。
|
||||
* 需求:card-key-goods-api.md §2.1~2.3
|
||||
*
|
||||
* @author Mike
|
||||
* @date 2026-07-31
|
||||
*/
|
||||
/** 卡密状态枚举(与后端 CardKeyStatusEnum 一致) */
|
||||
export const CARD_KEY_STATUS = {
|
||||
UNUSED: "UNUSED",
|
||||
ALLOCATED: "ALLOCATED",
|
||||
VOIDED: "VOIDED",
|
||||
};
|
||||
|
||||
export const CARD_KEY_STATUS_TEXT = {
|
||||
UNUSED: "未使用",
|
||||
ALLOCATED: "已分配",
|
||||
VOIDED: "已作废",
|
||||
};
|
||||
|
||||
export const CARD_KEY_STATUS_TAG = {
|
||||
UNUSED: "success",
|
||||
ALLOCATED: "info",
|
||||
VOIDED: "danger",
|
||||
};
|
||||
|
||||
/** Tab:全部 + 各状态 */
|
||||
export const CARD_KEY_STATUS_TABS = [
|
||||
{ key: "", label: "全部" },
|
||||
{ key: CARD_KEY_STATUS.UNUSED, label: "未使用" },
|
||||
{ key: CARD_KEY_STATUS.ALLOCATED, label: "已分配" },
|
||||
{ key: CARD_KEY_STATUS.VOIDED, label: "已作废" },
|
||||
];
|
||||
|
||||
export const E_COUPON_GOODS_TYPE = "E_COUPON";
|
||||
|
||||
export function formatCardKeyStatus(status) {
|
||||
return CARD_KEY_STATUS_TEXT[status] || status || "—";
|
||||
}
|
||||
@@ -64,6 +64,13 @@ export const otherRouter = {
|
||||
name: "goods-draft-operation-edit",
|
||||
component: () => import("@/views/goods/goods-seller/goodsOperation.vue")
|
||||
},
|
||||
/** 卡密商品(E_COUPON)卡池管理;query: skuId(必填), goodsId, goodsName */
|
||||
{
|
||||
path: "card-key-pool",
|
||||
title: "卡池管理",
|
||||
name: "card-key-pool",
|
||||
component: () => import("@/views/goods/card-key/cardKeyPool.vue")
|
||||
},
|
||||
{
|
||||
path: "add-coupon",
|
||||
title: "店铺优惠券",
|
||||
|
||||
485
seller/src/views/goods/card-key/cardKeyPool.vue
Normal file
485
seller/src/views/goods/card-key/cardKeyPool.vue
Normal file
@@ -0,0 +1,485 @@
|
||||
<!-- 卡密商品 · 卡池管理(原型 P-04;API-S-01~S-07)
|
||||
@author Mike
|
||||
@date 2026-07-31
|
||||
-->
|
||||
<template>
|
||||
<div class="search card-key-pool">
|
||||
<el-card>
|
||||
<div v-if="goodsName" class="pool-header">
|
||||
<el-button @click="goBack">返回</el-button>
|
||||
<span class="pool-meta">
|
||||
<strong>商品:</strong>{{ goodsName }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<el-alert
|
||||
v-if="!skuId"
|
||||
type="warning"
|
||||
show-icon
|
||||
:closable="false"
|
||||
class="mb_10"
|
||||
>
|
||||
缺少 SKU 参数,请从商品列表「卡池管理」进入。
|
||||
</el-alert>
|
||||
|
||||
<el-form
|
||||
v-else
|
||||
ref="searchFormRef"
|
||||
:model="searchForm"
|
||||
inline
|
||||
label-width="70px"
|
||||
class="search-form"
|
||||
@keyup.enter="handleSearch"
|
||||
>
|
||||
<el-form-item label="卡号" prop="cardNo">
|
||||
<el-input
|
||||
v-model="searchForm.cardNo"
|
||||
placeholder="卡号模糊搜索"
|
||||
clearable
|
||||
style="width: 240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="导入时间" prop="importRange">
|
||||
<el-date-picker
|
||||
v-model="importRange"
|
||||
type="datetimerange"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
start-placeholder="开始时间"
|
||||
end-placeholder="结束时间"
|
||||
style="width: 360px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" class="search-btn" @click="handleSearch">搜索</el-button>
|
||||
<el-button @click="handleReset">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
|
||||
<el-card v-if="skuId">
|
||||
<div class="goods-tab">
|
||||
<el-tabs v-model="statusTab" @tab-click="onStatusTabClick">
|
||||
<el-tab-pane
|
||||
v-for="tab in statusTabsWithCount"
|
||||
:key="tab.value"
|
||||
:label="tab.title"
|
||||
:name="tab.value"
|
||||
/>
|
||||
</el-tabs>
|
||||
</div>
|
||||
|
||||
<el-alert
|
||||
v-if="poolLocked"
|
||||
type="warning"
|
||||
show-icon
|
||||
:closable="false"
|
||||
class="mb_10"
|
||||
title="当前商品不可管理卡池(审核拒绝、商品已删除或店铺已关店)"
|
||||
/>
|
||||
|
||||
<div class="operation" style="margin: 10px 0">
|
||||
<el-button type="primary" :disabled="poolLocked" @click="importModal = true">
|
||||
批量导入
|
||||
</el-button>
|
||||
<el-button :disabled="poolLocked" @click="openAddDialog">单条新增</el-button>
|
||||
<el-button :loading="exportLoading" :disabled="poolLocked" @click="handleExport">
|
||||
导出
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
<el-table v-loading="loading" :data="data" class="mt_10" style="width: 100%">
|
||||
<el-table-column label="序号" width="60" align="center">
|
||||
<template #default="{ $index }">
|
||||
{{ (searchForm.pageNumber - 1) * searchForm.pageSize + $index + 1 }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<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 label="状态" width="100" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag v-if="row" :type="statusTag(row.status)" size="small">
|
||||
{{ formatStatus(row.status) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="createTime" label="导入时间" width="170" />
|
||||
<el-table-column prop="allocatedTime" label="发卡时间" width="170" />
|
||||
<el-table-column prop="orderSn" label="订单号" min-width="160" show-overflow-tooltip />
|
||||
<el-table-column label="操作" width="100" align="center" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<el-button
|
||||
v-if="row && row.status === 'UNUSED'"
|
||||
link
|
||||
type="danger"
|
||||
:disabled="poolLocked"
|
||||
@click="handleVoid(row)"
|
||||
>
|
||||
作废
|
||||
</el-button>
|
||||
<span v-else>—</span>
|
||||
</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"
|
||||
:page-sizes="[10, 20, 50, 100]"
|
||||
:total="total"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
size="small"
|
||||
@current-change="getList"
|
||||
@size-change="onPageSizeChange"
|
||||
/>
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<!-- 批量导入 -->
|
||||
<el-dialog v-model="importModal" title="批量导入卡密" width="520px" :close-on-click-modal="false">
|
||||
<p class="import-tip">
|
||||
Excel 模板:第 1 行表头,第 2 行起为数据;列 A 卡号、列 B 卡密;单次最多 10,000 行。
|
||||
</p>
|
||||
<el-button
|
||||
type="primary"
|
||||
link
|
||||
class="mb_10"
|
||||
:loading="templateLoading"
|
||||
@click="handleDownloadTemplate"
|
||||
>
|
||||
下载导入模板
|
||||
</el-button>
|
||||
<el-upload drag :show-file-list="false" accept=".xlsx" :before-upload="handleImportUpload">
|
||||
<div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
|
||||
</el-upload>
|
||||
<template #footer>
|
||||
<el-button @click="importModal = false">关闭</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 导入结果 -->
|
||||
<el-dialog v-model="importResultVisible" title="导入结果" width="560px">
|
||||
<p>成功 {{ importResult.successCount || 0 }} 条,失败 {{ importResult.failCount || 0 }} 条</p>
|
||||
<el-table
|
||||
v-if="importResult.failRows && importResult.failRows.length"
|
||||
:data="importResult.failRows"
|
||||
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>
|
||||
<template #footer>
|
||||
<el-button type="primary" @click="importResultVisible = false">确定</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 单条新增 -->
|
||||
<el-dialog v-model="addVisible" title="单条新增卡密" width="480px" :close-on-click-modal="false">
|
||||
<el-form ref="addFormRef" :model="addForm" :rules="addRules" label-width="80px">
|
||||
<el-form-item label="卡号" prop="cardNo">
|
||||
<el-input v-model="addForm.cardNo" placeholder="请输入卡号" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item label="卡密" prop="cardSecret">
|
||||
<el-input v-model="addForm.cardSecret" placeholder="请输入卡密" clearable />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="addVisible = false">取消</el-button>
|
||||
<el-button type="primary" :loading="addLoading" @click="submitAdd">确定</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
importCardKey,
|
||||
addCardKey,
|
||||
getCardKeyList,
|
||||
voidCardKey,
|
||||
getCardKeyStats,
|
||||
exportCardKey,
|
||||
downloadImportTemplate,
|
||||
} from "@/api/cardKey";
|
||||
import {
|
||||
formatCardKeyStatus,
|
||||
CARD_KEY_STATUS_TAG,
|
||||
} from "@/constants/cardKey";
|
||||
|
||||
/**
|
||||
* 商家卡池管理页:导入/新增/作废/导出,列表展示明文卡密(仅商家授权上下文)。
|
||||
* 入口:商品列表「卡池管理」,路由 query 须带 skuId;审核拒绝/删 SKU/关店时 poolLocked。
|
||||
*
|
||||
* @author Mike
|
||||
* @date 2026-07-31
|
||||
*/
|
||||
export default {
|
||||
name: "cardKeyPool",
|
||||
data() {
|
||||
return {
|
||||
skuId: "",
|
||||
goodsId: "",
|
||||
goodsName: "",
|
||||
statusTab: "ALL",
|
||||
stats: null,
|
||||
poolLocked: false,
|
||||
loading: false,
|
||||
exportLoading: false,
|
||||
templateLoading: false,
|
||||
data: [],
|
||||
total: 0,
|
||||
importRange: [],
|
||||
searchForm: {
|
||||
cardNo: "",
|
||||
pageNumber: 1,
|
||||
pageSize: 20,
|
||||
},
|
||||
importModal: false,
|
||||
importResultVisible: false,
|
||||
importResult: {},
|
||||
addVisible: false,
|
||||
addLoading: false,
|
||||
addForm: {
|
||||
cardNo: "",
|
||||
cardSecret: "",
|
||||
},
|
||||
addRules: {
|
||||
cardNo: [{ required: true, message: "卡号不能为空", trigger: "blur" }],
|
||||
cardSecret: [{ required: true, message: "卡密不能为空", trigger: "blur" }],
|
||||
},
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
statusTabsWithCount() {
|
||||
const s = this.stats || {};
|
||||
const withCount = (label, count) =>
|
||||
count != null && this.stats ? `${label}(${count})` : label;
|
||||
const total =
|
||||
(s.unusedCount || 0) + (s.allocatedCount || 0) + (s.voidedCount || 0);
|
||||
return [
|
||||
{ title: withCount("全部", total), value: "ALL" },
|
||||
{ title: withCount("未使用", s.unusedCount || 0), value: "UNUSED" },
|
||||
{ title: withCount("已分配", s.allocatedCount || 0), value: "ALLOCATED" },
|
||||
{ title: withCount("已作废", s.voidedCount || 0), value: "VOIDED" },
|
||||
];
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
formatStatus: formatCardKeyStatus,
|
||||
statusTag(status) {
|
||||
return CARD_KEY_STATUS_TAG[status] || "info";
|
||||
},
|
||||
goBack() {
|
||||
this.$router.back();
|
||||
},
|
||||
initFromRoute() {
|
||||
const q = this.$route.query;
|
||||
this.skuId = q.skuId || "";
|
||||
this.goodsId = q.goodsId || "";
|
||||
this.goodsName = q.goodsName || "";
|
||||
},
|
||||
onStatusTabClick(tab) {
|
||||
this.statusTab = tab.paneName;
|
||||
this.searchForm.pageNumber = 1;
|
||||
this.getList();
|
||||
},
|
||||
buildListParams() {
|
||||
const params = {
|
||||
skuId: this.skuId,
|
||||
pageNumber: this.searchForm.pageNumber,
|
||||
pageSize: this.searchForm.pageSize,
|
||||
};
|
||||
if (this.goodsId) params.goodsId = this.goodsId;
|
||||
if (this.searchForm.cardNo) params.cardNo = this.searchForm.cardNo;
|
||||
if (this.statusTab && this.statusTab !== "ALL") {
|
||||
params.status = this.statusTab;
|
||||
}
|
||||
if (this.importRange && this.importRange.length === 2) {
|
||||
params.createTimeStart = this.importRange[0];
|
||||
params.createTimeEnd = this.importRange[1];
|
||||
}
|
||||
return params;
|
||||
},
|
||||
loadStats() {
|
||||
if (!this.skuId) return;
|
||||
getCardKeyStats(this.skuId).then((res) => {
|
||||
if (res.success) {
|
||||
this.stats = res.result;
|
||||
}
|
||||
});
|
||||
},
|
||||
getList() {
|
||||
if (!this.skuId) return;
|
||||
this.loading = true;
|
||||
getCardKeyList(this.buildListParams())
|
||||
.then((res) => {
|
||||
this.loading = false;
|
||||
if (res.success) {
|
||||
this.data = res.result.records || [];
|
||||
this.total = res.result.total || 0;
|
||||
this.poolLocked = false;
|
||||
} else {
|
||||
this.checkPoolLocked(res);
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
/** 审核拒绝 / SKU 删除 / 关店时禁用卡池操作(S-06 / S-07 / EC-21~23) */
|
||||
checkPoolLocked(res) {
|
||||
const lockedCodes = [
|
||||
"CARD_KEY_GOODS_AUTH_REFUSE",
|
||||
"CARD_KEY_SKU_DELETED",
|
||||
"CARD_KEY_STORE_CLOSED",
|
||||
];
|
||||
if (res.code && lockedCodes.includes(String(res.code))) {
|
||||
this.poolLocked = true;
|
||||
}
|
||||
},
|
||||
handleSearch() {
|
||||
this.searchForm.pageNumber = 1;
|
||||
this.getList();
|
||||
},
|
||||
handleReset() {
|
||||
this.searchForm.cardNo = "";
|
||||
this.importRange = [];
|
||||
this.searchForm.pageNumber = 1;
|
||||
this.getList();
|
||||
},
|
||||
onPageSizeChange() {
|
||||
this.searchForm.pageNumber = 1;
|
||||
this.getList();
|
||||
},
|
||||
handleImportUpload(file) {
|
||||
if (!/\.xlsx$/i.test(file.name)) {
|
||||
this.$Message.error("请上传 .xlsx 文件");
|
||||
return false;
|
||||
}
|
||||
importCardKey(this.skuId, file).then((res) => {
|
||||
if (res.success) {
|
||||
this.importModal = false;
|
||||
this.importResult = res.result || {};
|
||||
this.importResultVisible = true;
|
||||
this.loadStats();
|
||||
this.getList();
|
||||
} else {
|
||||
this.checkPoolLocked(res);
|
||||
}
|
||||
});
|
||||
return false;
|
||||
},
|
||||
handleDownloadTemplate() {
|
||||
this.templateLoading = true;
|
||||
downloadImportTemplate()
|
||||
.then(() => {
|
||||
this.$Message.success("模板下载成功");
|
||||
})
|
||||
.catch(() => {})
|
||||
.finally(() => {
|
||||
this.templateLoading = false;
|
||||
});
|
||||
},
|
||||
openAddDialog() {
|
||||
this.addForm = { cardNo: "", cardSecret: "" };
|
||||
this.addVisible = true;
|
||||
},
|
||||
submitAdd() {
|
||||
this.$refs.addFormRef.validate((valid) => {
|
||||
if (!valid) return;
|
||||
this.addLoading = true;
|
||||
addCardKey({
|
||||
skuId: this.skuId,
|
||||
cardNo: this.addForm.cardNo.trim(),
|
||||
cardSecret: this.addForm.cardSecret.trim(),
|
||||
})
|
||||
.then((res) => {
|
||||
this.addLoading = false;
|
||||
if (res.success) {
|
||||
this.$Message.success("新增成功");
|
||||
this.addVisible = false;
|
||||
this.loadStats();
|
||||
this.getList();
|
||||
} else {
|
||||
this.checkPoolLocked(res);
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
this.addLoading = false;
|
||||
});
|
||||
});
|
||||
},
|
||||
handleVoid(row) {
|
||||
this.$Modal.confirm({
|
||||
title: "确认作废",
|
||||
content: `确定作废卡号「${row.cardNo}」?作废后不可恢复。`,
|
||||
onOk: () => {
|
||||
voidCardKey(row.id).then((res) => {
|
||||
if (res.success) {
|
||||
this.$Message.success("作废成功");
|
||||
this.loadStats();
|
||||
this.getList();
|
||||
}
|
||||
});
|
||||
},
|
||||
});
|
||||
},
|
||||
handleExport() {
|
||||
this.exportLoading = true;
|
||||
const params = { ...this.buildListParams() };
|
||||
delete params.pageNumber;
|
||||
delete params.pageSize;
|
||||
exportCardKey(params, this.skuId)
|
||||
.then(() => {
|
||||
this.$Message.success("导出成功");
|
||||
})
|
||||
.catch(() => {})
|
||||
.finally(() => {
|
||||
this.exportLoading = false;
|
||||
});
|
||||
},
|
||||
init() {
|
||||
this.initFromRoute();
|
||||
if (this.skuId) {
|
||||
this.loadStats();
|
||||
this.getList();
|
||||
}
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.init();
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "@/styles/table-common.scss";
|
||||
|
||||
.pool-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
flex-wrap: wrap;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.pool-meta {
|
||||
color: #606266;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.goods-tab {
|
||||
:deep(.el-tabs__item) {
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
.import-tip {
|
||||
margin: 0 0 12px;
|
||||
font-size: 13px;
|
||||
color: #909399;
|
||||
line-height: 1.5;
|
||||
}
|
||||
</style>
|
||||
@@ -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: [],
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
@click="toPrint"
|
||||
>打印电子面单</el-button>
|
||||
<el-button
|
||||
v-if="$route.query.orderType != 'VIRTUAL'"
|
||||
v-if="!isNonPhysicalOrder"
|
||||
type="primary"
|
||||
plain
|
||||
style="float: right"
|
||||
@@ -122,7 +122,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div style="width: 36%; float: left">
|
||||
<div class="div-item" v-if="orderInfo.order.deliveryMethod != 'SELF_PICK_UP'">
|
||||
<div class="div-item" v-if="!isECouponOrder && orderInfo.order.deliveryMethod != 'SELF_PICK_UP'">
|
||||
<div class="div-item-left">收货信息:</div>
|
||||
<div class="div-item-right">
|
||||
{{ orderInfo.order.consigneeName }}
|
||||
@@ -204,7 +204,7 @@
|
||||
</div>
|
||||
</div> -->
|
||||
|
||||
<div class="div-item" v-if="$route.query.orderType != 'VIRTUAL'">
|
||||
<div class="div-item" v-if="!isNonPhysicalOrder">
|
||||
<div class="div-item-left">配送方式:</div>
|
||||
<div class="div-item-right">
|
||||
{{ orderInfo.deliveryMethodValue }}
|
||||
@@ -256,6 +256,42 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<!-- E_COUPON:卡密来自订单详情 orderItems[].cardKeys(S-09,无独立 API-S-06) -->
|
||||
<div v-if="isECouponOrder" class="ecoupon-card-keys mt_10">
|
||||
<h4>卡密信息</h4>
|
||||
<el-alert
|
||||
v-if="!ecouponCardKeyDelivered"
|
||||
type="info"
|
||||
show-icon
|
||||
:closable="false"
|
||||
title="卡密尚未发放"
|
||||
class="mb_10"
|
||||
/>
|
||||
<el-table
|
||||
v-else-if="ecouponCardKeyRows.length"
|
||||
border
|
||||
:data="ecouponCardKeyRows"
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-table-column type="index" label="序号" width="60" align="center" />
|
||||
<el-table-column 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" />
|
||||
<el-table-column label="状态" width="90" align="center">
|
||||
<template #default="{ row }">
|
||||
<span v-if="row">{{ formatCardKeyStatus(row.status || 'ALLOCATED') }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="100" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-button v-if="row" link type="primary" @click="copyCardKey(row)">复制</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
|
||||
<div class="goods-total">
|
||||
<ul>
|
||||
<li>
|
||||
@@ -319,7 +355,7 @@
|
||||
<span class="label" v-if="typeList.length == 1 && index == 0" style="font-size:10px !important;"><a @click="gotoHomes" style="display: inline-block;border-top: 1px dashed;border-bottom: 1px dashed;color:black;width:80px;">{{item.promotionName}}:</a><span class="op-split">|</span>
|
||||
<span class="txt" v-if="typeList.length == 1 && index == 0" style="border-top: 1px dashed;border-bottom: 1px dashed;font-size:10px !important;">¥{{ $filters.unitPrice(item.discountPrice) }}</span>
|
||||
</li> -->
|
||||
<li>
|
||||
<li v-if="!isECouponOrder">
|
||||
<span class="label">运费:</span>
|
||||
<span class="txt">{{
|
||||
$filters.unitPrice(orderInfo.order.freightPrice, "¥")
|
||||
@@ -694,6 +730,7 @@ import * as RegExp from "@/libs/RegExp.js";
|
||||
import multipleMap from "@/views/my-components/map/multiple-map";
|
||||
import vueQr from "vue-qr";
|
||||
import { printElement } from "@/utils/print";
|
||||
import { formatCardKeyStatus } from "@/constants/cardKey";
|
||||
export default {
|
||||
name: "orderDetail",
|
||||
components: {
|
||||
@@ -814,11 +851,38 @@ export default {
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
/** 电子卡券订单:隐藏物流/发货/售后,展示 orderItems[].cardKeys(FR-S-04 / FR-S-05) */
|
||||
isECouponOrder() {
|
||||
const t = this.orderInfo?.order?.orderType;
|
||||
return t === "E_COUPON" || this.$route.query.orderType === "E_COUPON";
|
||||
},
|
||||
isVirtualOrder() {
|
||||
const t = this.orderInfo?.order?.orderType;
|
||||
return t === "VIRTUAL" || this.$route.query.orderType === "VIRTUAL";
|
||||
},
|
||||
isNonPhysicalOrder() {
|
||||
return this.isVirtualOrder || this.isECouponOrder;
|
||||
},
|
||||
ecouponCardKeyDelivered() {
|
||||
return (this.data || []).some((item) => item.cardKeyDelivered);
|
||||
},
|
||||
ecouponCardKeyRows() {
|
||||
const rows = [];
|
||||
(this.data || []).forEach((item) => {
|
||||
(item.cardKeys || []).forEach((ck) => {
|
||||
rows.push({
|
||||
...ck,
|
||||
goodsName: item.goodsName,
|
||||
});
|
||||
});
|
||||
});
|
||||
return rows;
|
||||
},
|
||||
canPartDelivery() {
|
||||
if (!this.allowOperation.ship) return false;
|
||||
const status = this.orderInfo.order && this.orderInfo.order.orderStatus;
|
||||
return (
|
||||
this.$route.query.orderType !== "VIRTUAL" &&
|
||||
!this.isNonPhysicalOrder &&
|
||||
["UNDELIVERED", "PARTS_DELIVERED"].includes(status) &&
|
||||
this.deliverableOrderItems.length > 0
|
||||
);
|
||||
@@ -845,6 +909,23 @@ export default {
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
formatCardKeyStatus,
|
||||
copyCardKey(row) {
|
||||
const text = `卡号:${row.cardNo || ""}\n卡密:${row.cardSecret || ""}`;
|
||||
if (navigator.clipboard && navigator.clipboard.writeText) {
|
||||
navigator.clipboard.writeText(text).then(() => {
|
||||
this.$Message.success("已复制到剪贴板");
|
||||
});
|
||||
} else {
|
||||
const ta = document.createElement("textarea");
|
||||
ta.value = text;
|
||||
document.body.appendChild(ta);
|
||||
ta.select();
|
||||
document.execCommand("copy");
|
||||
document.body.removeChild(ta);
|
||||
this.$Message.success("已复制到剪贴板");
|
||||
}
|
||||
},
|
||||
getPromotionText(row) {
|
||||
let resultText = "";
|
||||
if (row && row.promotionType) {
|
||||
|
||||
@@ -196,7 +196,7 @@ export default {
|
||||
receiptModalMode: "detail",
|
||||
uploadFileUrl: uploadFile,
|
||||
accessToken: {},
|
||||
receiptUploadData: { directoryPath: "receipt" },
|
||||
receiptUploadData: { directoryPath: "default" }, // OSS 上传目录(与后端存储配置一致)
|
||||
currentReceipt: {},
|
||||
selectedReceiptRow: null,
|
||||
searchForm: {
|
||||
|
||||
Reference in New Issue
Block a user