285 lines
13 KiB
Vue
285 lines
13 KiB
Vue
<template>
|
||
<div class="default-main ba-table-box">
|
||
<MallPageIntro page-key="playxConfig" />
|
||
|
||
<el-alert class="ba-table-alert" v-if="remark" :title="remark" type="info" show-icon />
|
||
|
||
<el-card shadow="never" v-loading="loading">
|
||
<el-form
|
||
v-if="!loading"
|
||
ref="formRef"
|
||
:model="form"
|
||
:rules="rules"
|
||
label-position="top"
|
||
@submit.prevent=""
|
||
@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">
|
||
<el-date-picker
|
||
v-model="form.mall_open_date"
|
||
type="date"
|
||
value-format="YYYY-MM-DD"
|
||
class="w100"
|
||
:placeholder="t('Please select field', { field: t('mall.playxConfig.mall_open_date') })"
|
||
/>
|
||
<div class="form-tip">{{ t('mall.playxConfig.mall_open_date_tip') }}</div>
|
||
<div v-if="activeMallOpenDate" class="form-tip">
|
||
{{ t('mall.playxConfig.active_mall_open_date') }}:{{ activeMallOpenDate }}
|
||
</div>
|
||
</el-form-item>
|
||
<el-form-item :label="t('mall.playxConfig.mall_event_end_date')" prop="mall_event_end_date">
|
||
<el-date-picker
|
||
v-model="form.mall_event_end_date"
|
||
type="date"
|
||
value-format="YYYY-MM-DD"
|
||
class="w100"
|
||
clearable
|
||
:placeholder="t('Please select field', { field: t('mall.playxConfig.mall_event_end_date') })"
|
||
/>
|
||
<div class="form-tip">{{ t('mall.playxConfig.mall_event_end_date_tip') }}</div>
|
||
<div v-if="activeEventEndDate" class="form-tip">
|
||
{{ t('mall.playxConfig.active_event_end_date') }}:{{ activeEventEndDate }}
|
||
</div>
|
||
</el-form-item>
|
||
|
||
<el-divider content-position="left">{{ t('mall.playxConfig.section_daily_source') }}</el-divider>
|
||
<el-form-item :label="t('mall.playxConfig.daily_points_enabled')" prop="daily_points_enabled">
|
||
<el-switch v-model="form.daily_points_enabled" />
|
||
<div class="form-tip">{{ t('mall.playxConfig.daily_points_enabled_tip') }}</div>
|
||
</el-form-item>
|
||
<el-form-item :label="t('mall.playxConfig.return_ratio')" prop="return_ratio">
|
||
<el-input-number
|
||
v-model="form.return_ratio"
|
||
:min="0"
|
||
:max="100"
|
||
:step="0.01"
|
||
:precision="2"
|
||
controls-position="right"
|
||
class="w100"
|
||
:disabled="!form.daily_points_enabled"
|
||
/>
|
||
<div class="form-tip">{{ t('mall.playxConfig.return_ratio_tip') }}</div>
|
||
</el-form-item>
|
||
|
||
<el-divider content-position="left">{{ t('mall.playxConfig.section_lifetime_source') }}</el-divider>
|
||
<el-form-item :label="t('mall.playxConfig.lifetime_points_enabled')" prop="lifetime_points_enabled">
|
||
<el-switch v-model="form.lifetime_points_enabled" />
|
||
<div class="form-tip">{{ t('mall.playxConfig.lifetime_points_enabled_tip') }}</div>
|
||
</el-form-item>
|
||
<el-form-item :label="t('mall.playxConfig.lifetime_points_ratio')" prop="lifetime_points_ratio">
|
||
<el-input-number
|
||
v-model="form.lifetime_points_ratio"
|
||
:min="0"
|
||
:max="100"
|
||
:step="0.01"
|
||
:precision="2"
|
||
controls-position="right"
|
||
class="w100"
|
||
:disabled="!form.lifetime_points_enabled"
|
||
/>
|
||
<div class="form-tip">{{ t('mall.playxConfig.lifetime_points_ratio_tip') }}</div>
|
||
</el-form-item>
|
||
<el-form-item>
|
||
<el-button
|
||
type="warning"
|
||
:loading="calculating"
|
||
:disabled="!form.lifetime_points_enabled"
|
||
@click="onCalculateLifetime()"
|
||
>
|
||
{{ t('mall.playxConfig.calculate_lifetime_btn') }}
|
||
</el-button>
|
||
<div class="form-tip">{{ t('mall.playxConfig.calculate_lifetime_tip') }}</div>
|
||
</el-form-item>
|
||
</el-tab-pane>
|
||
</el-tabs>
|
||
|
||
<el-form-item class="playx-config-submit">
|
||
<el-button type="primary" :loading="submitting" @click="onSubmit()">{{ t('Save') }}</el-button>
|
||
</el-form-item>
|
||
</el-form>
|
||
</el-card>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
import type { FormInstance, FormRules } from 'element-plus'
|
||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||
import { onMounted, reactive, ref, useTemplateRef } from 'vue'
|
||
import { useI18n } from 'vue-i18n'
|
||
import { calculateLifetimePoints, index, save } from '/@/api/backend/mall/playxConfig'
|
||
import MallPageIntro from '/@/components/mall/MallPageIntro.vue'
|
||
|
||
defineOptions({
|
||
name: 'mall/playxConfig',
|
||
})
|
||
|
||
const { t } = useI18n()
|
||
const formRef = useTemplateRef<FormInstance>('formRef')
|
||
const loading = ref(true)
|
||
const submitting = ref(false)
|
||
const calculating = ref(false)
|
||
const remark = ref('')
|
||
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.01,
|
||
daily_points_enabled: true,
|
||
lifetime_points_enabled: false,
|
||
lifetime_points_ratio: 0.1,
|
||
mall_open_date: '' as string | null,
|
||
mall_event_end_date: '' as string | null,
|
||
})
|
||
|
||
const validateEventDates = (_rule: unknown, _value: unknown, callback: (err?: Error) => void) => {
|
||
const start = form.mall_open_date
|
||
const end = form.mall_event_end_date
|
||
if (start && end && end < start) {
|
||
callback(new Error(t('mall.playxConfig.event_end_before_start')))
|
||
return
|
||
}
|
||
callback()
|
||
}
|
||
|
||
const rules: FormRules = {
|
||
return_ratio: [{ required: true, message: t('Please input field', { field: t('mall.playxConfig.return_ratio') }) }],
|
||
unlock_ratio: [{ required: true, message: t('Please input field', { field: t('mall.playxConfig.unlock_ratio') }) }],
|
||
points_to_cash_ratio: [{ required: true, message: t('Please input field', { field: t('mall.playxConfig.points_to_cash_ratio') }) }],
|
||
lifetime_points_ratio: [{ required: true, message: t('Please input field', { field: t('mall.playxConfig.lifetime_points_ratio') }) }],
|
||
mall_event_end_date: [{ validator: validateEventDates, trigger: 'change' }],
|
||
mall_open_date: [{ validator: validateEventDates, trigger: 'change' }],
|
||
}
|
||
|
||
const loadData = () => {
|
||
loading.value = true
|
||
index()
|
||
.then((res) => {
|
||
remark.value = res.data.remark ?? ''
|
||
const row = res.data.row ?? {}
|
||
activeMallOpenDate.value = row.active_mall_open_date ?? ''
|
||
activeEventEndDate.value = row.active_event_end_date ?? ''
|
||
if (row.return_ratio !== undefined && row.return_ratio !== null) {
|
||
form.return_ratio = parseFloat(String(row.return_ratio))
|
||
}
|
||
if (row.unlock_ratio !== undefined && row.unlock_ratio !== null) {
|
||
form.unlock_ratio = parseFloat(String(row.unlock_ratio))
|
||
}
|
||
if (row.points_to_cash_ratio !== undefined && row.points_to_cash_ratio !== null) {
|
||
form.points_to_cash_ratio = parseFloat(String(row.points_to_cash_ratio))
|
||
}
|
||
form.daily_points_enabled = Number(row.daily_points_enabled ?? 1) === 1
|
||
form.lifetime_points_enabled = Number(row.lifetime_points_enabled ?? 0) === 1
|
||
if (row.lifetime_points_ratio !== undefined && row.lifetime_points_ratio !== null) {
|
||
form.lifetime_points_ratio = parseFloat(String(row.lifetime_points_ratio))
|
||
}
|
||
form.mall_open_date = row.mall_open_date ? String(row.mall_open_date).slice(0, 10) : null
|
||
form.mall_event_end_date = row.mall_event_end_date ? String(row.mall_event_end_date).slice(0, 10) : null
|
||
})
|
||
.finally(() => {
|
||
loading.value = false
|
||
})
|
||
}
|
||
|
||
const onSubmit = () => {
|
||
formRef.value?.validate((valid) => {
|
||
if (!valid) return
|
||
submitting.value = true
|
||
save({
|
||
return_ratio: form.return_ratio,
|
||
unlock_ratio: form.unlock_ratio,
|
||
points_to_cash_ratio: form.points_to_cash_ratio,
|
||
daily_points_enabled: form.daily_points_enabled ? 1 : 0,
|
||
lifetime_points_enabled: form.lifetime_points_enabled ? 1 : 0,
|
||
lifetime_points_ratio: form.lifetime_points_ratio,
|
||
mall_open_date: form.mall_open_date || null,
|
||
mall_event_end_date: form.mall_event_end_date || null,
|
||
})
|
||
.then(() => {
|
||
loadData()
|
||
})
|
||
.finally(() => {
|
||
submitting.value = false
|
||
})
|
||
})
|
||
}
|
||
|
||
const onCalculateLifetime = () => {
|
||
ElMessageBox.confirm(t('mall.playxConfig.calculate_lifetime_confirm'), t('Reminder'), {
|
||
type: 'warning',
|
||
}).then(() => {
|
||
calculating.value = true
|
||
calculateLifetimePoints()
|
||
.then((res) => {
|
||
const stats = res.data ?? {}
|
||
ElMessage.success(
|
||
t('mall.playxConfig.calculate_lifetime_success', {
|
||
processed: stats.processed ?? 0,
|
||
updated: stats.updated ?? 0,
|
||
skipped: stats.skipped ?? 0,
|
||
})
|
||
)
|
||
loadData()
|
||
})
|
||
.finally(() => {
|
||
calculating.value = false
|
||
})
|
||
})
|
||
}
|
||
|
||
onMounted(() => {
|
||
loadData()
|
||
})
|
||
</script>
|
||
|
||
<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;
|
||
}
|
||
.playx-config-tabs {
|
||
margin-bottom: 8px;
|
||
}
|
||
.playx-config-submit {
|
||
margin-top: 16px;
|
||
margin-bottom: 0;
|
||
}
|
||
</style>
|