301 lines
7.8 KiB
Vue
301 lines
7.8 KiB
Vue
|
<template>
|
||
|
<div class="app-container">
|
||
|
<!-- 搜索工作栏 -->
|
||
|
<ContentWrap>
|
||
|
<el-form
|
||
|
class="-mb-15px"
|
||
|
:model="queryParams"
|
||
|
ref="queryFormRef"
|
||
|
:inline="true"
|
||
|
label-width="68px"
|
||
|
>
|
||
|
<el-form-item label="会员姓名" prop="name">
|
||
|
<el-input
|
||
|
v-model="queryParams.name"
|
||
|
placeholder="请输入会员姓名"
|
||
|
clearable
|
||
|
@keyup.enter="handleQuery"
|
||
|
class="!w-240px"
|
||
|
/>
|
||
|
</el-form-item>
|
||
|
<el-form-item label="会员状态" prop="vipStatus">
|
||
|
<el-select
|
||
|
v-model="queryParams.vipStatus"
|
||
|
placeholder="请选择会员状态"
|
||
|
clearable
|
||
|
class="!w-240px"
|
||
|
>
|
||
|
<el-option label="已开通" :value="true" />
|
||
|
<el-option label="未开通" :value="false" />
|
||
|
</el-select>
|
||
|
</el-form-item>
|
||
|
<el-form-item>
|
||
|
<el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
|
||
|
<el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
|
||
|
</el-form-item>
|
||
|
</el-form>
|
||
|
</ContentWrap>
|
||
|
|
||
|
<!-- 列表 -->
|
||
|
<ContentWrap>
|
||
|
<el-table
|
||
|
v-loading="loading"
|
||
|
:data="memberList"
|
||
|
:show-overflow-tooltip="true"
|
||
|
>
|
||
|
<el-table-column label="姓名" align="center" prop="name" />
|
||
|
<el-table-column label="手机号" align="center" prop="phone" />
|
||
|
<el-table-column label="VIP状态" align="center" prop="vipStatus" >
|
||
|
<template #default="scope">
|
||
|
<el-tag :type="scope.row.vipStatus ? 'success' : 'info'">
|
||
|
{{ scope.row.vipStatus ? '已开通' : '未开通' }}
|
||
|
</el-tag>
|
||
|
</template>
|
||
|
</el-table-column>
|
||
|
<el-table-column label="地址" align="center" prop="address" />
|
||
|
<el-table-column label="到期时间" align="center" prop="vipExpireDate" >
|
||
|
<template #default="scope">
|
||
|
<span v-if="scope.row.vipStatus" class="text-primary">{{ scope.row.vipExpireDate }}</span>
|
||
|
<span v-else class="text-gray-400">-</span>
|
||
|
</template>
|
||
|
</el-table-column>
|
||
|
<el-table-column label="操作" align="center" width="180">
|
||
|
<template #default="scope">
|
||
|
<div class="flex items-center justify-center">
|
||
|
<el-button
|
||
|
v-if="!scope.row.vipStatus"
|
||
|
type="primary"
|
||
|
link
|
||
|
size="small"
|
||
|
@click="handleRecharge(scope.row)"
|
||
|
>
|
||
|
<Icon icon="ep:video-play" />开通会员
|
||
|
</el-button>
|
||
|
<el-button
|
||
|
v-else
|
||
|
type="primary"
|
||
|
link
|
||
|
size="small"
|
||
|
@click="handleRecharge(scope.row)"
|
||
|
>
|
||
|
<Icon icon="ep:refresh" />续费
|
||
|
</el-button>
|
||
|
<el-button
|
||
|
v-if="scope.row.vipStatus"
|
||
|
type="danger"
|
||
|
link
|
||
|
size="small"
|
||
|
@click="handleCancel(scope.row)"
|
||
|
>
|
||
|
<Icon icon="ep:close" />取消会员
|
||
|
</el-button>
|
||
|
</div>
|
||
|
</template>
|
||
|
</el-table-column>
|
||
|
</el-table>
|
||
|
|
||
|
<!-- 分页 -->
|
||
|
<Pagination
|
||
|
:total="total"
|
||
|
v-model:page="queryParams.pageNo"
|
||
|
v-model:limit="queryParams.pageSize"
|
||
|
@pagination="getList"
|
||
|
/>
|
||
|
</ContentWrap>
|
||
|
|
||
|
<!-- 续费弹窗 -->
|
||
|
<RechargeDialog
|
||
|
v-model:visible="rechargeDialogVisible"
|
||
|
:member="currentMember"
|
||
|
@success="handleRechargeSuccess"
|
||
|
/>
|
||
|
|
||
|
<!-- 密码验证弹窗 -->
|
||
|
<PasswordVerifyDialog
|
||
|
v-model:visible="passwordVerifyVisible"
|
||
|
@success="handlePasswordVerifySuccess"
|
||
|
/>
|
||
|
</div>
|
||
|
</template>
|
||
|
|
||
|
<script setup>
|
||
|
import { ref, reactive, onMounted } from 'vue'
|
||
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
||
|
import RechargeDialog from './components/RechargeDialog.vue'
|
||
|
import PasswordVerifyDialog from './components/PasswordVerifyDialog.vue'
|
||
|
|
||
|
// 会员列表数据
|
||
|
const memberList = ref([])
|
||
|
const loading = ref(false)
|
||
|
const total = ref(0)
|
||
|
|
||
|
// 查询参数
|
||
|
const queryParams = reactive({
|
||
|
pageNo: 1,
|
||
|
pageSize: 10,
|
||
|
name: undefined,
|
||
|
vipStatus: undefined
|
||
|
})
|
||
|
|
||
|
const queryFormRef = ref()
|
||
|
|
||
|
// 对话框控制
|
||
|
const rechargeDialogVisible = ref(false)
|
||
|
const passwordVerifyVisible = ref(false)
|
||
|
|
||
|
// 当前选中的会员
|
||
|
const currentMember = ref(null)
|
||
|
|
||
|
// 模拟测试数据
|
||
|
const mockMembers = [
|
||
|
{
|
||
|
id: 1,
|
||
|
name: '张三',
|
||
|
phone: '13800138000',
|
||
|
vipStatus: true,
|
||
|
vipExpireDate: '2024-12-31'
|
||
|
},
|
||
|
{
|
||
|
id: 2,
|
||
|
name: '李四',
|
||
|
phone: '13800138001',
|
||
|
vipStatus: false,
|
||
|
vipExpireDate: ''
|
||
|
},
|
||
|
{
|
||
|
id: 3,
|
||
|
name: '王五',
|
||
|
phone: '13800138002',
|
||
|
vipStatus: true,
|
||
|
vipExpireDate: '2024-06-30'
|
||
|
}
|
||
|
]
|
||
|
|
||
|
// 获取会员列表
|
||
|
const getList = () => {
|
||
|
loading.value = true
|
||
|
setTimeout(() => {
|
||
|
// 模拟搜索过滤
|
||
|
let filteredList = [...mockMembers]
|
||
|
if (queryParams.name) {
|
||
|
filteredList = filteredList.filter(m => m.name.includes(queryParams.name))
|
||
|
}
|
||
|
if (queryParams.vipStatus !== undefined) {
|
||
|
filteredList = filteredList.filter(m => m.vipStatus === queryParams.vipStatus)
|
||
|
}
|
||
|
|
||
|
// 模拟分页
|
||
|
const start = (queryParams.pageNo - 1) * queryParams.pageSize
|
||
|
const end = start + queryParams.pageSize
|
||
|
memberList.value = filteredList.slice(start, end)
|
||
|
total.value = filteredList.length
|
||
|
|
||
|
loading.value = false
|
||
|
}, 500)
|
||
|
}
|
||
|
|
||
|
// 搜索按钮操作
|
||
|
const handleQuery = () => {
|
||
|
queryParams.pageNo = 1
|
||
|
getList()
|
||
|
}
|
||
|
|
||
|
// 重置按钮操作
|
||
|
const resetQuery = () => {
|
||
|
queryFormRef.value?.resetFields()
|
||
|
handleQuery()
|
||
|
}
|
||
|
|
||
|
// 续费/开通会员
|
||
|
const handleRecharge = (member) => {
|
||
|
currentMember.value = member
|
||
|
rechargeDialogVisible.value = true
|
||
|
}
|
||
|
|
||
|
// 续费成功
|
||
|
const handleRechargeSuccess = (data) => {
|
||
|
const member = mockMembers.find(m => m.id === data.memberId)
|
||
|
if (member) {
|
||
|
member.vipStatus = true
|
||
|
member.vipExpireDate = data.expireDate
|
||
|
getList()
|
||
|
ElMessage.success('操作成功')
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// 取消会员
|
||
|
const handleCancel = (member) => {
|
||
|
currentMember.value = member
|
||
|
passwordVerifyVisible.value = true
|
||
|
}
|
||
|
|
||
|
// 密码验证成功
|
||
|
const handlePasswordVerifySuccess = (password) => {
|
||
|
// TODO: 这里需要调用后端API验证密码并取消会员
|
||
|
// 模拟API调用
|
||
|
setTimeout(() => {
|
||
|
const index = mockMembers.findIndex(m => m.id === currentMember.value.id)
|
||
|
if (index !== -1) {
|
||
|
mockMembers[index] = {
|
||
|
...currentMember.value,
|
||
|
vipStatus: false,
|
||
|
vipExpireDate: ''
|
||
|
}
|
||
|
getList()
|
||
|
ElMessage.success('已取消会员资格')
|
||
|
// 关闭密码验证弹窗
|
||
|
passwordVerifyVisible.value = false
|
||
|
// 清空当前选中的会员
|
||
|
currentMember.value = null
|
||
|
}
|
||
|
}, 500)
|
||
|
}
|
||
|
|
||
|
onMounted(() => {
|
||
|
getList()
|
||
|
})
|
||
|
</script>
|
||
|
|
||
|
<style scoped>
|
||
|
.text-primary {
|
||
|
color: var(--el-color-primary);
|
||
|
}
|
||
|
|
||
|
.text-gray-400 {
|
||
|
color: var(--el-text-color-secondary);
|
||
|
}
|
||
|
|
||
|
:deep(.el-table) {
|
||
|
--el-table-border-color: var(--el-border-color-lighter);
|
||
|
--el-table-border: 1px solid var(--el-table-border-color);
|
||
|
--el-table-text-color: var(--el-text-color-regular);
|
||
|
--el-table-header-text-color: var(--el-text-color-secondary);
|
||
|
--el-table-row-hover-bg-color: var(--el-fill-color-light);
|
||
|
--el-table-current-row-bg-color: var(--el-color-primary-light-9);
|
||
|
--el-table-header-height: 50px;
|
||
|
--el-table-row-height: 60px;
|
||
|
}
|
||
|
|
||
|
:deep(.el-table__cell) {
|
||
|
padding: 12px 0;
|
||
|
}
|
||
|
|
||
|
.operation-buttons {
|
||
|
display: flex;
|
||
|
justify-content: center;
|
||
|
align-items: center;
|
||
|
gap: 4px;
|
||
|
}
|
||
|
|
||
|
:deep(.el-button--link) {
|
||
|
padding: 0 6px;
|
||
|
height: 26px;
|
||
|
font-size: 13px;
|
||
|
}
|
||
|
|
||
|
:deep(.el-button--link .el-icon) {
|
||
|
margin-right: 2px;
|
||
|
font-size: 14px;
|
||
|
}
|
||
|
</style>
|