Files
webman-buildadmin/web/src/views/backend/game/rewardConfig/index.vue

893 lines
33 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">
<el-card shadow="never" class="reward-form-card" v-loading="pageLoading">
<template #header>
<span class="card-title">{{ t('game.rewardConfigForm.title') }}</span>
</template>
<el-alert
v-if="showTemplateIntro"
class="intro-alert"
type="info"
:closable="false"
show-icon
>
<template #title>{{ t('game.rewardConfigForm.intro') }}</template>
</el-alert>
<el-form
ref="formRef"
class="reward-form-body"
@submit.prevent=""
:model="formModel"
:label-position="config.layout.shrink ? 'top' : 'right'"
:label-width="formLabelWidth + 'px'"
:rules="rules"
>
<template v-if="isSuperAdmin">
<el-form-item :label="t('game.rewardConfigForm.super_scope_label')">
<el-radio-group v-model="superEditScope" @change="onSuperScopeChange">
<el-radio-button value="template">{{ t('game.rewardConfigForm.super_scope_template') }}</el-radio-button>
<el-radio-button value="channel">{{ t('game.rewardConfigForm.super_scope_channel') }}</el-radio-button>
</el-radio-group>
</el-form-item>
<el-alert
v-if="superEditScope === 'channel'"
class="scope-hint"
type="warning"
:closable="false"
:title="t('game.rewardConfigForm.super_scope_hint')"
/>
<div v-if="superEditScope === 'channel'" class="channel-bar">
<div class="channel-picker">
<FormItem
:label="t('game.rewardConfig.game_channel_id')"
type="remoteSelect"
v-model="formModel.game_channel_id"
prop="game_channel_id"
:input-attr="channelRemoteAttr"
:placeholder="t('Please select field', { field: t('game.rewardConfig.game_channel_id') })"
/>
</div>
<el-button type="primary" @click="loadData" :disabled="!formModel.game_channel_id">{{ t('Refresh') }}</el-button>
</div>
</template>
<template v-if="showInner">
<el-form-item :label="t('game.rewardConfig.tier_reward_form')" prop="tier_reward_form">
<div class="block-editor tier-editor">
<div class="line line-head">
<span>{{ t('game.rewardConfig.grid_number') }}</span>
<span>{{ t('game.rewardConfig.ui_text') }}</span>
<span>{{ t('game.rewardConfig.ui_text_en') }}</span>
<span>{{ t('game.rewardConfig.real_ev') }}</span>
<span>{{ t('game.rewardConfig.tier') }}</span>
<span>{{ t('game.rewardConfig.remark') }}</span>
</div>
<div v-for="(row, idx) in tierRows" :key="'tier-' + idx" class="line">
<el-input v-model="row.grid_number" disabled />
<el-input
v-model="row.ui_text"
:placeholder="t('Please input field', { field: t('game.rewardConfig.ui_text') })"
@input="syncPayload"
/>
<el-input
v-model="row.ui_text_en"
:placeholder="t('Please input field', { field: t('game.rewardConfig.ui_text_en') })"
@input="syncPayload"
/>
<el-input
v-model="row.real_ev"
:placeholder="t('Please input field', { field: t('game.rewardConfig.real_ev') })"
@input="syncPayload"
/>
<el-select v-model="row.tier" style="width: 100px" @change="syncPayload">
<el-option :label="t('game.rewardConfig.tier_t1')" value="T1" />
<el-option :label="t('game.rewardConfig.tier_t2')" value="T2" />
<el-option :label="t('game.rewardConfig.tier_t3')" value="T3" />
<el-option :label="t('game.rewardConfig.tier_t4')" value="T4" />
<el-option :label="t('game.rewardConfig.tier_t5')" value="T5" />
</el-select>
<el-input
v-model="row.remark"
:placeholder="t('Please input field', { field: t('game.rewardConfig.remark') })"
@input="syncPayload"
/>
</div>
<div class="form-help">{{ t('game.rewardConfig.tier_reward_form_help') }}</div>
</div>
</el-form-item>
<el-form-item :label="t('game.rewardConfig.bigwin_form')" prop="bigwin_form">
<div class="block-editor bigwin-editor">
<div class="line line-head">
<span>{{ t('game.rewardConfig.grid_number') }}</span>
<span>{{ t('game.rewardConfig.ui_text') }}</span>
<span>{{ t('game.rewardConfig.real_ev') }}</span>
<span>{{ t('game.rewardConfig.tier') }}</span>
</div>
<div v-for="(row, idx) in bigwinRows" :key="'bigwin-' + idx" class="line">
<el-input v-model="row.grid_number" disabled />
<el-input
v-model="row.ui_text"
:placeholder="t('Please input field', { field: t('game.rewardConfig.ui_text') })"
@input="syncPayload"
/>
<el-input
v-model="row.real_ev"
:placeholder="t('Please input field', { field: t('game.rewardConfig.real_ev') })"
@input="syncPayload"
/>
<el-input v-model="row.tier" disabled style="width: 120px" />
</div>
<div class="form-help">{{ t('game.rewardConfig.bigwin_form_help') }}</div>
</div>
</el-form-item>
<el-form-item>
<el-button type="primary" :loading="submitLoading" @click="onSubmit">{{ t('game.rewardConfigForm.btn_add') }}</el-button>
<el-button @click="onReset">{{ t('game.rewardConfigForm.btn_reset') }}</el-button>
<el-button v-auth="'generateTierBoard'" @click="openGenTierDialog">{{ t('game.rewardConfigForm.btn_gen_tier') }}</el-button>
<el-button v-auth="'generateRewardWeight'" :loading="genWeightSubmitting" @click="onGenWeightClick">{{
t('game.rewardConfigForm.btn_gen_weight')
}}</el-button>
</el-form-item>
</template>
</el-form>
</el-card>
<el-dialog
v-model="genTierDialogVisible"
class="gen-tier-dialog"
:title="t('game.rewardConfigForm.gen_tier_title')"
:width="genTierDialogWidth"
:fullscreen="genTierDialogFullscreen"
:align-center="!genTierDialogFullscreen"
append-to-body
:close-on-click-modal="false"
destroy-on-close
>
<el-scrollbar :max-height="genTierScrollMaxHeight" class="gen-tier-scroll">
<div class="gen-rule">{{ t('game.rewardConfigForm.gen_tier_rule') }}</div>
<el-form
:model="genTierForm"
class="gen-tier-form"
:label-position="genTierFormLabelPosition"
:label-width="genTierFormLabelWidth"
>
<div class="gen-tier-block">
<div class="gen-tier-block-title">{{ t('game.rewardConfigForm.gen_t1_label') }}</div>
<el-form-item :label="t('game.rewardConfigForm.gen_t1_fixed') + '' + t('game.rewardConfigForm.gen_dir_cw') + ''">
<el-input-number v-model="genTierForm.t1_fixed_cw" :min="0" :max="26" :step="1" controls-position="right" />
</el-form-item>
<el-form-item :label="t('game.rewardConfigForm.gen_t1_fixed') + '' + t('game.rewardConfigForm.gen_dir_ccw') + ''">
<el-input-number v-model="genTierForm.t1_fixed_ccw" :min="0" :max="26" :step="1" controls-position="right" />
</el-form-item>
<el-form-item :label="t('game.rewardConfigForm.gen_settlement')">
<el-input-number v-model="genTierForm.amt_t1" :min="0" :step="1" controls-position="right" />
</el-form-item>
</div>
<div class="gen-tier-block">
<div class="gen-tier-block-title">{{ t('game.rewardConfigForm.gen_t2_label') }}</div>
<el-form-item :label="t('game.rewardConfigForm.gen_t2_min') + '' + t('game.rewardConfigForm.gen_dir_cw') + ''">
<el-input-number v-model="genTierForm.t2_min_cw" :min="0" :max="26" :step="1" controls-position="right" />
</el-form-item>
<el-form-item :label="t('game.rewardConfigForm.gen_t2_min') + '' + t('game.rewardConfigForm.gen_dir_ccw') + ''">
<el-input-number v-model="genTierForm.t2_min_ccw" :min="0" :max="26" :step="1" controls-position="right" />
</el-form-item>
<el-form-item :label="t('game.rewardConfigForm.gen_settlement')">
<el-input-number v-model="genTierForm.amt_t2" :min="0" :precision="2" :step="0.1" controls-position="right" />
</el-form-item>
</div>
<div class="gen-tier-block">
<div class="gen-tier-block-title">{{ t('game.rewardConfigForm.gen_t3_label') }}</div>
<el-form-item :label="t('game.rewardConfigForm.gen_t3_amt_only')">
<el-input-number v-model="genTierForm.amt_t3" :min="0" :precision="2" :step="0.1" controls-position="right" />
</el-form-item>
</div>
<div class="gen-tier-block">
<div class="gen-tier-block-title">{{ t('game.rewardConfigForm.gen_t4_label') }}</div>
<el-form-item :label="t('game.rewardConfigForm.gen_t4_fixed') + '' + t('game.rewardConfigForm.gen_dir_cw') + ''">
<el-input-number v-model="genTierForm.t4_fixed_cw" :min="0" :max="26" :step="1" controls-position="right" />
</el-form-item>
<el-form-item :label="t('game.rewardConfigForm.gen_t4_fixed') + '' + t('game.rewardConfigForm.gen_dir_ccw') + ''">
<el-input-number v-model="genTierForm.t4_fixed_ccw" :min="0" :max="26" :step="1" controls-position="right" />
</el-form-item>
<el-form-item :label="t('game.rewardConfigForm.gen_settlement')">
<el-input-number v-model="genTierForm.amt_t4" :precision="2" :step="0.1" controls-position="right" />
</el-form-item>
</div>
<div class="gen-tier-block">
<div class="gen-tier-block-title">{{ t('game.rewardConfigForm.gen_t5_label') }}</div>
<el-form-item :label="t('game.rewardConfigForm.gen_t5_fixed') + '' + t('game.rewardConfigForm.gen_dir_cw') + ''">
<el-input-number v-model="genTierForm.t5_fixed_cw" :min="0" :max="26" :step="1" controls-position="right" />
</el-form-item>
<el-form-item :label="t('game.rewardConfigForm.gen_t5_fixed') + '' + t('game.rewardConfigForm.gen_dir_ccw') + ''">
<el-input-number v-model="genTierForm.t5_fixed_ccw" :min="0" :max="26" :step="1" controls-position="right" />
</el-form-item>
<el-form-item :label="t('game.rewardConfigForm.gen_settlement')">
<el-input-number :model-value="0" disabled />
</el-form-item>
</div>
</el-form>
<div class="gen-tier-footer-hint">{{ t('game.rewardConfigForm.gen_tier_footer_hint') }}</div>
</el-scrollbar>
<template #footer>
<div class="gen-tier-dialog-footer">
<el-button @click="genTierDialogVisible = false">{{ t('game.rewardConfigForm.gen_tier_cancel') }}</el-button>
<el-button v-auth="'generateTierBoard'" type="primary" :loading="genTierSubmitting" @click="onGenTierSubmit">{{
t('game.rewardConfigForm.gen_tier_submit')
}}</el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup lang="ts">
import type { FormInstance, FormItemRule } from 'element-plus'
import { ElMessage, ElMessageBox } from 'element-plus'
import { useWindowSize } from '@vueuse/core'
import { computed, onMounted, reactive, ref, useTemplateRef, watch } from 'vue'
import { useI18n } from 'vue-i18n'
import FormItem from '/@/components/formItem/index.vue'
import { useConfig } from '/@/stores/config'
import { useAdminInfo } from '/@/stores/adminInfo'
import createAxios from '/@/utils/axios'
defineOptions({
name: 'game/rewardConfig',
})
type RewardRow = {
grid_number: string
ui_text: string
ui_text_en: string
real_ev: string
tier: string
remark: string
}
const { t } = useI18n()
const config = useConfig()
const adminInfo = useAdminInfo()
const { width: windowWidth } = useWindowSize()
/** 生成弹窗:窄屏全屏/宽百分比,宽屏固定最大宽度 */
const genTierDialogFullscreen = computed(() => windowWidth.value <= 520)
const genTierDialogWidth = computed(() => {
if (windowWidth.value <= 520) {
return '100%'
}
if (windowWidth.value <= 768) {
return '92%'
}
return '720px'
})
const genTierFormLabelPosition = computed(() => (windowWidth.value < 640 ? 'top' : 'right'))
const genTierFormLabelWidth = computed(() => (windowWidth.value < 640 ? 'auto' : '150px'))
const genTierScrollMaxHeight = computed(() => (genTierDialogFullscreen.value ? 'calc(100vh - 140px)' : 'min(70vh, 640px)'))
const formRef = useTemplateRef<FormInstance>('formRef')
const TIER_GRIDS = Array.from({ length: 26 }, (_v, i) => String(i + 5))
const BIGWIN_GRIDS = ['5', '10', '15', '20', '25', '30']
const isSuperAdmin = computed(() => adminInfo.super === true)
const formLabelWidth = computed(() => (config.layout.shrink ? 100 : 140))
const channelRemoteAttr = { pk: 'game_channel.id', field: 'name', remoteUrl: '/admin/game.Channel/index' }
const pageLoading = ref(false)
const submitLoading = ref(false)
const genTierDialogVisible = ref(false)
const genTierSubmitting = ref(false)
const genWeightSubmitting = ref(false)
const channelChangeSilent = ref(false)
const lastAutoLoadChannelId = ref<number | null>(null)
const genTierForm = reactive({
t1_fixed_cw: 3,
t1_fixed_ccw: 3,
t2_min_cw: 5,
t2_min_ccw: 5,
t4_fixed_cw: 1,
t4_fixed_ccw: 1,
t5_fixed_cw: 1,
t5_fixed_ccw: 1,
amt_t1: 3,
amt_t2: 1.5,
amt_t3: 0.5,
amt_t4: -0.4,
})
/** 超管template = game_channel_id 0 默认模板channel = 编辑指定渠道 */
const superEditScope = ref<'template' | 'channel'>('template')
/** 仅超管维护「全渠道默认模板」game_channel_id=0、未绑定具体渠道时展示顶部说明渠道管理员或超管选「指定渠道」时不展示 */
const showTemplateIntro = computed(() => isSuperAdmin.value && superEditScope.value === 'template')
const formModel = reactive({
id: null as number | string | null,
game_channel_id: 0 as number | string,
tier_reward_form: '',
bigwin_form: '',
})
const tierRows = ref<RewardRow[]>(
TIER_GRIDS.map((g) => ({ grid_number: g, ui_text: '', ui_text_en: '', real_ev: '', tier: 'T1', remark: '' }))
)
const bigwinRows = ref<RewardRow[]>(
BIGWIN_GRIDS.map((g) => ({ grid_number: g, ui_text: '', ui_text_en: '', real_ev: '', tier: 'BIGWIN', remark: '' }))
)
const showInner = computed(() => {
if (!isSuperAdmin.value) {
return true
}
if (superEditScope.value === 'template') {
return true
}
return !!formModel.game_channel_id
})
function parseRows(raw: unknown): RewardRow[] {
if (typeof raw !== 'string' || raw.trim() === '') return []
try {
const parsed = JSON.parse(raw) as unknown
if (!Array.isArray(parsed)) return []
const out: RewardRow[] = []
for (const item of parsed) {
if (item === null || typeof item !== 'object' || Array.isArray(item)) continue
const obj = item as Record<string, unknown>
out.push({
grid_number: String(obj.grid_number ?? ''),
ui_text: String(obj.ui_text ?? ''),
ui_text_en: String(obj.ui_text_en ?? ''),
real_ev: String(obj.real_ev ?? ''),
tier: String(obj.tier ?? ''),
remark: String(obj.remark ?? ''),
})
}
return out
} catch {
return []
}
}
function toTierRows(raw: unknown): RewardRow[] {
const parsed = parseRows(raw)
const map = new Map<string, RewardRow>()
for (const r of parsed) map.set(r.grid_number, r)
return TIER_GRIDS.map((g) => {
const row = map.get(g)
return {
grid_number: g,
ui_text: row?.ui_text ?? '',
ui_text_en: row?.ui_text_en ?? '',
real_ev: row?.real_ev ?? '',
tier: row?.tier && row.tier !== 'BIGWIN' ? row.tier : 'T1',
remark: row?.remark ?? '',
}
})
}
function toBigwinRows(raw: unknown): RewardRow[] {
const parsed = parseRows(raw)
const map = new Map<string, RewardRow>()
for (const r of parsed) map.set(r.grid_number, r)
return BIGWIN_GRIDS.map((g) => {
const row = map.get(g)
return {
grid_number: g,
ui_text: row?.ui_text ?? '',
ui_text_en: row?.ui_text_en ?? '',
real_ev: row?.real_ev ?? '',
tier: 'BIGWIN',
remark: row?.remark ?? '',
}
})
}
function syncPayload() {
formModel.tier_reward_form = JSON.stringify(tierRows.value)
formModel.bigwin_form = JSON.stringify(bigwinRows.value)
}
function applyRowToForm(row: Record<string, unknown>) {
channelChangeSilent.value = true
formModel.id = (row.id as number | string | null | undefined) ?? null
formModel.game_channel_id = (row.game_channel_id as number | string) ?? 0
tierRows.value = toTierRows(row.tier_reward_form)
bigwinRows.value = toBigwinRows(row.bigwin_form)
syncPayload()
channelChangeSilent.value = false
}
function validateForms(): string | undefined {
if (tierRows.value.length !== 26 || bigwinRows.value.length !== 6) {
return t('Parameter error')
}
for (let i = 0; i < tierRows.value.length; i++) {
const row = tierRows.value[i]
if (row.grid_number !== TIER_GRIDS[i]) return t('Parameter error')
if (row.ui_text.trim() === '' || row.real_ev.trim() === '') return t('Please input field', { field: t('game.rewardConfig.ui_text') })
if (!Number.isFinite(Number(row.real_ev))) return t('game.rewardConfig.real_ev')
if (!['T1', 'T2', 'T3', 'T4', 'T5'].includes(row.tier)) return t('Parameter error')
}
for (let i = 0; i < bigwinRows.value.length; i++) {
const row = bigwinRows.value[i]
if (row.grid_number !== BIGWIN_GRIDS[i]) return t('Parameter error')
if (row.tier !== 'BIGWIN') return t('Parameter error')
if (row.ui_text.trim() === '' || row.real_ev.trim() === '') return t('Please input field', { field: t('game.rewardConfig.ui_text') })
if (!Number.isFinite(Number(row.real_ev))) return t('game.rewardConfig.real_ev')
}
return undefined
}
const rules: Partial<Record<string, FormItemRule[]>> = reactive({
game_channel_id: [
{
validator: (_rule, _val, callback) => {
if (!isSuperAdmin.value || superEditScope.value !== 'channel') {
callback()
return
}
if (!_val && _val !== 0) {
callback(new Error(t('Please select field', { field: t('game.rewardConfig.game_channel_id') })))
return
}
if (_val === 0 || _val === '0') {
callback(new Error(t('Please select field', { field: t('game.rewardConfig.game_channel_id') })))
return
}
callback()
},
trigger: ['change', 'blur'],
},
],
tier_reward_form: [
{
validator: (_rule, _val, callback) => {
const err = validateForms()
if (err) return callback(new Error(err))
callback()
},
trigger: ['blur', 'change'],
},
],
bigwin_form: [
{
validator: (_rule, _val, callback) => {
const err = validateForms()
if (err) return callback(new Error(err))
callback()
},
trigger: ['blur', 'change'],
},
],
})
function resolveRequestChannelId(): number | null {
if (!isSuperAdmin.value) {
return null
}
if (superEditScope.value === 'template') {
return 0
}
const v = formModel.game_channel_id
if (!v && v !== 0) return null
const n = Number(v)
if (!Number.isFinite(n) || n <= 0) return null
return n
}
async function loadData() {
if (isSuperAdmin.value && superEditScope.value === 'channel' && !formModel.game_channel_id) {
return
}
pageLoading.value = true
try {
const params: Record<string, number> = {}
const cid = resolveRequestChannelId()
if (cid !== null) {
params.game_channel_id = cid
}
const res = await createAxios<{ row?: Record<string, unknown> }>(
{
url: '/admin/game.RewardConfig/index',
method: 'get',
params,
},
{ showErrorMessage: true, loading: false }
)
const row = res.data?.row
if (row && typeof row === 'object' && !Array.isArray(row)) {
applyRowToForm(row as Record<string, unknown>)
}
} catch (error: unknown) {
const err = error as { code?: string; message?: string }
if (err.code === 'ERR_CANCELED' || String(err.message ?? '').toLowerCase().includes('canceled')) {
return
}
} finally {
pageLoading.value = false
}
}
function onSuperScopeChange(val: string | number | boolean | undefined) {
if (val === 'template') {
formModel.game_channel_id = 0
loadData()
} else {
formModel.game_channel_id = ''
lastAutoLoadChannelId.value = null
formRef.value?.clearValidate(['game_channel_id'])
}
}
async function onSubmit() {
const form = formRef.value
if (!form) return
syncPayload()
await form.validate(async (valid) => {
if (!valid) return
submitLoading.value = true
try {
const body: Record<string, string | number> = {
tier_reward_form: formModel.tier_reward_form,
bigwin_form: formModel.bigwin_form,
}
const cid = resolveRequestChannelId()
if (cid !== null) {
body.game_channel_id = cid
}
await createAxios(
{
url: '/admin/game.RewardConfig/save',
method: 'post',
data: body,
},
{ showSuccessMessage: true, loading: false }
)
await loadData()
} finally {
submitLoading.value = false
}
})
}
async function onReset() {
formRef.value?.clearValidate()
await loadData()
}
function openGenTierDialog() {
genTierDialogVisible.value = true
}
async function onGenTierSubmit() {
genTierSubmitting.value = true
try {
const body: Record<string, string | number> = {
t1_fixed_cw: genTierForm.t1_fixed_cw,
t1_fixed_ccw: genTierForm.t1_fixed_ccw,
t2_min_cw: genTierForm.t2_min_cw,
t2_min_ccw: genTierForm.t2_min_ccw,
t4_fixed_cw: genTierForm.t4_fixed_cw,
t4_fixed_ccw: genTierForm.t4_fixed_ccw,
t5_fixed_cw: genTierForm.t5_fixed_cw,
t5_fixed_ccw: genTierForm.t5_fixed_ccw,
amt_t1: genTierForm.amt_t1,
amt_t2: genTierForm.amt_t2,
amt_t3: genTierForm.amt_t3,
amt_t4: genTierForm.amt_t4,
}
const cid = resolveRequestChannelId()
if (cid !== null) {
body.game_channel_id = cid
}
await createAxios(
{
url: '/admin/game.RewardConfig/generateTierBoard',
method: 'post',
data: body,
},
{ showSuccessMessage: true, loading: false }
)
genTierDialogVisible.value = false
await loadData()
} finally {
genTierSubmitting.value = false
}
}
async function onGenWeightClick() {
if (isSuperAdmin.value && superEditScope.value === 'channel' && !formModel.game_channel_id) {
ElMessage.warning(t('game.rewardConfigForm.gen_weight_need_channel'))
return
}
try {
await ElMessageBox.confirm(
t('game.rewardConfigForm.gen_weight_confirm_body'),
t('game.rewardConfigForm.gen_weight_confirm_title'),
{
type: 'warning',
confirmButtonText: t('game.rewardConfigForm.gen_weight_confirm_ok'),
cancelButtonText: t('Cancel'),
}
)
} catch {
return
}
genWeightSubmitting.value = true
try {
const body: Record<string, number> = {}
const cid = resolveRequestChannelId()
if (cid !== null) {
body.game_channel_id = cid
}
await createAxios(
{
url: '/admin/game.RewardConfig/generateRewardWeight',
method: 'post',
data: body,
},
{ showSuccessMessage: true, loading: false }
)
} finally {
genWeightSubmitting.value = false
}
}
onMounted(() => {
if (isSuperAdmin.value) {
superEditScope.value = 'template'
formModel.game_channel_id = 0
loadData()
} else {
loadData()
}
})
watch(
() => formModel.game_channel_id,
(val, oldVal) => {
if (!isSuperAdmin.value || superEditScope.value !== 'channel') {
return
}
if (channelChangeSilent.value) {
return
}
const nextCid = Number(val)
const prevCid = Number(oldVal)
if (Number.isFinite(nextCid) && Number.isFinite(prevCid) && nextCid === prevCid) {
return
}
if (!Number.isFinite(nextCid) || nextCid <= 0) {
return
}
if (lastAutoLoadChannelId.value === nextCid) {
return
}
lastAutoLoadChannelId.value = nextCid
void loadData()
}
)
</script>
<style scoped lang="scss">
.reward-form-card {
max-width: 1280px;
}
.card-title {
font-weight: 600;
}
.intro-alert {
margin-bottom: 16px;
}
.scope-hint {
margin-bottom: 12px;
}
.channel-bar {
display: flex;
align-items: flex-start;
gap: 12px;
margin-bottom: 16px;
flex-wrap: wrap;
}
.channel-picker {
flex: 1 1 360px;
min-width: 320px;
max-width: 520px;
}
.channel-picker :deep(.el-form-item) {
margin-bottom: 0;
}
.channel-picker :deep(.el-select) {
width: 100%;
}
.reward-form-body {
margin-top: 8px;
}
.block-editor {
width: 100%;
}
.tier-editor .line {
display: grid;
grid-template-columns: 64px minmax(88px, 1fr) minmax(88px, 1fr) minmax(88px, 1fr) minmax(96px, 110px) minmax(124px, 1fr);
column-gap: 10px;
row-gap: 8px;
margin-bottom: 8px;
}
.bigwin-editor .line {
display: grid;
grid-template-columns: 110px 1fr 1fr 120px;
gap: 8px;
margin-bottom: 8px;
}
.line-head {
margin-bottom: 6px;
color: var(--el-text-color-secondary);
font-size: 12px;
line-height: 20px;
font-weight: 600;
}
.form-help {
margin-top: 6px;
color: var(--el-text-color-secondary);
font-size: 12px;
}
@media (max-width: 768px) {
.reward-form-card :deep(.el-card__body) {
padding: 12px 10px;
}
.reward-form-body :deep(.el-form-item) {
margin-bottom: 12px;
}
.channel-picker {
min-width: 100%;
max-width: 100%;
}
.channel-bar {
gap: 8px;
margin-bottom: 10px;
}
.channel-bar > .el-button {
width: 100%;
}
/* 移动端主配置表:保持列宽,允许左右滚动,避免被压扁 */
.tier-editor,
.bigwin-editor {
overflow-x: auto;
-webkit-overflow-scrolling: touch;
padding-bottom: 4px;
}
.tier-editor .line {
min-width: 820px;
grid-template-columns: 48px 80px 80px 80px 100px 130px;
column-gap: 10px;
row-gap: 8px;
}
.bigwin-editor .line {
min-width: 560px;
grid-template-columns: 48px 80px 80px 100px;
}
.line-head {
font-size: 11px;
line-height: 16px;
}
.reward-form-body :deep(.el-form-item:last-child .el-form-item__content) {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
.reward-form-body :deep(.el-form-item:last-child .el-button) {
flex: 1 1 calc(50% - 8px);
min-width: 128px;
margin-left: 0;
}
}
</style>
<!-- 弹窗 append-to-body 后不在当前组件 DOM scoped 样式无法作用单独写全局选择器 -->
<style lang="scss">
.gen-tier-dialog.el-dialog {
box-sizing: border-box;
max-width: 100vw;
}
.gen-tier-dialog .el-dialog__header {
padding: 12px 14px;
margin-right: 0;
}
.gen-tier-dialog .el-dialog__body {
padding: 8px 12px 12px;
box-sizing: border-box;
overflow-x: hidden;
}
.gen-tier-dialog .el-dialog__footer {
padding: 10px 12px 14px;
}
.gen-tier-dialog .gen-tier-scroll {
padding-right: 4px;
}
.gen-tier-dialog .gen-rule {
white-space: pre-line;
overflow-wrap: anywhere;
word-break: break-word;
max-width: 100%;
box-sizing: border-box;
background: var(--el-fill-color-light);
padding: 12px 12px;
border-radius: 8px;
margin-bottom: 12px;
font-size: 13px;
line-height: 1.55;
color: var(--el-text-color-regular);
}
.gen-tier-dialog .gen-tier-block {
margin-bottom: 12px;
padding-bottom: 8px;
border-bottom: 1px solid var(--el-border-color-lighter);
}
.gen-tier-dialog .gen-tier-block:last-of-type {
border-bottom: none;
}
.gen-tier-dialog .gen-tier-block-title {
font-weight: 600;
margin-bottom: 8px;
color: var(--el-text-color-primary);
font-size: 14px;
}
.gen-tier-dialog .gen-tier-form .el-form-item {
margin-bottom: 14px;
}
.gen-tier-dialog .gen-tier-form.el-form--label-top .el-form-item__label {
line-height: 1.4;
margin-bottom: 6px;
height: auto;
white-space: normal;
word-break: break-word;
}
.gen-tier-dialog .gen-tier-form .el-input-number {
width: 100%;
max-width: 320px;
}
.gen-tier-dialog .gen-tier-footer-hint {
margin-top: 4px;
padding: 0 2px 8px;
font-size: 12px;
line-height: 1.5;
color: var(--el-text-color-secondary);
overflow-wrap: anywhere;
word-break: break-word;
}
.gen-tier-dialog .gen-tier-dialog-footer {
display: flex;
flex-wrap: wrap;
justify-content: flex-end;
gap: 8px;
width: 100%;
}
@media (max-width: 639px) {
.gen-tier-dialog .gen-tier-form .el-input-number {
max-width: none;
}
}
</style>