mirror of
https://gitee.com/beijing_hongye_huicheng/lilishop-ui.git
synced 2026-05-06 07:34:40 +08:00
微信小店相关内容
This commit is contained in:
@@ -373,6 +373,31 @@ export const getWxChannelsThirdCategory = (params) => {
|
||||
return getRequest(`/wxchannels/category/third`, params);
|
||||
};
|
||||
|
||||
// 微信小店类目申请分页
|
||||
export const getWxChannelsCategoryPage = (params) => {
|
||||
return getRequest(`/wxchannels/category`, params);
|
||||
};
|
||||
|
||||
// 初始化微信小店类目
|
||||
export const initWxChannelsCategory = () => {
|
||||
return postRequestWithNoForm(`/wxchannels/category/init`, {});
|
||||
};
|
||||
|
||||
// 申请微信小店类目
|
||||
export const applyWxChannelsCategory = (params) => {
|
||||
return postRequestWithNoForm(`/wxchannels/category/apply`, params);
|
||||
};
|
||||
|
||||
// 微信小店类目审核单详情
|
||||
export const getWxChannelsCategoryFlowDetail = (params) => {
|
||||
return getRequest(`/wxchannels/category/flow/detail`, params);
|
||||
};
|
||||
|
||||
// 微信小店类目详情
|
||||
export const getWxChannelsCategoryDetail = (params) => {
|
||||
return getRequest(`/wxchannels/category/detail`, params);
|
||||
};
|
||||
|
||||
// 微信视频号商品分页
|
||||
export const getWxChannelsGoodsPage = (params) => {
|
||||
return getRequest(`/wxchannels/goods`, params);
|
||||
@@ -388,11 +413,71 @@ export const getWxChannelsOverviewSummary = (params) => {
|
||||
return getRequest(`/wxchannels/overview/summary`, params);
|
||||
};
|
||||
|
||||
// 微信视频号概况-日报
|
||||
export const getWxChannelsOverviewDaily = (params) => {
|
||||
return getRequest(`/wxchannels/overview/daily`, params);
|
||||
};
|
||||
|
||||
// 微信视频号概况-报表导出
|
||||
export const exportWxChannelsOverview = (params) => {
|
||||
return getRequest(`/wxchannels/overview/export`, params, "blob");
|
||||
};
|
||||
|
||||
// 微信视频号退单分页
|
||||
export const getWxChannelsRefundPage = (params) => {
|
||||
return getRequest(`/wxchannels/refund`, params);
|
||||
};
|
||||
|
||||
// 微信小店资金-账户余额
|
||||
export const getWxChannelsFundsBalance = () => {
|
||||
return getRequest(`/wxchannels/funds/balance`);
|
||||
};
|
||||
|
||||
// 微信小店资金-结算账户信息
|
||||
export const getWxChannelsFundsBankAcct = () => {
|
||||
return getRequest(`/wxchannels/funds/bankacct`);
|
||||
};
|
||||
|
||||
// 微信小店资金-流水列表
|
||||
export const getWxChannelsFundsFlows = (params) => {
|
||||
return getRequest(`/wxchannels/funds/flows`, params);
|
||||
};
|
||||
|
||||
// 微信小店资金-流水详情
|
||||
export const getWxChannelsFundsFlowDetail = (params) => {
|
||||
return getRequest(`/wxchannels/funds/flows/detail`, params);
|
||||
};
|
||||
|
||||
// 微信小店资金-订单流水列表
|
||||
export const getWxChannelsFundsOrderFlow = (params) => {
|
||||
return getRequest(`/wxchannels/funds/orderflow`, params);
|
||||
};
|
||||
|
||||
// 微信小店配置
|
||||
export const getWxChannelsSetting = () => {
|
||||
return getRequest(`/wxchannels/setting`);
|
||||
};
|
||||
|
||||
// 保存微信小店配置
|
||||
export const saveWxChannelsSetting = (params) => {
|
||||
return postRequestWithNoForm(`/wxchannels/setting`, params);
|
||||
};
|
||||
|
||||
// 微信小店品牌库列表
|
||||
export const getWxChannelsBrandLibrary = (params) => {
|
||||
return getRequest(`/wxchannels/brand/library`, params);
|
||||
};
|
||||
|
||||
// 微信小店品牌资质申请列表
|
||||
export const getWxChannelsBrandList = (params) => {
|
||||
return getRequest(`/wxchannels/brand/list`, params);
|
||||
};
|
||||
|
||||
// 微信小店生效中的品牌资质列表
|
||||
export const getWxChannelsValidBrandList = (params) => {
|
||||
return getRequest(`/wxchannels/brand/valid/list`, params);
|
||||
};
|
||||
|
||||
// 分页查询敏感词
|
||||
|
||||
export const getSensitiveWordsPage = (params) => {
|
||||
|
||||
@@ -48,7 +48,9 @@ router.beforeEach((to, from, next) => {
|
||||
});
|
||||
|
||||
router.afterEach((to) => {
|
||||
if (!(to.meta && to.meta.noTag)) {
|
||||
Util.openNewPage(router.app, to.name, to.params, to.query);
|
||||
}
|
||||
ViewUI.LoadingBar.finish();
|
||||
window.scrollTo(0, 0);
|
||||
});
|
||||
|
||||
@@ -181,6 +181,21 @@ export const otherRouter = {
|
||||
component: () =>
|
||||
import("@/views/promotions/points-goods/points-goods-edit.vue")
|
||||
},
|
||||
{
|
||||
path: "promotions/wxchannels",
|
||||
title: "微信小店",
|
||||
name: "promotions-wxchannels",
|
||||
component: () =>
|
||||
import("@/views/promotions/wxchannels/index.vue")
|
||||
},
|
||||
{
|
||||
path: "promotions/wxchannels/category-apply",
|
||||
title: "申请类目",
|
||||
name: "promotions-wxchannels-category-apply",
|
||||
meta: { noTag: true },
|
||||
component: () =>
|
||||
import("@/views/promotions/wxchannels/category-apply.vue")
|
||||
},
|
||||
{
|
||||
path: "promotions/manager-points-goods-category",
|
||||
title: "积分商品分类",
|
||||
|
||||
211
manager/src/views/promotions/wxchannels/brand.vue
Normal file
211
manager/src/views/promotions/wxchannels/brand.vue
Normal file
@@ -0,0 +1,211 @@
|
||||
<template>
|
||||
<div class="wxchannels-brand">
|
||||
<Form inline :label-width="90" class="search-form">
|
||||
<FormItem label="列表类型">
|
||||
<Select v-model="queryForm.type" style="width: 200px" @on-change="handleTypeChange">
|
||||
<Option value="library">品牌库列表</Option>
|
||||
<Option value="list">资质申请列表</Option>
|
||||
<Option value="valid">生效资质列表</Option>
|
||||
</Select>
|
||||
</FormItem>
|
||||
<FormItem label="审核状态" v-if="queryForm.type === 'list'">
|
||||
<Select v-model="queryForm.status" clearable style="width: 180px" placeholder="全部">
|
||||
<Option v-for="item in statusOptions" :key="item.value" :value="item.value">{{ item.label }}</Option>
|
||||
</Select>
|
||||
</FormItem>
|
||||
<FormItem label="每页数量">
|
||||
<InputNumber :min="1" :max="200" v-model="queryForm.pageSize" style="width: 140px" />
|
||||
</FormItem>
|
||||
<Button type="primary" icon="ios-search" :loading="loading" @click="handleSearch">查询</Button>
|
||||
</Form>
|
||||
|
||||
<Table border :loading="loading" :columns="columns" :data="tableData" class="mt_10"></Table>
|
||||
|
||||
<div class="pager-bar">
|
||||
<span class="pager-text">第 {{ queryForm.pageNumber }} 页,当前条数:{{ tableData.length }}</span>
|
||||
<Button :disabled="loading || queryForm.pageNumber <= 1" @click="handlePrevPage">上一页</Button>
|
||||
<Button type="primary" :disabled="loading || !nextKey" @click="handleNextPage">下一页</Button>
|
||||
</div>
|
||||
|
||||
<Modal v-model="rawModalVisible" title="原始数据" width="760" footer-hide>
|
||||
<pre class="raw-json">{{ rawJson }}</pre>
|
||||
</Modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
getWxChannelsBrandLibrary,
|
||||
getWxChannelsBrandList,
|
||||
getWxChannelsValidBrandList,
|
||||
} from "@/api/index";
|
||||
|
||||
export default {
|
||||
name: "wxchannels-brand",
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
tableData: [],
|
||||
nextKey: "",
|
||||
rawModalVisible: false,
|
||||
rawJson: "",
|
||||
queryForm: {
|
||||
type: "library",
|
||||
status: "",
|
||||
pageNumber: 1,
|
||||
pageSize: 20,
|
||||
},
|
||||
statusOptions: [
|
||||
{ value: 0, label: "待审核" },
|
||||
{ value: 1, label: "审核通过" },
|
||||
{ value: 2, label: "审核驳回" },
|
||||
{ value: 3, label: "已撤回" },
|
||||
],
|
||||
columns: [
|
||||
{ title: "品牌ID", key: "brandId", minWidth: 120, tooltip: true },
|
||||
{ title: "中文名称", key: "chName", minWidth: 220, tooltip: true },
|
||||
{ title: "英文名称", key: "enName", minWidth: 220, tooltip: true },
|
||||
{ title: "创建时间", key: "createTime", minWidth: 170, tooltip: true },
|
||||
{ title: "更新时间", key: "updateTime", minWidth: 170, tooltip: true },
|
||||
{
|
||||
title: "操作",
|
||||
key: "action",
|
||||
width: 100,
|
||||
align: "center",
|
||||
render: (h, params) => {
|
||||
return h(
|
||||
"Button",
|
||||
{
|
||||
props: {
|
||||
type: "text",
|
||||
size: "small",
|
||||
},
|
||||
on: {
|
||||
click: () => this.showRaw(params.row.__raw || {}),
|
||||
},
|
||||
},
|
||||
"查看"
|
||||
);
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.loadPage();
|
||||
},
|
||||
methods: {
|
||||
handleTypeChange() {
|
||||
this.queryForm.status = "";
|
||||
this.queryForm.pageNumber = 1;
|
||||
this.loadPage();
|
||||
},
|
||||
handleSearch() {
|
||||
this.queryForm.pageNumber = 1;
|
||||
this.loadPage();
|
||||
},
|
||||
handleNextPage() {
|
||||
if (!this.nextKey) return;
|
||||
this.queryForm.pageNumber += 1;
|
||||
this.loadPage();
|
||||
},
|
||||
handlePrevPage() {
|
||||
if (this.queryForm.pageNumber <= 1) return;
|
||||
this.queryForm.pageNumber -= 1;
|
||||
this.loadPage();
|
||||
},
|
||||
async loadPage() {
|
||||
this.loading = true;
|
||||
try {
|
||||
const params = {
|
||||
pageNumber: Number(this.queryForm.pageNumber) || 1,
|
||||
pageSize: Number(this.queryForm.pageSize) || 20,
|
||||
};
|
||||
if (this.queryForm.type === "list" && this.queryForm.status !== "" && this.queryForm.status !== null) {
|
||||
params.status = this.queryForm.status;
|
||||
}
|
||||
const res = await this.fetchByType(params);
|
||||
if (res && res.success) {
|
||||
const result = res.result || {};
|
||||
const sourceList = this.extractList(result);
|
||||
this.tableData = sourceList.map((item) => this.normalizeRow(item));
|
||||
this.nextKey = result.next_key || result.nextKey || "";
|
||||
} else {
|
||||
this.tableData = [];
|
||||
this.nextKey = "";
|
||||
}
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
fetchByType(params) {
|
||||
if (this.queryForm.type === "list") {
|
||||
return getWxChannelsBrandList(params);
|
||||
}
|
||||
if (this.queryForm.type === "valid") {
|
||||
return getWxChannelsValidBrandList(params);
|
||||
}
|
||||
return getWxChannelsBrandLibrary(params);
|
||||
},
|
||||
extractList(result) {
|
||||
if (!result || typeof result !== "object") return [];
|
||||
const keys = ["brands", "brand_list", "list", "records", "items", "data"];
|
||||
for (let i = 0; i < keys.length; i += 1) {
|
||||
const value = result[keys[i]];
|
||||
if (Array.isArray(value)) return value;
|
||||
}
|
||||
if (Array.isArray(result)) return result;
|
||||
return [];
|
||||
},
|
||||
normalizeRow(item) {
|
||||
const raw = item || {};
|
||||
const brandId = raw.brand_id || raw.brandId || raw.id || "-";
|
||||
const chName = raw.ch_name || raw.chName || raw.brand_wording || raw.brandName || raw.name || "-";
|
||||
const enName = raw.en_name || raw.enName || "-";
|
||||
const createTime = raw.create_time || raw.createTime || "-";
|
||||
const updateTime = raw.update_time || raw.updateTime || "-";
|
||||
return {
|
||||
brandId,
|
||||
chName,
|
||||
enName,
|
||||
createTime,
|
||||
updateTime,
|
||||
__raw: raw,
|
||||
};
|
||||
},
|
||||
showRaw(raw) {
|
||||
this.rawJson = JSON.stringify(raw, null, 2);
|
||||
this.rawModalVisible = true;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.wxchannels-brand {
|
||||
min-height: 360px;
|
||||
|
||||
.pager-bar {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
margin-top: 12px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.pager-text {
|
||||
color: #666;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.raw-json {
|
||||
max-height: 420px;
|
||||
overflow: auto;
|
||||
margin: 0;
|
||||
background: #f7f7f7;
|
||||
border: 1px solid #eee;
|
||||
padding: 10px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
210
manager/src/views/promotions/wxchannels/category-apply.vue
Normal file
210
manager/src/views/promotions/wxchannels/category-apply.vue
Normal file
@@ -0,0 +1,210 @@
|
||||
<template>
|
||||
<div class="wxchannels-category-apply">
|
||||
<Card>
|
||||
<div class="toolbar">
|
||||
<Button @click="$router.back()">返回</Button>
|
||||
</div>
|
||||
|
||||
<div v-if="loading" class="loading-wrap">
|
||||
<Spin size="large"></Spin>
|
||||
</div>
|
||||
|
||||
<div v-else>
|
||||
<Row :gutter="12">
|
||||
<Col :span="12">
|
||||
<Card dis-hover>
|
||||
<p class="detail-title">类目ID</p>
|
||||
<p class="detail-value">{{ detailData.catId || "-" }}</p>
|
||||
</Card>
|
||||
</Col>
|
||||
<Col :span="12">
|
||||
<Card dis-hover>
|
||||
<p class="detail-title">类目名称</p>
|
||||
<p class="detail-value">{{ detailData.name || "-" }}</p>
|
||||
</Card>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<Card dis-hover class="mt_10">
|
||||
<p class="detail-title">属性信息(attr)</p>
|
||||
<div class="attr-grid">
|
||||
<span>支持虚拟发货:{{ boolText(detailData.attr && detailData.attr.shopNoShipment) }}</span>
|
||||
<span>定向准入:{{ boolText(detailData.attr && detailData.attr.accessPermitRequired) }}</span>
|
||||
<span>支持预售:{{ boolText(detailData.attr && detailData.attr.preSale) }}</span>
|
||||
<span>7天无理由退货:{{ boolText(detailData.attr && detailData.attr.sevenDayReturn) }}</span>
|
||||
<span>品牌定向准入:{{ boolText(detailData.attr && detailData.attr.isLimitBrand) }}</span>
|
||||
<span>保证金(分):{{ (detailData.attr && detailData.attr.deposit) || "-" }}</span>
|
||||
<span>价格下限(分):{{ (detailData.attr && detailData.attr.floorPrice) || "-" }}</span>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
<Form class="mt_10" :label-width="120">
|
||||
<FormItem label="品牌ID列表">
|
||||
<Input v-model="applyForm.brandIdsText" placeholder="可选,逗号分隔,如:1001,1002" />
|
||||
</FormItem>
|
||||
<FormItem label="证照组JSON">
|
||||
<Input
|
||||
type="textarea"
|
||||
:rows="14"
|
||||
v-model="applyForm.licenseGroupJson"
|
||||
placeholder='请输入 licenseGroupList 的 JSON 数组'
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem>
|
||||
<Button type="primary" :loading="submitting" @click="submitApply">提交申请</Button>
|
||||
</FormItem>
|
||||
</Form>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getWxChannelsCategoryDetail, applyWxChannelsCategory } from "@/api/index";
|
||||
|
||||
export default {
|
||||
name: "wxchannels-category-apply",
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
submitting: false,
|
||||
catId: "",
|
||||
detailData: {},
|
||||
fullCategoryPath: [],
|
||||
applyForm: {
|
||||
brandIdsText: "",
|
||||
licenseGroupJson:
|
||||
'[\n {\n "licenseGroupId": 1,\n "license": {\n "licenseId": 1,\n "fileIdList": ["file_id_1"],\n "licenseFieldList": [\n { "key": "field_key", "value": "field_value" }\n ]\n }\n }\n]',
|
||||
},
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.catId = this.$route.query.catId;
|
||||
this.parseFullCategoryPath();
|
||||
if (!this.catId) {
|
||||
this.$Message.warning("缺少类目ID");
|
||||
this.$router.back();
|
||||
return;
|
||||
}
|
||||
this.loadDetail();
|
||||
},
|
||||
methods: {
|
||||
parseFullCategoryPath() {
|
||||
const raw = this.$route.query.fullCategoryPath;
|
||||
if (!raw) {
|
||||
this.fullCategoryPath = [];
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const parsed = JSON.parse(raw);
|
||||
this.fullCategoryPath = Array.isArray(parsed)
|
||||
? parsed.map((item) => ({
|
||||
catId: item && (item.catId || item.id),
|
||||
catName: (item && (item.catName || item.name)) || "",
|
||||
}))
|
||||
: [];
|
||||
} catch (e) {
|
||||
this.fullCategoryPath = [];
|
||||
}
|
||||
},
|
||||
boolText(val) {
|
||||
if (val === true) return "是";
|
||||
if (val === false) return "否";
|
||||
return "-";
|
||||
},
|
||||
async loadDetail() {
|
||||
this.loading = true;
|
||||
try {
|
||||
const res = await getWxChannelsCategoryDetail({ catId: this.catId });
|
||||
if (res && res.success) {
|
||||
this.detailData = res.result || {};
|
||||
}
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
async submitApply() {
|
||||
let licenseGroupList = [];
|
||||
try {
|
||||
licenseGroupList = JSON.parse(this.applyForm.licenseGroupJson || "[]");
|
||||
} catch (e) {
|
||||
this.$Message.error("证照组JSON格式错误");
|
||||
return;
|
||||
}
|
||||
if (!Array.isArray(licenseGroupList) || licenseGroupList.length === 0) {
|
||||
this.$Message.warning("licenseGroupList不能为空");
|
||||
return;
|
||||
}
|
||||
const brandIds = (this.applyForm.brandIdsText || "")
|
||||
.split(",")
|
||||
.map((v) => Number(String(v).trim()))
|
||||
.filter((v) => Number.isFinite(v) && v > 0);
|
||||
|
||||
const categoryPath = Array.isArray(this.fullCategoryPath) ? this.fullCategoryPath : [];
|
||||
const catsV2 = categoryPath
|
||||
.filter((item) => item && item.catId)
|
||||
.map((item) => Number(item.catId))
|
||||
.filter((id) => Number.isFinite(id) && id > 0);
|
||||
if (catsV2.length === 0 && this.catId) {
|
||||
const fallbackId = Number(this.catId);
|
||||
if (Number.isFinite(fallbackId) && fallbackId > 0) {
|
||||
catsV2.push(fallbackId);
|
||||
}
|
||||
}
|
||||
if (catsV2.length === 0) {
|
||||
this.$Message.warning("缺少完整类目链");
|
||||
return;
|
||||
}
|
||||
|
||||
const payload = {
|
||||
// 按一级->二级->三级顺序传递完整分类链
|
||||
catsV2,
|
||||
licenseGroupList,
|
||||
};
|
||||
if (brandIds.length > 0) {
|
||||
payload.brandIds = brandIds;
|
||||
}
|
||||
this.submitting = true;
|
||||
try {
|
||||
const res = await applyWxChannelsCategory(payload);
|
||||
if (res && res.success) {
|
||||
this.$Message.success(`申请成功,审核单ID:${res.result || "-"}`);
|
||||
this.$router.back();
|
||||
}
|
||||
} finally {
|
||||
this.submitting = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.wxchannels-category-apply {
|
||||
.toolbar {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.loading-wrap {
|
||||
text-align: center;
|
||||
padding: 30px 0;
|
||||
}
|
||||
|
||||
.detail-title {
|
||||
color: #666;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.detail-value {
|
||||
font-size: 16px;
|
||||
color: #17233d;
|
||||
}
|
||||
|
||||
.attr-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 8px 16px;
|
||||
color: #515a6e;
|
||||
}
|
||||
</style>
|
||||
490
manager/src/views/promotions/wxchannels/category.vue
Normal file
490
manager/src/views/promotions/wxchannels/category.vue
Normal file
@@ -0,0 +1,490 @@
|
||||
<template>
|
||||
<div class="wxchannels-category">
|
||||
<Tabs v-model="innerTab">
|
||||
<TabPane label="三级类目" name="third">
|
||||
<div class="toolbar">
|
||||
<Button type="warning" :loading="initLoading" @click="handleInitCategory">初始化分类</Button>
|
||||
<Button type="primary" :loading="thirdLoading" style="margin-left: 10px" @click="loadThirdCategories">
|
||||
刷新
|
||||
</Button>
|
||||
</div>
|
||||
<Table
|
||||
border
|
||||
class="category-tree-table"
|
||||
update-show-children
|
||||
:loading="thirdLoading"
|
||||
:columns="thirdColumns"
|
||||
:data="thirdData"
|
||||
:load-data="loadCategoryChildren"
|
||||
row-key="catId"
|
||||
></Table>
|
||||
</TabPane>
|
||||
|
||||
<TabPane label="类目申请" name="apply">
|
||||
<Form inline :label-width="70" class="search-form">
|
||||
<FormItem label="状态">
|
||||
<Select v-model="applyQuery.status" clearable placeholder="全部" style="width: 180px">
|
||||
<Option value="PENDING">审核中</Option>
|
||||
<Option value="APPROVED">已通过</Option>
|
||||
<Option value="REJECTED">已拒绝</Option>
|
||||
</Select>
|
||||
</FormItem>
|
||||
<Button type="primary" icon="ios-search" :loading="applyLoading" @click="handleApplySearch">查询</Button>
|
||||
</Form>
|
||||
|
||||
<Table border class="mt_10" :loading="applyLoading" :columns="applyColumns" :data="applyData"></Table>
|
||||
<Row type="flex" justify="end" class="mt_10" style="margin-top: 10px">
|
||||
<Page
|
||||
:current="applyQuery.pageNumber"
|
||||
:total="applyTotal"
|
||||
:page-size="applyQuery.pageSize"
|
||||
@on-change="changeApplyPage"
|
||||
@on-page-size-change="changeApplyPageSize"
|
||||
:page-size-opts="[20, 50, 100]"
|
||||
size="small"
|
||||
show-total
|
||||
show-elevator
|
||||
show-sizer
|
||||
></Page>
|
||||
</Row>
|
||||
</TabPane>
|
||||
</Tabs>
|
||||
|
||||
<Modal v-model="detailModalVisible" :title="detailModalTitle" width="900" footer-hide>
|
||||
<div v-if="detailData">
|
||||
<Row :gutter="12">
|
||||
<Col :span="12">
|
||||
<Card dis-hover>
|
||||
<p class="detail-title">类目ID</p>
|
||||
<p class="detail-value">{{ detailData.catId || "-" }}</p>
|
||||
</Card>
|
||||
</Col>
|
||||
<Col :span="12">
|
||||
<Card dis-hover>
|
||||
<p class="detail-title">类目名称</p>
|
||||
<p class="detail-value">{{ detailData.name || "-" }}</p>
|
||||
</Card>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<Card dis-hover class="mt_10">
|
||||
<p class="detail-title">属性信息(attr)</p>
|
||||
<div class="attr-grid">
|
||||
<span>支持虚拟发货:{{ boolText(detailData.attr && detailData.attr.shopNoShipment) }}</span>
|
||||
<span>定向准入:{{ boolText(detailData.attr && detailData.attr.accessPermitRequired) }}</span>
|
||||
<span>支持预售:{{ boolText(detailData.attr && detailData.attr.preSale) }}</span>
|
||||
<span>7天无理由退货:{{ boolText(detailData.attr && detailData.attr.sevenDayReturn) }}</span>
|
||||
<span>品牌定向准入:{{ boolText(detailData.attr && detailData.attr.isLimitBrand) }}</span>
|
||||
<span>保证金(分):{{ (detailData.attr && detailData.attr.deposit) || "-" }}</span>
|
||||
<span>价格下限(分):{{ (detailData.attr && detailData.attr.floorPrice) || "-" }}</span>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
<Card dis-hover class="mt_10">
|
||||
<p class="detail-title">资质信息(productQuaList)</p>
|
||||
<Table
|
||||
border
|
||||
size="small"
|
||||
:columns="productQuaColumns"
|
||||
:data="Array.isArray(detailData.productQuaList) ? detailData.productQuaList : []"
|
||||
></Table>
|
||||
</Card>
|
||||
|
||||
<Card dis-hover class="mt_10">
|
||||
<p class="detail-title">原始JSON</p>
|
||||
<pre class="raw-json">{{ detailRawJson }}</pre>
|
||||
</Card>
|
||||
</div>
|
||||
</Modal>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
getWxChannelsThirdCategory,
|
||||
getWxChannelsCategoryPage,
|
||||
getWxChannelsCategoryFlowDetail,
|
||||
getWxChannelsCategoryDetail,
|
||||
initWxChannelsCategory,
|
||||
} from "@/api/index";
|
||||
|
||||
export default {
|
||||
name: "wxchannels-category",
|
||||
data() {
|
||||
return {
|
||||
innerTab: "third",
|
||||
thirdLoading: false,
|
||||
initLoading: false,
|
||||
applyLoading: false,
|
||||
thirdData: [],
|
||||
applyData: [],
|
||||
applyTotal: 0,
|
||||
// 保存用户当前选择的完整类目链(一级 -> 二级 -> 三级)
|
||||
fullCategoryPath: [],
|
||||
detailModalVisible: false,
|
||||
detailModalTitle: "",
|
||||
detailData: null,
|
||||
detailRawJson: "",
|
||||
applyQuery: {
|
||||
status: "",
|
||||
pageNumber: 1,
|
||||
pageSize: 20,
|
||||
},
|
||||
thirdColumns: [
|
||||
{
|
||||
title: "",
|
||||
key: "_treeExpand",
|
||||
width: 70,
|
||||
tree: true,
|
||||
render: (h) => h("span", { class: "tree-expand-placeholder" }, ""),
|
||||
},
|
||||
{ title: "类目名称", key: "catName", minWidth: 180, tooltip: true },
|
||||
{ title: "类目ID", key: "catId", width: 140 },
|
||||
{ title: "层级", key: "level", width: 90 },
|
||||
{
|
||||
title: "状态",
|
||||
key: "status",
|
||||
width: 120,
|
||||
render: (h, params) => {
|
||||
const row = params.row || {};
|
||||
if (Number(row.level) !== 3) {
|
||||
return h("span", "-");
|
||||
}
|
||||
const val = row.status;
|
||||
const map = {
|
||||
PENDING: { label: "审核中", color: "orange" },
|
||||
APPROVED: { label: "已通过", color: "green" },
|
||||
REJECTED: { label: "已拒绝", color: "red" },
|
||||
UNSUBMITTED: { label: "未申请", color: "default" },
|
||||
};
|
||||
const item = map[val] || { label: val || "-", color: "default" };
|
||||
return h("Tag", { props: { color: item.color } }, item.label);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "操作",
|
||||
key: "action",
|
||||
width: 90,
|
||||
align: "center",
|
||||
render: (h, params) => {
|
||||
const row = params.row || {};
|
||||
if (Number(row.level) !== 3) {
|
||||
return h("span", "-");
|
||||
}
|
||||
return h(
|
||||
"Button",
|
||||
{
|
||||
props: { type: "text", size: "small" },
|
||||
on: { click: () => this.goApplyPage(row) },
|
||||
},
|
||||
"申请"
|
||||
);
|
||||
},
|
||||
},
|
||||
],
|
||||
applyColumns: [
|
||||
{ title: "审核单ID", key: "auditId", minWidth: 120, tooltip: true },
|
||||
{ title: "类目ID", key: "catId", minWidth: 120, tooltip: true },
|
||||
{ title: "类目名称", key: "catName", minWidth: 180, tooltip: true },
|
||||
{
|
||||
title: "状态",
|
||||
key: "status",
|
||||
minWidth: 110,
|
||||
render: (h, params) => {
|
||||
const val = params.row.status;
|
||||
const map = {
|
||||
PENDING: { label: "审核中", color: "orange" },
|
||||
APPROVED: { label: "已通过", color: "green" },
|
||||
REJECTED: { label: "已拒绝", color: "red" },
|
||||
};
|
||||
const target = map[val] || { label: val || "-", color: "default" };
|
||||
return h("Tag", { props: { color: target.color } }, target.label);
|
||||
},
|
||||
},
|
||||
{ title: "创建时间", key: "createTime", minWidth: 170, tooltip: true },
|
||||
{
|
||||
title: "操作",
|
||||
key: "action",
|
||||
width: 180,
|
||||
render: (h, params) => {
|
||||
const row = params.row || {};
|
||||
const actions = [];
|
||||
actions.push(
|
||||
h(
|
||||
"Button",
|
||||
{
|
||||
props: { type: "text", size: "small" },
|
||||
on: { click: () => this.showCategoryDetail(row.catId) },
|
||||
},
|
||||
"类目详情"
|
||||
)
|
||||
);
|
||||
actions.push(
|
||||
h(
|
||||
"Button",
|
||||
{
|
||||
props: { type: "text", size: "small" },
|
||||
on: { click: () => this.showFlowDetail(row.auditId) },
|
||||
},
|
||||
"审核单详情"
|
||||
)
|
||||
);
|
||||
return h("div", actions);
|
||||
},
|
||||
},
|
||||
],
|
||||
productQuaColumns: [
|
||||
{ title: "资质ID", key: "quaId", minWidth: 100, tooltip: true },
|
||||
{ title: "资质名称", key: "name", minWidth: 180, tooltip: true },
|
||||
{
|
||||
title: "是否需申请",
|
||||
key: "needToApply",
|
||||
width: 100,
|
||||
render: (h, params) => h("span", params.row.needToApply ? "是" : "否"),
|
||||
},
|
||||
{
|
||||
title: "是否必填",
|
||||
key: "mandatory",
|
||||
width: 90,
|
||||
render: (h, params) => h("span", params.row.mandatory ? "是" : "否"),
|
||||
},
|
||||
{ title: "描述", key: "tips", minWidth: 240, tooltip: true },
|
||||
],
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
innerTab(val) {
|
||||
if (val === "apply" && this.applyData.length === 0) {
|
||||
this.loadApplyPage();
|
||||
}
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.loadThirdCategories();
|
||||
},
|
||||
methods: {
|
||||
boolText(val) {
|
||||
if (val === true) return "是";
|
||||
if (val === false) return "否";
|
||||
return "-";
|
||||
},
|
||||
async handleInitCategory() {
|
||||
this.initLoading = true;
|
||||
try {
|
||||
const res = await initWxChannelsCategory();
|
||||
if (res && res.success) {
|
||||
this.$Message.success("初始化分类成功");
|
||||
await this.loadThirdCategories();
|
||||
}
|
||||
} finally {
|
||||
this.initLoading = false;
|
||||
}
|
||||
},
|
||||
goApplyPage(row) {
|
||||
const catId = row && row.catId;
|
||||
if (!catId) {
|
||||
this.$Message.warning("缺少类目ID");
|
||||
return;
|
||||
}
|
||||
const path = this.findCategoryPathById(this.thirdData, catId) || [];
|
||||
this.fullCategoryPath = path.map((item) => ({
|
||||
catId: item.catId,
|
||||
catName: item.catName,
|
||||
}));
|
||||
// 兜底:如果树中未命中,至少带上当前三级类目
|
||||
if (this.fullCategoryPath.length === 0) {
|
||||
this.fullCategoryPath = [
|
||||
{
|
||||
catId,
|
||||
catName: (row && row.catName) || "",
|
||||
},
|
||||
];
|
||||
}
|
||||
this.$router.push({
|
||||
name: "promotions-wxchannels-category-apply",
|
||||
query: {
|
||||
catId,
|
||||
fullCategoryPath: JSON.stringify(this.fullCategoryPath),
|
||||
},
|
||||
});
|
||||
},
|
||||
findCategoryPathById(list, targetCatId, parentPath = []) {
|
||||
if (!Array.isArray(list) || list.length === 0) return [];
|
||||
for (let i = 0; i < list.length; i += 1) {
|
||||
const item = list[i] || {};
|
||||
const current = {
|
||||
catId: item.catId || item.cat_id,
|
||||
catName: item.catName || item.cat_name || "",
|
||||
level: item.level,
|
||||
};
|
||||
const nextPath = parentPath.concat(current);
|
||||
if (String(current.catId) === String(targetCatId)) {
|
||||
return nextPath;
|
||||
}
|
||||
if (Array.isArray(item.children) && item.children.length > 0) {
|
||||
const found = this.findCategoryPathById(item.children, targetCatId, nextPath);
|
||||
if (found.length > 0) {
|
||||
return found;
|
||||
}
|
||||
}
|
||||
}
|
||||
return [];
|
||||
},
|
||||
async loadThirdCategories() {
|
||||
this.thirdLoading = true;
|
||||
try {
|
||||
// 首次仅请求顶级类目,不传 parentCatId
|
||||
const res = await getWxChannelsThirdCategory({});
|
||||
const list = res && res.success && Array.isArray(res.result) ? res.result : [];
|
||||
this.thirdData = list.map((item) => this.normalizeCategoryNode(item));
|
||||
} finally {
|
||||
this.thirdLoading = false;
|
||||
}
|
||||
},
|
||||
normalizeCategoryNode(item) {
|
||||
const level = Number((item && item.level) || 0);
|
||||
const hasChildren = !!(item && (item.hasChildren ?? item.has_children));
|
||||
const node = {
|
||||
...(item || {}),
|
||||
catId: (item && (item.catId || item.cat_id)) || "",
|
||||
catName: (item && (item.catName || item.cat_name)) || "-",
|
||||
level,
|
||||
hasChildren,
|
||||
status: (item && item.status) || "",
|
||||
qualification: (item && item.qualification) || "-",
|
||||
qualificationType: item ? (item.qualificationType ?? item.qualification_type ?? 0) : 0,
|
||||
productQualification: (item && (item.productQualification || item.product_qualification)) || "-",
|
||||
productQualificationType: item ? (item.productQualificationType ?? item.product_qualification_type ?? 0) : 0,
|
||||
};
|
||||
// 仅在 hasChildren=true 时展示展开标识
|
||||
if (hasChildren) {
|
||||
node._loading = false;
|
||||
node.children = [];
|
||||
}
|
||||
return node;
|
||||
},
|
||||
loadCategoryChildren(row, callback) {
|
||||
if (!row || !row.catId || !row.hasChildren) {
|
||||
callback([]);
|
||||
return;
|
||||
}
|
||||
getWxChannelsThirdCategory({ parentCatId: row.catId })
|
||||
.then((res) => {
|
||||
if (res && res.success) {
|
||||
const list = Array.isArray(res.result) ? res.result : [];
|
||||
callback(list.map((item) => this.normalizeCategoryNode(item)));
|
||||
return;
|
||||
}
|
||||
callback([]);
|
||||
})
|
||||
.catch(() => callback([]));
|
||||
},
|
||||
handleApplySearch() {
|
||||
this.applyQuery.pageNumber = 1;
|
||||
this.loadApplyPage();
|
||||
},
|
||||
changeApplyPage(pageNumber) {
|
||||
this.applyQuery.pageNumber = pageNumber;
|
||||
this.loadApplyPage();
|
||||
},
|
||||
changeApplyPageSize(pageSize) {
|
||||
this.applyQuery.pageSize = pageSize;
|
||||
this.applyQuery.pageNumber = 1;
|
||||
this.loadApplyPage();
|
||||
},
|
||||
loadApplyPage() {
|
||||
this.applyLoading = true;
|
||||
const params = { ...this.applyQuery };
|
||||
if (!params.status) {
|
||||
delete params.status;
|
||||
}
|
||||
getWxChannelsCategoryPage(params)
|
||||
.then((res) => {
|
||||
if (res && res.success) {
|
||||
const page = res.result || {};
|
||||
this.applyData = Array.isArray(page.records) ? page.records : [];
|
||||
this.applyTotal = Number(page.total || 0);
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
this.applyLoading = false;
|
||||
});
|
||||
},
|
||||
async showCategoryDetail(catId) {
|
||||
if (!catId) {
|
||||
this.$Message.warning("缺少类目ID");
|
||||
return;
|
||||
}
|
||||
const res = await getWxChannelsCategoryDetail({ catId });
|
||||
if (res && res.success) {
|
||||
this.detailModalTitle = "类目详情";
|
||||
this.detailData = res.result || {};
|
||||
this.detailRawJson = JSON.stringify(this.detailData, null, 2);
|
||||
this.detailModalVisible = true;
|
||||
}
|
||||
},
|
||||
async showFlowDetail(auditId) {
|
||||
if (!auditId) {
|
||||
this.$Message.warning("缺少审核单ID");
|
||||
return;
|
||||
}
|
||||
const res = await getWxChannelsCategoryFlowDetail({ auditId });
|
||||
if (res && res.success) {
|
||||
this.detailModalTitle = "审核单详情";
|
||||
this.detailRawJson = JSON.stringify(res.result || {}, null, 2);
|
||||
this.detailModalVisible = true;
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.wxchannels-category {
|
||||
min-height: 360px;
|
||||
}
|
||||
|
||||
.toolbar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
::v-deep .category-tree-table .tree-expand-placeholder {
|
||||
display: inline-block;
|
||||
width: 1px;
|
||||
}
|
||||
|
||||
::v-deep .category-tree-table .ivu-table-row td:first-child .ivu-table-cell {
|
||||
padding-left: 10px;
|
||||
padding-right: 8px;
|
||||
}
|
||||
|
||||
.raw-json {
|
||||
max-height: 420px;
|
||||
overflow: auto;
|
||||
margin: 0;
|
||||
background: #f7f7f7;
|
||||
border: 1px solid #eee;
|
||||
padding: 10px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.detail-title {
|
||||
color: #666;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.detail-value {
|
||||
font-size: 16px;
|
||||
color: #17233d;
|
||||
}
|
||||
|
||||
.attr-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 8px 16px;
|
||||
color: #515a6e;
|
||||
}
|
||||
</style>
|
||||
567
manager/src/views/promotions/wxchannels/funds.vue
Normal file
567
manager/src/views/promotions/wxchannels/funds.vue
Normal file
@@ -0,0 +1,567 @@
|
||||
<template>
|
||||
<div class="wxchannels-funds">
|
||||
<Row :gutter="12">
|
||||
<Col :span="12">
|
||||
<Card dis-hover>
|
||||
<div slot="title">账户余额</div>
|
||||
<Button size="small" :loading="balanceLoading" @click="loadBalance">刷新</Button>
|
||||
<div class="mt_10 balance-grid">
|
||||
<div>可提现余额(分):{{ balanceInfo.availableAmount }}</div>
|
||||
<div>待结算余额(分):{{ balanceInfo.pendingAmount }}</div>
|
||||
<div>二级商户号:{{ balanceInfo.subMchid }}</div>
|
||||
<div>错误码:{{ balanceInfo.errcode }}</div>
|
||||
</div>
|
||||
<pre class="raw-json">{{ balanceJson }}</pre>
|
||||
</Card>
|
||||
</Col>
|
||||
<Col :span="12">
|
||||
<Card dis-hover>
|
||||
<div slot="title">结算账户</div>
|
||||
<Button size="small" :loading="bankLoading" @click="loadBankAcct">刷新</Button>
|
||||
<div class="mt_10 balance-grid">
|
||||
<div>错误码:{{ bankInfo.errcode }}</div>
|
||||
<div>账户类型:{{ bankInfo.bankAccountType }}</div>
|
||||
<div>开户银行:{{ bankInfo.accountBank }}</div>
|
||||
<div>开户银行省市编码:{{ bankInfo.bankAddressCode }}</div>
|
||||
<div>开户银行联行号:{{ bankInfo.bankBranchId }}</div>
|
||||
<div>开户银行全称:{{ bankInfo.bankName }}</div>
|
||||
<div>银行账号:{{ bankInfo.accountNumber }}</div>
|
||||
<div>账户名称:{{ bankInfo.accountName }}</div>
|
||||
</div>
|
||||
<pre class="raw-json">{{ bankJson }}</pre>
|
||||
</Card>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<Tabs v-model="innerTab" class="mt_10">
|
||||
<TabPane label="资金流水" name="flows">
|
||||
<Form inline :label-width="80" class="search-form">
|
||||
<FormItem label="支付单号">
|
||||
<Input v-model="flowQuery.transactionId" clearable placeholder="transactionId" style="width: 220px" />
|
||||
</FormItem>
|
||||
<FormItem label="每页数量">
|
||||
<InputNumber :min="1" :max="100" v-model="flowQuery.pageSize" style="width: 120px" />
|
||||
</FormItem>
|
||||
<FormItem label="时间范围">
|
||||
<DatePicker
|
||||
type="daterange"
|
||||
v-model="flowDateRange"
|
||||
format="yyyy-MM-dd"
|
||||
clearable
|
||||
@on-change="handleFlowDateChange"
|
||||
style="width: 240px"
|
||||
></DatePicker>
|
||||
</FormItem>
|
||||
<Button type="primary" icon="ios-search" :loading="flowLoading" @click="handleFlowSearch">查询</Button>
|
||||
</Form>
|
||||
|
||||
<Table border :loading="flowLoading" :columns="flowColumns" :data="flowData" class="mt_10"></Table>
|
||||
<div class="pager-bar">
|
||||
<span class="pager-text">当前条数:{{ flowData.length }}</span>
|
||||
<Button :disabled="flowLoading || !hasFlowPrev" @click="handleFlowPrev">上一页</Button>
|
||||
<Button type="primary" :disabled="flowLoading || !flowNextKey" @click="handleFlowNext">下一页</Button>
|
||||
</div>
|
||||
</TabPane>
|
||||
|
||||
<TabPane label="订单流水" name="orderFlow">
|
||||
<Form inline :label-width="90" class="search-form">
|
||||
<FormItem label="结算状态">
|
||||
<Select v-model="orderFlowQuery.orderSettleState" style="width: 140px">
|
||||
<Option :value="0">未结算</Option>
|
||||
<Option :value="1">已结算</Option>
|
||||
</Select>
|
||||
</FormItem>
|
||||
<FormItem label="订单状态">
|
||||
<InputNumber :min="0" v-model="orderFlowQuery.orderState" style="width: 120px" />
|
||||
</FormItem>
|
||||
<FormItem label="支付方式">
|
||||
<InputNumber :min="0" v-model="orderFlowQuery.orderPayMethod" style="width: 120px" />
|
||||
</FormItem>
|
||||
<FormItem label="订单ID">
|
||||
<Input v-model="orderFlowQuery.orderId" clearable style="width: 180px" />
|
||||
</FormItem>
|
||||
<FormItem label="页大小">
|
||||
<InputNumber :min="1" :max="100" v-model="orderFlowQuery.limit" style="width: 120px" />
|
||||
</FormItem>
|
||||
<FormItem label="偏移量">
|
||||
<InputNumber :min="0" v-model="orderFlowQuery.offset" style="width: 120px" />
|
||||
</FormItem>
|
||||
<FormItem label="使用上下文">
|
||||
<i-switch v-model="orderFlowQuery.usePageCtx">
|
||||
<span slot="open">是</span>
|
||||
<span slot="close">否</span>
|
||||
</i-switch>
|
||||
</FormItem>
|
||||
<FormItem label="分页上下文" v-if="orderFlowQuery.usePageCtx">
|
||||
<Input v-model="orderFlowQuery.pageCtx" clearable style="width: 220px" placeholder="可为空,自动使用上次返回" />
|
||||
</FormItem>
|
||||
<FormItem label="时间范围">
|
||||
<DatePicker
|
||||
type="daterange"
|
||||
v-model="orderFlowDateRange"
|
||||
format="yyyy-MM-dd"
|
||||
clearable
|
||||
@on-change="handleOrderFlowDateChange"
|
||||
style="width: 240px"
|
||||
></DatePicker>
|
||||
</FormItem>
|
||||
<Button type="primary" icon="ios-search" :loading="orderFlowLoading" @click="handleOrderFlowSearch">查询</Button>
|
||||
</Form>
|
||||
|
||||
<Table border :loading="orderFlowLoading" :columns="orderFlowColumns" :data="orderFlowData" class="mt_10"></Table>
|
||||
<div class="pager-bar">
|
||||
<span class="pager-text">总数量:{{ orderFlowTotalCount }},pageCtx:{{ orderFlowRespPageCtx || "-" }}</span>
|
||||
</div>
|
||||
</TabPane>
|
||||
</Tabs>
|
||||
|
||||
<Modal v-model="detailModalVisible" :title="detailModalTitle" width="900" footer-hide>
|
||||
<div v-if="detailType === 'flowDetail' && flowDetailData">
|
||||
<Row :gutter="12">
|
||||
<Col :span="8"><p class="detail-item">流水ID:{{ flowDetailData.flowId || "-" }}</p></Col>
|
||||
<Col :span="8"><p class="detail-item">资金类型:{{ flowDetailData.fundsTypeDesc || "-" }}</p></Col>
|
||||
<Col :span="8"><p class="detail-item">流水类型:{{ flowTypeText(flowDetailData.flowType) }}</p></Col>
|
||||
</Row>
|
||||
<Row :gutter="12">
|
||||
<Col :span="8"><p class="detail-item">金额(分):{{ flowDetailData.amount || "-" }}</p></Col>
|
||||
<Col :span="8"><p class="detail-item">余额(分):{{ flowDetailData.balance || "-" }}</p></Col>
|
||||
<Col :span="8"><p class="detail-item">记账时间:{{ flowDetailData.bookkeepingTime || "-" }}</p></Col>
|
||||
</Row>
|
||||
<p class="detail-item">备注:{{ flowDetailData.remark || "-" }}</p>
|
||||
|
||||
<Table
|
||||
border
|
||||
size="small"
|
||||
class="mt_10"
|
||||
:columns="flowRelatedColumns"
|
||||
:data="Array.isArray(flowDetailData.relatedInfoList) ? flowDetailData.relatedInfoList : []"
|
||||
></Table>
|
||||
|
||||
<pre class="raw-json mt_10">{{ detailRawJson }}</pre>
|
||||
</div>
|
||||
<pre v-else class="raw-json">{{ detailRawJson }}</pre>
|
||||
</Modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
getWxChannelsFundsBalance,
|
||||
getWxChannelsFundsBankAcct,
|
||||
getWxChannelsFundsFlows,
|
||||
getWxChannelsFundsFlowDetail,
|
||||
getWxChannelsFundsOrderFlow,
|
||||
} from "@/api/index";
|
||||
|
||||
export default {
|
||||
name: "wxchannels-funds",
|
||||
data() {
|
||||
return {
|
||||
innerTab: "flows",
|
||||
balanceLoading: false,
|
||||
bankLoading: false,
|
||||
flowLoading: false,
|
||||
orderFlowLoading: false,
|
||||
balanceInfo: {
|
||||
errcode: "-",
|
||||
errmsg: "-",
|
||||
availableAmount: "-",
|
||||
pendingAmount: "-",
|
||||
subMchid: "-",
|
||||
},
|
||||
bankInfo: {
|
||||
errcode: "-",
|
||||
bankAccountType: "-",
|
||||
accountBank: "-",
|
||||
bankAddressCode: "-",
|
||||
bankBranchId: "-",
|
||||
bankName: "-",
|
||||
accountNumber: "-",
|
||||
accountName: "-",
|
||||
},
|
||||
balanceJson: "{}",
|
||||
bankJson: "{}",
|
||||
flowData: [],
|
||||
orderFlowData: [],
|
||||
orderFlowTotalCount: 0,
|
||||
orderFlowRespPageCtx: "",
|
||||
flowDateRange: null,
|
||||
orderFlowDateRange: null,
|
||||
flowNextKey: "",
|
||||
flowCursorStack: [],
|
||||
flowCurrentCursor: "",
|
||||
detailModalVisible: false,
|
||||
detailModalTitle: "",
|
||||
detailType: "raw",
|
||||
flowDetailData: null,
|
||||
detailRawJson: "",
|
||||
flowQuery: {
|
||||
page: 1,
|
||||
pageSize: 10,
|
||||
transactionId: "",
|
||||
startTime: null,
|
||||
endTime: null,
|
||||
},
|
||||
orderFlowQuery: {
|
||||
orderSettleState: 1,
|
||||
orderState: null,
|
||||
orderPayMethod: null,
|
||||
orderId: "",
|
||||
limit: 10,
|
||||
offset: 0,
|
||||
usePageCtx: false,
|
||||
pageCtx: "",
|
||||
begin: null,
|
||||
end: null,
|
||||
},
|
||||
flowColumns: [
|
||||
{ title: "流水ID", key: "flowId", minWidth: 140, tooltip: true },
|
||||
{ title: "资金类型", key: "fundsTypeDesc", minWidth: 140, tooltip: true },
|
||||
{ title: "流水类型", key: "flowTypeText", minWidth: 100, tooltip: true },
|
||||
{ title: "流水金额(分)", key: "amount", minWidth: 120, tooltip: true },
|
||||
{ title: "余额(分)", key: "balance", minWidth: 120, tooltip: true },
|
||||
{ title: "支付单号", key: "transactionId", minWidth: 180, tooltip: true },
|
||||
{ title: "记账时间", key: "bookkeepingTime", minWidth: 170, tooltip: true },
|
||||
{ title: "备注", key: "remark", minWidth: 200, tooltip: true },
|
||||
{
|
||||
title: "操作",
|
||||
key: "action",
|
||||
width: 100,
|
||||
render: (h, params) => {
|
||||
return h(
|
||||
"Button",
|
||||
{
|
||||
props: { type: "text", size: "small" },
|
||||
on: { click: () => this.showFlowDetail(params.row.flowId) },
|
||||
},
|
||||
"详情"
|
||||
);
|
||||
},
|
||||
},
|
||||
],
|
||||
orderFlowColumns: [
|
||||
{ title: "订单ID", key: "orderId", minWidth: 180, tooltip: true },
|
||||
{ title: "结算状态", key: "orderSettleState", minWidth: 100, tooltip: true },
|
||||
{ title: "订单状态", key: "orderState", minWidth: 100, tooltip: true },
|
||||
{ title: "支付方式", key: "orderPayMethod", minWidth: 100, tooltip: true },
|
||||
{ title: "创建时间(秒)", key: "orderCreateTime", minWidth: 120, tooltip: true },
|
||||
{ title: "支付时间(秒)", key: "orderPaidTime", minWidth: 120, tooltip: true },
|
||||
{ title: "商户实收(分)", key: "mchReceivedAmount", minWidth: 120, tooltip: true },
|
||||
{ title: "支出金额(分)", key: "expenseAmount", minWidth: 120, tooltip: true },
|
||||
{ title: "预计结算(分)", key: "mchSettleAmount", minWidth: 120, tooltip: true },
|
||||
{ title: "用户实付(分)", key: "buyerPaidAmount", minWidth: 120, tooltip: true },
|
||||
{
|
||||
title: "原始数据",
|
||||
key: "raw",
|
||||
width: 100,
|
||||
render: (h, params) => {
|
||||
return h(
|
||||
"Button",
|
||||
{
|
||||
props: { type: "text", size: "small" },
|
||||
on: { click: () => this.showRaw("订单流水详情", params.row.__raw || {}) },
|
||||
},
|
||||
"查看"
|
||||
);
|
||||
},
|
||||
},
|
||||
],
|
||||
flowRelatedColumns: [
|
||||
{ title: "关联类型", key: "relatedType", minWidth: 100, tooltip: true },
|
||||
{ title: "关联订单号", key: "orderId", minWidth: 180, tooltip: true },
|
||||
{ title: "关联售后单号", key: "aftersaleId", minWidth: 180, tooltip: true },
|
||||
{ title: "关联提现单号", key: "withdrawId", minWidth: 180, tooltip: true },
|
||||
{ title: "关联支付单号", key: "transactionId", minWidth: 180, tooltip: true },
|
||||
{ title: "记账时间", key: "bookkeepingTime", minWidth: 170, tooltip: true },
|
||||
],
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
hasFlowPrev() {
|
||||
return this.flowCursorStack.length > 0;
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.loadBalance();
|
||||
this.loadBankAcct();
|
||||
this.loadFlows();
|
||||
},
|
||||
methods: {
|
||||
showRaw(title, raw) {
|
||||
this.detailModalTitle = title;
|
||||
this.detailType = "raw";
|
||||
this.flowDetailData = null;
|
||||
this.detailRawJson = JSON.stringify(raw || {}, null, 2);
|
||||
this.detailModalVisible = true;
|
||||
},
|
||||
flowTypeText(val) {
|
||||
if (Number(val) === 1) return "收入";
|
||||
if (Number(val) === 2) return "支出";
|
||||
return val || "-";
|
||||
},
|
||||
loadBalance() {
|
||||
this.balanceLoading = true;
|
||||
getWxChannelsFundsBalance()
|
||||
.then((res) => {
|
||||
if (res && res.success) {
|
||||
const result = res.result || {};
|
||||
this.balanceInfo = {
|
||||
errcode: result.errcode ?? "-",
|
||||
errmsg: result.errmsg || "-",
|
||||
availableAmount: result.availableAmount ?? result.available_amount ?? "-",
|
||||
pendingAmount: result.pendingAmount ?? result.pending_amount ?? "-",
|
||||
subMchid: result.subMchid || result.sub_mchid || "-",
|
||||
};
|
||||
this.balanceJson = JSON.stringify(result, null, 2);
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
this.balanceLoading = false;
|
||||
});
|
||||
},
|
||||
loadBankAcct() {
|
||||
this.bankLoading = true;
|
||||
getWxChannelsFundsBankAcct()
|
||||
.then((res) => {
|
||||
if (res && res.success) {
|
||||
const result = res.result || {};
|
||||
const accountInfo = result.accountInfo || result.account_info || {};
|
||||
this.bankInfo = {
|
||||
errcode: result.errcode ?? "-",
|
||||
bankAccountType: accountInfo.bankAccountType || accountInfo.bank_account_type || "-",
|
||||
accountBank: accountInfo.accountBank || accountInfo.account_bank || "-",
|
||||
bankAddressCode: accountInfo.bankAddressCode || accountInfo.bank_address_code || "-",
|
||||
bankBranchId: accountInfo.bankBranchId || accountInfo.bank_branch_id || "-",
|
||||
bankName: accountInfo.bankName || accountInfo.bank_name || "-",
|
||||
accountNumber: accountInfo.accountNumber || accountInfo.account_number || "-",
|
||||
accountName: accountInfo.accountName || accountInfo.account_name || "-",
|
||||
};
|
||||
this.bankJson = JSON.stringify(result, null, 2);
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
this.bankLoading = false;
|
||||
});
|
||||
},
|
||||
handleFlowDateChange(v) {
|
||||
if (!v || v.length !== 2) {
|
||||
this.flowQuery.startTime = null;
|
||||
this.flowQuery.endTime = null;
|
||||
return;
|
||||
}
|
||||
const begin = v[0] ? Math.floor(new Date(`${v[0]}T00:00:00`).getTime() / 1000) : null;
|
||||
const end = v[1] ? Math.floor(new Date(`${v[1]}T23:59:59`).getTime() / 1000) : null;
|
||||
this.flowQuery.startTime = Number.isFinite(begin) ? begin : null;
|
||||
this.flowQuery.endTime = Number.isFinite(end) ? end : null;
|
||||
},
|
||||
handleFlowSearch() {
|
||||
this.flowQuery.page = 1;
|
||||
this.flowCurrentCursor = "";
|
||||
this.flowNextKey = "";
|
||||
this.flowCursorStack = [];
|
||||
this.loadFlows("");
|
||||
},
|
||||
handleFlowNext() {
|
||||
if (!this.flowNextKey) return;
|
||||
this.flowCursorStack.push(this.flowCurrentCursor || "");
|
||||
this.flowQuery.page += 1;
|
||||
this.loadFlows(this.flowNextKey);
|
||||
},
|
||||
handleFlowPrev() {
|
||||
if (!this.hasFlowPrev) return;
|
||||
const prev = this.flowCursorStack.pop() || "";
|
||||
this.flowQuery.page = Math.max(1, this.flowQuery.page - 1);
|
||||
this.loadFlows(prev);
|
||||
},
|
||||
loadFlows(nextKey = "") {
|
||||
this.flowLoading = true;
|
||||
this.flowCurrentCursor = nextKey || "";
|
||||
const params = {
|
||||
page: this.flowQuery.page,
|
||||
pageSize: this.flowQuery.pageSize,
|
||||
nextKey: nextKey || undefined,
|
||||
startTime: this.flowQuery.startTime,
|
||||
endTime: this.flowQuery.endTime,
|
||||
transactionId: this.flowQuery.transactionId || undefined,
|
||||
};
|
||||
Object.keys(params).forEach((k) => {
|
||||
if (params[k] === null || params[k] === "" || params[k] === undefined) delete params[k];
|
||||
});
|
||||
getWxChannelsFundsFlows(params)
|
||||
.then((res) => {
|
||||
if (res && res.success) {
|
||||
const result = res.result || {};
|
||||
const list = this.extractList(result);
|
||||
this.flowData = list.map((item) => this.normalizeFlow(item));
|
||||
this.flowNextKey = result.next_key || result.nextKey || "";
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
this.flowLoading = false;
|
||||
});
|
||||
},
|
||||
async showFlowDetail(flowId) {
|
||||
if (!flowId) {
|
||||
this.$Message.warning("缺少流水ID");
|
||||
return;
|
||||
}
|
||||
const res = await getWxChannelsFundsFlowDetail({ flowId });
|
||||
if (res && res.success) {
|
||||
const result = res.result || {};
|
||||
this.detailModalTitle = "资金流水详情";
|
||||
this.detailType = "flowDetail";
|
||||
this.flowDetailData = result.fundsFlow || result.funds_flow || null;
|
||||
this.detailRawJson = JSON.stringify(result, null, 2);
|
||||
this.detailModalVisible = true;
|
||||
}
|
||||
},
|
||||
handleOrderFlowDateChange(v) {
|
||||
if (!v || v.length !== 2) {
|
||||
this.orderFlowQuery.begin = null;
|
||||
this.orderFlowQuery.end = null;
|
||||
return;
|
||||
}
|
||||
const begin = v[0] ? Math.floor(new Date(`${v[0]}T00:00:00`).getTime() / 1000) : null;
|
||||
const end = v[1] ? Math.floor(new Date(`${v[1]}T23:59:59`).getTime() / 1000) : null;
|
||||
this.orderFlowQuery.begin = Number.isFinite(begin) ? begin : null;
|
||||
this.orderFlowQuery.end = Number.isFinite(end) ? end : null;
|
||||
},
|
||||
handleOrderFlowSearch() {
|
||||
if (this.orderFlowQuery.orderSettleState === null || this.orderFlowQuery.orderSettleState === undefined) {
|
||||
this.$Message.warning("请填写订单结算状态");
|
||||
return;
|
||||
}
|
||||
if (!this.orderFlowQuery.limit) {
|
||||
this.$Message.warning("请填写页大小");
|
||||
return;
|
||||
}
|
||||
this.orderFlowLoading = true;
|
||||
const params = {
|
||||
...this.orderFlowQuery,
|
||||
};
|
||||
if (params.usePageCtx && !params.pageCtx && this.orderFlowRespPageCtx) {
|
||||
params.pageCtx = this.orderFlowRespPageCtx;
|
||||
}
|
||||
Object.keys(params).forEach((k) => {
|
||||
if (params[k] === null || params[k] === "" || params[k] === undefined) delete params[k];
|
||||
});
|
||||
getWxChannelsFundsOrderFlow(params)
|
||||
.then((res) => {
|
||||
if (res && res.success) {
|
||||
const result = res.result || {};
|
||||
this.orderFlowTotalCount = Number(result.totalCount ?? result.total_count ?? 0);
|
||||
this.orderFlowRespPageCtx = result.pageCtx || result.page_ctx || "";
|
||||
const list = Array.isArray(result.dataList || result.data_list) ? result.dataList || result.data_list : [];
|
||||
this.orderFlowData = list.map((item) => this.normalizeOrderFlow(item));
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
this.orderFlowLoading = false;
|
||||
});
|
||||
},
|
||||
extractList(result) {
|
||||
if (!result || typeof result !== "object") return [];
|
||||
const keys = [
|
||||
"fundsFlowList",
|
||||
"funds_flow_list",
|
||||
"list",
|
||||
"records",
|
||||
"items",
|
||||
"data",
|
||||
"flows",
|
||||
"order_flows",
|
||||
"orderFlowList",
|
||||
"flow_list",
|
||||
];
|
||||
for (let i = 0; i < keys.length; i += 1) {
|
||||
const value = result[keys[i]];
|
||||
if (Array.isArray(value)) return value;
|
||||
}
|
||||
if (Array.isArray(result)) return result;
|
||||
return [];
|
||||
},
|
||||
normalizeFlow(item) {
|
||||
const raw = item || {};
|
||||
const relatedList = Array.isArray(raw.relatedInfoList || raw.related_info_list)
|
||||
? raw.relatedInfoList || raw.related_info_list
|
||||
: [];
|
||||
const firstRelated = relatedList[0] || {};
|
||||
return {
|
||||
flowId: raw.flow_id || raw.flowId || raw.id || "-",
|
||||
fundsTypeDesc: raw.fundsTypeDesc || raw.funds_type_desc || "-",
|
||||
flowTypeText: this.flowTypeText(raw.flowType ?? raw.flow_type),
|
||||
transactionId:
|
||||
firstRelated.transactionId ||
|
||||
firstRelated.transaction_id ||
|
||||
raw.transaction_id ||
|
||||
raw.transactionId ||
|
||||
raw.pay_transaction_id ||
|
||||
"-",
|
||||
amount: raw.amount ?? "-",
|
||||
balance: raw.balance ?? "-",
|
||||
bookkeepingTime: raw.bookkeepingTime || raw.bookkeeping_time || raw.create_time || raw.createTime || "-",
|
||||
remark: raw.remark || "-",
|
||||
__raw: raw,
|
||||
};
|
||||
},
|
||||
normalizeOrderFlow(item) {
|
||||
const raw = item || {};
|
||||
return {
|
||||
orderId: raw.order_id || raw.orderId || "-",
|
||||
orderSettleState: raw.order_settle_state ?? raw.orderSettleState ?? "-",
|
||||
orderState: raw.order_state ?? raw.orderState ?? "-",
|
||||
orderPayMethod: raw.order_pay_method ?? raw.orderPayMethod ?? "-",
|
||||
orderCreateTime: raw.order_create_time ?? raw.orderCreateTime ?? "-",
|
||||
orderPaidTime: raw.order_paid_time ?? raw.orderPaidTime ?? "-",
|
||||
mchReceivedAmount: raw.mch_received_amount ?? raw.mchReceivedAmount ?? "-",
|
||||
expenseAmount: raw.expense_amount ?? raw.expenseAmount ?? "-",
|
||||
mchSettleAmount: raw.mch_settle_amount ?? raw.mchSettleAmount ?? "-",
|
||||
buyerPaidAmount: raw.buyer_paid_amount ?? raw.buyerPaidAmount ?? "-",
|
||||
__raw: raw,
|
||||
};
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.wxchannels-funds {
|
||||
min-height: 360px;
|
||||
}
|
||||
|
||||
.pager-bar {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
margin-top: 12px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.pager-text {
|
||||
color: #666;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.raw-json {
|
||||
max-height: 240px;
|
||||
overflow: auto;
|
||||
margin-top: 10px;
|
||||
margin-bottom: 0;
|
||||
background: #f7f7f7;
|
||||
border: 1px solid #eee;
|
||||
padding: 10px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.balance-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 8px 16px;
|
||||
color: #515a6e;
|
||||
}
|
||||
|
||||
.detail-item {
|
||||
color: #515a6e;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
</style>
|
||||
138
manager/src/views/promotions/wxchannels/goods.vue
Normal file
138
manager/src/views/promotions/wxchannels/goods.vue
Normal file
@@ -0,0 +1,138 @@
|
||||
<template>
|
||||
<div class="wxchannels-goods">
|
||||
<Row>
|
||||
<Form :model="searchForm" inline :label-width="70" class="search-form">
|
||||
<FormItem label="商品名称" prop="goodsName">
|
||||
<Input
|
||||
v-model="searchForm.goodsName"
|
||||
placeholder="请输入商品名称"
|
||||
clearable
|
||||
style="width: 220px"
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem label="状态" prop="status">
|
||||
<Select v-model="searchForm.status" clearable style="width: 180px" placeholder="全部">
|
||||
<Option value="APPROVED">已通过</Option>
|
||||
<Option value="PENDING">审核中</Option>
|
||||
<Option value="REJECTED">已拒绝</Option>
|
||||
</Select>
|
||||
</FormItem>
|
||||
<Button @click="handleSearch" type="primary" icon="ios-search" class="search-btn" :loading="loading">查询</Button>
|
||||
</Form>
|
||||
</Row>
|
||||
|
||||
<Table :loading="loading" border :columns="columns" :data="data" class="mt_10"></Table>
|
||||
<Row type="flex" justify="end" class="mt_10" style="margin-top: 10px">
|
||||
<Page
|
||||
:current="searchForm.pageNumber"
|
||||
:total="total"
|
||||
:page-size="searchForm.pageSize"
|
||||
@on-change="changePage"
|
||||
@on-page-size-change="changePageSize"
|
||||
:page-size-opts="[20, 50, 100]"
|
||||
size="small"
|
||||
show-total
|
||||
show-elevator
|
||||
show-sizer
|
||||
></Page>
|
||||
</Row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getWxChannelsGoodsPage } from "@/api/index";
|
||||
|
||||
export default {
|
||||
name: "wxchannels-goods",
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
total: 0,
|
||||
data: [],
|
||||
searchForm: {
|
||||
goodsName: "",
|
||||
status: "",
|
||||
pageNumber: 1,
|
||||
pageSize: 20,
|
||||
},
|
||||
columns: [
|
||||
{
|
||||
title: "商品图片",
|
||||
key: "goodsImage",
|
||||
width: 90,
|
||||
align: "center",
|
||||
render: (h, params) => {
|
||||
return h("img", {
|
||||
attrs: { src: params.row.goodsImage || "", alt: "加载图片失败" },
|
||||
style: {
|
||||
width: "50px",
|
||||
height: "50px",
|
||||
objectFit: "cover",
|
||||
borderRadius: "4px",
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
{ title: "商品名称", key: "goodsName", minWidth: 220, tooltip: true },
|
||||
{ title: "平台商品ID", key: "goodsId", minWidth: 130, tooltip: true },
|
||||
{ title: "平台SKU ID", key: "skuId", minWidth: 130, tooltip: true },
|
||||
{ title: "店铺", key: "storeName", minWidth: 160, tooltip: true },
|
||||
{ title: "分类", key: "categoryName", minWidth: 160, tooltip: true },
|
||||
{ title: "销售价", key: "costPrice", width: 100 },
|
||||
{ title: "微信小店价", key: "channelPrice", width: 110 },
|
||||
{ title: "库存", key: "stock", width: 90 },
|
||||
{
|
||||
title: "状态",
|
||||
key: "status",
|
||||
width: 110,
|
||||
render: (h, params) => {
|
||||
const val = params.row.status;
|
||||
const map = {
|
||||
APPROVED: { label: "已通过", color: "green" },
|
||||
PENDING: { label: "审核中", color: "orange" },
|
||||
REJECTED: { label: "已拒绝", color: "red" },
|
||||
};
|
||||
const item = map[val] || { label: val || "-", color: "default" };
|
||||
return h("Tag", { props: { color: item.color } }, item.label);
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.loadPage();
|
||||
},
|
||||
methods: {
|
||||
handleSearch() {
|
||||
this.searchForm.pageNumber = 1;
|
||||
this.loadPage();
|
||||
},
|
||||
changePage(pageNumber) {
|
||||
this.searchForm.pageNumber = pageNumber;
|
||||
this.loadPage();
|
||||
},
|
||||
changePageSize(pageSize) {
|
||||
this.searchForm.pageSize = pageSize;
|
||||
this.searchForm.pageNumber = 1;
|
||||
this.loadPage();
|
||||
},
|
||||
loadPage() {
|
||||
this.loading = true;
|
||||
const params = { ...this.searchForm };
|
||||
if (!params.goodsName) delete params.goodsName;
|
||||
if (!params.status) delete params.status;
|
||||
getWxChannelsGoodsPage(params)
|
||||
.then((res) => {
|
||||
if (res && res.success) {
|
||||
const page = res.result || {};
|
||||
this.data = Array.isArray(page.records) ? page.records : [];
|
||||
this.total = Number(page.total || 0);
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
60
manager/src/views/promotions/wxchannels/index.vue
Normal file
60
manager/src/views/promotions/wxchannels/index.vue
Normal file
@@ -0,0 +1,60 @@
|
||||
<template>
|
||||
<Card>
|
||||
<Tabs v-model="activeTab">
|
||||
<TabPane label="微信小店设置" name="setting">
|
||||
<WxChannelsSetting v-if="activeTab === 'setting'" />
|
||||
</TabPane>
|
||||
<TabPane label="品牌列表" name="brand">
|
||||
<WxChannelsBrand v-if="activeTab === 'brand'" />
|
||||
</TabPane>
|
||||
<TabPane label="微信小店类目" name="category">
|
||||
<WxChannelsCategory v-if="activeTab === 'category'" />
|
||||
</TabPane>
|
||||
<TabPane label="微信小店商品" name="goods">
|
||||
<WxChannelsGoods v-if="activeTab === 'goods'" />
|
||||
</TabPane>
|
||||
<TabPane label="微信小店订单" name="order">
|
||||
<WxChannelsOrder v-if="activeTab === 'order'" />
|
||||
</TabPane>
|
||||
<TabPane label="微信小店退单" name="refund">
|
||||
<WxChannelsRefund v-if="activeTab === 'refund'" />
|
||||
</TabPane>
|
||||
<TabPane label="微信小店资金" name="funds">
|
||||
<WxChannelsFunds v-if="activeTab === 'funds'" />
|
||||
</TabPane>
|
||||
<TabPane label="微信小店概况" name="overview">
|
||||
<WxChannelsOverview v-if="activeTab === 'overview'" />
|
||||
</TabPane>
|
||||
</Tabs>
|
||||
</Card>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import WxChannelsSetting from "./setting.vue";
|
||||
import WxChannelsBrand from "./brand.vue";
|
||||
import WxChannelsCategory from "./category.vue";
|
||||
import WxChannelsGoods from "./goods.vue";
|
||||
import WxChannelsOrder from "./order.vue";
|
||||
import WxChannelsRefund from "./refund.vue";
|
||||
import WxChannelsFunds from "./funds.vue";
|
||||
import WxChannelsOverview from "./overview.vue";
|
||||
|
||||
export default {
|
||||
name: "promotions-wxchannels",
|
||||
components: {
|
||||
WxChannelsSetting,
|
||||
WxChannelsBrand,
|
||||
WxChannelsCategory,
|
||||
WxChannelsGoods,
|
||||
WxChannelsOrder,
|
||||
WxChannelsRefund,
|
||||
WxChannelsFunds,
|
||||
WxChannelsOverview,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
activeTab: "setting",
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
178
manager/src/views/promotions/wxchannels/order.vue
Normal file
178
manager/src/views/promotions/wxchannels/order.vue
Normal file
@@ -0,0 +1,178 @@
|
||||
<template>
|
||||
<div class="wxchannels-order">
|
||||
<Row>
|
||||
<Form :model="searchForm" inline :label-width="70" class="search-form">
|
||||
<FormItem label="订单编号" prop="channelOrderSn">
|
||||
<Input
|
||||
v-model="searchForm.channelOrderSn"
|
||||
placeholder="微信小店订单编号"
|
||||
clearable
|
||||
style="width: 220px"
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem label="会员昵称" prop="memberNickName">
|
||||
<Input
|
||||
v-model="searchForm.memberNickName"
|
||||
placeholder="请输入会员昵称"
|
||||
clearable
|
||||
style="width: 180px"
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem label="商品名称" prop="goodsName">
|
||||
<Input
|
||||
v-model="searchForm.goodsName"
|
||||
placeholder="请输入商品名称"
|
||||
clearable
|
||||
style="width: 200px"
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem label="状态" prop="status">
|
||||
<Input v-model="searchForm.status" placeholder="订单状态" clearable style="width: 140px" />
|
||||
</FormItem>
|
||||
<FormItem label="场景" prop="scene">
|
||||
<Select v-model="searchForm.scene" clearable style="width: 140px" placeholder="全部">
|
||||
<Option value="LIVE">直播</Option>
|
||||
<Option value="WINDOW">橱窗</Option>
|
||||
</Select>
|
||||
</FormItem>
|
||||
<FormItem label="下单时间">
|
||||
<DatePicker
|
||||
type="daterange"
|
||||
v-model="selectDate"
|
||||
format="yyyy-MM-dd"
|
||||
clearable
|
||||
@on-change="selectDateRange"
|
||||
placeholder="选择起始时间"
|
||||
style="width: 240px"
|
||||
></DatePicker>
|
||||
</FormItem>
|
||||
<Button @click="handleSearch" type="primary" icon="ios-search" class="search-btn" :loading="loading">查询</Button>
|
||||
</Form>
|
||||
</Row>
|
||||
|
||||
<Table :loading="loading" border :columns="columns" :data="data" class="mt_10"></Table>
|
||||
<Row type="flex" justify="end" class="mt_10" style="margin-top: 10px">
|
||||
<Page
|
||||
:current="searchForm.pageNumber"
|
||||
:total="total"
|
||||
:page-size="searchForm.pageSize"
|
||||
@on-change="changePage"
|
||||
@on-page-size-change="changePageSize"
|
||||
:page-size-opts="[20, 50, 100]"
|
||||
size="small"
|
||||
show-total
|
||||
show-elevator
|
||||
show-sizer
|
||||
></Page>
|
||||
</Row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getWxChannelsOrderPage } from "@/api/index";
|
||||
|
||||
export default {
|
||||
name: "wxchannels-order",
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
total: 0,
|
||||
data: [],
|
||||
selectDate: null,
|
||||
searchForm: {
|
||||
channelOrderSn: "",
|
||||
memberNickName: "",
|
||||
goodsName: "",
|
||||
status: "",
|
||||
scene: "",
|
||||
startTime: null,
|
||||
endTime: null,
|
||||
pageNumber: 1,
|
||||
pageSize: 20,
|
||||
},
|
||||
columns: [
|
||||
{ title: "微信小店订单编号", key: "channelOrderSn", minWidth: 200, tooltip: true },
|
||||
{ title: "平台订单编号", key: "orderSn", minWidth: 200, tooltip: true },
|
||||
{ title: "会员ID", key: "memberId", minWidth: 120, tooltip: true },
|
||||
{ title: "会员昵称", key: "memberNickName", minWidth: 140, tooltip: true },
|
||||
{ title: "订单金额", key: "amount", width: 110 },
|
||||
{
|
||||
title: "订单状态",
|
||||
key: "status",
|
||||
width: 130,
|
||||
render: (h, params) => {
|
||||
const val = params.row.status;
|
||||
if (!val) return h("span", "-");
|
||||
return h("Tag", { props: { color: "blue" } }, val);
|
||||
},
|
||||
},
|
||||
{ title: "带货微信小店名称", key: "channelName", minWidth: 160, tooltip: true },
|
||||
{
|
||||
title: "下单场景",
|
||||
key: "scene",
|
||||
width: 110,
|
||||
render: (h, params) => {
|
||||
const val = params.row.scene;
|
||||
const map = {
|
||||
LIVE: { label: "直播", color: "orange" },
|
||||
WINDOW: { label: "橱窗", color: "purple" },
|
||||
};
|
||||
const item = map[val] || { label: val || "-", color: "default" };
|
||||
return h("Tag", { props: { color: item.color } }, item.label);
|
||||
},
|
||||
},
|
||||
{ title: "创建时间", key: "createTime", minWidth: 160, tooltip: true },
|
||||
],
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.loadPage();
|
||||
},
|
||||
methods: {
|
||||
selectDateRange(v) {
|
||||
if (!v || v.length !== 2) {
|
||||
this.searchForm.startTime = null;
|
||||
this.searchForm.endTime = null;
|
||||
return;
|
||||
}
|
||||
const startStr = v[0];
|
||||
const endStr = v[1];
|
||||
const start = startStr ? new Date(`${startStr}T00:00:00`).getTime() : null;
|
||||
const end = endStr ? new Date(`${endStr}T23:59:59`).getTime() : null;
|
||||
this.searchForm.startTime = Number.isFinite(start) ? start : null;
|
||||
this.searchForm.endTime = Number.isFinite(end) ? end : null;
|
||||
},
|
||||
handleSearch() {
|
||||
this.searchForm.pageNumber = 1;
|
||||
this.loadPage();
|
||||
},
|
||||
changePage(pageNumber) {
|
||||
this.searchForm.pageNumber = pageNumber;
|
||||
this.loadPage();
|
||||
},
|
||||
changePageSize(pageSize) {
|
||||
this.searchForm.pageSize = pageSize;
|
||||
this.searchForm.pageNumber = 1;
|
||||
this.loadPage();
|
||||
},
|
||||
loadPage() {
|
||||
this.loading = true;
|
||||
const params = { ...this.searchForm };
|
||||
Object.keys(params).forEach((k) => {
|
||||
if (params[k] === null || params[k] === "" || params[k] === undefined) delete params[k];
|
||||
});
|
||||
getWxChannelsOrderPage(params)
|
||||
.then((res) => {
|
||||
if (res && res.success) {
|
||||
const page = res.result || {};
|
||||
this.data = Array.isArray(page.records) ? page.records : [];
|
||||
this.total = Number(page.total || 0);
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
202
manager/src/views/promotions/wxchannels/overview.vue
Normal file
202
manager/src/views/promotions/wxchannels/overview.vue
Normal file
@@ -0,0 +1,202 @@
|
||||
<template>
|
||||
<div class="wxchannels-overview">
|
||||
<Row>
|
||||
<Form inline :label-width="70" class="search-form">
|
||||
<FormItem label="时间范围">
|
||||
<DatePicker
|
||||
type="daterange"
|
||||
v-model="dateRange"
|
||||
format="yyyy-MM-dd"
|
||||
clearable
|
||||
@on-change="handleDateChange"
|
||||
style="width: 260px"
|
||||
></DatePicker>
|
||||
</FormItem>
|
||||
<Button type="primary" icon="ios-search" :loading="loading" @click="handleSearch">查询</Button>
|
||||
<Button style="margin-left: 8px" :loading="exportLoading" @click="handleExport">导出报表</Button>
|
||||
</Form>
|
||||
</Row>
|
||||
|
||||
<Row :gutter="12" class="mt_10">
|
||||
<Col :span="8">
|
||||
<Card dis-hover>
|
||||
<p class="title">总销售额</p>
|
||||
<p class="value">{{ formatAmount(summary.totalSales) }}</p>
|
||||
</Card>
|
||||
</Col>
|
||||
<Col :span="8">
|
||||
<Card dis-hover>
|
||||
<p class="title">直播间销售额</p>
|
||||
<p class="value">{{ formatAmount(summary.liveSales) }}</p>
|
||||
</Card>
|
||||
</Col>
|
||||
<Col :span="8">
|
||||
<Card dis-hover>
|
||||
<p class="title">橱窗销售额</p>
|
||||
<p class="value">{{ formatAmount(summary.windowSales) }}</p>
|
||||
</Card>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<Row :gutter="12" class="mt_10">
|
||||
<Col :span="8">
|
||||
<Card dis-hover>
|
||||
<p class="title">总退款金额</p>
|
||||
<p class="value refund">{{ formatAmount(summary.totalRefund) }}</p>
|
||||
</Card>
|
||||
</Col>
|
||||
<Col :span="8">
|
||||
<Card dis-hover>
|
||||
<p class="title">直播间退款金额</p>
|
||||
<p class="value refund">{{ formatAmount(summary.liveRefund) }}</p>
|
||||
</Card>
|
||||
</Col>
|
||||
<Col :span="8">
|
||||
<Card dis-hover>
|
||||
<p class="title">橱窗退款金额</p>
|
||||
<p class="value refund">{{ formatAmount(summary.windowRefund) }}</p>
|
||||
</Card>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<Table border :loading="loading" :columns="dailyColumns" :data="dailyData" class="mt_10"></Table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
getWxChannelsOverviewSummary,
|
||||
getWxChannelsOverviewDaily,
|
||||
exportWxChannelsOverview,
|
||||
} from "@/api/index";
|
||||
|
||||
export default {
|
||||
name: "wxchannels-overview",
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
exportLoading: false,
|
||||
dateRange: null,
|
||||
query: {
|
||||
startTime: null,
|
||||
endTime: null,
|
||||
},
|
||||
summary: {
|
||||
totalSales: 0,
|
||||
liveSales: 0,
|
||||
windowSales: 0,
|
||||
totalRefund: 0,
|
||||
liveRefund: 0,
|
||||
windowRefund: 0,
|
||||
},
|
||||
dailyData: [],
|
||||
dailyColumns: [
|
||||
{ title: "日期", key: "date", minWidth: 120, tooltip: true },
|
||||
{ title: "总销售额", key: "totalSales", minWidth: 120 },
|
||||
{ title: "直播间销售额", key: "liveSales", minWidth: 120 },
|
||||
{ title: "橱窗销售额", key: "windowSales", minWidth: 120 },
|
||||
{ title: "总退款金额", key: "totalRefund", minWidth: 120 },
|
||||
{ title: "直播间退款金额", key: "liveRefund", minWidth: 120 },
|
||||
{ title: "橱窗退款金额", key: "windowRefund", minWidth: 120 },
|
||||
],
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.handleSearch();
|
||||
},
|
||||
methods: {
|
||||
formatAmount(val) {
|
||||
const n = Number(val || 0);
|
||||
return Number.isFinite(n) ? n.toFixed(2) : "0.00";
|
||||
},
|
||||
handleDateChange(v) {
|
||||
if (!v || v.length !== 2) {
|
||||
this.query.startTime = null;
|
||||
this.query.endTime = null;
|
||||
return;
|
||||
}
|
||||
const start = v[0] ? new Date(`${v[0]}T00:00:00`).getTime() : null;
|
||||
const end = v[1] ? new Date(`${v[1]}T23:59:59`).getTime() : null;
|
||||
this.query.startTime = Number.isFinite(start) ? start : null;
|
||||
this.query.endTime = Number.isFinite(end) ? end : null;
|
||||
},
|
||||
buildParams() {
|
||||
const params = {
|
||||
startTime: this.query.startTime,
|
||||
endTime: this.query.endTime,
|
||||
};
|
||||
Object.keys(params).forEach((k) => {
|
||||
if (params[k] === null || params[k] === undefined || params[k] === "") delete params[k];
|
||||
});
|
||||
return params;
|
||||
},
|
||||
async handleSearch() {
|
||||
this.loading = true;
|
||||
try {
|
||||
const params = this.buildParams();
|
||||
const [summaryRes, dailyRes] = await Promise.all([
|
||||
getWxChannelsOverviewSummary(params),
|
||||
getWxChannelsOverviewDaily(params),
|
||||
]);
|
||||
if (summaryRes && summaryRes.success) {
|
||||
this.summary = {
|
||||
...this.summary,
|
||||
...(summaryRes.result || {}),
|
||||
};
|
||||
}
|
||||
if (dailyRes && dailyRes.success) {
|
||||
this.dailyData = Array.isArray(dailyRes.result) ? dailyRes.result : [];
|
||||
}
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
handleExport() {
|
||||
this.exportLoading = true;
|
||||
const params = this.buildParams();
|
||||
exportWxChannelsOverview(params)
|
||||
.then((res) => {
|
||||
const blob = new Blob([res], {
|
||||
type: "application/vnd.ms-excel;charset=utf-8",
|
||||
});
|
||||
if ("download" in document.createElement("a")) {
|
||||
const link = document.createElement("a");
|
||||
link.download = "微信小店概况报表.xls";
|
||||
link.style.display = "none";
|
||||
link.href = URL.createObjectURL(blob);
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
URL.revokeObjectURL(link.href);
|
||||
document.body.removeChild(link);
|
||||
} else {
|
||||
navigator.msSaveBlob(blob, "微信小店概况报表.xls");
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
this.exportLoading = false;
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.wxchannels-overview {
|
||||
min-height: 360px;
|
||||
}
|
||||
|
||||
.title {
|
||||
color: #666;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.value {
|
||||
font-size: 22px;
|
||||
font-weight: 600;
|
||||
color: #2d8cf0;
|
||||
}
|
||||
|
||||
.value.refund {
|
||||
color: #ed4014;
|
||||
}
|
||||
</style>
|
||||
186
manager/src/views/promotions/wxchannels/refund.vue
Normal file
186
manager/src/views/promotions/wxchannels/refund.vue
Normal file
@@ -0,0 +1,186 @@
|
||||
<template>
|
||||
<div class="wxchannels-refund">
|
||||
<Row>
|
||||
<Form :model="searchForm" inline :label-width="70" class="search-form">
|
||||
<FormItem label="退单编号" prop="channelRefundSn">
|
||||
<Input
|
||||
v-model="searchForm.channelRefundSn"
|
||||
placeholder="微信小店退单编号"
|
||||
clearable
|
||||
style="width: 220px"
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem label="订单编号" prop="channelOrderSn">
|
||||
<Input
|
||||
v-model="searchForm.channelOrderSn"
|
||||
placeholder="微信小店订单编号"
|
||||
clearable
|
||||
style="width: 220px"
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem label="会员昵称" prop="memberNickName">
|
||||
<Input
|
||||
v-model="searchForm.memberNickName"
|
||||
placeholder="请输入会员昵称"
|
||||
clearable
|
||||
style="width: 180px"
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem label="商品名称" prop="goodsName">
|
||||
<Input
|
||||
v-model="searchForm.goodsName"
|
||||
placeholder="请输入商品名称"
|
||||
clearable
|
||||
style="width: 200px"
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem label="状态" prop="status">
|
||||
<Input v-model="searchForm.status" placeholder="退单状态" clearable style="width: 140px" />
|
||||
</FormItem>
|
||||
<FormItem label="场景" prop="scene">
|
||||
<Select v-model="searchForm.scene" clearable style="width: 140px" placeholder="全部">
|
||||
<Option value="LIVE">直播</Option>
|
||||
<Option value="WINDOW">橱窗</Option>
|
||||
</Select>
|
||||
</FormItem>
|
||||
<FormItem label="下单时间">
|
||||
<DatePicker
|
||||
type="daterange"
|
||||
v-model="selectDate"
|
||||
format="yyyy-MM-dd"
|
||||
clearable
|
||||
@on-change="selectDateRange"
|
||||
placeholder="选择起始时间"
|
||||
style="width: 240px"
|
||||
></DatePicker>
|
||||
</FormItem>
|
||||
<Button @click="handleSearch" type="primary" icon="ios-search" class="search-btn" :loading="loading">查询</Button>
|
||||
</Form>
|
||||
</Row>
|
||||
|
||||
<Table :loading="loading" border :columns="columns" :data="data" class="mt_10"></Table>
|
||||
<Row type="flex" justify="end" class="mt_10" style="margin-top: 10px">
|
||||
<Page
|
||||
:current="searchForm.pageNumber"
|
||||
:total="total"
|
||||
:page-size="searchForm.pageSize"
|
||||
@on-change="changePage"
|
||||
@on-page-size-change="changePageSize"
|
||||
:page-size-opts="[20, 50, 100]"
|
||||
size="small"
|
||||
show-total
|
||||
show-elevator
|
||||
show-sizer
|
||||
></Page>
|
||||
</Row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getWxChannelsRefundPage } from "@/api/index";
|
||||
|
||||
export default {
|
||||
name: "wxchannels-refund",
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
total: 0,
|
||||
data: [],
|
||||
selectDate: null,
|
||||
searchForm: {
|
||||
channelRefundSn: "",
|
||||
channelOrderSn: "",
|
||||
memberNickName: "",
|
||||
goodsName: "",
|
||||
status: "",
|
||||
scene: "",
|
||||
startTime: null,
|
||||
endTime: null,
|
||||
pageNumber: 1,
|
||||
pageSize: 20,
|
||||
},
|
||||
columns: [
|
||||
{ title: "微信小店退单编号", key: "channelRefundSn", minWidth: 200, tooltip: true },
|
||||
{ title: "微信小店订单编号", key: "channelOrderSn", minWidth: 200, tooltip: true },
|
||||
{ title: "会员ID", key: "memberId", minWidth: 120, tooltip: true },
|
||||
{ title: "会员昵称", key: "memberNickName", minWidth: 140, tooltip: true },
|
||||
{ title: "退款金额", key: "amount", width: 110 },
|
||||
{
|
||||
title: "退单状态",
|
||||
key: "status",
|
||||
width: 130,
|
||||
render: (h, params) => {
|
||||
const val = params.row.status;
|
||||
if (!val) return h("span", "-");
|
||||
return h("Tag", { props: { color: "blue" } }, val);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "下单场景",
|
||||
key: "scene",
|
||||
width: 110,
|
||||
render: (h, params) => {
|
||||
const val = params.row.scene;
|
||||
const map = {
|
||||
LIVE: { label: "直播", color: "orange" },
|
||||
WINDOW: { label: "橱窗", color: "purple" },
|
||||
};
|
||||
const item = map[val] || { label: val || "-", color: "default" };
|
||||
return h("Tag", { props: { color: item.color } }, item.label);
|
||||
},
|
||||
},
|
||||
{ title: "创建时间", key: "createTime", minWidth: 160, tooltip: true },
|
||||
],
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.loadPage();
|
||||
},
|
||||
methods: {
|
||||
selectDateRange(v) {
|
||||
if (!v || v.length !== 2) {
|
||||
this.searchForm.startTime = null;
|
||||
this.searchForm.endTime = null;
|
||||
return;
|
||||
}
|
||||
const startStr = v[0];
|
||||
const endStr = v[1];
|
||||
const start = startStr ? new Date(`${startStr}T00:00:00`).getTime() : null;
|
||||
const end = endStr ? new Date(`${endStr}T23:59:59`).getTime() : null;
|
||||
this.searchForm.startTime = Number.isFinite(start) ? start : null;
|
||||
this.searchForm.endTime = Number.isFinite(end) ? end : null;
|
||||
},
|
||||
handleSearch() {
|
||||
this.searchForm.pageNumber = 1;
|
||||
this.loadPage();
|
||||
},
|
||||
changePage(pageNumber) {
|
||||
this.searchForm.pageNumber = pageNumber;
|
||||
this.loadPage();
|
||||
},
|
||||
changePageSize(pageSize) {
|
||||
this.searchForm.pageSize = pageSize;
|
||||
this.searchForm.pageNumber = 1;
|
||||
this.loadPage();
|
||||
},
|
||||
loadPage() {
|
||||
this.loading = true;
|
||||
const params = { ...this.searchForm };
|
||||
Object.keys(params).forEach((k) => {
|
||||
if (params[k] === null || params[k] === "" || params[k] === undefined) delete params[k];
|
||||
});
|
||||
getWxChannelsRefundPage(params)
|
||||
.then((res) => {
|
||||
if (res && res.success) {
|
||||
const page = res.result || {};
|
||||
this.data = Array.isArray(page.records) ? page.records : [];
|
||||
this.total = Number(page.total || 0);
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
182
manager/src/views/promotions/wxchannels/setting.vue
Normal file
182
manager/src/views/promotions/wxchannels/setting.vue
Normal file
@@ -0,0 +1,182 @@
|
||||
<template>
|
||||
<div class="wxchannels-setting">
|
||||
<Form
|
||||
ref="settingForm"
|
||||
:model="settingForm"
|
||||
:rules="formValidate"
|
||||
:label-width="170"
|
||||
label-position="right"
|
||||
>
|
||||
<FormItem label="微信小店 AppId" prop="appId">
|
||||
<Input
|
||||
v-model="settingForm.appId"
|
||||
clearable
|
||||
placeholder="请输入微信小店 AppId"
|
||||
style="width: 420px"
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem label="微信小店 AppSecret" prop="appSecret">
|
||||
<Input
|
||||
v-model="settingForm.appSecret"
|
||||
clearable
|
||||
placeholder="请输入微信小店 AppSecret"
|
||||
style="width: 420px"
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem label="接口基础地址" prop="apiBase">
|
||||
<Input
|
||||
v-model="settingForm.apiBase"
|
||||
clearable
|
||||
placeholder="https://api.weixin.qq.com/minishop"
|
||||
style="width: 420px"
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem label="获取 access_token 地址" prop="tokenUrl">
|
||||
<Input
|
||||
v-model="settingForm.tokenUrl"
|
||||
clearable
|
||||
placeholder="https://api.weixin.qq.com/cgi-bin/token"
|
||||
style="width: 420px"
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem label="消息回调 Token" prop="callbackToken">
|
||||
<Input
|
||||
v-model="settingForm.callbackToken"
|
||||
clearable
|
||||
placeholder="请输入消息回调 Token"
|
||||
style="width: 420px"
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem label="消息回调 EncodingAESKey" prop="encodingAesKey">
|
||||
<Input
|
||||
v-model="settingForm.encodingAesKey"
|
||||
clearable
|
||||
placeholder="请输入消息回调 EncodingAESKey"
|
||||
style="width: 420px"
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem>
|
||||
<Button type="primary" :loading="submitLoading" @click="handleSubmit">
|
||||
保存设置
|
||||
</Button>
|
||||
<Button style="margin-left: 8px" @click="handleReset">重置</Button>
|
||||
</FormItem>
|
||||
</Form>
|
||||
<Spin size="large" fix v-if="loading"></Spin>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getWxChannelsSetting, saveWxChannelsSetting } from "@/api/index";
|
||||
|
||||
const defaultSettingForm = () => ({
|
||||
appId: "",
|
||||
appSecret: "",
|
||||
apiBase: "",
|
||||
tokenUrl: "",
|
||||
callbackToken: "",
|
||||
encodingAesKey: "",
|
||||
});
|
||||
|
||||
export default {
|
||||
name: "wxchannels-setting",
|
||||
data() {
|
||||
const validateUrl = (rule, value, callback) => {
|
||||
if (!value) {
|
||||
callback(new Error(rule.message));
|
||||
return;
|
||||
}
|
||||
const urlPattern = /^https?:\/\/.+/;
|
||||
if (!urlPattern.test(value)) {
|
||||
callback(new Error("请输入正确的 URL 地址"));
|
||||
return;
|
||||
}
|
||||
callback();
|
||||
};
|
||||
return {
|
||||
loading: false,
|
||||
settingForm: defaultSettingForm(),
|
||||
originalForm: defaultSettingForm(),
|
||||
submitLoading: false,
|
||||
formValidate: {
|
||||
appId: [
|
||||
{ required: true, message: "请输入微信小店 AppId", trigger: "blur" }
|
||||
],
|
||||
appSecret: [
|
||||
{ required: true, message: "请输入微信小店 AppSecret", trigger: "blur" }
|
||||
],
|
||||
apiBase: [
|
||||
{
|
||||
required: true,
|
||||
validator: validateUrl,
|
||||
message: "请输入接口基础地址",
|
||||
trigger: "blur",
|
||||
}
|
||||
],
|
||||
tokenUrl: [
|
||||
{
|
||||
required: true,
|
||||
validator: validateUrl,
|
||||
message: "请输入获取 access_token 地址",
|
||||
trigger: "blur",
|
||||
}
|
||||
],
|
||||
},
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
async loadData() {
|
||||
this.loading = true;
|
||||
try {
|
||||
const res = await getWxChannelsSetting();
|
||||
if (res && res.success) {
|
||||
const nextForm = {
|
||||
...defaultSettingForm(),
|
||||
...(res.result || {}),
|
||||
};
|
||||
this.settingForm = nextForm;
|
||||
this.originalForm = { ...nextForm };
|
||||
}
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
handleSubmit() {
|
||||
this.$refs.settingForm.validate(async (valid) => {
|
||||
if (!valid) {
|
||||
return;
|
||||
}
|
||||
this.submitLoading = true;
|
||||
try {
|
||||
const payload = { ...this.settingForm };
|
||||
const res = await saveWxChannelsSetting(payload);
|
||||
if (res && res.success) {
|
||||
this.originalForm = { ...payload };
|
||||
this.$Message.success("保存成功");
|
||||
}
|
||||
} finally {
|
||||
this.submitLoading = false;
|
||||
}
|
||||
});
|
||||
},
|
||||
handleReset() {
|
||||
this.settingForm = { ...this.originalForm };
|
||||
this.$nextTick(() => {
|
||||
this.$refs.settingForm.clearValidate();
|
||||
});
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.loadData().catch(() => {
|
||||
this.$Message.error("获取设置失败");
|
||||
});
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.wxchannels-setting {
|
||||
position: relative;
|
||||
min-height: 320px;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user