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

223 lines
6.3 KiB
Vue
Raw Normal View History

<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>