feat(admin,api,player): settlement stats, team crests, MS fields and list bet summary
This commit is contained in:
@@ -4,9 +4,8 @@ import { useRoute, useRouter } from 'vue-router';
|
||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||
import { useAdminLocale } from '../../composables/useAdminLocale';
|
||||
import api from '../../api';
|
||||
import CountryFlagSelect from '../../components/outright/CountryFlagSelect.vue';
|
||||
import LogoUrlField from '../../components/LogoUrlField.vue';
|
||||
import {
|
||||
countryFlagUrl,
|
||||
getBuiltinCountry,
|
||||
resolveCountryCode,
|
||||
type BuiltinCountry,
|
||||
@@ -30,6 +29,7 @@ interface SelectionRow {
|
||||
logoUrl: string | null;
|
||||
editOdds: number;
|
||||
editCountryCode: string;
|
||||
editLogoUrl: string;
|
||||
}
|
||||
|
||||
const loading = ref(false);
|
||||
@@ -39,7 +39,9 @@ const meta = ref({
|
||||
leagueZh: '',
|
||||
leagueEn: '',
|
||||
leagueCode: '',
|
||||
matchName: '',
|
||||
titleZh: '',
|
||||
titleEn: '',
|
||||
titleMs: '',
|
||||
status: 'DRAFT',
|
||||
expectedCanonicalCount: null as number | null,
|
||||
playerVisible: true,
|
||||
@@ -53,6 +55,7 @@ const addForm = ref({
|
||||
teamCode: '',
|
||||
teamZh: '',
|
||||
teamEn: '',
|
||||
logoUrl: '',
|
||||
odds: 10,
|
||||
});
|
||||
|
||||
@@ -81,7 +84,9 @@ async function load() {
|
||||
leagueZh: string;
|
||||
leagueEn: string;
|
||||
leagueCode: string;
|
||||
matchName: string;
|
||||
titleZh: string;
|
||||
titleEn: string;
|
||||
titleMs: string;
|
||||
status: string;
|
||||
expectedCanonicalCount: number | null;
|
||||
playerVisible: boolean;
|
||||
@@ -92,7 +97,9 @@ async function load() {
|
||||
leagueZh: payload.leagueZh,
|
||||
leagueEn: payload.leagueEn,
|
||||
leagueCode: payload.leagueCode,
|
||||
matchName: payload.matchName,
|
||||
titleZh: payload.titleZh ?? '',
|
||||
titleEn: payload.titleEn ?? '',
|
||||
titleMs: payload.titleMs ?? '',
|
||||
status: payload.status,
|
||||
expectedCanonicalCount: payload.expectedCanonicalCount,
|
||||
playerVisible: payload.playerVisible,
|
||||
@@ -103,6 +110,7 @@ async function load() {
|
||||
logoUrl: s.logoUrl ?? null,
|
||||
editOdds: Number(s.odds),
|
||||
editCountryCode: resolveCountryCode(s.teamCode, s.logoUrl ?? null),
|
||||
editLogoUrl: s.logoUrl ?? '',
|
||||
}));
|
||||
} catch (e: unknown) {
|
||||
const err = e as { response?: { data?: { error?: string } } };
|
||||
@@ -119,7 +127,9 @@ async function saveMeta() {
|
||||
try {
|
||||
await api.put(`/admin/outrights/${matchId.value}`, {
|
||||
status: meta.value.status,
|
||||
matchName: meta.value.matchName,
|
||||
titleZh: meta.value.titleZh,
|
||||
titleEn: meta.value.titleEn,
|
||||
titleMs: meta.value.titleMs,
|
||||
});
|
||||
ElMessage.success(t('msg.saved'));
|
||||
await load();
|
||||
@@ -171,12 +181,12 @@ async function submitAdd() {
|
||||
teamCode: country.code,
|
||||
teamZh: country.nameZh,
|
||||
teamEn: country.nameEn,
|
||||
logoUrl: countryFlagUrl(country),
|
||||
logoUrl: addForm.value.logoUrl.trim() || undefined,
|
||||
odds: addForm.value.odds,
|
||||
});
|
||||
ElMessage.success(t('msg.saved'));
|
||||
addVisible.value = false;
|
||||
addForm.value = { countryCode: '', teamCode: '', teamZh: '', teamEn: '', odds: 10 };
|
||||
addForm.value = { countryCode: '', teamCode: '', teamZh: '', teamEn: '', logoUrl: '', odds: 10 };
|
||||
await load();
|
||||
} catch (e: unknown) {
|
||||
const err = e as { response?: { data?: { error?: string } } };
|
||||
@@ -255,8 +265,29 @@ function rowDisplayCode(row: SelectionRow) {
|
||||
function isRowDirty(row: SelectionRow) {
|
||||
const countryDirty =
|
||||
row.editCountryCode !== resolveCountryCode(row.teamCode, row.logoUrl);
|
||||
const logoDirty = (row.editLogoUrl || '').trim() !== (row.logoUrl || '').trim();
|
||||
const oddsDirty = row.editOdds !== Number(row.odds);
|
||||
return countryDirty || oddsDirty;
|
||||
return countryDirty || logoDirty || oddsDirty;
|
||||
}
|
||||
|
||||
function onRowCountryPick(row: SelectionRow, country: BuiltinCountry) {
|
||||
row.editCountryCode = country.code;
|
||||
}
|
||||
|
||||
function onRowLogoChange(row: SelectionRow, url: string) {
|
||||
row.editLogoUrl = url;
|
||||
const code = resolveCountryCode(row.editCountryCode || row.teamCode, url);
|
||||
if (code) row.editCountryCode = code;
|
||||
}
|
||||
|
||||
function onAddLogoChange(url: string) {
|
||||
addForm.value.logoUrl = url;
|
||||
const code = resolveCountryCode(addForm.value.countryCode, url);
|
||||
if (code) addForm.value.countryCode = code;
|
||||
}
|
||||
|
||||
function onAddLogoPick(country: BuiltinCountry) {
|
||||
onAddCountryPick(country);
|
||||
}
|
||||
|
||||
async function saveRow(row: SelectionRow) {
|
||||
@@ -271,8 +302,9 @@ async function saveRow(row: SelectionRow) {
|
||||
const country = getBuiltinCountry(row.editCountryCode);
|
||||
const countryDirty =
|
||||
row.editCountryCode !== resolveCountryCode(row.teamCode, row.logoUrl);
|
||||
const logoDirty = (row.editLogoUrl || '').trim() !== (row.logoUrl || '').trim();
|
||||
|
||||
if (countryDirty) {
|
||||
if (countryDirty || logoDirty) {
|
||||
if (!country) {
|
||||
ElMessage.warning(t('outright.err_country'));
|
||||
return;
|
||||
@@ -281,7 +313,7 @@ async function saveRow(row: SelectionRow) {
|
||||
teamCode: country.code,
|
||||
teamZh: country.nameZh,
|
||||
teamEn: country.nameEn,
|
||||
logoUrl: countryFlagUrl(country),
|
||||
logoUrl: row.editLogoUrl.trim() || undefined,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -322,13 +354,19 @@ async function saveRow(row: SelectionRow) {
|
||||
|
||||
<section class="panel settings-panel">
|
||||
<div class="settings-top">
|
||||
<el-input
|
||||
v-model="meta.matchName"
|
||||
size="small"
|
||||
class="title-input"
|
||||
:placeholder="t('outright.field.title_placeholder')"
|
||||
@keyup.enter="saveMeta"
|
||||
/>
|
||||
<div class="title-fields">
|
||||
<el-form label-width="88px" size="small" @submit.prevent="saveMeta">
|
||||
<el-form-item :label="t('outright.field.title_zh')">
|
||||
<el-input v-model="meta.titleZh" @keyup.enter="saveMeta" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('outright.field.title_en')">
|
||||
<el-input v-model="meta.titleEn" @keyup.enter="saveMeta" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('outright.field.title_ms')">
|
||||
<el-input v-model="meta.titleMs" @keyup.enter="saveMeta" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
<div class="settings-actions">
|
||||
<el-button size="small" :loading="saving" @click="saveMeta">
|
||||
{{ t('common.save') }}
|
||||
@@ -369,11 +407,14 @@ async function saveRow(row: SelectionRow) {
|
||||
<div class="table-wrap">
|
||||
<el-table :data="selections" stripe size="small" empty-text="—">
|
||||
<el-table-column prop="rank" :label="t('outright.col.rank')" width="72" align="center" />
|
||||
<el-table-column :label="t('outright.col.country')" min-width="220">
|
||||
<el-table-column :label="t('outright.col.country')" min-width="340">
|
||||
<template #default="{ row }">
|
||||
<CountryFlagSelect
|
||||
v-model="row.editCountryCode"
|
||||
:disabled="!!savingRowId"
|
||||
<LogoUrlField
|
||||
:model-value="row.editLogoUrl"
|
||||
compact
|
||||
:team-code="row.editCountryCode || row.teamCode"
|
||||
@update:model-value="onRowLogoChange(row, $event)"
|
||||
@pick="onRowCountryPick(row, $event)"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
@@ -431,13 +472,14 @@ async function saveRow(row: SelectionRow) {
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<el-dialog v-model="addVisible" :title="t('outright.btn.add_team')" width="440px">
|
||||
<el-dialog v-model="addVisible" :title="t('outright.btn.add_team')" width="520px">
|
||||
<el-form label-width="100px">
|
||||
<el-form-item :label="t('outright.col.country')" required>
|
||||
<CountryFlagSelect
|
||||
v-model="addForm.countryCode"
|
||||
size="default"
|
||||
@pick="onAddCountryPick"
|
||||
<LogoUrlField
|
||||
:model-value="addForm.logoUrl"
|
||||
:team-code="addForm.countryCode"
|
||||
@update:model-value="onAddLogoChange"
|
||||
@pick="onAddLogoPick"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="addForm.teamCode" :label="t('outright.col.code')">
|
||||
@@ -505,9 +547,14 @@ async function saveRow(row: SelectionRow) {
|
||||
|
||||
.settings-top {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
gap: 8px;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.title-fields {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.league-meta {
|
||||
@@ -528,11 +575,6 @@ async function saveRow(row: SelectionRow) {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.title-input {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.panel-head.compact {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
@@ -59,6 +59,7 @@ const createForm = ref({
|
||||
leagueId: '',
|
||||
titleZh: '',
|
||||
titleEn: '',
|
||||
titleMs: '',
|
||||
status: 'PUBLISHED',
|
||||
});
|
||||
|
||||
@@ -166,7 +167,7 @@ async function submitCreate() {
|
||||
const { data } = await api.post('/admin/outrights', createForm.value);
|
||||
ElMessage.success(t('msg.saved'));
|
||||
createVisible.value = false;
|
||||
createForm.value = { leagueId: '', titleZh: '', titleEn: '', status: 'PUBLISHED' };
|
||||
createForm.value = { leagueId: '', titleZh: '', titleEn: '', titleMs: '', status: 'PUBLISHED' };
|
||||
listReady.value = false;
|
||||
rowDetails.value = {};
|
||||
await loadEvents(false);
|
||||
@@ -292,7 +293,7 @@ onMounted(() => {
|
||||
</el-table>
|
||||
</el-card>
|
||||
|
||||
<el-dialog v-model="createVisible" :title="t('outright.btn.create_event')" width="440px">
|
||||
<el-dialog v-model="createVisible" :title="t('outright.btn.create_event')" width="480px">
|
||||
<el-form label-width="100px" size="small">
|
||||
<el-form-item :label="t('outright.field.league')">
|
||||
<el-select v-model="createForm.leagueId" filterable style="width: 100%">
|
||||
@@ -310,6 +311,9 @@ onMounted(() => {
|
||||
<el-form-item :label="t('outright.field.title_en')">
|
||||
<el-input v-model="createForm.titleEn" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('outright.field.title_ms')">
|
||||
<el-input v-model="createForm.titleMs" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button size="small" @click="createVisible = false">{{ t('common.cancel') }}</el-button>
|
||||
|
||||
Reference in New Issue
Block a user