Files
gamegroup2/frontend/src/components/gameBlacklist/CreateBlacklistDialog.vue
T
congsh 60ad9a04cd feat: phase 4 - 积分竞猜和游戏黑名单 v0.3.0
竞猜功能:发起竞猜、下注、关闭、开奖、奖池分配
黑名单功能:标记游戏、按原因/严重程度筛选、详情展开
修复:双重结算、TOCTOU竞态、订阅泄漏、选项选择兼容性

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-19 00:21:43 +08:00

206 lines
4.4 KiB
Vue

<script setup lang="ts">
import { ref } from 'vue'
import { ElMessage } from 'element-plus'
import { createBlacklistEntry } from '@/api/gameBlacklist'
import { useGroupStore } from '@/stores/group'
import { BlacklistReasonMap, BlacklistSeverityMap } from '@/types'
import type { BlacklistReason, BlacklistSeverity } from '@/types'
const visible = defineModel<boolean>({ default: false })
const emit = defineEmits<{
created: []
}>()
const groupStore = useGroupStore()
const form = ref({
gameName: '',
reason: '' as BlacklistReason | '',
severity: '' as BlacklistSeverity | '',
description: '',
})
const loading = ref(false)
function resetForm() {
form.value = {
gameName: '',
reason: '',
severity: '',
description: '',
}
}
async function handleSubmit() {
// 校验必填字段
if (!form.value.gameName.trim()) {
ElMessage.warning('请输入游戏名称')
return
}
if (!form.value.reason) {
ElMessage.warning('请选择原因')
return
}
if (!form.value.severity) {
ElMessage.warning('请选择严重程度')
return
}
if (!form.value.description.trim()) {
ElMessage.warning('请填写描述')
return
}
const groupId = groupStore.currentGroupId
if (!groupId) {
ElMessage.error('请先选择群组')
return
}
loading.value = true
try {
await createBlacklistEntry({
group: groupId,
gameName: form.value.gameName.trim(),
reason: form.value.reason,
severity: form.value.severity,
description: form.value.description.trim(),
})
visible.value = false
resetForm()
ElMessage.success('标记成功')
emit('created')
} catch (error: any) {
ElMessage.error(error.message || '标记失败')
} finally {
loading.value = false
}
}
function handleOpen() {
resetForm()
}
</script>
<template>
<el-dialog
v-model="visible"
title="标记坑游戏"
width="460px"
@open="handleOpen"
>
<div class="create-form">
<!-- 游戏名称 -->
<div class="form-field">
<label>游戏名称 <span class="required">*</span></label>
<el-input
v-model="form.gameName"
placeholder="输入游戏名称"
maxlength="100"
show-word-limit
/>
</div>
<!-- 原因选择 -->
<div class="form-field">
<label>原因 <span class="required">*</span></label>
<el-select
v-model="form.reason"
placeholder="选择原因"
style="width: 100%"
>
<el-option
v-for="(label, key) in BlacklistReasonMap"
:key="key"
:label="label"
:value="key"
/>
</el-select>
</div>
<!-- 严重程度 -->
<div class="form-field">
<label>严重程度 <span class="required">*</span></label>
<el-select
v-model="form.severity"
placeholder="选择严重程度"
style="width: 100%"
>
<el-option
v-for="(label, key) in BlacklistSeverityMap"
:key="key"
:label="label"
:value="key"
/>
</el-select>
</div>
<!-- 描述 -->
<div class="form-field">
<label>描述 <span class="required">*</span></label>
<el-input
v-model="form.description"
type="textarea"
placeholder="描述一下为什么这游戏坑"
:rows="3"
maxlength="500"
show-word-limit
/>
</div>
</div>
<template #footer>
<el-button @click="visible = false">取消</el-button>
<button class="submit-btn" :disabled="loading" @click="handleSubmit">
{{ loading ? '提交中...' : '提交' }}
</button>
</template>
</el-dialog>
</template>
<style scoped>
.create-form {
display: flex;
flex-direction: column;
gap: 20px;
}
.form-field {
display: flex;
flex-direction: column;
gap: 8px;
}
.form-field label {
font-size: 14px;
font-weight: 500;
color: var(--gg-text);
}
.required {
color: var(--gg-danger);
}
.submit-btn {
padding: 8px 20px;
border: none;
border-radius: var(--gg-radius-sm);
background: var(--gg-primary);
color: #fff;
font-size: 14px;
font-weight: 500;
cursor: pointer;
transition: opacity 0.2s;
}
.submit-btn:hover {
opacity: 0.85;
}
.submit-btn:disabled {
opacity: 0.6;
cursor: not-allowed;
}
</style>