删除激活码页面

This commit is contained in:
dzl
2025-09-09 11:00:25 +08:00
parent 789f0f1804
commit 7b4cb1d21b
3 changed files with 0 additions and 645 deletions

View File

@@ -30,11 +30,6 @@
<template #title>用户审核</template>
</el-menu-item>
<el-menu-item v-if="userStore.isAdmin" index="/registration-codes">
<el-icon><Ticket /></el-icon>
<template #title>激活码管理</template>
</el-menu-item>
<el-menu-item v-if="userStore.isAdmin" index="/products">
<el-icon><Goods /></el-icon>
<template #title>商品管理</template>

View File

@@ -50,16 +50,6 @@ const routes = [
requiresAdmin: true
}
},
{
path: 'registration-codes',
name: 'RegistrationCodes',
component: () => import('@/views/RegistrationCodes.vue'),
meta: {
title: '激活码管理 - 炬融圈',
icon: 'Ticket',
requiresAdmin: true
}
},
{
path: 'products',
name: 'Products',

View File

@@ -1,630 +0,0 @@
<template>
<div class="registration-codes-page">
<div class="page-header">
<h1 class="page-title">激活码管理</h1>
<p class="page-subtitle">管理系统中的激活码</p>
</div>
<!-- 搜索和操作栏 -->
<el-card class="search-card" shadow="never">
<el-row :gutter="20" class="search-row">
<el-col :xs="24" :sm="12" :md="8">
<el-input
v-model="searchForm.keyword"
placeholder="搜索激活码"
prefix-icon="Search"
clearable
@input="handleSearch"
/>
</el-col>
<el-col :xs="24" :sm="12" :md="6">
<el-select
v-model="searchForm.status"
placeholder="选择状态"
clearable
@change="handleSearch"
>
<el-option label="全部" value="" />
<el-option label="未使用" value="unused" />
<el-option label="已使用" value="used" />
<el-option label="已过期" value="expired" />
</el-select>
</el-col>
<el-col :xs="24" :sm="12" :md="6">
<el-select
v-model="searchForm.sort"
placeholder="排序方式"
@change="handleSearch"
>
<el-option label="创建时间(新到旧)" value="created_at_desc" />
<el-option label="创建时间(旧到新)" value="created_at_asc" />
<el-option label="过期时间(新到旧)" value="expires_at_desc" />
<el-option label="过期时间(旧到新)" value="expires_at_asc" />
</el-select>
</el-col>
<el-col :xs="24" :sm="12" :md="4">
<el-button type="primary" @click="handleReset">
<el-icon><Refresh /></el-icon>
重置
</el-button>
</el-col>
</el-row>
</el-card>
<!-- 激活码列表 -->
<el-card class="table-card" shadow="never">
<template #header>
<div class="card-header">
<span class="card-title">激活码列表 ({{ pagination.total }})</span>
<div class="header-actions">
<el-button type="success" @click="showBatchCreateDialog">
<el-icon><Plus /></el-icon>
批量生成
</el-button>
<el-button type="primary" @click="showCreateDialog">
<el-icon><Plus /></el-icon>
生成激活码
</el-button>
</div>
</div>
</template>
<el-table
v-loading="loading"
:data="registrationCodes"
stripe
style="width: 100%"
@sort-change="handleSortChange"
>
<el-table-column type="index" label="#" width="60" />
<el-table-column
prop="code"
label="激活码"
min-width="200"
>
<template #default="{ row }">
<div class="code-info">
<el-text class="code-text" copyable>{{ row.code }}</el-text>
</div>
</template>
</el-table-column>
<el-table-column label="状态" width="100">
<template #default="{ row }">
<el-tag :type="getStatusType(row.status)" size="small">
{{ getStatusText(row.status) }}
</el-tag>
</template>
</el-table-column>
<el-table-column
prop="created_at"
label="创建时间"
sortable="custom"
width="180"
>
<template #default="{ row }">
{{ formatDate(row.created_at) }}
</template>
</el-table-column>
<el-table-column
prop="expires_at"
label="过期时间"
sortable="custom"
width="180"
>
<template #default="{ row }">
{{ formatDate(row.expires_at) }}
</template>
</el-table-column>
<el-table-column
prop="used_at"
label="使用时间"
width="180"
>
<template #default="{ row }">
{{ row.used_at ? formatDate(row.used_at) : '-' }}
</template>
</el-table-column>
<el-table-column label="创建者" width="120">
<template #default="{ row }">
{{ row.created_by_admin || '-' }}
</template>
</el-table-column>
<el-table-column label="使用者" width="120">
<template #default="{ row }">
{{ row.used_by_username || '-' }}
</template>
</el-table-column>
<el-table-column label="操作" width="120" fixed="right">
<template #default="{ row }">
<el-button
v-if="row.status === 'unused'"
type="danger"
size="small"
@click="handleDelete(row)"
>
删除
</el-button>
<el-text v-else type="info" size="small">-</el-text>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<el-pagination
v-model:current-page="pagination.page"
v-model:page-size="pagination.limit"
:total="pagination.total"
:page-sizes="[10, 20, 50, 100]"
layout="total, sizes, prev, pager, next, jumper"
@size-change="handleSizeChange"
@current-change="handlePageChange"
/>
</el-card>
<!-- 生成激活码对话框 -->
<el-dialog
v-model="dialogVisible"
title="生成激活码"
width="500px"
:before-close="handleDialogClose"
>
<el-form
ref="codeFormRef"
:model="codeForm"
:rules="codeRules"
label-width="100px"
>
<el-form-item label="过期时间" prop="expiresAt">
<el-date-picker
v-model="codeForm.expiresAt"
type="datetime"
placeholder="选择过期时间"
format="YYYY-MM-DD HH:mm:ss"
value-format="YYYY-MM-DD HH:mm:ss"
:disabled-date="disabledDate"
style="width: 100%"
/>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="handleSubmit" :loading="submitting">
生成
</el-button>
</template>
</el-dialog>
<!-- 批量生成激活码对话框 -->
<el-dialog
v-model="batchDialogVisible"
title="批量生成激活码"
width="500px"
:before-close="handleBatchDialogClose"
>
<el-form
ref="batchFormRef"
:model="batchForm"
:rules="batchRules"
label-width="100px"
>
<el-form-item label="生成数量" prop="count">
<el-input-number
v-model="batchForm.count"
:min="1"
:max="100"
placeholder="请输入生成数量"
style="width: 100%"
/>
</el-form-item>
<el-form-item label="过期时间" prop="expiresAt">
<el-date-picker
v-model="batchForm.expiresAt"
type="datetime"
placeholder="选择过期时间"
format="YYYY-MM-DD HH:mm:ss"
value-format="YYYY-MM-DD HH:mm:ss"
:disabled-date="disabledDate"
style="width: 100%"
/>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="batchDialogVisible = false">取消</el-button>
<el-button type="primary" @click="handleBatchSubmit" :loading="batchSubmitting">
批量生成
</el-button>
</template>
</el-dialog>
</div>
</template>
<script setup>
import { ref, reactive, onMounted } from 'vue'
import { useUserStore } from '@/stores/user'
import api from '@/utils/api'
import { ElMessage, ElMessageBox } from 'element-plus'
import {
Search,
Refresh,
Plus
} from '@element-plus/icons-vue'
import { debounce } from 'lodash-es'
const userStore = useUserStore()
// 响应式数据
const loading = ref(false)
const submitting = ref(false)
const batchSubmitting = ref(false)
const dialogVisible = ref(false)
const batchDialogVisible = ref(false)
const registrationCodes = ref([])
// 搜索表单
const searchForm = reactive({
keyword: '',
status: '',
sort: 'created_at_desc'
})
// 分页数据
const pagination = reactive({
page: 1,
limit: 20,
total: 0
})
// 激活码表单
const codeFormRef = ref()
const codeForm = reactive({
expiresAt: ''
})
// 批量生成表单
const batchFormRef = ref()
const batchForm = reactive({
count: 10,
expiresAt: ''
})
// 表单验证规则
const codeRules = {
expiresAt: [
{ required: true, message: '请选择过期时间', trigger: 'change' }
]
}
const batchRules = {
count: [
{ required: true, message: '请输入生成数量', trigger: 'blur' },
{ type: 'number', min: 1, max: 100, message: '生成数量必须在1-100之间', trigger: 'blur' }
],
expiresAt: [
{ required: true, message: '请选择过期时间', trigger: 'change' }
]
}
/**
* 获取激活码列表
*/
const fetchRegistrationCodes = async () => {
loading.value = true
try {
// 处理排序参数,支持下划线分割的格式
let sortField = 'created_at'
let sortOrder = 'desc'
if (searchForm.sort.includes('_')) {
const parts = searchForm.sort.split('_')
if (parts.length >= 2) {
sortField = parts.slice(0, -1).join('_') // 处理字段名中可能包含下划线的情况
sortOrder = parts[parts.length - 1]
}
}
const params = {
page: pagination.page,
limit: pagination.limit,
keyword: searchForm.keyword,
status: searchForm.status,
sort: sortField,
order: sortOrder
}
const response = await api.registrationCodes.getRegistrationCodes(params)
const { codes, pagination: { total } } = response.data.data
registrationCodes.value = codes
pagination.total = total
} catch (error) {
console.error('获取激活码列表失败:', error)
ElMessage.error('获取激活码列表失败')
} finally {
loading.value = false
}
}
// 防抖搜索
const handleSearch = debounce(() => {
pagination.page = 1
fetchRegistrationCodes()
}, 300)
// 重置搜索
const handleReset = () => {
searchForm.keyword = ''
searchForm.status = ''
searchForm.sort = 'created_at_desc'
pagination.page = 1
fetchRegistrationCodes()
}
// 排序变化
const handleSortChange = ({ prop, order }) => {
if (prop && order) {
const orderMap = { ascending: 'asc', descending: 'desc' }
searchForm.sort = `${prop}_${orderMap[order]}`
handleSearch()
}
}
// 分页变化
const handlePageChange = (page) => {
pagination.page = page
fetchRegistrationCodes()
}
const handleSizeChange = (size) => {
pagination.limit = size
pagination.page = 1
fetchRegistrationCodes()
}
// 显示生成对话框
const showCreateDialog = () => {
resetCodeForm()
dialogVisible.value = true
}
// 显示批量生成对话框
const showBatchCreateDialog = () => {
resetBatchForm()
batchDialogVisible.value = true
}
// 生成激活码
const handleSubmit = async () => {
try {
await codeFormRef.value.validate()
submitting.value = true
await api.registrationCodes.createRegistrationCode({
expiresAt: codeForm.expiresAt
})
ElMessage.success('激活码生成成功')
dialogVisible.value = false
fetchRegistrationCodes()
} catch (error) {
console.error('生成激活码失败:', error)
} finally {
submitting.value = false
}
}
// 批量生成激活码
const handleBatchSubmit = async () => {
try {
await batchFormRef.value.validate()
batchSubmitting.value = true
await api.registrationCodes.batchCreateRegistrationCodes({
count: batchForm.count,
expiresAt: batchForm.expiresAt
})
ElMessage.success(`成功生成 ${batchForm.count} 个激活码`)
batchDialogVisible.value = false
fetchRegistrationCodes()
} catch (error) {
console.error('批量生成激活码失败:', error)
} finally {
batchSubmitting.value = false
}
}
// 删除激活码
const handleDelete = async (code) => {
try {
await ElMessageBox.confirm(
`确定要删除激活码 "${code.code}" 吗?此操作不可恢复。`,
'确认删除',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}
)
await api.registrationCodes.deleteRegistrationCode(code.id)
ElMessage.success('激活码删除成功')
fetchRegistrationCodes()
} catch (error) {
if (error !== 'cancel') {
console.error('删除激活码失败:', error)
}
}
}
// 关闭对话框
const handleDialogClose = () => {
resetCodeForm()
dialogVisible.value = false
}
const handleBatchDialogClose = () => {
resetBatchForm()
batchDialogVisible.value = false
}
// 重置表单
const resetCodeForm = () => {
Object.assign(codeForm, {
expiresAt: ''
})
if (codeFormRef.value) {
codeFormRef.value.clearValidate()
}
}
const resetBatchForm = () => {
Object.assign(batchForm, {
count: 10,
expiresAt: ''
})
if (batchFormRef.value) {
batchFormRef.value.clearValidate()
}
}
// 禁用过去的日期
const disabledDate = (time) => {
return time.getTime() < Date.now() - 24 * 60 * 60 * 1000
}
// 获取状态类型
const getStatusType = (status) => {
const typeMap = {
unused: 'success',
used: 'info',
expired: 'danger'
}
return typeMap[status] || 'info'
}
// 获取状态文本
const getStatusText = (status) => {
const textMap = {
unused: '未使用',
used: '已使用',
expired: '已过期'
}
return textMap[status] || status
}
// 格式化日期
const formatDate = (dateString) => {
if (!dateString) return '-'
const date = new Date(dateString)
return date.toLocaleString('zh-CN', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit'
})
}
// 组件挂载时获取数据
onMounted(() => {
fetchRegistrationCodes()
})
</script>
<style scoped>
.registration-codes-page {
padding: 20px;
}
.page-header {
margin-bottom: 20px;
}
.page-title {
font-size: 24px;
font-weight: 600;
color: #303133;
margin: 0 0 8px 0;
}
.page-subtitle {
font-size: 14px;
color: #909399;
margin: 0;
}
.search-card {
margin-bottom: 20px;
}
.search-row {
align-items: center;
}
.table-card {
margin-bottom: 20px;
}
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
}
.card-title {
font-size: 16px;
font-weight: 600;
color: #303133;
}
.header-actions {
display: flex;
gap: 10px;
}
.code-info {
display: flex;
flex-direction: column;
}
.code-text {
font-family: 'Courier New', monospace;
font-size: 13px;
}
.el-pagination {
margin-top: 20px;
justify-content: center;
}
@media (max-width: 768px) {
.registration-codes-page {
padding: 10px;
}
.card-header {
flex-direction: column;
gap: 10px;
align-items: stretch;
}
.header-actions {
justify-content: center;
}
}
</style>