Commit feb96717 by lijiabin

【需求 20520】 feat: 完成后台大转盘

parent 6f7c627b
<template>
<el-dialog v-model="visible" title="抽奖配置" width="500px">
<el-form :model="form" label-width="100px" :rules="formRules" ref="formRef">
<el-form-item label="开放时间" prop="openRangeTime">
<el-date-picker
v-model="form.openRangeTime"
type="daterange"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
format="YYYY-MM-DD"
value-format="X"
/>
</el-form-item>
<el-form-item label="参与费用" prop="costYaCoin">
<el-input-number
v-model="form.costYaCoin"
:min="0"
:max="1000000"
controls-position="right"
/>
<span class="ml-2">YA币</span>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="visible = false">取消</el-button>
<el-button type="primary" @click="handleSubmit" :loading="loading">保存</el-button>
</template>
</el-dialog>
</template>
<script setup lang="ts">
import { getWheelConfig, setWheelConfig } from '@/api/backend'
import { useResetData } from '@/hooks'
import type { FormInstance, FormRules } from 'element-plus'
import { push } from 'notivue'
import { formatSeconds } from '@/utils'
const formRules: FormRules = {
openRangeTime: [{ required: true, message: '请选择开放时间', trigger: 'change' }],
costYaCoin: [{ required: true, message: '请输入参与费用', trigger: 'change' }],
}
const [form, resetForm] = useResetData<{
openRangeTime: string[]
costYaCoin: number
}>({
openRangeTime: [],
costYaCoin: 0,
})
const visible = ref(false)
const formRef = ref<FormInstance>()
const loading = ref(false)
const open = async () => {
const { data } = await getWheelConfig()
if (!data) {
resetForm()
} else {
form.value = {
openRangeTime: [String(data.startTime), String(data.endTime)],
costYaCoin: data.costYaCoin,
}
}
visible.value = true
}
const handleSubmit = async () => {
await formRef.value?.validate()
loading.value = true
try {
await setWheelConfig({
startTime: form.value.openRangeTime[0]!,
endTime: formatSeconds(form.value.openRangeTime[1]!),
costYaCoin: form.value.costYaCoin,
})
push.success('保存成功')
visible.value = false
} catch (error) {
console.error(error)
} finally {
loading.value = false
}
}
defineExpose({ open })
</script>
<template>
<div class="official-tag-page">
<!-- 搜索栏 -->
<div class="search-section">
<el-input
v-model="searchParams.name"
placeholder="请输入奖品名称"
clearable
class="w-200px! mr-12px"
/>
<el-select
v-model="searchParams.isEnabled"
placeholder="是否启用"
clearable
class="w-200px! mr-12px"
>
<el-option label="是" :value="BooleanFlag.YES" />
<el-option label="否" :value="BooleanFlag.NO" />
</el-select>
<el-button type="primary" @click="refresh">
<el-icon><IEpSearch /></el-icon>
搜索
</el-button>
<el-button @click="reset">重置</el-button>
<el-button type="primary" @click="handleAdd">
<el-icon><IEpPlus /></el-icon>
新增
</el-button>
<el-button type="primary" @click="handleWheelConfig">
<el-icon class="mr-2"><IEpSetting /></el-icon>
抽奖配置
</el-button>
</div>
<!-- 表格区域 -->
<div class="table-section">
<div class="table-wrapper">
<el-table v-loading="loading" :data="list" height="100%">
<el-table-column prop="name" label="名称" />
<el-table-column prop="imageUrl" label="图片">
<template #default="{ row }">
<el-image
v-if="row.imageUrl"
:preview-teleported="true"
:src="row.imageUrl"
class="w-20 h-20 object-cover"
:preview-src-list="[row.imageUrl]"
/>
<span v-else>暂无图片</span>
</template>
</el-table-column>
<el-table-column prop="quantity" label="奖品数量" />
<el-table-column prop="probability" label="中奖概率">
<template #default="{ row }"> {{ row.probability }}% </template>
</el-table-column>
<el-table-column prop="isEnabled" label="是否启用">
<template #default="{ row }">
{{ row.isEnabled === BooleanFlag.YES ? '是' : '否' }}
</template>
</el-table-column>
<el-table-column prop="createdAt" label="创建时间">
<template #default="{ row }">
{{ dayjs(row.createdAt * 1000).format('YYYY-MM-DD HH:mm:ss') }}
</template>
</el-table-column>
<el-table-column label="操作" fixed="right" width="140">
<template #default="{ row }">
<el-button type="primary" link @click="handleEdit(row)">编辑</el-button>
<el-button type="danger" link @click="handleDelete(row)">删除</el-button>
</template>
</el-table-column>
</el-table>
</div>
<!-- 分页 -->
<div class="pagination-wrapper">
<el-pagination
v-model:current-page="searchParams.current"
v-model:page-size="searchParams.size"
:total="total"
:page-sizes="[10, 20, 30]"
layout="total, sizes, prev, pager, next, jumper"
@size-change="changePageSize"
@current-change="goToPage"
/>
</div>
</div>
<!-- 新增/编辑对话框 -->
<el-dialog
v-model="dialogVisible"
:title="dialogTitle"
width="510px"
top="20vh"
:close-on-click-modal="false"
>
<el-form ref="formRef" :model="form" :rules="formRules" label-width="auto">
<el-form-item label="名称" prop="name">
<el-input v-model="form.name" placeholder="请输入名称(最多25个字符)" maxlength="25" />
</el-form-item>
<el-form-item label="图片" prop="imageUrl">
<UploadFile v-model="form.imageUrl" :limit="1" />
</el-form-item>
<el-form-item label="奖品数量" prop="quantity">
<el-input-number
v-model="form.quantity"
:min="0"
:max="999999"
controls-position="right"
/>
</el-form-item>
<el-form-item label="中奖概率" prop="probability">
<el-input-number
v-model="form.probability"
:min="0"
:max="100"
:precision="2"
:step="0.01"
controls-position="right"
/>
<span class="ml-2">%</span>
</el-form-item>
<el-form-item label="是否启用" prop="isEnabled">
<el-switch
v-model="form.isEnabled"
:active-value="BooleanFlag.YES"
:inactive-value="BooleanFlag.NO"
active-text="是"
inactive-text="否"
/>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" :loading="submitLoading" @click="handleSubmit">
<el-icon class="btn-icon"><IEpUpload /></el-icon>
保存
</el-button>
</template>
</el-dialog>
<WheelConfig ref="wheelConfigRef" />
</div>
</template>
<script setup lang="tsx">
import { usePageSearch, useResetData } from '@/hooks'
import { getWheelPrizeList, addOrUpdateWheelPrize, deleteWheelPrize } from '@/api/backend'
import type { FormInstance, FormRules } from 'element-plus'
import type { BackendWheelPrizeListItemDto, BackendAddOrUpdateWheelPrizeDto } from '@/api/backend'
import UploadFile from '@/components/common/UploadFile/index.vue'
import dayjs from 'dayjs'
import WheelConfig from './components/wheelConfigDialog.vue'
import { push } from 'notivue'
import { useMessageBox } from '@/hooks'
import { BooleanFlag } from '@/constants'
const { confirm } = useMessageBox()
const { loading, list, total, reset, goToPage, changePageSize, refresh, searchParams, search } =
usePageSearch(getWheelPrizeList)
const dialogVisible = ref(false)
const dialogTitle = computed(() => (form.value.id ? '编辑' : '新增'))
const formRef = ref<FormInstance>()
const [form, resetForm] = useResetData<BackendAddOrUpdateWheelPrizeDto>({
id: undefined,
imageUrl: '',
name: '',
quantity: 0,
probability: 0,
isEnabled: BooleanFlag.YES,
})
const formRules: FormRules = {
name: [{ required: true, message: '请输入名称', trigger: 'blur' }],
imageUrl: [{ required: true, message: '请上传图片', trigger: 'change' }],
quantity: [{ required: true, message: '请输入奖品数量', trigger: 'change' }],
probability: [{ required: true, message: '请输入中奖概率', trigger: 'change' }],
isEnabled: [{ required: true, message: '请选择是否启用', trigger: 'change' }],
}
const submitLoading = ref(false)
const handleAdd = () => {
resetForm()
dialogVisible.value = true
}
const handleEdit = (row: BackendWheelPrizeListItemDto) => {
resetForm()
form.value = {
id: row.id,
name: row.name,
imageUrl: row.imageUrl,
quantity: row.quantity,
probability: row.probability,
isEnabled: row.isEnabled,
}
dialogVisible.value = true
}
const handleDelete = async (row: BackendWheelPrizeListItemDto) => {
await confirm({
title: '提示',
message: `确定要删除奖品"${row.name}"吗?`,
type: 'danger',
})
try {
await deleteWheelPrize([row.id])
push.success('删除成功')
refresh()
} catch (error) {
if (error !== 'cancel') {
push.error('删除失败')
}
}
}
const handleSubmit = async () => {
if (!formRef.value) return
try {
submitLoading.value = true
await formRef.value.validate()
await addOrUpdateWheelPrize(form.value)
push.success(form.value.id ? '编辑成功' : '新增成功')
dialogVisible.value = false
if (form.value.id) {
search()
} else {
refresh()
}
} catch (error) {
console.error('表单验证失败:', error)
} finally {
submitLoading.value = false
}
}
const wheelConfigRef = ref<InstanceType<typeof WheelConfig>>()
const handleWheelConfig = () => {
wheelConfigRef.value?.open()
}
</script>
<style scoped lang="scss">
.official-tag-page {
height: 100%;
display: flex;
flex-direction: column;
gap: 16px;
}
.search-section {
background: #fff;
border-radius: 8px;
padding: 20px;
display: flex;
flex-wrap: wrap;
gap: 12px 0;
flex-shrink: 0;
}
.table-section {
flex: 1;
background: #fff;
border-radius: 8px;
padding: 20px;
display: flex;
flex-direction: column;
min-height: 0;
}
.table-wrapper {
flex: 1;
min-height: 0;
}
.pagination-wrapper {
display: flex;
justify-content: flex-end;
padding-top: 16px;
flex-shrink: 0;
}
.btn-icon {
margin-right: 4px;
}
</style>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment