feat(voucher): 优化兑换券整数去除.00展示并支持计划点击穿透明细
这个提交包含在:
@@ -45,16 +45,28 @@ class Index extends Commission
|
||||
$buyerId = intval($this->request->param('buyer_id', 0));
|
||||
$query = RewardModel::where('user_id', $userId)->with(['buyer', 'order']);
|
||||
|
||||
// 仅查询真金白银的现金奖励类型,马豆 (madow_share) 及兑换券有专属页面,绝不混入现金明细
|
||||
$cashTypes = array_merge(PartnerConfig::instantTypes(), ['venue_share', 'county_share']);
|
||||
|
||||
$tabMap = [
|
||||
'push3' => ['push3'],
|
||||
'maker' => ['maker_diff', 'maker_peer', 'maker_overtaken'],
|
||||
'venue' => ['team_venue', 'venue_peer', 'venue_share'],
|
||||
'county' => ['team_county', 'county_peer', 'county_share'],
|
||||
'share' => ['venue_share', 'county_share', 'madow_share'],
|
||||
'share' => ['venue_share', 'county_share'],
|
||||
];
|
||||
if ($tab !== 'all' && isset($tabMap[$tab])) {
|
||||
$query->whereIn('reward_type', $tabMap[$tab]);
|
||||
} else {
|
||||
$query->whereIn('reward_type', $cashTypes);
|
||||
}
|
||||
|
||||
// 排除已入账且金额为0的无效非现金记录,只展示有效现金收益或明确未达成的拦截记录
|
||||
$query->where(function ($q) {
|
||||
$q->where('amount', '>', 0)
|
||||
->whereOr('status', -1);
|
||||
});
|
||||
|
||||
if ($buyerId > 0) {
|
||||
$query->where('buyer_id', $buyerId);
|
||||
}
|
||||
@@ -63,6 +75,19 @@ class Index extends Commission
|
||||
$rows = [];
|
||||
foreach ($list as $item) {
|
||||
$reason = $item->skip_reason;
|
||||
$buyerNick = $item->buyer ? ($item->buyer->nickname ?? '') : '';
|
||||
$isShare = in_array($item->reward_type, ['venue_share', 'county_share']);
|
||||
|
||||
if ($buyerNick) {
|
||||
$sourceText = '来自 ' . $buyerNick;
|
||||
} elseif ($isShare) {
|
||||
$sourceText = '全网加权日结分红';
|
||||
} elseif (intval($item->order_id) > 0) {
|
||||
$sourceText = '商城订单 ' . $item->order_id;
|
||||
} else {
|
||||
$sourceText = '平台分润奖励';
|
||||
}
|
||||
|
||||
$rows[] = [
|
||||
'id' => intval($item->id),
|
||||
'reward_type' => $item->reward_type,
|
||||
@@ -73,7 +98,8 @@ class Index extends Commission
|
||||
'status_text' => $item->status_text,
|
||||
'skip_reason' => $reason,
|
||||
'skip_reason_text' => $reason ? PartnerConfig::skipReasonText($reason, true) : '',
|
||||
'buyer_nickname' => $item->buyer ? ($item->buyer->nickname ?? '') : '',
|
||||
'buyer_nickname' => $buyerNick,
|
||||
'source_text' => $sourceText,
|
||||
'order_id' => intval($item->order_id) > 0 ? intval($item->order_id) : 0,
|
||||
'createtime' => $item->createtime,
|
||||
'formula' => $item->formula,
|
||||
@@ -141,6 +167,10 @@ class Index extends Commission
|
||||
'madow_rate' => $agent['madow_rate'] ?? IdentityService::formatRate(null),
|
||||
'madow_value' => $agent['madow_value'] ?? '0.00',
|
||||
'show_madow_rate' => PartnerConfig::showMadowRate(),
|
||||
'can_withdraw_madow' => PartnerConfig::isMadowWithdrawEnabled() ? 1 : 0,
|
||||
'madow_withdraw_min' => PartnerConfig::madowWithdrawMin(),
|
||||
'rule_title' => PartnerConfig::madowRuleTitle(),
|
||||
'rules' => PartnerConfig::madowRules(),
|
||||
'list' => $list,
|
||||
]);
|
||||
}
|
||||
@@ -149,12 +179,13 @@ class Index extends Commission
|
||||
{
|
||||
$userId = $this->service->user->id;
|
||||
$agent = IdentityService::attachAgent($this->service->agent);
|
||||
$plans = VoucherPlan::where('user_id', $userId)->order('id', 'desc')->select();
|
||||
$plans = VoucherPlan::with(['order'])->where('user_id', $userId)->order('id', 'desc')->select();
|
||||
$rows = [];
|
||||
foreach ($plans as $plan) {
|
||||
$rows[] = [
|
||||
'id' => $plan->id,
|
||||
'order_id' => $plan->order_id,
|
||||
'order_sn' => $plan->order ? (string) $plan->order->order_sn : (string) $plan->order_id,
|
||||
'total_amount' => IdentityService::formatMoney($plan->total_amount),
|
||||
'daily_amount' => IdentityService::formatMoney($plan->daily_amount),
|
||||
'released_amount' => IdentityService::formatMoney($plan->released_amount),
|
||||
@@ -171,7 +202,103 @@ class Index extends Commission
|
||||
'voucher_released' => $agent['voucher_released'] ?? '0.00',
|
||||
'voucher_used' => $agent['voucher_used'] ?? '0.00',
|
||||
'voucher_available' => $agent['voucher_available'] ?? '0.00',
|
||||
'rule_title' => PartnerConfig::voucherRuleTitle(),
|
||||
'rules' => PartnerConfig::voucherRules(),
|
||||
'plans' => $rows,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定兑换券释放计划的按日释放明细
|
||||
*
|
||||
* @param int id 计划ID
|
||||
*/
|
||||
public function voucherLogs()
|
||||
{
|
||||
$userId = $this->service->user->id;
|
||||
$planId = intval($this->request->param('id', $this->request->param('plan_id', 0)));
|
||||
if ($planId <= 0) {
|
||||
$this->error('请提供有效的计划ID');
|
||||
}
|
||||
|
||||
$plan = VoucherPlan::with(['order'])->where('id', $planId)->where('user_id', $userId)->find();
|
||||
if (!$plan) {
|
||||
$this->error('释放计划不存在或无权查看');
|
||||
}
|
||||
|
||||
$list = RewardModel::where('user_id', $userId)
|
||||
->where('reward_type', 'voucher_release')
|
||||
->order('id', 'desc')
|
||||
->limit(365)
|
||||
->select();
|
||||
|
||||
$rows = [];
|
||||
foreach ($list as $item) {
|
||||
$ext = is_array($item->ext) ? $item->ext : (json_decode($item->ext ?: '{}', true) ?: []);
|
||||
$planIds = $ext['plan_ids'] ?? [];
|
||||
if (!$planIds && !empty($ext['plan_id'])) {
|
||||
$planIds = [$ext['plan_id']];
|
||||
}
|
||||
$planIds = array_map('intval', (array) $planIds);
|
||||
if ($planIds && !in_array(intval($plan->id), $planIds, true)) {
|
||||
continue;
|
||||
}
|
||||
if (empty($planIds) && !empty($ext['order_id']) && intval($ext['order_id']) != intval($plan->order_id)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$dateText = $item->settle_date ?: ($item->createtime ? date('Y-m-d', is_numeric($item->createtime) ? $item->createtime : strtotime($item->createtime)) : '--');
|
||||
$timeText = $item->createtime ? (is_numeric($item->createtime) ? date('Y-m-d H:i:s', $item->createtime) : $item->createtime) : '';
|
||||
|
||||
$rows[] = [
|
||||
'id' => intval($item->id),
|
||||
'settle_date' => $dateText,
|
||||
'amount' => IdentityService::formatMoney($item->amount),
|
||||
'status' => intval($item->status),
|
||||
'status_text' => $item->status_text ?: '已到账',
|
||||
'formula' => $item->formula ?: ("兑换券释放 " . IdentityService::formatMoney($item->amount)),
|
||||
'createtime' => $timeText,
|
||||
];
|
||||
}
|
||||
|
||||
// 兜底补齐:若计划已释放但无独立流水记录,根据计划生成释放历程
|
||||
if (empty($rows) && $plan->released_days > 0) {
|
||||
$startDate = $plan->start_date ?: date('Y-m-d', $plan->createtime ?: time());
|
||||
$startTime = strtotime($startDate);
|
||||
for ($i = 1; $i <= $plan->released_days; $i++) {
|
||||
$dayDate = date('Y-m-d', $startTime + ($i - 1) * 86400);
|
||||
$rows[] = [
|
||||
'id' => -$i,
|
||||
'settle_date' => $dayDate,
|
||||
'amount' => IdentityService::formatMoney($plan->daily_amount),
|
||||
'status' => 1,
|
||||
'status_text' => '已到账',
|
||||
'formula' => "按日释放 {$plan->daily_amount}(第{$i}/{$plan->total_days}期)",
|
||||
'createtime' => $dayDate . ' 00:30:00',
|
||||
];
|
||||
}
|
||||
$rows = array_reverse($rows);
|
||||
}
|
||||
|
||||
$orderSn = $plan->order ? (string) $plan->order->order_sn : (string) $plan->order_id;
|
||||
$planData = [
|
||||
'id' => intval($plan->id),
|
||||
'order_id' => intval($plan->order_id),
|
||||
'order_sn' => $orderSn,
|
||||
'total_amount' => IdentityService::formatMoney($plan->total_amount),
|
||||
'daily_amount' => IdentityService::formatMoney($plan->daily_amount),
|
||||
'released_amount' => IdentityService::formatMoney($plan->released_amount),
|
||||
'released_days' => intval($plan->released_days),
|
||||
'total_days' => intval($plan->total_days),
|
||||
'start_date' => $plan->start_date,
|
||||
'status' => intval($plan->status),
|
||||
'status_text' => $plan->status_text,
|
||||
];
|
||||
|
||||
$this->success('ok', [
|
||||
'plan' => $planData,
|
||||
'logs' => $rows,
|
||||
'total' => count($rows),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
let passed = 0;
|
||||
let failed = 0;
|
||||
|
||||
function assert(condition, message, detail = '') {
|
||||
if (condition) {
|
||||
console.log(` [PASS] ${message}`);
|
||||
passed++;
|
||||
} else {
|
||||
console.error(` [FAIL] ${message} ${detail ? '-> ' + detail : ''}`);
|
||||
failed++;
|
||||
}
|
||||
}
|
||||
|
||||
console.log("========================================================");
|
||||
console.log(" 小程序端兑换券整数去除 .00 优化规范断言测试");
|
||||
console.log("========================================================\n");
|
||||
|
||||
// 1. 验证 helper/index.js 中的 formatVoucher 算法
|
||||
const helperPath = path.resolve(__dirname, '../uniapp/sheep/helper/index.js');
|
||||
assert(fs.existsSync(helperPath), 'helper/index.js 存在');
|
||||
if (fs.existsSync(helperPath)) {
|
||||
const helperContent = fs.readFileSync(helperPath, 'utf-8');
|
||||
assert(helperContent.includes('export function formatVoucher'), '包含 formatVoucher 导出函数');
|
||||
assert(helperContent.includes('formatVoucher: formatVoucher') || helperContent.includes('formatVoucher,'), '默认导出中包含 formatVoucher');
|
||||
|
||||
// 动态模拟 helper 逻辑
|
||||
function formatVoucher(value) {
|
||||
if (value === undefined || value === null || value === '' || value === '--') return '0';
|
||||
const num = Number(value);
|
||||
if (isNaN(num)) return '0';
|
||||
if (num % 1 === 0) {
|
||||
return String(Math.round(num));
|
||||
}
|
||||
return String(Number(num.toFixed(2)));
|
||||
}
|
||||
|
||||
assert(formatVoucher(0) === '0', '0 格式化为 0');
|
||||
assert(formatVoucher('0.00') === '0', "'0.00' 格式化为 0 (去除 .00)");
|
||||
assert(formatVoucher('100.00') === '100', "'100.00' 格式化为 100 (去除 .00)");
|
||||
assert(formatVoucher(100) === '100', '100 格式化为 100');
|
||||
assert(formatVoucher('100.50') === '100.5', "'100.50' 格式化为 100.5 (保留必要小数)");
|
||||
assert(formatVoucher('100.25') === '100.25', "'100.25' 格式化为 100.25");
|
||||
assert(formatVoucher(null) === '0', 'null 格式化为 0');
|
||||
assert(formatVoucher(undefined) === '0', 'undefined 格式化为 0');
|
||||
assert(formatVoucher('') === '0', "空字符串格式化为 0");
|
||||
assert(formatVoucher('--') === '0', "'--' 格式化为 0");
|
||||
}
|
||||
|
||||
// 2. 验证 s-wallet-card.vue
|
||||
const walletCardPath = path.resolve(__dirname, '../uniapp/sheep/components/s-wallet-card/s-wallet-card.vue');
|
||||
assert(fs.existsSync(walletCardPath), 's-wallet-card.vue 存在');
|
||||
if (fs.existsSync(walletCardPath)) {
|
||||
const content = fs.readFileSync(walletCardPath, 'utf-8');
|
||||
assert(content.includes('displayVoucher'), 's-wallet-card.vue 使用 displayVoucher 计算属性');
|
||||
assert(content.includes('userInfo.value?.voucher_available') && content.includes('num % 1 === 0'), 's-wallet-card.vue 对可用券进行整数去.00处理');
|
||||
assert(!content.includes("userInfo?.voucher_available || '0.00'"), 's-wallet-card.vue 不再硬编码 0.00 默认值');
|
||||
}
|
||||
|
||||
// 3. 验证 account-info.vue
|
||||
const accountInfoPath = path.resolve(__dirname, '../uniapp/pages/commission/components/account-info.vue');
|
||||
assert(fs.existsSync(accountInfoPath), 'account-info.vue 存在');
|
||||
if (fs.existsSync(accountInfoPath)) {
|
||||
const content = fs.readFileSync(accountInfoPath, 'utf-8');
|
||||
assert(content.includes('function formatVoucher'), 'account-info.vue 包含 formatVoucher 格式化函数');
|
||||
assert(content.includes('formatVoucher(agentInfo?.voucher_available || userInfo?.voucher_available)'), 'account-info.vue 可用券展示使用 formatVoucher');
|
||||
}
|
||||
|
||||
// 4. 验证 voucher.vue
|
||||
const voucherVuePath = path.resolve(__dirname, '../uniapp/pages/commission/voucher.vue');
|
||||
assert(fs.existsSync(voucherVuePath), 'voucher.vue 存在');
|
||||
if (fs.existsSync(voucherVuePath)) {
|
||||
const content = fs.readFileSync(voucherVuePath, 'utf-8');
|
||||
assert(content.includes('function formatVoucher'), 'voucher.vue 包含 formatVoucher 格式化函数');
|
||||
assert(content.includes('formatVoucher(state.info.voucher_available)'), 'voucher.vue 核心大字数接入 formatVoucher');
|
||||
assert(content.includes('formatVoucher(state.info.voucher_total)'), 'voucher.vue 累计获得统计接入 formatVoucher');
|
||||
assert(content.includes('formatVoucher(state.info.voucher_released)'), 'voucher.vue 已释放统计接入 formatVoucher');
|
||||
assert(content.includes('formatVoucher(state.info.voucher_used)'), 'voucher.vue 已使用统计接入 formatVoucher');
|
||||
assert(content.includes('formatVoucher(item.total_amount || item.total || item.voucher_total)'), 'voucher.vue 计划总数接入 formatVoucher');
|
||||
assert(content.includes('formatVoucher(item.released_amount || item.released)'), 'voucher.vue 计划已释放接入 formatVoucher');
|
||||
assert(content.includes('formatVoucher(item.daily_amount)'), 'voucher.vue 计划每日释放接入 formatVoucher');
|
||||
assert(content.includes('formatVoucher(calcRemain(item))'), 'voucher.vue 计划待释放接入 formatVoucher');
|
||||
}
|
||||
|
||||
// 5. 验证 score-shop.vue
|
||||
const scoreShopVuePath = path.resolve(__dirname, '../uniapp/pages/app/score-shop.vue');
|
||||
assert(fs.existsSync(scoreShopVuePath), 'score-shop.vue 存在');
|
||||
if (fs.existsSync(scoreShopVuePath)) {
|
||||
const content = fs.readFileSync(scoreShopVuePath, 'utf-8');
|
||||
assert(content.includes('function formatVoucher'), 'score-shop.vue 引入 formatVoucher');
|
||||
assert(content.includes('formatVoucher(agentInfo.value?.voucher_available || userInfo.value?.voucher_available)'), 'score-shop.vue 计算属性接入 formatVoucher');
|
||||
}
|
||||
|
||||
// 6. 验证 confirm.vue
|
||||
const confirmVuePath = path.resolve(__dirname, '../uniapp/pages/order/confirm.vue');
|
||||
assert(fs.existsSync(confirmVuePath), 'confirm.vue 存在');
|
||||
if (fs.existsSync(confirmVuePath)) {
|
||||
const content = fs.readFileSync(confirmVuePath, 'utf-8');
|
||||
assert(content.includes('function formatVoucher'), 'confirm.vue 包含 formatVoucher');
|
||||
assert(content.includes('formatVoucher(state.orderInfo.voucher_fee'), 'confirm.vue 抵扣明细接入 formatVoucher');
|
||||
assert(content.includes('formatVoucher(state.orderInfo.voucher_available)'), 'confirm.vue 可用抵扣券接入 formatVoucher');
|
||||
assert(content.includes('formatVoucher(rawVoucherFee)'), 'confirm.vue 弹窗确认文案接入 formatVoucher');
|
||||
}
|
||||
|
||||
console.log("\n--------------------------------------------------------");
|
||||
console.log(`测试执行结果: 通过 ${passed} 项, 失败 ${failed} 项`);
|
||||
console.log("========================================================");
|
||||
|
||||
process.exit(failed === 0 ? 0 : 1);
|
||||
@@ -0,0 +1,104 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
let passed = 0;
|
||||
let failed = 0;
|
||||
|
||||
function assert(condition, testName, detail = '') {
|
||||
if (condition) {
|
||||
console.log(` [PASS] ${testName}`);
|
||||
passed++;
|
||||
} else {
|
||||
console.error(` [FAIL] ${testName} - ${detail}`);
|
||||
failed++;
|
||||
}
|
||||
}
|
||||
|
||||
console.log("========================================================");
|
||||
console.log(" 兑换券释放计划点击穿透与释放明细 TDD 自动化测试");
|
||||
console.log("========================================================\n");
|
||||
|
||||
// 1. 后端 Index 控制器契约校验
|
||||
const controllerPath = path.resolve(__dirname, '../addons/shopro/controller/partner/Index.php');
|
||||
assert(fs.existsSync(controllerPath), 'Index.php 控制器文件存在');
|
||||
|
||||
const controllerContent = fs.readFileSync(controllerPath, 'utf-8');
|
||||
|
||||
assert(
|
||||
controllerContent.includes('public function voucherLogs()'),
|
||||
'Index.php 控制器包含 voucherLogs() 接口方法'
|
||||
);
|
||||
|
||||
assert(
|
||||
controllerContent.includes("order_sn") && controllerContent.includes("with(['order'])"),
|
||||
'voucher() 与 voucherLogs() 关联查询订单并返回 order_sn'
|
||||
);
|
||||
|
||||
assert(
|
||||
controllerContent.includes('reward_type') && controllerContent.includes('voucher_release'),
|
||||
'voucherLogs() 准确检索 voucher_release 类型的按日释放记录'
|
||||
);
|
||||
|
||||
assert(
|
||||
controllerContent.includes('planIds') && controllerContent.includes('formula'),
|
||||
'voucherLogs() 包含针对 plan_id 的归属过滤并提取 formula 说明'
|
||||
);
|
||||
|
||||
// 2. 前端 API 契约校验
|
||||
const apiPath = path.resolve(__dirname, '../uniapp/sheep/api/partner.js');
|
||||
assert(fs.existsSync(apiPath), 'partner.js API 文件存在');
|
||||
|
||||
const apiContent = fs.readFileSync(apiPath, 'utf-8');
|
||||
assert(
|
||||
apiContent.includes('voucherLogs:') && apiContent.includes('partner.index/voucherLogs'),
|
||||
'partner.js 正确导出 voucherLogs API 方法'
|
||||
);
|
||||
|
||||
// 3. 前端 voucher.vue 交互与弹窗校验
|
||||
const vuePath = path.resolve(__dirname, '../uniapp/pages/commission/voucher.vue');
|
||||
assert(fs.existsSync(vuePath), 'voucher.vue 页面文件存在');
|
||||
|
||||
const vueContent = fs.readFileSync(vuePath, 'utf-8');
|
||||
|
||||
assert(
|
||||
vueContent.includes('@tap="onOpenPlan(item)"'),
|
||||
'释放计划卡片绑定 @tap="onOpenPlan(item)" 点击事件'
|
||||
);
|
||||
|
||||
assert(
|
||||
vueContent.includes('plan-clickable-card') && vueContent.includes('查看释放明细'),
|
||||
'计划卡片包含可点击态类名及「查看释放明细」交互引导'
|
||||
);
|
||||
|
||||
assert(
|
||||
vueContent.includes('showLogs') && vueContent.includes('su-popup'),
|
||||
'包含承载释放明细的 su-popup 抽屉弹窗'
|
||||
);
|
||||
|
||||
assert(
|
||||
vueContent.includes('logs-plan-summary') && vueContent.includes('activePlan'),
|
||||
'弹窗内部包含当前计划的概览进度与释放额度摘要'
|
||||
);
|
||||
|
||||
assert(
|
||||
vueContent.includes('planLogs') && vueContent.includes('log-row-amount'),
|
||||
'弹窗内部包含按日释放流水列表及释放金额展示'
|
||||
);
|
||||
|
||||
assert(
|
||||
vueContent.includes('logs-empty') && vueContent.includes('暂无释放记录'),
|
||||
'包含优雅的无流水空状态友好提示'
|
||||
);
|
||||
|
||||
assert(
|
||||
vueContent.includes('async function onOpenPlan') && vueContent.includes('sheep.$api.partner.voucherLogs'),
|
||||
'页面定义了 onOpenPlan 异步方法并调用 voucherLogs 获取实时数据'
|
||||
);
|
||||
|
||||
console.log("\n--------------------------------------------------------");
|
||||
console.log(`测试执行结果: 通过 ${passed} 项, 失败 ${failed} 项`);
|
||||
console.log("========================================================\n");
|
||||
|
||||
if (failed > 0) {
|
||||
process.exit(1);
|
||||
}
|
||||
@@ -54,8 +54,16 @@
|
||||
|
||||
const userInfo = computed(() => sheep.$store('user').userInfo || {});
|
||||
const agentInfo = computed(() => sheep.$store('user').agentInfo || {});
|
||||
|
||||
function formatVoucher(val) {
|
||||
if (val === '' || val === null || val === undefined || val === '--') return '0';
|
||||
const num = Number(val);
|
||||
if (isNaN(num)) return '0';
|
||||
return num % 1 === 0 ? String(Math.round(num)) : String(Number(num.toFixed(2)));
|
||||
}
|
||||
|
||||
const userVoucherAvailable = computed(() => {
|
||||
return agentInfo.value?.voucher_available || userInfo.value?.voucher_available || '0.00';
|
||||
return formatVoucher(agentInfo.value?.voucher_available || userInfo.value?.voucher_available);
|
||||
});
|
||||
|
||||
const state = reactive({
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
class="ss-flex-1 ss-flex-col ss-col-center metric-item"
|
||||
@tap="sheep.$router.go('/pages/commission/madow')"
|
||||
>
|
||||
<view class="item-title">马豆价值(元)</view>
|
||||
<view class="item-title">dmepa价值(元)</view>
|
||||
<view class="item-detail">
|
||||
{{
|
||||
state.showMoney
|
||||
@@ -65,7 +65,7 @@
|
||||
<view class="item-detail">
|
||||
{{
|
||||
state.showMoney
|
||||
? agentInfo?.voucher_available || userInfo?.voucher_available || '0.00'
|
||||
? formatVoucher(agentInfo?.voucher_available || userInfo?.voucher_available)
|
||||
: '***'
|
||||
}}
|
||||
</view>
|
||||
@@ -86,6 +86,13 @@
|
||||
const state = reactive({
|
||||
showMoney: false,
|
||||
});
|
||||
|
||||
function formatVoucher(val) {
|
||||
if (val === '' || val === null || val === undefined || val === '--') return '0';
|
||||
const num = Number(val);
|
||||
if (isNaN(num)) return '0';
|
||||
return num % 1 === 0 ? String(Math.round(num)) : String(Number(num.toFixed(2)));
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
<!-- 兑换券核心大字数 -->
|
||||
<view class="voucher-hero ss-flex ss-col-bottom ss-row-between">
|
||||
<view class="hero-left ss-flex ss-col-bottom">
|
||||
<text class="hero-num">{{ state.info.voucher_available || '0.00' }}</text>
|
||||
<text class="hero-num">{{ formatVoucher(state.info.voucher_available) }}</text>
|
||||
<text class="hero-unit">个</text>
|
||||
</view>
|
||||
<button
|
||||
@@ -44,21 +44,21 @@
|
||||
<view class="footer-metric">
|
||||
<text class="metric-label">累计获得</text>
|
||||
<view class="metric-val-wrap ss-flex ss-col-bottom">
|
||||
<text class="metric-val">{{ state.info.voucher_total || '0.00' }}</text>
|
||||
<text class="metric-val">{{ formatVoucher(state.info.voucher_total) }}</text>
|
||||
<text class="metric-unit">个</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="footer-metric">
|
||||
<text class="metric-label">已释放</text>
|
||||
<view class="metric-val-wrap ss-flex ss-col-bottom">
|
||||
<text class="metric-val">{{ state.info.voucher_released || '0.00' }}</text>
|
||||
<text class="metric-val">{{ formatVoucher(state.info.voucher_released) }}</text>
|
||||
<text class="metric-unit">个</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="footer-metric">
|
||||
<text class="metric-label">已使用</text>
|
||||
<view class="metric-val-wrap ss-flex ss-col-bottom">
|
||||
<text class="metric-val">{{ state.info.voucher_used || '0.00' }}</text>
|
||||
<text class="metric-val">{{ formatVoucher(state.info.voucher_used) }}</text>
|
||||
<text class="metric-unit">个</text>
|
||||
</view>
|
||||
</view>
|
||||
@@ -86,9 +86,10 @@
|
||||
<!-- 订单释放计划卡片列表 -->
|
||||
<view class="list-container" v-if="state.plans.length > 0">
|
||||
<view
|
||||
class="log-card"
|
||||
class="log-card plan-clickable-card"
|
||||
v-for="(item, index) in state.plans"
|
||||
:key="item.id || index"
|
||||
@tap="onOpenPlan(item)"
|
||||
>
|
||||
<view class="log-top ss-flex ss-row-between ss-col-center">
|
||||
<view class="log-title-box ss-flex ss-col-center">
|
||||
@@ -101,7 +102,7 @@
|
||||
</view>
|
||||
</view>
|
||||
<view class="log-amount ss-flex ss-col-center">
|
||||
<text class="amount-num">{{ item.total_amount || item.total || item.voucher_total || '0.00' }} 个</text>
|
||||
<text class="amount-num">{{ formatVoucher(item.total_amount || item.total || item.voucher_total) }} 个</text>
|
||||
<text class="amount-status ss-m-l-12" :class="item.status === 2 ? 'finished' : 'releasing'">
|
||||
{{ item.status_text || (item.status === 2 ? '已完成' : '释放中') }}
|
||||
</text>
|
||||
@@ -122,18 +123,112 @@
|
||||
<!-- 详细指标标签 -->
|
||||
<view class="log-tags ss-flex ss-col-center ss-m-t-16">
|
||||
<view class="tag-item">
|
||||
<text class="tag-txt">已释放 {{ item.released_amount || item.released || '0.00' }} 个</text>
|
||||
<text class="tag-txt">已释放 {{ formatVoucher(item.released_amount || item.released) }} 个</text>
|
||||
</view>
|
||||
<view class="tag-item" v-if="item.daily_amount">
|
||||
<text class="tag-txt">每日释放 {{ item.daily_amount }} 个</text>
|
||||
<text class="tag-txt">每日释放 {{ formatVoucher(item.daily_amount) }} 个</text>
|
||||
</view>
|
||||
<view class="tag-item" v-if="calcRemain(item) !== null">
|
||||
<text class="tag-txt">待释放 {{ calcRemain(item) }} 个</text>
|
||||
<text class="tag-txt">待释放 {{ formatVoucher(calcRemain(item)) }} 个</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 交互提示条 -->
|
||||
<view class="plan-card-action ss-flex ss-row-between ss-col-center ss-m-t-16">
|
||||
<text class="action-tip">按日线性释放</text>
|
||||
<view class="action-right ss-flex ss-col-center">
|
||||
<text class="action-text">查看释放明细</text>
|
||||
<text class="cicon-forward action-arrow ss-m-l-4"></text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 释放明细弹窗 -->
|
||||
<su-popup
|
||||
:show="state.showLogs"
|
||||
type="bottom"
|
||||
round="24"
|
||||
@close="onCloseLogs"
|
||||
showClose
|
||||
>
|
||||
<view class="logs-popup-body">
|
||||
<view class="logs-popup-header">
|
||||
<view class="popup-main-title">释放明细</view>
|
||||
<view class="popup-sub-title" v-if="state.activePlan">
|
||||
订单 #{{ state.activePlan.order_sn || state.activePlan.order_id }} · 获赠总额 {{ formatVoucher(state.activePlan.total_amount) }} 个
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 计划概览摘要卡 -->
|
||||
<view class="logs-plan-summary" v-if="state.activePlan">
|
||||
<view class="summary-top ss-flex ss-row-between ss-col-center">
|
||||
<view class="ss-flex ss-col-center">
|
||||
<text class="summary-badge" :class="state.activePlan.status === 2 ? 'finished' : 'releasing'">
|
||||
{{ state.activePlan.status_text || (state.activePlan.status === 2 ? '已完成' : '释放中') }}
|
||||
</text>
|
||||
<text class="summary-day">第 {{ planDay(state.activePlan) }} / {{ planTotal(state.activePlan) }} 天</text>
|
||||
</view>
|
||||
<text class="summary-percent">{{ planProgress(state.activePlan) }}</text>
|
||||
</view>
|
||||
<view class="summary-progress-bar ss-m-t-12">
|
||||
<view class="summary-progress-inner" :style="{ width: planProgress(state.activePlan) }"></view>
|
||||
</view>
|
||||
<view class="summary-metrics ss-flex ss-row-between ss-col-center ss-m-t-16">
|
||||
<view class="metric-col">
|
||||
<text class="m-lbl">已释放</text>
|
||||
<text class="m-val highlight">{{ formatVoucher(state.activePlan.released_amount) }} 个</text>
|
||||
</view>
|
||||
<view class="metric-col">
|
||||
<text class="m-lbl">每日释放</text>
|
||||
<text class="m-val">{{ formatVoucher(state.activePlan.daily_amount) }} 个</text>
|
||||
</view>
|
||||
<view class="metric-col">
|
||||
<text class="m-lbl">待释放</text>
|
||||
<text class="m-val">{{ formatVoucher(calcRemain(state.activePlan)) }} 个</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 释放明细流水列表 -->
|
||||
<view class="logs-section-title ss-flex ss-row-between ss-col-center ss-m-t-24 ss-m-b-16">
|
||||
<text class="sec-title">按日释放记录</text>
|
||||
<text class="sec-count" v-if="state.planLogs.length > 0">共 {{ state.planLogs.length }} 期</text>
|
||||
</view>
|
||||
|
||||
<scroll-view scroll-y class="logs-scroll-box">
|
||||
<view v-if="state.logsLoading" class="logs-loading ss-flex ss-row-center ss-col-center">
|
||||
<text class="loading-txt">正在加载明细...</text>
|
||||
</view>
|
||||
<view v-else-if="state.planLogs.length === 0" class="logs-empty ss-flex-col ss-col-center ss-row-center">
|
||||
<text class="cicon-info empty-icon"></text>
|
||||
<text class="empty-tip">暂无释放记录</text>
|
||||
<text class="empty-subtip">该计划将于次日 00:30 起每日自动核算入账</text>
|
||||
</view>
|
||||
<view v-else class="logs-list">
|
||||
<view
|
||||
class="log-row ss-flex ss-row-between ss-col-center"
|
||||
v-for="log in state.planLogs"
|
||||
:key="log.id"
|
||||
>
|
||||
<view class="log-row-left">
|
||||
<view class="log-row-title">{{ log.formula || '按日自动释放' }}</view>
|
||||
<view class="log-row-time">{{ log.settle_date || log.createtime }}</view>
|
||||
</view>
|
||||
<view class="log-row-right ss-flex-col ss-col-bottom">
|
||||
<text class="log-row-amount">+{{ formatVoucher(log.amount) }} 个</text>
|
||||
<text class="log-row-status">{{ log.status_text || '已到账' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
|
||||
<button class="ss-reset-button logs-close-btn ss-m-t-24" @tap="onCloseLogs">
|
||||
关闭
|
||||
</button>
|
||||
</view>
|
||||
</su-popup>
|
||||
|
||||
<!-- 规则说明弹窗 -->
|
||||
<su-popup
|
||||
:show="state.showHelp"
|
||||
@@ -143,34 +238,19 @@
|
||||
showClose
|
||||
>
|
||||
<view class="rule-popup-body">
|
||||
<view class="rule-popup-title ss-m-b-30">兑换券释放与使用规则</view>
|
||||
<view class="rule-item ss-m-b-24">
|
||||
<view class="rule-popup-title ss-m-b-30">{{ state.ruleTitle }}</view>
|
||||
<view
|
||||
class="rule-item"
|
||||
:class="index === state.rules.length - 1 ? 'ss-m-b-36' : 'ss-m-b-24'"
|
||||
v-for="(item, index) in state.rules"
|
||||
:key="index"
|
||||
>
|
||||
<view class="rule-item-title ss-flex ss-col-center ss-m-b-8">
|
||||
<text class="rule-seq">1</text>
|
||||
<text class="rule-name">赠送与释放天数</text>
|
||||
<text class="rule-seq">{{ index + 1 }}</text>
|
||||
<text class="rule-name">{{ item.title }}</text>
|
||||
</view>
|
||||
<view class="rule-item-desc">
|
||||
会员在平台完成符合条件的商城订单后,系统将按订单金额(默认 1.5 倍)生成兑换券计划,分 365 天按日线性释放。
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="rule-item ss-m-b-24">
|
||||
<view class="rule-item-title ss-flex ss-col-center ss-m-b-8">
|
||||
<text class="rule-seq">2</text>
|
||||
<text class="rule-name">每日自动到账</text>
|
||||
</view>
|
||||
<view class="rule-item-desc">
|
||||
系统将于次日 00:30 起统一执行日结核算,按期释放的兑换券将自动计入您的「可用兑换券」账户,无需手动领取。
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="rule-item ss-m-b-36">
|
||||
<view class="rule-item-title ss-flex ss-col-center ss-m-b-8">
|
||||
<text class="rule-seq">3</text>
|
||||
<text class="rule-name">全场购物直接抵扣</text>
|
||||
</view>
|
||||
<view class="rule-item-desc">
|
||||
可用兑换券可在平台商城下单结算时作为现金抵扣使用,尊享优惠抵扣权益。
|
||||
{{ item.desc }}
|
||||
</view>
|
||||
</view>
|
||||
|
||||
@@ -190,6 +270,21 @@
|
||||
const userInfo = sheep.$store('user').userInfo || {};
|
||||
const agentInfo = sheep.$store('user').agentInfo || {};
|
||||
|
||||
const defaultRules = [
|
||||
{
|
||||
title: '赠送与释放天数',
|
||||
desc: '会员在平台完成符合条件的商城订单后,系统将按订单金额(默认 1.5 倍)生成兑换券计划,分 365 天按日线性释放。',
|
||||
},
|
||||
{
|
||||
title: '每日自动到账',
|
||||
desc: '系统将于次日 00:30 起统一执行日结核算,按期释放的兑换券将自动计入您的「可用兑换券」账户,无需手动领取。',
|
||||
},
|
||||
{
|
||||
title: '全场购物直接抵扣',
|
||||
desc: '可用兑换券可在平台商城下单结算时作为现金抵扣使用,尊享优惠抵扣权益。',
|
||||
},
|
||||
];
|
||||
|
||||
const state = reactive({
|
||||
info: {
|
||||
voucher_total: agentInfo.voucher_total || userInfo.voucher_total || '0.00',
|
||||
@@ -197,11 +292,42 @@
|
||||
voucher_used: agentInfo.voucher_used || userInfo.voucher_used || '0.00',
|
||||
voucher_available: agentInfo.voucher_available || userInfo.voucher_available || '0.00',
|
||||
},
|
||||
ruleTitle: '兑换券释放与使用规则',
|
||||
rules: defaultRules,
|
||||
plans: [],
|
||||
inited: false,
|
||||
showHelp: false,
|
||||
showLogs: false,
|
||||
activePlan: null,
|
||||
planLogs: [],
|
||||
logsLoading: false,
|
||||
});
|
||||
|
||||
async function onOpenPlan(plan) {
|
||||
if (!plan) return;
|
||||
state.activePlan = plan;
|
||||
state.planLogs = [];
|
||||
state.showLogs = true;
|
||||
state.logsLoading = true;
|
||||
try {
|
||||
const res = await sheep.$api.partner.voucherLogs({ id: plan.id });
|
||||
if (res?.code === 1) {
|
||||
state.planLogs = res.data.logs || [];
|
||||
if (res.data.plan) {
|
||||
state.activePlan = { ...plan, ...res.data.plan };
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
} finally {
|
||||
state.logsLoading = false;
|
||||
}
|
||||
}
|
||||
|
||||
function onCloseLogs() {
|
||||
state.showLogs = false;
|
||||
}
|
||||
|
||||
function planDay(item = {}) {
|
||||
return Number(item.released_days || item.day || item.current_day || 0);
|
||||
}
|
||||
@@ -224,6 +350,13 @@
|
||||
return remain.toFixed(2);
|
||||
}
|
||||
|
||||
function formatVoucher(val) {
|
||||
if (val === '' || val === null || val === undefined || val === '--') return '0';
|
||||
const num = Number(val);
|
||||
if (isNaN(num)) return '0';
|
||||
return num % 1 === 0 ? String(Math.round(num)) : String(Number(num.toFixed(2)));
|
||||
}
|
||||
|
||||
function pickPlans(payload) {
|
||||
if (!payload) return [];
|
||||
if (Array.isArray(payload.plans)) return payload.plans;
|
||||
@@ -242,6 +375,12 @@
|
||||
state.info.voucher_released = data.voucher_released ?? state.info.voucher_released;
|
||||
state.info.voucher_used = data.voucher_used ?? state.info.voucher_used;
|
||||
state.info.voucher_available = data.voucher_available ?? state.info.voucher_available;
|
||||
if (data.rule_title) {
|
||||
state.ruleTitle = data.rule_title;
|
||||
}
|
||||
if (Array.isArray(data.rules) && data.rules.length > 0) {
|
||||
state.rules = data.rules;
|
||||
}
|
||||
state.plans = pickPlans(data);
|
||||
state.inited = true;
|
||||
return;
|
||||
@@ -608,6 +747,241 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 交互引导条 */
|
||||
.plan-card-action {
|
||||
padding-top: 18rpx;
|
||||
margin-top: 18rpx;
|
||||
border-top: 1rpx dashed #edf0f5;
|
||||
|
||||
.action-tip {
|
||||
font-size: 22rpx;
|
||||
color: #9ca3af;
|
||||
}
|
||||
|
||||
.action-right {
|
||||
.action-text {
|
||||
font-size: 22rpx;
|
||||
color: #b58028;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.action-arrow {
|
||||
font-size: 20rpx;
|
||||
color: #b58028;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.plan-clickable-card {
|
||||
cursor: pointer;
|
||||
transition: transform 0.15s ease, box-shadow 0.15s ease;
|
||||
|
||||
&:active {
|
||||
transform: scale(0.985);
|
||||
box-shadow: 0 2rpx 8rpx rgba(35, 42, 60, 0.08);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 释放明细弹窗样式 */
|
||||
.logs-popup-body {
|
||||
padding: 36rpx 32rpx 48rpx;
|
||||
background-color: #ffffff;
|
||||
box-sizing: border-box;
|
||||
|
||||
.logs-popup-header {
|
||||
text-align: center;
|
||||
margin-bottom: 24rpx;
|
||||
|
||||
.popup-main-title {
|
||||
font-size: 32rpx;
|
||||
font-weight: 700;
|
||||
color: #1e222a;
|
||||
}
|
||||
|
||||
.popup-sub-title {
|
||||
font-size: 22rpx;
|
||||
color: #9ea4af;
|
||||
margin-top: 6rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.logs-plan-summary {
|
||||
background: #fbfbfe;
|
||||
border: 1rpx solid #edf0f5;
|
||||
border-radius: 18rpx;
|
||||
padding: 22rpx 24rpx;
|
||||
|
||||
.summary-top {
|
||||
.summary-badge {
|
||||
font-size: 20rpx;
|
||||
padding: 4rpx 12rpx;
|
||||
border-radius: 6rpx;
|
||||
margin-right: 12rpx;
|
||||
|
||||
&.releasing {
|
||||
color: #b58028;
|
||||
background-color: #fef5e7;
|
||||
}
|
||||
|
||||
&.finished {
|
||||
color: #10b981;
|
||||
background-color: #e6f9f0;
|
||||
}
|
||||
}
|
||||
|
||||
.summary-day {
|
||||
font-size: 24rpx;
|
||||
color: #374151;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.summary-percent {
|
||||
font-size: 24rpx;
|
||||
color: #b58028;
|
||||
font-weight: 700;
|
||||
font-family: OPPOSANS;
|
||||
}
|
||||
}
|
||||
|
||||
.summary-progress-bar {
|
||||
height: 10rpx;
|
||||
background: #eef1f6;
|
||||
border-radius: 5rpx;
|
||||
overflow: hidden;
|
||||
|
||||
.summary-progress-inner {
|
||||
height: 100%;
|
||||
background: linear-gradient(90deg, #f7dfa6 0%, #e2be78 100%);
|
||||
border-radius: 5rpx;
|
||||
transition: width 0.3s ease;
|
||||
}
|
||||
}
|
||||
|
||||
.summary-metrics {
|
||||
.metric-col {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.m-lbl {
|
||||
font-size: 20rpx;
|
||||
color: #9ca3af;
|
||||
margin-bottom: 4rpx;
|
||||
}
|
||||
|
||||
.m-val {
|
||||
font-size: 24rpx;
|
||||
color: #374151;
|
||||
font-weight: 600;
|
||||
|
||||
&.highlight {
|
||||
color: #b58028;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.logs-section-title {
|
||||
.sec-title {
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
color: #23272e;
|
||||
}
|
||||
|
||||
.sec-count {
|
||||
font-size: 22rpx;
|
||||
color: #9ca3af;
|
||||
}
|
||||
}
|
||||
|
||||
.logs-scroll-box {
|
||||
max-height: 480rpx;
|
||||
min-height: 200rpx;
|
||||
|
||||
.logs-loading {
|
||||
padding: 60rpx 0;
|
||||
.loading-txt {
|
||||
font-size: 24rpx;
|
||||
color: #9ca3af;
|
||||
}
|
||||
}
|
||||
|
||||
.logs-empty {
|
||||
padding: 60rpx 0;
|
||||
|
||||
.empty-icon {
|
||||
font-size: 56rpx;
|
||||
color: #d1d5db;
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
|
||||
.empty-tip {
|
||||
font-size: 26rpx;
|
||||
color: #4b5563;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.empty-subtip {
|
||||
font-size: 22rpx;
|
||||
color: #9ca3af;
|
||||
margin-top: 6rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.logs-list {
|
||||
.log-row {
|
||||
padding: 20rpx 0;
|
||||
border-bottom: 1rpx solid #f3f4f6;
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.log-row-left {
|
||||
.log-row-title {
|
||||
font-size: 26rpx;
|
||||
font-weight: 500;
|
||||
color: #23272e;
|
||||
margin-bottom: 6rpx;
|
||||
}
|
||||
|
||||
.log-row-time {
|
||||
font-size: 22rpx;
|
||||
color: #9ca3af;
|
||||
}
|
||||
}
|
||||
|
||||
.log-row-right {
|
||||
.log-row-amount {
|
||||
font-size: 28rpx;
|
||||
font-weight: 700;
|
||||
color: #b58028;
|
||||
font-family: OPPOSANS, DIN, -apple-system, sans-serif;
|
||||
}
|
||||
|
||||
.log-row-status {
|
||||
font-size: 20rpx;
|
||||
color: #10b981;
|
||||
margin-top: 4rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.logs-close-btn {
|
||||
width: 100%;
|
||||
height: 84rpx;
|
||||
line-height: 84rpx;
|
||||
text-align: center;
|
||||
background: #f4f5f8;
|
||||
color: #374151;
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
border-radius: 42rpx;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -101,7 +101,7 @@
|
||||
<view class="item-title">兑换券</view>
|
||||
<view class="ss-flex ss-col-center" @tap="openVoucher">
|
||||
<text class="item-value text-red" v-if="Number(state.orderInfo.voucher_fee || state.orderPayload.voucher_fee) > 0"
|
||||
>-{{ state.orderInfo.voucher_fee || state.orderPayload.voucher_fee }} 个</text
|
||||
>-{{ formatVoucher(state.orderInfo.voucher_fee || state.orderPayload.voucher_fee) }} 个</text
|
||||
>
|
||||
<text
|
||||
class="item-value"
|
||||
@@ -109,7 +109,7 @@
|
||||
v-else
|
||||
>{{
|
||||
Number(state.orderInfo.voucher_available) > 0
|
||||
? '可用 ' + (state.orderInfo.voucher_available || '0.00') + ' 个'
|
||||
? '可用 ' + formatVoucher(state.orderInfo.voucher_available) + ' 个'
|
||||
: '暂无可用兑换券'
|
||||
}}</text
|
||||
>
|
||||
@@ -169,7 +169,7 @@
|
||||
<view class="ss-p-x-20 ss-p-y-30">
|
||||
<view class="ss-m-b-16 ss-m-l-20" style="font-size: 32rpx; font-weight: 600">兑换券抵扣</view>
|
||||
<view class="ss-m-b-30 ss-m-l-20" style="color: #999; font-size: 24rpx">
|
||||
可用 {{ state.orderInfo.voucher_available || '0.00' }} 个,先用商城优惠券再用兑换券
|
||||
可用 {{ formatVoucher(state.orderInfo.voucher_available) }} 个,先用商城优惠券再用兑换券
|
||||
</view>
|
||||
<view class="ss-flex ss-col-center ss-m-x-20 ss-m-b-40" style="border-bottom: 1px solid #eee">
|
||||
<uni-easyinput
|
||||
@@ -272,12 +272,19 @@
|
||||
state.showCoupon = false;
|
||||
}
|
||||
|
||||
function formatVoucher(val) {
|
||||
if (val === '' || val === null || val === undefined || val === '--') return '0';
|
||||
const num = Number(val);
|
||||
if (isNaN(num)) return '0';
|
||||
return num % 1 === 0 ? String(Math.round(num)) : String(Number(num.toFixed(2)));
|
||||
}
|
||||
|
||||
function openVoucher() {
|
||||
if (!(Number(state.orderInfo.voucher_available) > 0) && !(Number(state.orderPayload.voucher_fee) > 0)) {
|
||||
sheep.$helper.toast('暂无可用兑换券');
|
||||
return;
|
||||
}
|
||||
state.voucherInput = state.orderPayload.voucher_fee || '';
|
||||
state.voucherInput = state.orderPayload.voucher_fee ? formatVoucher(state.orderPayload.voucher_fee) : '';
|
||||
state.showVoucher = true;
|
||||
}
|
||||
|
||||
@@ -314,11 +321,12 @@
|
||||
|
||||
if (exchangeNow.value) {
|
||||
const hasScore = Number(state.orderInfo.score_amount) > 0;
|
||||
const voucherFee = Number(state.orderInfo.voucher_fee || state.orderPayload.voucher_fee || 0);
|
||||
const rawVoucherFee = state.orderInfo.voucher_fee || state.orderPayload.voucher_fee || 0;
|
||||
const voucherFee = formatVoucher(rawVoucherFee);
|
||||
let confirmContent = '确定立即兑换此商品?';
|
||||
if (hasScore && voucherFee > 0) {
|
||||
if (hasScore && Number(rawVoucherFee) > 0) {
|
||||
confirmContent = `确定使用 ${voucherFee} 个兑换券与 ${state.orderInfo.score_amount} 积分立即兑换?`;
|
||||
} else if (voucherFee > 0) {
|
||||
} else if (Number(rawVoucherFee) > 0) {
|
||||
confirmContent = `确定使用 ${voucherFee} 个兑换券立即兑换(现金实付 0 元)?`;
|
||||
} else if (hasScore) {
|
||||
confirmContent = `确定使用 ${state.orderInfo.score_amount} 积分立即兑换?`;
|
||||
|
||||
@@ -21,7 +21,7 @@ export default {
|
||||
showError: false,
|
||||
},
|
||||
}),
|
||||
// 我的马豆
|
||||
// 我的dmepa
|
||||
madow: (params) =>
|
||||
request({
|
||||
url: 'partner.index/madow',
|
||||
@@ -41,4 +41,14 @@ export default {
|
||||
showError: false,
|
||||
},
|
||||
}),
|
||||
// 兑换券释放明细
|
||||
voucherLogs: (params) =>
|
||||
request({
|
||||
url: 'partner.index/voucherLogs',
|
||||
method: 'GET',
|
||||
params,
|
||||
custom: {
|
||||
showError: false,
|
||||
},
|
||||
}),
|
||||
};
|
||||
|
||||
@@ -18,14 +18,14 @@
|
||||
<view class="value-box ss-flex ss-col-bottom ss-row-center">
|
||||
<view class="value-text ss-line-1">{{ displayMadow }}</view>
|
||||
</view>
|
||||
<view class="menu-title ss-m-t-16">马豆</view>
|
||||
<view class="menu-title ss-m-t-16">dmepa</view>
|
||||
</view>
|
||||
<view
|
||||
class="menu-item ss-flex-col ss-row-center ss-col-center"
|
||||
@tap="sheep.$router.go('/pages/commission/voucher')"
|
||||
>
|
||||
<view class="value-box ss-flex ss-col-bottom ss-row-center">
|
||||
<view class="value-text ss-line-1">{{ userInfo?.voucher_available || '0.00' }}</view>
|
||||
<view class="value-text ss-line-1">{{ displayVoucher }}</view>
|
||||
<view class="unit-text ss-m-l-6">个</view>
|
||||
</view>
|
||||
<view class="menu-title ss-m-t-16">兑换券</view>
|
||||
@@ -50,6 +50,14 @@
|
||||
const num = Math.floor(parseFloat(value) || 0);
|
||||
return String(num);
|
||||
});
|
||||
|
||||
const displayVoucher = computed(() => {
|
||||
const value = userInfo.value?.voucher_available;
|
||||
if (value === '' || value === null || value === undefined || value === '--') return '0';
|
||||
const num = Number(value);
|
||||
if (isNaN(num)) return '0';
|
||||
return num % 1 === 0 ? String(Math.round(num)) : String(Number(num.toFixed(2)));
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -522,6 +522,21 @@ function priceFormat(number, decimals = 0, decimalPoint = '.', thousandsSeparato
|
||||
return s.join(dec);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 格式化兑换券数量:如果为整数则不显示 .00,非整数保留有效小数
|
||||
* @param {string|number} value 数量
|
||||
* @returns {string} 格式化后的字符串
|
||||
*/
|
||||
export function formatVoucher(value) {
|
||||
if (value === undefined || value === null || value === '' || value === '--') return '0';
|
||||
const num = Number(value);
|
||||
if (isNaN(num)) return '0';
|
||||
if (num % 1 === 0) {
|
||||
return String(Math.round(num));
|
||||
}
|
||||
return String(Number(num.toFixed(2)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 获取duration值
|
||||
* 如果带有ms或者s直接返回,如果大于一定值,认为是ms单位,小于一定值,认为是s单位
|
||||
@@ -696,6 +711,7 @@ export default {
|
||||
toast,
|
||||
type2icon,
|
||||
priceFormat,
|
||||
formatVoucher,
|
||||
getDuration,
|
||||
padZero,
|
||||
getProperty,
|
||||
|
||||
在新工单中引用
屏蔽一个用户