🎨 新增兑换码功能
This commit is contained in:
80
src/api/cdk/index.js
Normal file
80
src/api/cdk/index.js
Normal file
@@ -0,0 +1,80 @@
|
||||
import service from '@/utils/request'
|
||||
|
||||
|
||||
// 获取兑换码库列表
|
||||
export const mkList = (params) => {
|
||||
return service({
|
||||
url: '/cdk/mk/list',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
// 新增兑换码库
|
||||
export const addMk = (data) => {
|
||||
return service({
|
||||
url: '/cdk/mk',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// 编辑兑换码库
|
||||
export const editMk = (data) => {
|
||||
return service({
|
||||
url: '/cdk/mk',
|
||||
method: 'put',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
// 删除兑换码库
|
||||
export const delMk = (data) => {
|
||||
return service({
|
||||
url: '/cdk/mk',
|
||||
method: 'delete',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// 获取兑换码库详情
|
||||
export const mkDetail = (params) => {
|
||||
return service({
|
||||
url: '/cdk/mk',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
// =======================CDK相关===========================
|
||||
|
||||
// 获取兑换码列表
|
||||
export const cdkList = (params) => {
|
||||
return service({
|
||||
url: '/cdk/list',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
// 新增兑换码
|
||||
export const addCdk = (data) => {
|
||||
return service({
|
||||
url: '/cdk/generate',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// 作废兑换码
|
||||
export const delCdk = (data) => {
|
||||
return service({
|
||||
url: '/cdk',
|
||||
method: 'delete',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
@@ -5,6 +5,7 @@
|
||||
"/src/view/bot/bot/botForm.vue": "BotForm",
|
||||
"/src/view/category/category/category.vue": "Category",
|
||||
"/src/view/category/category/categoryForm.vue": "CategoryForm",
|
||||
"/src/view/cdk/index.vue": "CdkManagement",
|
||||
"/src/view/dashboard/components/banner.vue": "Banner",
|
||||
"/src/view/dashboard/components/card.vue": "Card",
|
||||
"/src/view/dashboard/components/charts-content-numbers.vue": "ChartsContentNumbers",
|
||||
@@ -25,6 +26,7 @@
|
||||
"/src/view/goods/article/edit.vue": "Edit",
|
||||
"/src/view/goods/article/index.vue": "Index",
|
||||
"/src/view/goods/index.vue": "goods",
|
||||
"/src/view/goods/teacher_vip/index.vue": "Index",
|
||||
"/src/view/goods/vip/index.vue": "VipList",
|
||||
"/src/view/init/index.vue": "Init",
|
||||
"/src/view/layout/aside/asideComponent/asyncSubmenu.vue": "AsyncSubmenu",
|
||||
|
661
src/view/cdk/cdkManage.vue
Normal file
661
src/view/cdk/cdkManage.vue
Normal file
@@ -0,0 +1,661 @@
|
||||
<template>
|
||||
<div>
|
||||
<!-- 页面头部 -->
|
||||
<div class="page-header">
|
||||
<div class="header-left">
|
||||
<el-button @click="goBack" type="text" class="back-btn">
|
||||
<el-icon><ArrowLeft /></el-icon>
|
||||
返回兑换码库管理
|
||||
</el-button>
|
||||
<h2>{{ libraryInfo.codeName }} - 兑换码管理</h2>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<el-button type="primary" @click="handleGenerateCdk">生成兑换码</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 搜索区域 -->
|
||||
<div class="gva-search-box">
|
||||
<el-form :inline="true" :model="searchForm" class="demo-form-inline">
|
||||
<el-form-item label="兑换码">
|
||||
<el-input v-model="searchForm.code" placeholder="请输入兑换码" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item label="状态">
|
||||
<el-select v-model="searchForm.status" placeholder="请选择状态" clearable>
|
||||
<el-option label="未使用" :value="1" />
|
||||
<el-option label="已使用" :value="2" />
|
||||
<el-option label="已作废" :value="3" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="使用者">
|
||||
<el-input v-model="searchForm.useName" placeholder="请输入使用者名称" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="searchData">查询</el-button>
|
||||
<el-button @click="resetData">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
|
||||
<!-- 统计信息 -->
|
||||
<div class="stats-box">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="6">
|
||||
<div class="stat-item">
|
||||
<div class="stat-value">{{ stats.total }}</div>
|
||||
<div class="stat-label">总数量</div>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<div class="stat-item unused">
|
||||
<div class="stat-value">{{ stats.unused }}</div>
|
||||
<div class="stat-label">未使用</div>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<div class="stat-item used">
|
||||
<div class="stat-value">{{ stats.used }}</div>
|
||||
<div class="stat-label">已使用</div>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<div class="stat-item invalid">
|
||||
<div class="stat-value">{{ stats.invalid }}</div>
|
||||
<div class="stat-label">已作废</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
|
||||
<!-- 表格区域 -->
|
||||
<div class="gva-table-box">
|
||||
<el-table
|
||||
:data="tableData"
|
||||
v-loading="tableLoading"
|
||||
style="width: 100%"
|
||||
class="gva-table"
|
||||
stripe
|
||||
>
|
||||
<el-table-column prop="ID" label="ID" width="80" />
|
||||
<el-table-column prop="code" label="兑换码" min-width="180">
|
||||
<template #default="{ row }">
|
||||
<div class="code-cell">
|
||||
<span>{{ row.code }}</span>
|
||||
<el-button
|
||||
type="text"
|
||||
@click="copyCode(row.code)"
|
||||
class="copy-btn"
|
||||
title="复制兑换码"
|
||||
>
|
||||
<el-icon><CopyDocument /></el-icon>
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="status" label="状态" width="100">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="getStatusType(row.status)">
|
||||
{{ getStatusText(row.status) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="validDay" label="有效期(天)" width="120" />
|
||||
<el-table-column prop="expireAt" label="到期时间" width="180">
|
||||
<template #default="{ row }">
|
||||
<span :class="{ 'text-danger': isExpiringSoon(row.expireAt), 'text-expired': isExpired(row.expireAt) }">
|
||||
{{ formatDate(row.expireAt) }}
|
||||
</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="useName" label="使用者" width="120">
|
||||
<template #default="{ row }">
|
||||
{{ row.useName || '-' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="useAt" label="使用时间" width="180">
|
||||
<template #default="{ row }">
|
||||
{{ row.useAt ? formatDate(row.useAt) : '-' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="CreatedAt" label="创建时间" width="180">
|
||||
<template #default="{ row }">
|
||||
{{ formatDate(row.CreatedAt) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="120" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<el-button
|
||||
type="text"
|
||||
@click="handleDelete(row)"
|
||||
style="color: #f56c6c"
|
||||
:disabled="row.status !== 1"
|
||||
>
|
||||
作废
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<el-pagination
|
||||
v-model:current-page="queryParams.page"
|
||||
v-model:page-size="queryParams.pageSize"
|
||||
:page-sizes="[10, 20, 50, 100]"
|
||||
:total="total"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="handleCurrentChange"
|
||||
class="gva-pagination"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 生成兑换码弹窗 -->
|
||||
<el-dialog
|
||||
v-model="generateDialogVisible"
|
||||
title="生成兑换码"
|
||||
width="500px"
|
||||
:before-close="handleGenerateClose"
|
||||
>
|
||||
<el-form
|
||||
ref="generateFormRef"
|
||||
:model="generateForm"
|
||||
:rules="getDynamicRules()"
|
||||
label-width="120px"
|
||||
>
|
||||
<el-form-item label="兑换码库">
|
||||
<el-input v-model="libraryInfo.codeName" disabled />
|
||||
</el-form-item>
|
||||
<el-form-item label="生成数量" prop="number">
|
||||
<el-input-number
|
||||
v-model="generateForm.number"
|
||||
:min="1"
|
||||
:max="1000"
|
||||
placeholder="请输入生成数量"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="有效期" prop="validityType">
|
||||
<el-radio-group v-model="generateForm.validityType" @change="handleValidityTypeChange">
|
||||
<el-radio :label="'permanent'">永久有效</el-radio>
|
||||
<el-radio :label="'custom'">自定义天数</el-radio>
|
||||
</el-radio-group>
|
||||
<div v-if="generateForm.validityType === 'custom'" style="margin-top: 10px;">
|
||||
<el-input-number
|
||||
v-model="generateForm.customDays"
|
||||
:min="1"
|
||||
:max="3650"
|
||||
placeholder="请输入有效期天数"
|
||||
style="width: 200px"
|
||||
/>
|
||||
<span style="margin-left: 8px;">天</span>
|
||||
</div>
|
||||
<div style="font-size: 12px; color: #999; margin-top: 5px;">
|
||||
{{ generateForm.validityType === 'permanent' ? '兑换码永久有效,不会过期' : '兑换码的有效期,超过此期限将无法使用' }}
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="handleGenerateClose">取消</el-button>
|
||||
<el-button type="primary" @click="submitGenerate">确定</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { cdkList, addCdk, delCdk } from '@/api/cdk/index.js'
|
||||
import { ref, onMounted, computed, watch } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { ArrowLeft, CopyDocument } from '@element-plus/icons-vue'
|
||||
|
||||
defineOptions({
|
||||
name: 'CdkManage'
|
||||
})
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
|
||||
// 兑换码库信息
|
||||
const libraryInfo = ref({
|
||||
ID: '',
|
||||
codeName: ''
|
||||
})
|
||||
|
||||
// 表格相关数据
|
||||
const tableLoading = ref(false)
|
||||
const tableData = ref([])
|
||||
const queryParams = ref({
|
||||
page: 1,
|
||||
pageSize: 10,
|
||||
eid: 0
|
||||
})
|
||||
const total = ref(0)
|
||||
|
||||
// 搜索表单
|
||||
const searchForm = ref({
|
||||
code: '',
|
||||
status: '',
|
||||
useName: ''
|
||||
})
|
||||
|
||||
// 统计数据
|
||||
const stats = computed(() => {
|
||||
const unused = tableData.value.filter(item => item.status === 1).length
|
||||
const used = tableData.value.filter(item => item.status === 2).length
|
||||
const invalid = tableData.value.filter(item => item.status === 3).length
|
||||
|
||||
return {
|
||||
total: total.value,
|
||||
unused,
|
||||
used,
|
||||
invalid
|
||||
}
|
||||
})
|
||||
|
||||
// 生成兑换码弹窗
|
||||
const generateDialogVisible = ref(false)
|
||||
const generateFormRef = ref(null)
|
||||
const generateForm = ref({
|
||||
eid: 0,
|
||||
number: 1,
|
||||
expirer: 30,
|
||||
validityType: 'custom', // 'permanent' 或 'custom'
|
||||
customDays: 30
|
||||
})
|
||||
|
||||
// 表单验证规则
|
||||
const generateRules = {
|
||||
number: [
|
||||
{ required: true, message: '请输入生成数量', trigger: 'blur' },
|
||||
{ type: 'number', min: 1, max: 1000, message: '生成数量必须在1-1000之间', trigger: 'blur' }
|
||||
],
|
||||
validityType: [
|
||||
{ required: true, message: '请选择有效期类型', trigger: 'change' }
|
||||
]
|
||||
}
|
||||
|
||||
// 动态验证规则
|
||||
const getDynamicRules = () => {
|
||||
const rules = { ...generateRules }
|
||||
if (generateForm.value.validityType === 'custom') {
|
||||
rules.customDays = [
|
||||
{ required: true, message: '请输入有效期天数', trigger: 'blur' },
|
||||
{ type: 'number', min: 1, max: 3650, message: '有效期必须在1-3650天之间', trigger: 'blur' }
|
||||
]
|
||||
}
|
||||
return rules
|
||||
}
|
||||
|
||||
// 格式化日期
|
||||
const formatDate = (dateStr) => {
|
||||
if (!dateStr) return '-'
|
||||
return new Date(dateStr).toLocaleString('zh-CN')
|
||||
}
|
||||
|
||||
// 获取状态文本
|
||||
const getStatusText = (status) => {
|
||||
const statusMap = {
|
||||
1: '未使用',
|
||||
2: '已使用',
|
||||
3: '已作废'
|
||||
}
|
||||
return statusMap[status] || '未知'
|
||||
}
|
||||
|
||||
// 获取状态类型
|
||||
const getStatusType = (status) => {
|
||||
const typeMap = {
|
||||
1: 'success',
|
||||
2: 'warning',
|
||||
3: 'danger'
|
||||
}
|
||||
return typeMap[status] || 'info'
|
||||
}
|
||||
|
||||
// 判断是否即将过期(7天内)
|
||||
const isExpiringSoon = (expireTime) => {
|
||||
if (!expireTime) return false
|
||||
const expireDate = new Date(expireTime)
|
||||
const now = new Date()
|
||||
const diffTime = expireDate.getTime() - now.getTime()
|
||||
const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24))
|
||||
return diffDays <= 7 && diffDays > 0
|
||||
}
|
||||
|
||||
// 判断是否已过期
|
||||
const isExpired = (expireTime) => {
|
||||
if (!expireTime) return false
|
||||
const expireDate = new Date(expireTime)
|
||||
const now = new Date()
|
||||
return expireDate.getTime() < now.getTime()
|
||||
}
|
||||
|
||||
// 复制兑换码
|
||||
const copyCode = async (code) => {
|
||||
try {
|
||||
await navigator.clipboard.writeText(code)
|
||||
ElMessage.success('兑换码已复制到剪贴板')
|
||||
} catch (error) {
|
||||
// 降级方案
|
||||
const textArea = document.createElement('textarea')
|
||||
textArea.value = code
|
||||
document.body.appendChild(textArea)
|
||||
textArea.select()
|
||||
document.execCommand('copy')
|
||||
document.body.removeChild(textArea)
|
||||
ElMessage.success('兑换码已复制到剪贴板')
|
||||
}
|
||||
}
|
||||
|
||||
// 搜索数据
|
||||
const searchData = () => {
|
||||
queryParams.value.page = 1
|
||||
getList()
|
||||
}
|
||||
|
||||
// 重置搜索
|
||||
const resetData = () => {
|
||||
searchForm.value = {
|
||||
code: '',
|
||||
status: '',
|
||||
useName: ''
|
||||
}
|
||||
queryParams.value.page = 1
|
||||
getList()
|
||||
}
|
||||
|
||||
// 分页处理
|
||||
const handleSizeChange = (size) => {
|
||||
queryParams.value.pageSize = size
|
||||
getList()
|
||||
}
|
||||
|
||||
const handleCurrentChange = (page) => {
|
||||
queryParams.value.page = page
|
||||
getList()
|
||||
}
|
||||
|
||||
// 获取兑换码列表
|
||||
async function getList() {
|
||||
tableLoading.value = true
|
||||
try {
|
||||
const params = {
|
||||
...queryParams.value,
|
||||
...searchForm.value
|
||||
}
|
||||
const res = await cdkList(params)
|
||||
if (res.code === 0) {
|
||||
tableData.value = res.data.list || []
|
||||
total.value = res.data.total || 0
|
||||
} else {
|
||||
ElMessage.error(res.msg || '获取数据失败')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取兑换码列表失败:', error)
|
||||
ElMessage.error('获取数据失败')
|
||||
} finally {
|
||||
tableLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 处理有效期类型变化
|
||||
const handleValidityTypeChange = (type) => {
|
||||
if (type === 'permanent') {
|
||||
generateForm.value.expirer = 0
|
||||
} else {
|
||||
generateForm.value.expirer = generateForm.value.customDays || 30
|
||||
}
|
||||
}
|
||||
|
||||
// 生成兑换码
|
||||
const handleGenerateCdk = () => {
|
||||
generateForm.value = {
|
||||
eid: Number(libraryInfo.value.ID),
|
||||
number: 1,
|
||||
expirer: 30,
|
||||
validityType: 'custom',
|
||||
customDays: 30
|
||||
}
|
||||
generateDialogVisible.value = true
|
||||
}
|
||||
|
||||
// 关闭生成弹窗
|
||||
const handleGenerateClose = () => {
|
||||
generateDialogVisible.value = false
|
||||
generateFormRef.value?.resetFields()
|
||||
}
|
||||
|
||||
// 提交生成
|
||||
const submitGenerate = async () => {
|
||||
if (!generateFormRef.value) return
|
||||
|
||||
// 动态设置验证规则
|
||||
generateFormRef.value.clearValidate()
|
||||
|
||||
// 根据选择的有效期类型设置expirer值
|
||||
if (generateForm.value.validityType === 'permanent') {
|
||||
generateForm.value.expirer = 0
|
||||
} else {
|
||||
generateForm.value.expirer = generateForm.value.customDays
|
||||
}
|
||||
|
||||
// 验证表单
|
||||
let isValid = true
|
||||
|
||||
// 验证生成数量
|
||||
if (!generateForm.value.number || generateForm.value.number < 1 || generateForm.value.number > 1000) {
|
||||
ElMessage.error('生成数量必须在1-1000之间')
|
||||
isValid = false
|
||||
}
|
||||
|
||||
// 验证自定义天数
|
||||
if (generateForm.value.validityType === 'custom') {
|
||||
if (!generateForm.value.customDays || generateForm.value.customDays < 1 || generateForm.value.customDays > 3650) {
|
||||
ElMessage.error('有效期必须在1-3650天之间')
|
||||
isValid = false
|
||||
}
|
||||
}
|
||||
|
||||
if (isValid) {
|
||||
try {
|
||||
// 准备提交的数据
|
||||
const submitData = {
|
||||
eid: generateForm.value.eid,
|
||||
number: generateForm.value.number,
|
||||
expirer: generateForm.value.expirer
|
||||
}
|
||||
|
||||
const res = await addCdk(submitData)
|
||||
if (res.code === 0) {
|
||||
ElMessage.success('生成成功')
|
||||
handleGenerateClose()
|
||||
getList()
|
||||
} else {
|
||||
ElMessage.error(res.msg || '生成失败')
|
||||
}
|
||||
} catch (error) {
|
||||
ElMessage.error('生成失败')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 作废兑换码
|
||||
const handleDelete = async (row) => {
|
||||
try {
|
||||
await ElMessageBox.confirm('确定要作废这个兑换码吗?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
})
|
||||
|
||||
const res = await delCdk({ ID: row.ID })
|
||||
if (res.code === 0) {
|
||||
ElMessage.success('作废成功')
|
||||
getList()
|
||||
} else {
|
||||
ElMessage.error(res.msg || '作废失败')
|
||||
}
|
||||
} catch (error) {
|
||||
// 用户取消作废
|
||||
}
|
||||
}
|
||||
|
||||
// 返回上一页
|
||||
const goBack = () => {
|
||||
router.go(-1)
|
||||
}
|
||||
|
||||
// 页面初始化
|
||||
onMounted(() => {
|
||||
// 从路由参数获取兑换码库信息
|
||||
const { id, name } = route.query
|
||||
if (id && name) {
|
||||
libraryInfo.value = {
|
||||
ID: id,
|
||||
codeName: decodeURIComponent(name)
|
||||
}
|
||||
queryParams.value.eid = id
|
||||
getList()
|
||||
} else {
|
||||
ElMessage.error('缺少兑换码库信息')
|
||||
goBack()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.page-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 20px;
|
||||
background: #fff;
|
||||
border-radius: 4px;
|
||||
margin-bottom: 20px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
|
||||
.header-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.back-btn {
|
||||
margin-right: 15px;
|
||||
color: #409eff;
|
||||
|
||||
&:hover {
|
||||
color: #66b1ff;
|
||||
}
|
||||
}
|
||||
|
||||
h2 {
|
||||
margin: 0;
|
||||
font-size: 18px;
|
||||
color: #303133;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.gva-search-box {
|
||||
padding: 20px;
|
||||
background: #fff;
|
||||
border-radius: 4px;
|
||||
margin-bottom: 20px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.stats-box {
|
||||
padding: 20px;
|
||||
background: #fff;
|
||||
border-radius: 4px;
|
||||
margin-bottom: 20px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
|
||||
.stat-item {
|
||||
text-align: center;
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
background: #f8f9fa;
|
||||
|
||||
&.unused {
|
||||
background: #f0f9ff;
|
||||
color: #0369a1;
|
||||
}
|
||||
|
||||
&.used {
|
||||
background: #fefce8;
|
||||
color: #a16207;
|
||||
}
|
||||
|
||||
&.invalid {
|
||||
background: #fef2f2;
|
||||
color: #dc2626;
|
||||
}
|
||||
|
||||
.stat-value {
|
||||
font-size: 28px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
font-size: 14px;
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.gva-table-box {
|
||||
background: #fff;
|
||||
border-radius: 4px;
|
||||
padding: 20px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.gva-table {
|
||||
:deep(.el-table__header) {
|
||||
background-color: #f5f7fa;
|
||||
}
|
||||
|
||||
:deep(.el-table__row:hover > td) {
|
||||
background-color: #f5f7fa !important;
|
||||
}
|
||||
}
|
||||
|
||||
.gva-pagination {
|
||||
margin-top: 20px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.demo-form-inline {
|
||||
.el-form-item {
|
||||
margin-right: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.code-cell {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
.copy-btn {
|
||||
margin-left: 8px;
|
||||
padding: 4px;
|
||||
|
||||
&:hover {
|
||||
color: #409eff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.text-danger {
|
||||
color: #f56c6c;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.text-expired {
|
||||
color: #909399;
|
||||
text-decoration: line-through;
|
||||
}
|
||||
</style>
|
747
src/view/cdk/index.vue
Normal file
747
src/view/cdk/index.vue
Normal file
@@ -0,0 +1,747 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="cdk-library-management">
|
||||
<!-- 兑换码库管理 -->
|
||||
<div class="gva-search-box">
|
||||
<el-form :inline="true" :model="mkSearchForm" class="demo-form-inline">
|
||||
<el-form-item label="兑换码库名称">
|
||||
<el-input v-model="mkSearchForm.codeName" placeholder="请输入兑换码库名称" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="searchMkData">查询</el-button>
|
||||
<el-button @click="resetMkData">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
|
||||
<div class="gva-table-box">
|
||||
<div class="gva-btn-list">
|
||||
<el-button type="primary" @click="handleAddMk">新增兑换码库</el-button>
|
||||
</div>
|
||||
|
||||
<el-table
|
||||
:data="mkTableData"
|
||||
v-loading="mkTableLoading"
|
||||
style="width: 100%"
|
||||
class="gva-table"
|
||||
stripe
|
||||
>
|
||||
<el-table-column prop="ID" label="ID" width="80" />
|
||||
<el-table-column prop="codeName" label="兑换码库名称" min-width="150" />
|
||||
<el-table-column prop="type" label="类型" width="100">
|
||||
<template #default="{ row }">
|
||||
{{ getTypeText(row.type) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="item" label="关联项目" width="150">
|
||||
<template #default="{ row }">
|
||||
{{ getItemDisplayName(row) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="num" label="兑换码数量" width="120" />
|
||||
<el-table-column prop="no" label="已使用数量" width="120" />
|
||||
<el-table-column label="使用率" width="100">
|
||||
<template #default="{ row }">
|
||||
{{ getUsageRate(row.no, row.num) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="CreatedAt" label="创建时间" width="180">
|
||||
<template #default="{ row }">
|
||||
{{ formatDate(row.CreatedAt) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="180" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<el-button type="text" @click="handleEditMk(row)">编辑</el-button>
|
||||
<el-button type="text" @click="handleManageCdk(row)">兑换码管理</el-button>
|
||||
<el-button type="text" @click="handleDeleteMk(row)" style="color: #f56c6c">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<el-pagination
|
||||
v-model:current-page="mkQueryParams.page"
|
||||
v-model:page-size="mkQueryParams.pageSize"
|
||||
:page-sizes="[10, 20, 50, 100]"
|
||||
:total="mkTotal"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
@size-change="handleMkSizeChange"
|
||||
@current-change="handleMkCurrentChange"
|
||||
class="gva-pagination"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 新增/编辑兑换码库弹窗 -->
|
||||
<el-dialog
|
||||
v-model="mkDialogVisible"
|
||||
:title="mkDialogTitle"
|
||||
width="500px"
|
||||
:before-close="handleMkClose"
|
||||
>
|
||||
<el-form
|
||||
ref="mkFormRef"
|
||||
:model="mkForm"
|
||||
:rules="mkRules"
|
||||
label-width="120px"
|
||||
>
|
||||
<el-form-item label="兑换码库名称" prop="codeName">
|
||||
<el-input v-model="mkForm.codeName" placeholder="请输入兑换码库名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="类型" prop="type">
|
||||
<el-select v-model="mkForm.type" placeholder="请选择类型" style="width: 100%" @change="handleTypeChange">
|
||||
<el-option label="VIP" :value="1" />
|
||||
<el-option label="讲师VIP" :value="2" />
|
||||
<el-option label="课程" :value="3" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item :label="getItemLabel(mkForm.type)" prop="item">
|
||||
<el-button type="primary" @click="handleSelectItem" style="width: 100%">
|
||||
{{ selectedItemName || '请选择' + getItemLabel(mkForm.type) }}
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="handleMkClose">取消</el-button>
|
||||
<el-button type="primary" @click="submitMk">确定</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 选择项目弹窗 -->
|
||||
<el-dialog
|
||||
v-model="selectItemDialogVisible"
|
||||
:title="'选择' + getItemLabel(mkForm.type)"
|
||||
width="900px"
|
||||
:before-close="handleSelectItemClose"
|
||||
>
|
||||
<!-- 搜索框 -->
|
||||
<div style="margin-bottom: 15px;">
|
||||
<el-input
|
||||
v-model="itemSearchKeyword"
|
||||
placeholder="请输入关键词搜索"
|
||||
clearable
|
||||
@input="handleItemSearch"
|
||||
style="width: 300px;"
|
||||
>
|
||||
<template #prefix>
|
||||
<el-icon><Search /></el-icon>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
|
||||
<el-table
|
||||
:data="filteredItemOptions"
|
||||
v-loading="itemOptionsLoading"
|
||||
style="width: 100%"
|
||||
@row-click="handleItemRowClick"
|
||||
highlight-current-row
|
||||
>
|
||||
<el-table-column prop="ID" label="ID" width="80" />
|
||||
<el-table-column :prop="getItemNameProp(mkForm.type)" :label="getItemLabel(mkForm.type) + '名称'" min-width="200" />
|
||||
|
||||
<!-- VIP套餐相关列 -->
|
||||
<template v-if="mkForm.type === 1">
|
||||
<el-table-column prop="price" label="价格" width="100">
|
||||
<template #default="{ row }">
|
||||
¥{{ row.price }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="duration" label="时长(天)" width="100" />
|
||||
</template>
|
||||
|
||||
<!-- 讲师VIP相关列 -->
|
||||
<template v-if="mkForm.type === 2">
|
||||
<el-table-column prop="price" label="价格" width="100">
|
||||
<template #default="{ row }">
|
||||
¥{{ row.price }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="teacher_name" label="讲师" width="120" />
|
||||
<el-table-column prop="desc" label="描述" width="150" show-overflow-tooltip />
|
||||
</template>
|
||||
|
||||
<!-- 课程相关列 -->
|
||||
<template v-if="mkForm.type === 3">
|
||||
<el-table-column prop="price" label="价格" width="100">
|
||||
<template #default="{ row }">
|
||||
¥{{ row.price }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="teacherName" label="讲师" width="120" />
|
||||
<el-table-column prop="isFree" label="是否免费" width="100">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="row.isFree === 1 ? 'success' : 'info'">
|
||||
{{ row.isFree === 1 ? '免费' : '付费' }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="status" label="状态" width="100">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="row.status === 1 ? 'success' : 'danger'">
|
||||
{{ row.status === 1 ? '上架' : '下架' }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</template>
|
||||
</el-table>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="handleSelectItemClose">取消</el-button>
|
||||
<el-button type="primary" @click="confirmSelectItem" :disabled="!selectedItem">确定</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {
|
||||
mkList,
|
||||
addMk,
|
||||
editMk,
|
||||
delMk
|
||||
} from '@/api/cdk/index.js'
|
||||
import { list as vipList } from '@/api/goods/vip.js'
|
||||
import { list as teacherVipList } from '@/api/goods/teacherVip.js'
|
||||
import { list as goodsList } from '@/api/goods/index.js'
|
||||
import { ref, onMounted, computed } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { Search } from '@element-plus/icons-vue'
|
||||
|
||||
defineOptions({
|
||||
name: 'CdkLibraryManagement'
|
||||
})
|
||||
|
||||
const router = useRouter()
|
||||
|
||||
// 格式化日期
|
||||
const formatDate = (dateStr) => {
|
||||
if (!dateStr) return '-'
|
||||
return new Date(dateStr).toLocaleString('zh-CN')
|
||||
}
|
||||
|
||||
// 兑换码库相关数据
|
||||
const mkTableLoading = ref(false)
|
||||
const mkTableData = ref([])
|
||||
const mkQueryParams = ref({
|
||||
page: 1,
|
||||
pageSize: 10
|
||||
})
|
||||
const mkTotal = ref(0)
|
||||
const mkSearchForm = ref({
|
||||
codeName: ''
|
||||
})
|
||||
|
||||
|
||||
|
||||
// 兑换码库弹窗相关
|
||||
const mkDialogVisible = ref(false)
|
||||
const mkFormRef = ref(null)
|
||||
const mkForm = ref({
|
||||
ID: '',
|
||||
codeName: '',
|
||||
type: 1,
|
||||
item: ''
|
||||
})
|
||||
const mkDialogTitle = computed(() => mkForm.value.ID ? '编辑兑换码库' : '新增兑换码库')
|
||||
|
||||
// 选择项目相关
|
||||
const selectItemDialogVisible = ref(false)
|
||||
const itemOptions = ref([])
|
||||
const itemOptionsLoading = ref(false)
|
||||
const selectedItem = ref(null)
|
||||
const selectedItemName = ref('')
|
||||
const itemSearchKeyword = ref('')
|
||||
|
||||
// 过滤后的项目选项
|
||||
const filteredItemOptions = computed(() => {
|
||||
if (!itemSearchKeyword.value) {
|
||||
return itemOptions.value
|
||||
}
|
||||
|
||||
const keyword = itemSearchKeyword.value.toLowerCase()
|
||||
return itemOptions.value.filter(item => {
|
||||
const nameProp = getItemNameProp(mkForm.value.type)
|
||||
const name = item[nameProp]?.toLowerCase() || ''
|
||||
const teacherName = (item.teacher_name || item.teacherName || '').toLowerCase()
|
||||
const desc = (item.desc || '').toLowerCase()
|
||||
|
||||
return name.includes(keyword) ||
|
||||
teacherName.includes(keyword) ||
|
||||
desc.includes(keyword) ||
|
||||
item.ID.toString().includes(keyword)
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// 项目名称缓存
|
||||
const itemNameCache = ref({})
|
||||
|
||||
// 表单验证规则
|
||||
const mkRules = {
|
||||
codeName: [
|
||||
{ required: true, message: '请输入兑换码库名称', trigger: 'blur' }
|
||||
],
|
||||
type: [
|
||||
{ required: true, message: '请选择类型', trigger: 'change' }
|
||||
],
|
||||
item: [
|
||||
{ required: true, message: '请选择对应的项目', trigger: 'change' }
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 获取类型文本
|
||||
const getTypeText = (type) => {
|
||||
const typeMap = {
|
||||
1: 'VIP',
|
||||
2: '讲师VIP',
|
||||
3: '课程'
|
||||
}
|
||||
return typeMap[type] || '未知'
|
||||
}
|
||||
|
||||
// 获取使用率
|
||||
const getUsageRate = (used, total) => {
|
||||
if (!total || total === 0) return '0%'
|
||||
const rate = ((used || 0) / total * 100).toFixed(1)
|
||||
return `${rate}%`
|
||||
}
|
||||
|
||||
// 获取项目标签
|
||||
const getItemLabel = (type) => {
|
||||
const labelMap = {
|
||||
1: 'VIP套餐',
|
||||
2: '讲师VIP',
|
||||
3: '课程'
|
||||
}
|
||||
return labelMap[type] || '项目'
|
||||
}
|
||||
|
||||
// 获取项目名称属性
|
||||
const getItemNameProp = (type) => {
|
||||
const propMap = {
|
||||
1: 'name',
|
||||
2: 'title',
|
||||
3: 'title'
|
||||
}
|
||||
return propMap[type] || 'name'
|
||||
}
|
||||
|
||||
// 处理类型变化
|
||||
const handleTypeChange = (type) => {
|
||||
// 清空已选择的项目
|
||||
mkForm.value.item = ''
|
||||
selectedItem.value = null
|
||||
selectedItemName.value = ''
|
||||
console.log('类型变更为:', getTypeText(type))
|
||||
}
|
||||
|
||||
// 处理选择项目
|
||||
const handleSelectItem = async () => {
|
||||
if (!mkForm.value.type) {
|
||||
ElMessage.warning('请先选择类型')
|
||||
return
|
||||
}
|
||||
|
||||
await loadItemOptions()
|
||||
selectItemDialogVisible.value = true
|
||||
}
|
||||
|
||||
// 加载项目选项
|
||||
const loadItemOptions = async () => {
|
||||
itemOptionsLoading.value = true
|
||||
try {
|
||||
let res
|
||||
const params = { page: 1, pageSize: 1000 }
|
||||
|
||||
switch (mkForm.value.type) {
|
||||
case 1: // VIP
|
||||
res = await vipList(params)
|
||||
break
|
||||
case 2: // 讲师VIP
|
||||
res = await teacherVipList(params)
|
||||
break
|
||||
case 3: // 课程
|
||||
res = await goodsList(params)
|
||||
break
|
||||
default:
|
||||
itemOptions.value = []
|
||||
return
|
||||
}
|
||||
|
||||
if (res.code === 0) {
|
||||
itemOptions.value = res.data.list || []
|
||||
} else {
|
||||
ElMessage.error(res.msg || '获取数据失败')
|
||||
itemOptions.value = []
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载项目选项失败:', error)
|
||||
ElMessage.error('加载数据失败')
|
||||
itemOptions.value = []
|
||||
} finally {
|
||||
itemOptionsLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 处理项目行点击
|
||||
const handleItemRowClick = (row) => {
|
||||
selectedItem.value = row
|
||||
}
|
||||
|
||||
// 确认选择项目
|
||||
const confirmSelectItem = () => {
|
||||
if (!selectedItem.value) {
|
||||
ElMessage.warning('请选择一个项目')
|
||||
return
|
||||
}
|
||||
|
||||
mkForm.value.item = selectedItem.value.ID
|
||||
const nameProp = getItemNameProp(mkForm.value.type)
|
||||
selectedItemName.value = selectedItem.value[nameProp]
|
||||
|
||||
handleSelectItemClose()
|
||||
}
|
||||
|
||||
// 关闭选择项目弹窗
|
||||
const handleSelectItemClose = () => {
|
||||
selectItemDialogVisible.value = false
|
||||
selectedItem.value = null
|
||||
itemSearchKeyword.value = ''
|
||||
}
|
||||
|
||||
// 处理项目搜索
|
||||
const handleItemSearch = () => {
|
||||
// 搜索逻辑已在 computed 中处理
|
||||
}
|
||||
|
||||
// 获取项目显示名称
|
||||
const getItemDisplayName = (row) => {
|
||||
const cacheKey = `${row.type}_${row.item}`
|
||||
if (itemNameCache.value[cacheKey]) {
|
||||
return itemNameCache.value[cacheKey]
|
||||
}
|
||||
return `ID: ${row.item}`
|
||||
}
|
||||
|
||||
// 加载项目名称缓存
|
||||
const loadItemNameCache = async () => {
|
||||
try {
|
||||
// 获取所有类型的数据并缓存名称
|
||||
const [vipRes, teacherVipRes, goodsRes] = await Promise.all([
|
||||
vipList({ page: 1, pageSize: 1000 }),
|
||||
teacherVipList({ page: 1, pageSize: 1000 }),
|
||||
goodsList({ page: 1, pageSize: 1000 })
|
||||
])
|
||||
|
||||
// 缓存VIP名称
|
||||
if (vipRes.code === 0 && vipRes.data.list) {
|
||||
vipRes.data.list.forEach(item => {
|
||||
itemNameCache.value[`1_${item.ID}`] = item.name
|
||||
})
|
||||
}
|
||||
|
||||
// 缓存讲师VIP名称
|
||||
if (teacherVipRes.code === 0 && teacherVipRes.data.list) {
|
||||
teacherVipRes.data.list.forEach(item => {
|
||||
itemNameCache.value[`2_${item.ID}`] = item.title
|
||||
})
|
||||
}
|
||||
|
||||
// 缓存课程名称
|
||||
if (goodsRes.code === 0 && goodsRes.data.list) {
|
||||
goodsRes.data.list.forEach(item => {
|
||||
itemNameCache.value[`3_${item.ID}`] = item.title
|
||||
})
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载项目名称缓存失败:', error)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 兑换码库相关方法
|
||||
const searchMkData = () => {
|
||||
mkQueryParams.value.page = 1
|
||||
getMkList()
|
||||
}
|
||||
|
||||
const resetMkData = () => {
|
||||
mkSearchForm.value = {
|
||||
codeName: ''
|
||||
}
|
||||
mkQueryParams.value = {
|
||||
page: 1,
|
||||
pageSize: 10
|
||||
}
|
||||
getMkList()
|
||||
}
|
||||
|
||||
const handleMkSizeChange = (size) => {
|
||||
mkQueryParams.value.pageSize = size
|
||||
getMkList()
|
||||
}
|
||||
|
||||
const handleMkCurrentChange = (page) => {
|
||||
mkQueryParams.value.page = page
|
||||
getMkList()
|
||||
}
|
||||
|
||||
async function getMkList() {
|
||||
mkTableLoading.value = true
|
||||
try {
|
||||
const params = {
|
||||
...mkQueryParams.value,
|
||||
...mkSearchForm.value
|
||||
}
|
||||
const res = await mkList(params)
|
||||
if (res.code === 0) {
|
||||
mkTableData.value = res.data.list || []
|
||||
mkTotal.value = res.data.total || 0
|
||||
}
|
||||
} finally {
|
||||
mkTableLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 获取兑换码库选项
|
||||
async function getMkOptions() {
|
||||
try {
|
||||
const res = await mkList({ page: 1, pageSize: 1000 })
|
||||
if (res.code === 0) {
|
||||
mkOptions.value = res.data.list || []
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取兑换码库选项失败:', error)
|
||||
}
|
||||
}
|
||||
|
||||
const handleAddMk = () => {
|
||||
mkForm.value = {
|
||||
ID: '',
|
||||
codeName: '',
|
||||
type: 1,
|
||||
item: ''
|
||||
}
|
||||
selectedItemName.value = ''
|
||||
selectedItem.value = null
|
||||
mkDialogVisible.value = true
|
||||
}
|
||||
|
||||
const handleEditMk = async (row) => {
|
||||
mkForm.value = {
|
||||
ID: row.ID,
|
||||
codeName: row.codeName,
|
||||
type: row.type,
|
||||
item: row.item
|
||||
}
|
||||
|
||||
// 获取已选择项目的名称
|
||||
await loadItemOptions()
|
||||
const nameProp = getItemNameProp(row.type)
|
||||
const selectedItemData = itemOptions.value.find(item => item.ID === row.item)
|
||||
selectedItemName.value = selectedItemData ? selectedItemData[nameProp] : `ID: ${row.item}`
|
||||
|
||||
mkDialogVisible.value = true
|
||||
}
|
||||
|
||||
const handleManageCdk = (row) => {
|
||||
// 跳转到兑换码管理页面,传递兑换码库ID和名称
|
||||
router.push({
|
||||
path: 'cdkManage',
|
||||
query: {
|
||||
id: row.ID,
|
||||
name: encodeURIComponent(row.codeName)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
const handleDeleteMk = async (row) => {
|
||||
try {
|
||||
await ElMessageBox.confirm('确定要删除这个兑换码库吗?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
})
|
||||
|
||||
const res = await delMk({ ID: row.ID })
|
||||
if (res.code === 0) {
|
||||
ElMessage.success('删除成功')
|
||||
getMkList()
|
||||
} else {
|
||||
ElMessage.error(res.msg || '删除失败')
|
||||
}
|
||||
} catch (error) {
|
||||
// 用户取消删除
|
||||
}
|
||||
}
|
||||
|
||||
const handleMkClose = () => {
|
||||
mkDialogVisible.value = false
|
||||
mkFormRef.value?.resetFields()
|
||||
selectedItemName.value = ''
|
||||
selectedItem.value = null
|
||||
}
|
||||
|
||||
const submitMk = async () => {
|
||||
if (!mkFormRef.value) return
|
||||
await mkFormRef.value.validate(async (valid) => {
|
||||
if (valid) {
|
||||
try {
|
||||
const isEdit = !!mkForm.value.ID
|
||||
const res = isEdit ? await editMk(mkForm.value) : await addMk(mkForm.value)
|
||||
|
||||
if (res.code === 0) {
|
||||
ElMessage.success(isEdit ? '编辑成功' : '新增成功')
|
||||
handleMkClose()
|
||||
getMkList()
|
||||
} else {
|
||||
ElMessage.error(res.msg || (isEdit ? '编辑失败' : '新增失败'))
|
||||
}
|
||||
} catch (error) {
|
||||
ElMessage.error('操作失败')
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 兑换码相关方法
|
||||
const searchCdkData = () => {
|
||||
cdkQueryParams.value.page = 1
|
||||
getCdkList()
|
||||
}
|
||||
|
||||
const resetCdkData = () => {
|
||||
cdkSearchForm.value = {
|
||||
code: '',
|
||||
mkId: '',
|
||||
status: ''
|
||||
}
|
||||
cdkQueryParams.value = {
|
||||
page: 1,
|
||||
pageSize: 10
|
||||
}
|
||||
getCdkList()
|
||||
}
|
||||
|
||||
const handleCdkSizeChange = (size) => {
|
||||
cdkQueryParams.value.pageSize = size
|
||||
getCdkList()
|
||||
}
|
||||
|
||||
const handleCdkCurrentChange = (page) => {
|
||||
cdkQueryParams.value.page = page
|
||||
getCdkList()
|
||||
}
|
||||
|
||||
async function getCdkList() {
|
||||
cdkTableLoading.value = true
|
||||
try {
|
||||
const params = {
|
||||
...cdkQueryParams.value,
|
||||
...cdkSearchForm.value
|
||||
}
|
||||
const res = await cdkList(params)
|
||||
if (res.code === 0) {
|
||||
cdkTableData.value = res.data.list || []
|
||||
cdkTotal.value = res.data.total || 0
|
||||
}
|
||||
} finally {
|
||||
cdkTableLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const handleDeleteCdk = async (row) => {
|
||||
try {
|
||||
await ElMessageBox.confirm('确定要作废这个兑换码吗?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
})
|
||||
|
||||
const res = await delCdk({ ID: row.ID })
|
||||
if (res.code === 0) {
|
||||
ElMessage.success('作废成功')
|
||||
getCdkList()
|
||||
} else {
|
||||
ElMessage.error(res.msg || '作废失败')
|
||||
}
|
||||
} catch (error) {
|
||||
// 用户取消作废
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 页面初始化
|
||||
onMounted(() => {
|
||||
loadItemNameCache()
|
||||
getMkList()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.cdk-library-management {
|
||||
// 兑换码库管理样式
|
||||
}
|
||||
|
||||
.gva-search-box {
|
||||
padding: 20px;
|
||||
background: #fff;
|
||||
border-radius: 4px;
|
||||
margin-bottom: 20px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.gva-table-box {
|
||||
background: #fff;
|
||||
border-radius: 4px;
|
||||
padding: 20px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
|
||||
.gva-btn-list {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.gva-table {
|
||||
:deep(.el-table__header) {
|
||||
background-color: #f5f7fa;
|
||||
}
|
||||
|
||||
:deep(.el-table__row:hover > td) {
|
||||
background-color: #f5f7fa !important;
|
||||
}
|
||||
}
|
||||
|
||||
.gva-pagination {
|
||||
margin-top: 20px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.demo-form-inline {
|
||||
.el-form-item {
|
||||
margin-right: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.text-danger {
|
||||
color: #f56c6c;
|
||||
font-weight: bold;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
Reference in New Issue
Block a user