优化
This commit is contained in:
@@ -8,12 +8,19 @@ export default {
|
||||
description_ms: 'Description (Malay)',
|
||||
remark: 'Remark',
|
||||
score: 'Points',
|
||||
score_tip: 'Available points deducted when the user redeems this item.',
|
||||
type: 'Type',
|
||||
'type 1': 'Bonus',
|
||||
'type 2': 'Physical',
|
||||
'type 3': 'Withdraw',
|
||||
amount: 'Cash amount',
|
||||
amount_tip: 'Cash granted on redemption (stored on the order). You may use the suggested value below (points × mall reference ratio) or enter your own.',
|
||||
amount_suggested: 'Suggested cash amount: {amount} ({score} points × ratio {ratio})',
|
||||
amount_apply_suggested: 'Use suggested amount',
|
||||
amount_ratio_missing: 'Mall points-to-cash reference ratio is unavailable; enter cash amount manually.',
|
||||
points_to_cash_ratio_label: 'Reference ratio',
|
||||
multiplier: 'Turnover multiplier',
|
||||
multiplier_tip: 'Turnover requirement sent with bonus/withdraw grant.',
|
||||
category: 'Category',
|
||||
category_title: 'Category title',
|
||||
admin_id: 'Created by',
|
||||
|
||||
@@ -57,6 +57,6 @@ export default {
|
||||
},
|
||||
playxConfig: {
|
||||
title: 'About this page',
|
||||
desc: 'Split into Event and General tabs: event start/end dates (effective next day; no conversion for end-day business data from the day after end), point sources and lifetime batch job; unlock and cash ratios under General.',
|
||||
desc: 'General tab first: daily claim cap ratio. Event tab: dates, point sources, lifetime batch job. Product points and cash face value are set per item in Product management.',
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
export default {
|
||||
tab_event: 'Event settings',
|
||||
tab_regular: 'General settings',
|
||||
tab_event: 'Event settings',
|
||||
section_event_period: 'Event period',
|
||||
section_daily_source: 'Source 1: yesterday net win/loss',
|
||||
daily_points_enabled: 'Enable daily loss to points',
|
||||
@@ -24,9 +24,8 @@ export default {
|
||||
mall_event_end_date_tip: 'Takes effect the next day; leave empty for no end. On the day after end, rows for the end business date no longer convert points or unlock cap; later business dates are excluded too.',
|
||||
active_event_end_date: 'Active end date',
|
||||
event_end_before_start: 'End date cannot be earlier than start date',
|
||||
section_other: 'Other settings',
|
||||
unlock_ratio: 'Unlock ratio (unlock_ratio)',
|
||||
unlock_ratio_tip: 'Daily push: daily claim cap = yesterday total deposit × this ratio (within event window).',
|
||||
points_to_cash_ratio: 'Points to cash ratio',
|
||||
points_to_cash_ratio_tip: 'For withdrawable cash display: cash ≈ available points × this ratio.',
|
||||
unlock_ratio: 'Daily claim cap ratio',
|
||||
unlock_ratio_tip: 'Daily push: today claim cap = yesterday total deposit × this ratio (within event window).',
|
||||
points_to_cash_ratio: 'Points-to-cash reference ratio',
|
||||
points_to_cash_ratio_tip: 'Asset API withdrawable_cash = available points × this ratio. Suggested cash face value on bonus/withdraw items = redemption points × this ratio (editable per item).',
|
||||
}
|
||||
|
||||
@@ -7,11 +7,13 @@ export default {
|
||||
available_points: 'Available points',
|
||||
adjust_available_points: 'Adjust points',
|
||||
today_limit: 'Daily claim cap',
|
||||
today_limit_tip: 'Usually set by daily push: yesterday total deposit × unlock ratio in mall settings; 0 when the event is closed or settlement is not allowed. See “Daily cap date” for the business day. Manual edits here may be overwritten on the next successful push.',
|
||||
today_claimed: 'Claimed today',
|
||||
today_limit_date: 'Daily cap date',
|
||||
points_claim_cap: 'Lifetime claim cap',
|
||||
total_points_claimed: 'Total points claimed',
|
||||
event_period_win_loss_net: 'Event period net W/L (adds while open, pauses when closed, resumes on reopen)',
|
||||
event_period_win_loss_net: 'Event period cumulative net W/L',
|
||||
event_period_win_loss_net_tip: 'Adds yesterday net win/loss from each daily push while the event is open; pauses when closed; resumes when reopened without reset.',
|
||||
create_time: 'Created at',
|
||||
update_time: 'Updated at',
|
||||
'quick Search Fields': 'ID, PlayX user ID, username, phone',
|
||||
|
||||
@@ -8,12 +8,19 @@ export default {
|
||||
description_ms: '描述(马来语)',
|
||||
remark: '备注',
|
||||
score: '兑换积分',
|
||||
score_tip: '用户兑换本商品时扣除的可用积分。',
|
||||
type: '类型',
|
||||
'type 1': '红利(BONUS)',
|
||||
'type 2': '实物(PHYSICAL)',
|
||||
'type 3': '提现(WITHDRAW)',
|
||||
amount: '现金面值',
|
||||
amount_tip: '兑换成功后写入订单并推送给 PlayX 的现金金额。可参考下方建议值(兑换积分 × 商城积分兑现金参考比例),也可按业务自行填写。',
|
||||
amount_suggested: '建议现金面值:{amount}(兑换积分 {score} × 比例 {ratio})',
|
||||
amount_apply_suggested: '填入建议金额',
|
||||
amount_ratio_missing: '暂未读取到商城「积分兑现金参考比例」,建议金额无法计算;现金面值请手工填写。',
|
||||
points_to_cash_ratio_label: '当前参考比例',
|
||||
multiplier: '流水倍数',
|
||||
multiplier_tip: '红利/提现发放时一并推送的流水倍数要求。',
|
||||
category: '红利业务类别',
|
||||
category_title: '类别展示名',
|
||||
admin_id: '创建管理员',
|
||||
|
||||
@@ -57,6 +57,6 @@ export default {
|
||||
},
|
||||
playxConfig: {
|
||||
title: '页面说明',
|
||||
desc: '分为「活动配置」与「常规配置」:活动开始/结束日(隔日生效,结束日次日起不再折算该日业务数据的昨日净输赢与解锁上限)、两种积分来源及终身一键计算;常规项为解锁比例与积分折现比例。',
|
||||
desc: '「常规配置」:今日积分领取上限比例、积分兑现金参考比例(资产展示与商品现金面值建议);「活动配置」含活动起止日与积分来源。',
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
export default {
|
||||
tab_event: '活动配置',
|
||||
tab_regular: '常规配置',
|
||||
tab_event: '活动配置',
|
||||
section_event_period: '活动时间',
|
||||
section_daily_source: '积分来源一:昨日净输赢',
|
||||
daily_points_enabled: '启用昨日净输赢转积分',
|
||||
@@ -24,9 +24,8 @@ export default {
|
||||
mall_event_end_date_tip: '设置后隔日生效,可留空表示不限制结束。结束日次日不再对结束日当天业务数据的昨日净输赢折算积分与解锁上限;结束日之后的业务日期也不再折算。',
|
||||
active_event_end_date: '当前生效的结束日',
|
||||
event_end_before_start: '活动结束日不能早于开始日',
|
||||
section_other: '其他参数',
|
||||
unlock_ratio: '解锁比例(unlock_ratio)',
|
||||
unlock_ratio_tip: '每日推送中:今日可领取上限 = 昨日总充值 × 本比例(受活动时间规则约束)。',
|
||||
points_to_cash_ratio: '积分折算现金比例',
|
||||
points_to_cash_ratio_tip: '用于资产接口中「可提现现金」等展示:现金 ≈ 可用积分 × 本比例。',
|
||||
unlock_ratio: '今日积分领取上限比例',
|
||||
unlock_ratio_tip: '每日推送:今日可领取上限 = 昨日总充值 × 本比例(受活动时间规则约束)。',
|
||||
points_to_cash_ratio: '积分兑现金参考比例',
|
||||
points_to_cash_ratio_tip: '资产接口 withdrawable_cash = 可用积分 × 本比例;商品管理添加红利/提现商品时,「现金面值」建议金额 = 兑换积分 × 本比例(可手工覆盖)。',
|
||||
}
|
||||
|
||||
@@ -7,11 +7,13 @@ export default {
|
||||
available_points: '可用积分',
|
||||
adjust_available_points: '调整积分',
|
||||
today_limit: '今日可领取上限',
|
||||
today_limit_tip: '一般由每日推送自动写入:昨日总充值 × 商城参数「今日积分领取上限比例」;活动关闭或不允许结算时为 0。对应业务日见「今日上限日期」。后台可手工改,下次成功推送可能覆盖。',
|
||||
today_claimed: '今日已领取',
|
||||
today_limit_date: '今日上限日期',
|
||||
points_claim_cap: '领取积分上限',
|
||||
total_points_claimed: '累计已领取积分',
|
||||
event_period_win_loss_net: '活动期累计净输赢(开放时累加,关闭暂停,再开续累)',
|
||||
event_period_win_loss_net: '活动期间累积净输赢',
|
||||
event_period_win_loss_net_tip: '活动开放时按每日推送的「昨日净输赢」累加;关闭期间不累加,再次开启后接着累加,不重置。',
|
||||
create_time: '创建时间',
|
||||
update_time: '修改时间',
|
||||
'quick Search Fields': 'ID、playX用户ID、用户名、手机号',
|
||||
|
||||
@@ -96,26 +96,40 @@
|
||||
type="number"
|
||||
v-model="baTable.form.items!.score"
|
||||
prop="score"
|
||||
:block-help="t('mall.item.score_tip')"
|
||||
:input-attr="{ step: 1 }"
|
||||
:placeholder="t('Please input field', { field: t('mall.item.score') })"
|
||||
/>
|
||||
|
||||
<!-- BONUS / WITHDRAW:现金面值、流水倍数 -->
|
||||
<FormItem
|
||||
v-if="isBonusOrWithdraw"
|
||||
:label="t('mall.item.amount')"
|
||||
type="number"
|
||||
v-model="baTable.form.items!.amount"
|
||||
prop="amount"
|
||||
:input-attr="{ step: 0.01 }"
|
||||
:placeholder="t('Please input field', { field: t('mall.item.amount') })"
|
||||
/>
|
||||
<el-form-item v-if="isBonusOrWithdraw" :label="t('mall.item.amount')" prop="amount">
|
||||
<el-input-number
|
||||
v-model="baTable.form.items!.amount"
|
||||
:min="0"
|
||||
:step="0.01"
|
||||
:precision="2"
|
||||
controls-position="right"
|
||||
class="w100"
|
||||
:placeholder="t('Please input field', { field: t('mall.item.amount') })"
|
||||
/>
|
||||
<div v-if="suggestedAmount !== null" class="form-tip amount-suggest-row">
|
||||
<span>{{ suggestedAmountHint }}</span>
|
||||
<el-button type="primary" link @click="applySuggestedAmount">
|
||||
{{ t('mall.item.amount_apply_suggested') }}
|
||||
</el-button>
|
||||
</div>
|
||||
<div v-else-if="isBonusOrWithdraw && pointsRatioLoaded" class="form-tip">
|
||||
{{ t('mall.item.amount_ratio_missing') }}
|
||||
</div>
|
||||
<div class="form-tip">{{ amountBlockHelp }}</div>
|
||||
</el-form-item>
|
||||
<FormItem
|
||||
v-if="isBonusOrWithdraw"
|
||||
:label="t('mall.item.multiplier')"
|
||||
type="number"
|
||||
v-model="baTable.form.items!.multiplier"
|
||||
prop="multiplier"
|
||||
:block-help="t('mall.item.multiplier_tip')"
|
||||
:input-attr="{ step: 1 }"
|
||||
:placeholder="t('Please input field', { field: t('mall.item.multiplier') })"
|
||||
/>
|
||||
@@ -190,8 +204,9 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { FormItemRule } from 'element-plus'
|
||||
import { computed, inject, reactive, useTemplateRef, watch } from 'vue'
|
||||
import { computed, inject, reactive, ref, useTemplateRef, watch } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { index as fetchPlayxConfig } from '/@/api/backend/mall/playxConfig'
|
||||
import FormItem from '/@/components/formItem/index.vue'
|
||||
import { useConfig } from '/@/stores/config'
|
||||
import type baTableClass from '/@/utils/baTable'
|
||||
@@ -203,6 +218,79 @@ const baTable = inject('baTable') as baTableClass
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const pointsToCashRatio = ref<number | null>(null)
|
||||
const pointsRatioLoaded = ref(false)
|
||||
|
||||
const loadPointsToCashRatio = () => {
|
||||
pointsRatioLoaded.value = false
|
||||
fetchPlayxConfig()
|
||||
.then((res) => {
|
||||
const row = res.data.row ?? {}
|
||||
const raw = row.points_to_cash_ratio
|
||||
if (raw !== undefined && raw !== null && raw !== '') {
|
||||
const parsed = parseFloat(String(raw))
|
||||
pointsToCashRatio.value = Number.isFinite(parsed) ? parsed : null
|
||||
} else {
|
||||
pointsToCashRatio.value = null
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
pointsToCashRatio.value = null
|
||||
})
|
||||
.finally(() => {
|
||||
pointsRatioLoaded.value = true
|
||||
})
|
||||
}
|
||||
|
||||
watch(
|
||||
() => baTable.form.operate,
|
||||
(op) => {
|
||||
if (op === 'Add' || op === 'Edit') {
|
||||
loadPointsToCashRatio()
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
|
||||
const suggestedAmount = computed(() => {
|
||||
const ratio = pointsToCashRatio.value
|
||||
if (ratio === null || ratio < 0) {
|
||||
return null
|
||||
}
|
||||
const score = Number(baTable.form.items?.score)
|
||||
if (!Number.isFinite(score) || score < 0) {
|
||||
return null
|
||||
}
|
||||
return Math.round(score * ratio * 100) / 100
|
||||
})
|
||||
|
||||
const suggestedAmountHint = computed(() => {
|
||||
if (suggestedAmount.value === null || pointsToCashRatio.value === null) {
|
||||
return ''
|
||||
}
|
||||
const score = Number(baTable.form.items?.score)
|
||||
return t('mall.item.amount_suggested', {
|
||||
amount: suggestedAmount.value,
|
||||
score: Number.isFinite(score) ? score : 0,
|
||||
ratio: pointsToCashRatio.value,
|
||||
})
|
||||
})
|
||||
|
||||
const amountBlockHelp = computed(() => {
|
||||
const base = t('mall.item.amount_tip')
|
||||
if (pointsToCashRatio.value === null) {
|
||||
return base
|
||||
}
|
||||
return `${base} ${t('mall.item.points_to_cash_ratio_label')}:${pointsToCashRatio.value}。`
|
||||
})
|
||||
|
||||
const applySuggestedAmount = () => {
|
||||
if (suggestedAmount.value === null || !baTable.form.items) {
|
||||
return
|
||||
}
|
||||
baTable.form.items.amount = suggestedAmount.value
|
||||
}
|
||||
|
||||
const itemType = computed(() => baTable.form.items!.type)
|
||||
const isBonus = computed(() => itemType.value === 1 || itemType.value === '1')
|
||||
const isPhysical = computed(() => itemType.value === 2 || itemType.value === '2')
|
||||
@@ -359,4 +447,20 @@ const rules: Partial<Record<string, FormItemRule[]>> = reactive({
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss"></style>
|
||||
<style scoped lang="scss">
|
||||
.w100 {
|
||||
width: 100%;
|
||||
}
|
||||
.form-tip {
|
||||
margin-top: 6px;
|
||||
font-size: 12px;
|
||||
color: var(--el-text-color-secondary);
|
||||
line-height: 1.5;
|
||||
}
|
||||
.amount-suggest-row {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
gap: 4px 8px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -15,6 +15,33 @@
|
||||
@keyup.enter="onSubmit()"
|
||||
>
|
||||
<el-tabs v-model="activeTab" class="playx-config-tabs">
|
||||
<el-tab-pane :label="t('mall.playxConfig.tab_regular')" name="regular">
|
||||
<el-form-item :label="t('mall.playxConfig.unlock_ratio')" prop="unlock_ratio">
|
||||
<el-input-number
|
||||
v-model="form.unlock_ratio"
|
||||
:min="0"
|
||||
:max="100"
|
||||
:step="0.01"
|
||||
:precision="2"
|
||||
controls-position="right"
|
||||
class="w100"
|
||||
/>
|
||||
<div class="form-tip">{{ t('mall.playxConfig.unlock_ratio_tip') }}</div>
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('mall.playxConfig.points_to_cash_ratio')" prop="points_to_cash_ratio">
|
||||
<el-input-number
|
||||
v-model="form.points_to_cash_ratio"
|
||||
:min="0"
|
||||
:max="100"
|
||||
:step="0.01"
|
||||
:precision="4"
|
||||
controls-position="right"
|
||||
class="w100"
|
||||
/>
|
||||
<div class="form-tip">{{ t('mall.playxConfig.points_to_cash_ratio_tip') }}</div>
|
||||
</el-form-item>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane :label="t('mall.playxConfig.tab_event')" name="event">
|
||||
<el-divider content-position="left">{{ t('mall.playxConfig.section_event_period') }}</el-divider>
|
||||
<el-form-item :label="t('mall.playxConfig.mall_open_date')" prop="mall_open_date">
|
||||
@@ -94,34 +121,6 @@
|
||||
<div class="form-tip">{{ t('mall.playxConfig.calculate_lifetime_tip') }}</div>
|
||||
</el-form-item>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane :label="t('mall.playxConfig.tab_regular')" name="regular">
|
||||
<el-divider content-position="left">{{ t('mall.playxConfig.section_other') }}</el-divider>
|
||||
<el-form-item :label="t('mall.playxConfig.unlock_ratio')" prop="unlock_ratio">
|
||||
<el-input-number
|
||||
v-model="form.unlock_ratio"
|
||||
:min="0"
|
||||
:max="100"
|
||||
:step="0.01"
|
||||
:precision="2"
|
||||
controls-position="right"
|
||||
class="w100"
|
||||
/>
|
||||
<div class="form-tip">{{ t('mall.playxConfig.unlock_ratio_tip') }}</div>
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('mall.playxConfig.points_to_cash_ratio')" prop="points_to_cash_ratio">
|
||||
<el-input-number
|
||||
v-model="form.points_to_cash_ratio"
|
||||
:min="0"
|
||||
:max="100"
|
||||
:step="0.01"
|
||||
:precision="2"
|
||||
controls-position="right"
|
||||
class="w100"
|
||||
/>
|
||||
<div class="form-tip">{{ t('mall.playxConfig.points_to_cash_ratio_tip') }}</div>
|
||||
</el-form-item>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
|
||||
<el-form-item class="playx-config-submit">
|
||||
@@ -150,14 +149,14 @@ const loading = ref(true)
|
||||
const submitting = ref(false)
|
||||
const calculating = ref(false)
|
||||
const remark = ref('')
|
||||
const activeTab = ref('event')
|
||||
const activeTab = ref('regular')
|
||||
const activeMallOpenDate = ref('')
|
||||
const activeEventEndDate = ref('')
|
||||
|
||||
const form = reactive({
|
||||
return_ratio: 0.1,
|
||||
unlock_ratio: 0.1,
|
||||
points_to_cash_ratio: 0.1,
|
||||
points_to_cash_ratio: 0.01,
|
||||
daily_points_enabled: true,
|
||||
lifetime_points_enabled: false,
|
||||
lifetime_points_ratio: 0.1,
|
||||
|
||||
@@ -120,7 +120,8 @@ const baTable = new baTableClass(
|
||||
label: t('mall.userAsset.event_period_win_loss_net'),
|
||||
prop: 'event_period_win_loss_net',
|
||||
align: 'center',
|
||||
minWidth: 160,
|
||||
minWidth: 140,
|
||||
showOverflowTooltip: true,
|
||||
operator: 'RANGE',
|
||||
sortable: true,
|
||||
},
|
||||
|
||||
@@ -74,6 +74,7 @@
|
||||
type="number"
|
||||
v-model="baTable.form.items!.today_limit"
|
||||
prop="today_limit"
|
||||
:block-help="t('mall.userAsset.today_limit_tip')"
|
||||
:input-attr="{ step: 1, min: 0 }"
|
||||
:placeholder="t('Please input field', { field: t('mall.userAsset.today_limit') })"
|
||||
/>
|
||||
@@ -82,6 +83,7 @@
|
||||
type="number"
|
||||
v-model="baTable.form.items!.event_period_win_loss_net"
|
||||
prop="event_period_win_loss_net"
|
||||
:block-help="t('mall.userAsset.event_period_win_loss_net_tip')"
|
||||
:input-attr="{ disabled: true, step: 0.01 }"
|
||||
/>
|
||||
<FormItem
|
||||
|
||||
Reference in New Issue
Block a user