Files
webman-buildadmin-mall/web/src/views/backend/mall/playxConfig/index.vue
2026-07-27 16:19:31 +08:00

285 lines
13 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<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>