Files
1818uniapp-admin/src/views/config/points.vue

223 lines
6.3 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="points-page">
<div class="page-header">
<h2>积分套餐管理</h2>
<a-button type="primary" @click="handleAdd">新增套餐</a-button>
</div>
<a-alert
message="套餐名称格式说明"
description="套餐名称使用逗号分隔第一个为Tab显示名称后面的为描述内容。例如体验版,新手体验,小额充值,有效期365天"
type="info"
show-icon
style="margin-bottom: 16px"
/>
<a-table :dataSource="packages" :loading="loading" :columns="columns" rowKey="id">
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'name'">
<div>
<div style="font-weight: bold">{{ getTabName(record.name) }}</div>
<div style="color: #999; font-size: 12px">{{ getDescriptions(record.name) }}</div>
</div>
</template>
<template v-if="column.key === 'status'">
<a-tag :color="record.status === 1 ? 'green' : 'default'">
{{ record.status === 1 ? '上架' : '下架' }}
</a-tag>
</template>
<template v-if="column.key === 'action'">
<a-space>
<a @click="handleEdit(record)">编辑</a>
<a @click="handleToggleStatus(record)">{{ record.status === 1 ? '下架' : '上架' }}</a>
<a-popconfirm title="确定删除该套餐?" @confirm="handleDelete(record.id)">
<a style="color: #ff4d4f">删除</a>
</a-popconfirm>
</a-space>
</template>
</template>
</a-table>
<!-- 新增/编辑弹窗 -->
<a-modal
v-model:open="modalVisible"
:title="isEdit ? '编辑套餐' : '新增套餐'"
:confirmLoading="submitLoading"
@ok="handleSubmit"
width="600px"
>
<a-form :model="form" :label-col="{ span: 6 }" :wrapper-col="{ span: 16 }">
<a-form-item label="套餐名称" required>
<a-input v-model:value="form.name" placeholder="格式:套餐名,描述1,描述2..." />
</a-form-item>
<a-form-item label="积分数量" required>
<a-input-number v-model:value="form.points" :min="1" style="width: 100%" />
</a-form-item>
<a-form-item label="价格(元)" required>
<a-input-number v-model:value="form.price" :min="0" :precision="2" style="width: 100%" />
</a-form-item>
<a-form-item label="原价(元)">
<a-input-number v-model:value="form.originalPrice" :min="0" :precision="2" style="width: 100%" />
</a-form-item>
<a-form-item label="赠送积分">
<a-input-number v-model:value="form.bonusPoints" :min="0" style="width: 100%" />
</a-form-item>
<a-form-item label="有效期(天)">
<a-input-number v-model:value="form.validDays" :min="1" style="width: 100%" />
</a-form-item>
<a-form-item label="排序">
<a-input-number v-model:value="form.sort" :min="0" style="width: 100%" />
</a-form-item>
<a-form-item label="状态">
<a-radio-group v-model:value="form.status">
<a-radio :value="1">上架</a-radio>
<a-radio :value="0">下架</a-radio>
</a-radio-group>
</a-form-item>
</a-form>
</a-modal>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import { message } from 'ant-design-vue'
import {
getPointsPackages,
createPointsPackage,
updatePointsPackage,
deletePointsPackage,
updatePointsPackageStatus
} from '@/api/points'
const loading = ref(false)
const packages = ref([])
const modalVisible = ref(false)
const submitLoading = ref(false)
const isEdit = ref(false)
const defaultForm = {
id: null,
name: '',
points: 1000,
price: 10,
originalPrice: null,
bonusPoints: 0,
validDays: 365,
sort: 0,
status: 1
}
const form = ref({ ...defaultForm })
const columns = [
{ title: 'ID', dataIndex: 'id', width: 60 },
{ title: '套餐名称', key: 'name', width: 250 },
{ title: '积分', dataIndex: 'points', width: 80 },
{ title: '赠送', dataIndex: 'bonusPoints', width: 80 },
{ title: '价格', dataIndex: 'price', width: 80 },
{ title: '原价', dataIndex: 'originalPrice', width: 80 },
{ title: '排序', dataIndex: 'sort', width: 60 },
{ title: '状态', key: 'status', width: 80 },
{ title: '操作', key: 'action', width: 150 }
]
const getTabName = (name) => {
if (!name) return '-'
return name.split(/[,]+/)[0]
}
const getDescriptions = (name) => {
if (!name) return ''
const parts = name.split(/[,]+/)
return parts.length > 1 ? parts.slice(1).join(' | ') : ''
}
const loadPackages = async () => {
loading.value = true
try {
const res = await getPointsPackages()
packages.value = res || []
} catch (e) {
message.error('获取套餐列表失败')
} finally {
loading.value = false
}
}
const handleAdd = () => {
isEdit.value = false
form.value = { ...defaultForm }
modalVisible.value = true
}
const handleEdit = (record) => {
isEdit.value = true
form.value = { ...record }
modalVisible.value = true
}
const handleSubmit = async () => {
if (!form.value.name || !form.value.points || !form.value.price) {
message.warning('请填写必填项')
return
}
submitLoading.value = true
try {
if (isEdit.value) {
await updatePointsPackage(form.value)
message.success('更新成功')
} else {
await createPointsPackage(form.value)
message.success('创建成功')
}
modalVisible.value = false
loadPackages()
} catch (e) {
message.error(e.message || '操作失败')
} finally {
submitLoading.value = false
}
}
const handleDelete = async (id) => {
try {
await deletePointsPackage(id)
message.success('删除成功')
loadPackages()
} catch (e) {
message.error('删除失败')
}
}
const handleToggleStatus = async (record) => {
const newStatus = record.status === 1 ? 0 : 1
try {
await updatePointsPackageStatus(record.id, newStatus)
message.success(newStatus === 1 ? '已上架' : '已下架')
loadPackages()
} catch (e) {
message.error('操作失败')
}
}
onMounted(() => {
loadPackages()
})
</script>
<style scoped>
.points-page {
padding: 20px;
}
.page-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
.page-header h2 {
margin: 0;
font-size: 18px;
}
</style>