714 lines
20 KiB
Vue
714 lines
20 KiB
Vue
<script lang="ts" setup>
|
||
import { computed, reactive, ref, watch } from 'vue';
|
||
|
||
import { Page } from '@vben/common-ui';
|
||
import { useAccessStore } from '@vben/stores';
|
||
|
||
import {
|
||
Button,
|
||
Card,
|
||
Input,
|
||
InputNumber,
|
||
message,
|
||
Modal,
|
||
Space,
|
||
Spin,
|
||
Switch,
|
||
Tag,
|
||
TextArea,
|
||
} from 'antdv-next';
|
||
|
||
import {
|
||
getResidentInviteConfig,
|
||
saveResidentInviteConfig,
|
||
} from '#/api/legacy/resident-activity';
|
||
import ActivityResourceGroupSelectDrawer from '#/views/props/components/activity-resource-group-select-drawer.vue';
|
||
import { getAllowedSysOrigins } from '#/views/system/shared';
|
||
|
||
defineOptions({ name: 'ResidentInviteConfigPage' });
|
||
|
||
function createReward() {
|
||
return {
|
||
goldAmount: 0,
|
||
id: null,
|
||
rewardGroupId: null,
|
||
rewardGroupName: '',
|
||
};
|
||
}
|
||
|
||
function createRule(threshold = 1) {
|
||
return {
|
||
enabled: true,
|
||
goldAmount: 0,
|
||
id: null,
|
||
rewardGroupId: null,
|
||
rewardGroupName: '',
|
||
threshold,
|
||
};
|
||
}
|
||
|
||
const accessStore = useAccessStore();
|
||
const sysOriginOptions = computed(() => {
|
||
const options = getAllowedSysOrigins(accessStore.accessCodes || []);
|
||
return options.length > 0 ? options : getAllowedSysOrigins([]);
|
||
});
|
||
|
||
const loading = ref(false);
|
||
const saving = ref(false);
|
||
const groupPickerOpen = ref(false);
|
||
|
||
const pickerTarget = reactive({
|
||
listKey: '',
|
||
rowIndex: -1,
|
||
});
|
||
|
||
const form = reactive<Record<string, any>>({
|
||
antiCheat: {
|
||
sameDeviceOnce: true,
|
||
sameIpOnce: true,
|
||
},
|
||
downloadUrl: '',
|
||
enabled: false,
|
||
inviteeBindReward: createReward(),
|
||
inviteeRechargeRules: [],
|
||
inviterBindReward: createReward(),
|
||
inviterTotalRechargeRules: [],
|
||
inviterValidCountRules: [],
|
||
serviceContact: '',
|
||
shareDesc: '',
|
||
shareTitle: '',
|
||
sysOrigin: '',
|
||
timezone: 'Asia/Riyadh',
|
||
});
|
||
|
||
const statusMeta = computed(() => (
|
||
form.enabled
|
||
? { color: 'success', text: '已启用' }
|
||
: { color: 'default', text: '已关闭' }
|
||
));
|
||
|
||
const summaryCards = computed(() => [
|
||
{
|
||
label: '邀请方首邀奖励',
|
||
value: formatRewardSummary(form.inviterBindReward),
|
||
},
|
||
{
|
||
label: '被邀请方注册礼包',
|
||
value: `${formatRewardSummary(form.inviteeBindReward)} / H5 固定展示 +1000 金币`,
|
||
},
|
||
{
|
||
label: '被邀请人充值阶梯',
|
||
value: formatRuleSummary('inviteeRechargeRules'),
|
||
},
|
||
{
|
||
label: '邀请方任务阶梯',
|
||
value: [
|
||
`人数 ${formatRuleSummary('inviterValidCountRules')}`,
|
||
`充值 ${formatRuleSummary('inviterTotalRechargeRules')}`,
|
||
].join(' / '),
|
||
},
|
||
]);
|
||
|
||
function formatRewardSummary(reward: Record<string, any>) {
|
||
const parts: string[] = [];
|
||
const goldAmount = Number(reward?.goldAmount || 0);
|
||
if (goldAmount > 0) {
|
||
parts.push(`${goldAmount} 金币`);
|
||
}
|
||
if (reward?.rewardGroupName) {
|
||
parts.push(String(reward.rewardGroupName));
|
||
}
|
||
return parts.length > 0 ? parts.join(' + ') : '未配置';
|
||
}
|
||
|
||
function formatRuleSummary(listKey: string) {
|
||
const list = (form[listKey] || []) as Array<Record<string, any>>;
|
||
if (list.length === 0) {
|
||
return '0 个阶梯';
|
||
}
|
||
const enabledCount = list.filter((item) => item.enabled !== false).length;
|
||
return `${enabledCount}/${list.length} 启用`;
|
||
}
|
||
|
||
function normalizeReward(value: null | Record<string, any> | undefined) {
|
||
return {
|
||
goldAmount: Number(value?.goldAmount || 0),
|
||
id: value?.id ?? null,
|
||
rewardGroupId: value?.rewardGroupId ?? null,
|
||
rewardGroupName: value?.rewardGroupName || '',
|
||
};
|
||
}
|
||
|
||
function normalizeRuleList(list: Array<Record<string, any>> | null | undefined) {
|
||
return (list || []).map((item) => ({
|
||
enabled: item?.enabled !== false,
|
||
goldAmount: Number(item?.goldAmount || 0),
|
||
id: item?.id ?? null,
|
||
rewardGroupId: item?.rewardGroupId ?? null,
|
||
rewardGroupName: item?.rewardGroupName || '',
|
||
threshold: Number(item?.threshold || 0),
|
||
}));
|
||
}
|
||
|
||
function normalizeConfig(result: null | Record<string, any> | undefined) {
|
||
const value = result ?? {};
|
||
form.sysOrigin = String(value.sysOrigin || form.sysOrigin || sysOriginOptions.value[0]?.value || '');
|
||
form.enabled = Boolean(value.enabled);
|
||
form.downloadUrl = String(value.downloadUrl || '');
|
||
form.shareTitle = String(value.shareTitle || '');
|
||
form.shareDesc = String(value.shareDesc || '');
|
||
form.serviceContact = String(value.serviceContact || '');
|
||
form.timezone = String(value.timezone || 'Asia/Riyadh');
|
||
form.antiCheat.sameIpOnce = value?.antiCheat?.sameIpOnce !== false;
|
||
form.antiCheat.sameDeviceOnce = value?.antiCheat?.sameDeviceOnce !== false;
|
||
form.inviterBindReward = normalizeReward(value.inviterBindReward);
|
||
form.inviteeBindReward = normalizeReward(value.inviteeBindReward);
|
||
form.inviteeRechargeRules = normalizeRuleList(value.inviteeRechargeRules);
|
||
form.inviterValidCountRules = normalizeRuleList(value.inviterValidCountRules);
|
||
form.inviterTotalRechargeRules = normalizeRuleList(value.inviterTotalRechargeRules);
|
||
}
|
||
|
||
async function loadData() {
|
||
if (!form.sysOrigin) {
|
||
return;
|
||
}
|
||
loading.value = true;
|
||
try {
|
||
const result = await getResidentInviteConfig(form.sysOrigin);
|
||
normalizeConfig(result);
|
||
} finally {
|
||
loading.value = false;
|
||
}
|
||
}
|
||
|
||
watch(
|
||
sysOriginOptions,
|
||
(options) => {
|
||
if (!form.sysOrigin) {
|
||
form.sysOrigin = String(options[0]?.value || '');
|
||
}
|
||
},
|
||
{ immediate: true },
|
||
);
|
||
|
||
watch(
|
||
() => form.sysOrigin,
|
||
(value) => {
|
||
if (value) {
|
||
void loadData();
|
||
}
|
||
},
|
||
{ immediate: true },
|
||
);
|
||
|
||
function addRule(listKey: string) {
|
||
const list = form[listKey] as Array<Record<string, any>>;
|
||
const nextThreshold = list.length > 0 ? Number(list[list.length - 1]?.threshold || 0) + 1 : 1;
|
||
list.push(createRule(nextThreshold));
|
||
}
|
||
|
||
function normalizeRowIndex(index: number | string) {
|
||
return typeof index === 'number' ? index : Number(index || 0);
|
||
}
|
||
|
||
function removeRule(listKey: string, index: number | string) {
|
||
(form[listKey] as Array<Record<string, any>>).splice(normalizeRowIndex(index), 1);
|
||
}
|
||
|
||
function openRewardPicker(listKey: string, rowIndex: number | string = -1) {
|
||
pickerTarget.listKey = listKey;
|
||
pickerTarget.rowIndex = normalizeRowIndex(rowIndex);
|
||
groupPickerOpen.value = true;
|
||
}
|
||
|
||
function clearRewardGroup(listKey: string, rowIndex: number | string = -1) {
|
||
const normalizedRowIndex = normalizeRowIndex(rowIndex);
|
||
const target = normalizedRowIndex >= 0
|
||
? (form[listKey] as Array<Record<string, any>>)[normalizedRowIndex]
|
||
: form[listKey];
|
||
target.rewardGroupId = null;
|
||
target.rewardGroupName = '';
|
||
}
|
||
|
||
function handleRewardGroupSelect(row: Record<string, any>) {
|
||
const target = pickerTarget.rowIndex >= 0
|
||
? (form[pickerTarget.listKey] as Array<Record<string, any>>)[pickerTarget.rowIndex]
|
||
: form[pickerTarget.listKey];
|
||
target.rewardGroupId = row.id;
|
||
target.rewardGroupName = row.name;
|
||
groupPickerOpen.value = false;
|
||
}
|
||
|
||
async function submitForm() {
|
||
saving.value = true;
|
||
try {
|
||
await saveResidentInviteConfig({
|
||
antiCheat: { ...form.antiCheat },
|
||
downloadUrl: String(form.downloadUrl || '').trim(),
|
||
enabled: Boolean(form.enabled),
|
||
inviteeBindReward: { ...form.inviteeBindReward },
|
||
inviteeRechargeRules: (form.inviteeRechargeRules || []).map((item: Record<string, any>) => ({
|
||
enabled: Boolean(item.enabled),
|
||
goldAmount: Number(item.goldAmount || 0),
|
||
id: item.id ?? null,
|
||
rewardGroupId: item.rewardGroupId ?? null,
|
||
threshold: Number(item.threshold || 0),
|
||
})),
|
||
inviterBindReward: { ...form.inviterBindReward },
|
||
inviterTotalRechargeRules: (form.inviterTotalRechargeRules || []).map((item: Record<string, any>) => ({
|
||
enabled: Boolean(item.enabled),
|
||
goldAmount: Number(item.goldAmount || 0),
|
||
id: item.id ?? null,
|
||
rewardGroupId: item.rewardGroupId ?? null,
|
||
threshold: Number(item.threshold || 0),
|
||
})),
|
||
inviterValidCountRules: (form.inviterValidCountRules || []).map((item: Record<string, any>) => ({
|
||
enabled: Boolean(item.enabled),
|
||
goldAmount: Number(item.goldAmount || 0),
|
||
id: item.id ?? null,
|
||
rewardGroupId: item.rewardGroupId ?? null,
|
||
threshold: Number(item.threshold || 0),
|
||
})),
|
||
serviceContact: String(form.serviceContact || '').trim(),
|
||
shareDesc: String(form.shareDesc || '').trim(),
|
||
shareTitle: String(form.shareTitle || '').trim(),
|
||
sysOrigin: form.sysOrigin,
|
||
timezone: String(form.timezone || '').trim(),
|
||
});
|
||
message.success('保存成功');
|
||
await loadData();
|
||
} finally {
|
||
saving.value = false;
|
||
}
|
||
}
|
||
|
||
function handleSubmit() {
|
||
Modal.confirm({
|
||
async onOk() {
|
||
await submitForm();
|
||
},
|
||
title: '确认覆盖当前系统的整套邀请活动配置?',
|
||
});
|
||
}
|
||
|
||
const rewardSections = [
|
||
{ key: 'inviteeRechargeRules', title: '被邀请人充值达标奖励', thresholdLabel: '充值金币阈值' },
|
||
{ key: 'inviterValidCountRules', title: '邀请有效人数任务', thresholdLabel: '邀请人数阈值' },
|
||
{ key: 'inviterTotalRechargeRules', title: '邀请名下累计充值任务', thresholdLabel: '累计充值金币阈值' },
|
||
];
|
||
</script>
|
||
|
||
<template>
|
||
<Page auto-content-height title="邀请活动配置">
|
||
<div class="invite-config-page">
|
||
<Card :bordered="false" class="overview-card">
|
||
<div class="toolbar">
|
||
<Space align="end" wrap>
|
||
<div class="toolbar-item">
|
||
<div class="field-label">系统</div>
|
||
<SysOriginSelect
|
||
v-model:value="form.sysOrigin"
|
||
:options="sysOriginOptions"
|
||
style="width: 180px"
|
||
/>
|
||
</div>
|
||
<Tag :color="statusMeta.color">
|
||
{{ statusMeta.text }}
|
||
</Tag>
|
||
</Space>
|
||
<Button :loading="saving" type="primary" @click="handleSubmit">
|
||
保存配置
|
||
</Button>
|
||
</div>
|
||
|
||
<div class="summary-grid">
|
||
<div
|
||
v-for="item in summaryCards"
|
||
:key="item.label"
|
||
class="summary-item"
|
||
>
|
||
<div class="summary-label">{{ item.label }}</div>
|
||
<div class="summary-value" :title="item.value">{{ item.value }}</div>
|
||
</div>
|
||
</div>
|
||
</Card>
|
||
|
||
<Spin :spinning="loading">
|
||
<div class="content-grid">
|
||
<Card :bordered="false" class="config-card basic-card" title="基础设置">
|
||
<div class="form-grid">
|
||
<div class="field compact-field">
|
||
<div class="field-label">活动开关</div>
|
||
<Switch v-model:checked="form.enabled" />
|
||
</div>
|
||
<div class="field">
|
||
<div class="field-label">时区</div>
|
||
<Input v-model:value="form.timezone" />
|
||
</div>
|
||
<div class="field field-span-2">
|
||
<div class="field-label">下载地址</div>
|
||
<Input v-model:value="form.downloadUrl" />
|
||
</div>
|
||
<div class="field">
|
||
<div class="field-label">分享标题</div>
|
||
<Input v-model:value="form.shareTitle" />
|
||
</div>
|
||
<div class="field">
|
||
<div class="field-label">客服联系方式</div>
|
||
<Input v-model:value="form.serviceContact" />
|
||
</div>
|
||
<div class="field field-span-2">
|
||
<div class="field-label">分享描述</div>
|
||
<TextArea
|
||
v-model:value="form.shareDesc"
|
||
:auto-size="{ minRows: 2, maxRows: 4 }"
|
||
/>
|
||
</div>
|
||
<div class="anti-cheat field-span-2">
|
||
<div class="switch-row">
|
||
<span class="field-label">同 IP 只计一次</span>
|
||
<Switch v-model:checked="form.antiCheat.sameIpOnce" />
|
||
</div>
|
||
<div class="switch-row">
|
||
<span class="field-label">同设备只计一次</span>
|
||
<Switch v-model:checked="form.antiCheat.sameDeviceOnce" />
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</Card>
|
||
|
||
<div class="reward-card-list">
|
||
<Card :bordered="false" class="config-card" title="邀请方首邀奖励">
|
||
<div class="reward-row">
|
||
<div class="field amount-field">
|
||
<div class="field-label">金币</div>
|
||
<InputNumber
|
||
v-model:value="form.inviterBindReward.goldAmount"
|
||
:min="0"
|
||
style="width: 100%"
|
||
/>
|
||
</div>
|
||
<div class="field field-grow">
|
||
<div class="field-label">奖励组</div>
|
||
<div class="reward-group-box">
|
||
<Tag v-if="form.inviterBindReward.rewardGroupName" color="blue">
|
||
{{ form.inviterBindReward.rewardGroupName }}
|
||
</Tag>
|
||
<span v-else class="placeholder">未选择奖励组</span>
|
||
</div>
|
||
</div>
|
||
<Space class="reward-actions" wrap>
|
||
<Button @click="openRewardPicker('inviterBindReward')">选择奖励组</Button>
|
||
<Button @click="clearRewardGroup('inviterBindReward')">清空</Button>
|
||
</Space>
|
||
</div>
|
||
</Card>
|
||
|
||
<Card :bordered="false" class="config-card" title="被邀请方注册礼包(可不填,H5 固定展示 +1000 金币)">
|
||
<div class="reward-row">
|
||
<div class="field amount-field">
|
||
<div class="field-label">金币</div>
|
||
<InputNumber
|
||
v-model:value="form.inviteeBindReward.goldAmount"
|
||
:min="0"
|
||
style="width: 100%"
|
||
/>
|
||
</div>
|
||
<div class="field field-grow">
|
||
<div class="field-label">奖励组</div>
|
||
<div class="reward-group-box">
|
||
<Tag v-if="form.inviteeBindReward.rewardGroupName" color="blue">
|
||
{{ form.inviteeBindReward.rewardGroupName }}
|
||
</Tag>
|
||
<span v-else class="placeholder">未选择奖励组</span>
|
||
</div>
|
||
</div>
|
||
<Space class="reward-actions" wrap>
|
||
<Button @click="openRewardPicker('inviteeBindReward')">选择奖励组</Button>
|
||
<Button @click="clearRewardGroup('inviteeBindReward')">清空</Button>
|
||
</Space>
|
||
</div>
|
||
</Card>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="rule-section-list">
|
||
<Card
|
||
v-for="section in rewardSections"
|
||
:key="section.key"
|
||
:bordered="false"
|
||
class="config-card"
|
||
:title="section.title"
|
||
>
|
||
<template #extra>
|
||
<Button type="dashed" @click="addRule(section.key)">新增阶梯</Button>
|
||
</template>
|
||
|
||
<div v-if="(form[section.key] || []).length === 0" class="empty-box">
|
||
暂无配置
|
||
</div>
|
||
|
||
<div
|
||
v-for="(item, index) in form[section.key]"
|
||
:key="`${section.key}-${index}`"
|
||
class="rule-item"
|
||
>
|
||
<div class="field amount-field">
|
||
<div class="field-label">{{ section.thresholdLabel }}</div>
|
||
<InputNumber v-model:value="item.threshold" :min="1" style="width: 100%" />
|
||
</div>
|
||
<div class="field amount-field">
|
||
<div class="field-label">金币</div>
|
||
<InputNumber v-model:value="item.goldAmount" :min="0" style="width: 100%" />
|
||
</div>
|
||
<div class="field field-grow">
|
||
<div class="field-label">奖励组</div>
|
||
<div class="reward-group-box">
|
||
<Tag v-if="item.rewardGroupName" color="blue">
|
||
{{ item.rewardGroupName }}
|
||
</Tag>
|
||
<span v-else class="placeholder">未选择奖励组</span>
|
||
</div>
|
||
</div>
|
||
<div class="field switch-field">
|
||
<div class="field-label">启用</div>
|
||
<Switch v-model:checked="item.enabled" />
|
||
</div>
|
||
<Space class="rule-actions" wrap>
|
||
<Button @click="openRewardPicker(section.key, index)">选择奖励组</Button>
|
||
<Button @click="clearRewardGroup(section.key, index)">清空</Button>
|
||
<Button danger @click="removeRule(section.key, index)">删除</Button>
|
||
</Space>
|
||
</div>
|
||
</Card>
|
||
</div>
|
||
</Spin>
|
||
</div>
|
||
|
||
<ActivityResourceGroupSelectDrawer
|
||
:open="groupPickerOpen"
|
||
:sys-origin="form.sysOrigin"
|
||
@close="groupPickerOpen = false"
|
||
@select="handleRewardGroupSelect"
|
||
/>
|
||
</Page>
|
||
</template>
|
||
|
||
<style scoped>
|
||
.invite-config-page {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 16px;
|
||
}
|
||
|
||
.overview-card,
|
||
.config-card {
|
||
border-radius: 8px;
|
||
}
|
||
|
||
.toolbar {
|
||
display: flex;
|
||
gap: 16px;
|
||
align-items: flex-end;
|
||
justify-content: space-between;
|
||
margin-bottom: 16px;
|
||
}
|
||
|
||
.toolbar-item {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 8px;
|
||
}
|
||
|
||
.field-label {
|
||
color: #475569;
|
||
font-size: 13px;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.summary-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||
gap: 12px;
|
||
}
|
||
|
||
.summary-item {
|
||
min-width: 0;
|
||
padding: 12px;
|
||
background: #f8fafc;
|
||
border: 1px solid #eef2f7;
|
||
border-radius: 6px;
|
||
}
|
||
|
||
.summary-label {
|
||
color: #64748b;
|
||
font-size: 12px;
|
||
}
|
||
|
||
.summary-value {
|
||
margin-top: 6px;
|
||
overflow: hidden;
|
||
color: #0f172a;
|
||
font-weight: 600;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.content-grid {
|
||
display: grid;
|
||
grid-template-columns: minmax(0, 1.15fr) minmax(360px, 0.85fr);
|
||
gap: 16px;
|
||
align-items: start;
|
||
margin-top: 16px;
|
||
}
|
||
|
||
.reward-card-list,
|
||
.rule-section-list {
|
||
display: grid;
|
||
gap: 16px;
|
||
}
|
||
|
||
.rule-section-list {
|
||
margin-top: 16px;
|
||
}
|
||
|
||
.form-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||
gap: 16px;
|
||
}
|
||
|
||
.field {
|
||
min-width: 0;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 8px;
|
||
}
|
||
|
||
.compact-field {
|
||
align-items: flex-start;
|
||
}
|
||
|
||
.field-span-2 {
|
||
grid-column: span 2;
|
||
}
|
||
|
||
.anti-cheat {
|
||
display: grid;
|
||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||
gap: 12px;
|
||
}
|
||
|
||
.switch-row {
|
||
min-height: 48px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
padding: 12px;
|
||
background: #f8fafc;
|
||
border: 1px solid #eef2f7;
|
||
border-radius: 6px;
|
||
gap: 12px;
|
||
}
|
||
|
||
.reward-row {
|
||
display: grid;
|
||
grid-template-columns: 160px minmax(220px, 1fr) auto;
|
||
gap: 12px;
|
||
align-items: end;
|
||
}
|
||
|
||
.rule-item {
|
||
display: grid;
|
||
grid-template-columns: 160px 160px minmax(240px, 1fr) 88px auto;
|
||
gap: 12px;
|
||
align-items: end;
|
||
padding: 12px;
|
||
background: #fbfdff;
|
||
border: 1px solid #eef2f7;
|
||
border-radius: 6px;
|
||
}
|
||
|
||
.rule-item + .rule-item {
|
||
margin-top: 12px;
|
||
}
|
||
|
||
.amount-field {
|
||
width: 160px;
|
||
}
|
||
|
||
.field-grow {
|
||
min-width: 0;
|
||
}
|
||
|
||
.switch-field {
|
||
width: 88px;
|
||
}
|
||
|
||
.reward-group-box {
|
||
min-height: 32px;
|
||
display: flex;
|
||
align-items: center;
|
||
min-width: 0;
|
||
padding: 0 11px;
|
||
overflow: hidden;
|
||
background: #fff;
|
||
border: 1px solid #d9d9d9;
|
||
border-radius: 6px;
|
||
}
|
||
|
||
.placeholder {
|
||
color: #94a3b8;
|
||
}
|
||
|
||
.reward-actions,
|
||
.rule-actions {
|
||
justify-content: flex-end;
|
||
}
|
||
|
||
.empty-box {
|
||
color: #94a3b8;
|
||
padding: 12px 0;
|
||
}
|
||
|
||
@media (max-width: 1280px) {
|
||
.content-grid {
|
||
grid-template-columns: 1fr;
|
||
}
|
||
|
||
.rule-item {
|
||
grid-template-columns: 160px 160px minmax(240px, 1fr);
|
||
}
|
||
|
||
.switch-field,
|
||
.rule-actions {
|
||
width: auto;
|
||
grid-column: span 1;
|
||
}
|
||
}
|
||
|
||
@media (max-width: 900px) {
|
||
.toolbar {
|
||
align-items: stretch;
|
||
flex-direction: column;
|
||
}
|
||
|
||
.summary-grid,
|
||
.form-grid,
|
||
.anti-cheat,
|
||
.reward-row,
|
||
.rule-item {
|
||
grid-template-columns: 1fr;
|
||
}
|
||
|
||
.field-span-2 {
|
||
grid-column: span 1;
|
||
}
|
||
|
||
.amount-field,
|
||
.switch-field {
|
||
width: 100%;
|
||
}
|
||
}
|
||
</style>
|