feat: 添加独立其他设置菜单及有效端参数保存

This commit is contained in:
Yvan
2026-11-04 21:18:31 +08:00
parent c2e8b729be
commit ca9ce99b66
27 changed files with 805 additions and 9 deletions
+4
View File
@@ -0,0 +1,4 @@
import service from '@/utils/request'
export const getOtherSettings = () => service({ url: '/sw/settings/effectiveness', method: 'get', donNotShowLoading: true })
export const saveOtherSettings = data => service({ url: '/sw/settings/effectiveness', method: 'put', data, donNotShowLoading: true })
+1
View File
@@ -59,6 +59,7 @@
"/src/view/sw/ChannelDataStatistics.vue": "SwChannelDataStatistics",
"/src/view/sw/ChannelList.vue": "SwChannelList",
"/src/view/sw/GameAnalytics.vue": "SwGameAnalytics",
"/src/view/sw/OtherSettings.vue": "SwOtherSettings",
"/src/view/sw/RankingAnalysis.vue": "RankingAnalysis",
"/src/view/sw/RechargeDetails.vue": "SwRechargeDetails",
"/src/view/sw/RegistrationDetails.vue": "SwRegistrationDetails",
+112
View File
@@ -0,0 +1,112 @@
<template>
<el-result v-if="!admin" icon="warning" title="无权访问" sub-title="其他设置仅超级管理员可见、可修改。" />
<div v-else class="other-settings">
<el-text class="settings-intro" type="info">集中配置渠道有效端的判定参数,仅超级管理员可修改。</el-text>
<el-card shadow="never" v-loading="loading">
<el-tabs v-model="tab">
<el-tab-pane label="有效端" name="effectiveness">
<el-alert v-if="error" :title="error" type="error" :closable="false" show-icon class="settings-error" />
<div class="settings-heading">
<h3>有效端判定参数</h3>
<el-text type="info">渠道注册后,在判定窗口内累计充值达到金额门槛,即视为有效端。</el-text>
</div>
<el-form v-if="loaded" ref="formRef" :model="form" :rules="rules" label-position="top" :disabled="loading || saving" @submit.prevent="save">
<el-form-item label="达标金额(元)" prop="amount">
<el-input-number v-model="form.amount" :min="0" :max="Number.MAX_SAFE_INTEGER" :step="100" :precision="0" controls-position="right" />
<el-text class="field-help" type="info">默认 ¥3,000,支持 0 及以上整数。</el-text>
</el-form-item>
<el-form-item label="判定窗口(天)" prop="days">
<el-input-number v-model="form.days" :min="1" :max="Number.MAX_SAFE_INTEGER" :precision="0" controls-position="right" />
<el-text class="field-help" type="info">从渠道注册时间起算的连续天数,默认 30 天。</el-text>
</el-form-item>
</el-form>
<div class="rule-preview">
<strong>判定口径说明</strong>
<ol>
<li>渠道注册后 <b>{{ form.days ?? '—' }} 天</b>内累计充值流水满 <b>{{ amountText }}</b>,即为有效端。</li>
<li>归属月份为<b>达标当天所在的月份</b>,该渠道计入当月有效端。</li>
<li>注册满 <b>{{ form.days ?? '—' }} 天</b>仍未达标,不算有效端,之后流水上涨也不补记。</li>
<li>一旦达标,有效端标记保持,不因后续流水回落而取消。</li>
</ol>
</div>
<div class="settings-actions">
<el-button type="primary" :loading="saving" :disabled="loading || !loaded" @click="save">保存</el-button>
<el-button :disabled="loading || saving" @click="restoreDefaults">恢复默认</el-button>
<el-button v-if="error" link :disabled="loading || saving" @click="load">重新加载</el-button>
<el-text type="info" role="status">{{ status }}</el-text>
</div>
</el-tab-pane>
</el-tabs>
</el-card>
</div>
</template>
<script setup>
import { computed, onMounted, reactive, ref } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { useUserStore } from '@/pinia/modules/user'
import { getOtherSettings, saveOtherSettings } from '@/api/otherSettings'
defineOptions({ name: 'SwOtherSettings' })
const user = useUserStore()
const admin = computed(() => Number(user.userInfo.authorityId) === 888)
const tab = ref('effectiveness'), formRef = ref()
const defaults = ref({ amount: 3000, days: 30 })
const form = reactive({ ...defaults.value })
const loading = ref(false), saving = ref(false), loaded = ref(false), error = ref(''), status = ref('')
const amountText = computed(() => Number.isSafeInteger(form.amount) ? `¥${form.amount.toLocaleString('zh-CN')}` : '—')
const validateInteger = minimum => (_rule, value, callback) => callback(Number.isSafeInteger(value) && value >= minimum ? undefined : new Error(`请输入不小于 ${minimum} 的有效整数`))
const rules = {
amount: [{ validator: validateInteger(0), trigger: ['blur', 'change'] }],
days: [{ validator: validateInteger(1), trigger: ['blur', 'change'] }]
}
const apply = data => {
Object.assign(form, { amount: data.amount, days: data.days })
defaults.value = data.defaults
loaded.value = true
}
const load = async () => {
if (!admin.value || loading.value || saving.value) return
loading.value = true; loaded.value = false; error.value = ''; status.value = ''
try {
const res = await getOtherSettings()
if (res.code !== 0) { error.value = res.msg || '设置读取失败,请重试'; return }
apply(res.data)
} catch { error.value = '设置读取失败,请重试' } finally { loading.value = false }
}
const restoreDefaults = () => {
Object.assign(form, defaults.value)
loaded.value = true; error.value = ''; status.value = '已恢复默认值,点击「保存」后生效'
formRef.value?.clearValidate()
}
const save = async () => {
if (!admin.value || saving.value || loading.value || !loaded.value) return
if (!await formRef.value.validate().catch(() => false)) return
if (saving.value) return
saving.value = true
try {
await ElMessageBox.confirm(`确认保存有效端参数?达标金额 ${amountText.value},判定窗口 ${form.days} 天。`, '保存设置', { type: 'warning', confirmButtonText: '确认保存', cancelButtonText: '取消' })
} catch { saving.value = false; return }
try {
const res = await saveOtherSettings({ amount: form.amount, days: form.days })
if (res.code !== 0) { error.value = res.msg || '保存失败,请重试'; return }
apply(res.data); error.value = ''; status.value = '参数已保存'
ElMessage.success('参数已保存')
} catch { error.value = '保存失败,请重试' } finally { saving.value = false }
}
onMounted(load)
</script>
<style scoped>
.other-settings { max-width: 860px; }
.settings-intro { display: block; margin-bottom: 16px; }
.settings-heading { margin: 4px 0 24px; }
.settings-heading h3 { margin: 0 0 8px; font-size: 17px; }
.settings-error { margin-bottom: 16px; }
.field-help { margin-left: 16px; }
.rule-preview { padding: 16px; border-radius: 6px; background: var(--el-fill-color-light); line-height: 1.9; color: var(--el-text-color-regular); }
.rule-preview ol { margin: 8px 0 0; padding-left: 24px; }
.settings-actions { display: flex; align-items: center; flex-wrap: wrap; gap: 12px; margin: 24px 0 4px; }
.settings-actions .el-button + .el-button { margin-left: 0; }
@media (max-width: 600px) { .field-help { display: block; margin: 8px 0 0; width: 100%; } }
</style>