Initial commit: One Pipe System
完整的管理系统,包含账户管理、卡片管理、套餐管理、财务管理等功能模块。 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
276
src/views/account-management/customer-account/index.vue
Normal file
276
src/views/account-management/customer-account/index.vue
Normal file
@@ -0,0 +1,276 @@
|
||||
<template>
|
||||
<div class="page-content">
|
||||
<ElRow>
|
||||
<ElCol :xs="24" :sm="12" :lg="6">
|
||||
<ElInput v-model="searchQuery" placeholder="账号/姓名/手机号" clearable></ElInput>
|
||||
</ElCol>
|
||||
<div style="width: 12px"></div>
|
||||
<ElCol :xs="24" :sm="12" :lg="6">
|
||||
<ElSelect v-model="typeFilter" placeholder="客户类型" clearable style="width: 100%">
|
||||
<ElOption label="代理商" value="agent" />
|
||||
<ElOption label="企业客户" value="enterprise" />
|
||||
</ElSelect>
|
||||
</ElCol>
|
||||
<div style="width: 12px"></div>
|
||||
<ElCol :xs="24" :sm="12" :lg="6">
|
||||
<ElSelect v-model="statusFilter" placeholder="账号状态" clearable style="width: 100%">
|
||||
<ElOption label="正常" value="active" />
|
||||
<ElOption label="禁用" value="disabled" />
|
||||
<ElOption label="已锁定" value="locked" />
|
||||
</ElSelect>
|
||||
</ElCol>
|
||||
<div style="width: 12px"></div>
|
||||
<ElCol :xs="24" :sm="12" :lg="6" class="el-col2">
|
||||
<ElButton v-ripple @click="handleSearch">搜索</ElButton>
|
||||
</ElCol>
|
||||
</ElRow>
|
||||
|
||||
<ArtTable :data="filteredData" index>
|
||||
<template #default>
|
||||
<ElTableColumn label="账号" prop="username" min-width="120" />
|
||||
<ElTableColumn label="真实姓名" prop="realName" />
|
||||
<ElTableColumn label="客户类型" prop="customerType">
|
||||
<template #default="scope">
|
||||
<ElTag :type="scope.row.customerType === 'agent' ? '' : 'success'">
|
||||
{{ scope.row.customerType === 'agent' ? '代理商' : '企业客户' }}
|
||||
</ElTag>
|
||||
</template>
|
||||
</ElTableColumn>
|
||||
<ElTableColumn label="所属组织" prop="organizationName" show-overflow-tooltip />
|
||||
<ElTableColumn label="手机号" prop="phone" />
|
||||
<ElTableColumn label="邮箱" prop="email" show-overflow-tooltip />
|
||||
<ElTableColumn label="登录次数" prop="loginCount" />
|
||||
<ElTableColumn label="最后登录" prop="lastLoginTime" width="180" />
|
||||
<ElTableColumn label="状态" prop="status">
|
||||
<template #default="scope">
|
||||
<ElTag :type="getStatusTagType(scope.row.status)">
|
||||
{{ getStatusText(scope.row.status) }}
|
||||
</ElTag>
|
||||
</template>
|
||||
</ElTableColumn>
|
||||
<ElTableColumn label="创建时间" prop="createTime" width="180" />
|
||||
<ElTableColumn fixed="right" label="操作" width="280">
|
||||
<template #default="scope">
|
||||
<el-button link @click="viewDetail(scope.row)">详情</el-button>
|
||||
<el-button link @click="unbindPhone(scope.row)">解绑手机</el-button>
|
||||
<el-button link @click="resetPassword(scope.row)">重置密码</el-button>
|
||||
<el-button
|
||||
link
|
||||
:type="scope.row.status === 'active' ? 'danger' : 'primary'"
|
||||
@click="toggleStatus(scope.row)"
|
||||
>
|
||||
{{ scope.row.status === 'active' ? '禁用' : '启用' }}
|
||||
</el-button>
|
||||
</template>
|
||||
</ElTableColumn>
|
||||
</template>
|
||||
</ArtTable>
|
||||
|
||||
<!-- 操作记录对话框 -->
|
||||
<ElDialog v-model="detailDialogVisible" title="账号详情" width="800px" align-center>
|
||||
<ElDescriptions :column="2" border>
|
||||
<ElDescriptionsItem label="账号">{{ currentAccount?.username }}</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="真实姓名">{{ currentAccount?.realName }}</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="客户类型">{{
|
||||
currentAccount?.customerType === 'agent' ? '代理商' : '企业客户'
|
||||
}}</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="所属组织">{{
|
||||
currentAccount?.organizationName
|
||||
}}</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="手机号">{{ currentAccount?.phone }}</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="邮箱">{{ currentAccount?.email }}</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="注册时间">{{ currentAccount?.createTime }}</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="最后登录">{{
|
||||
currentAccount?.lastLoginTime
|
||||
}}</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="登录次数">{{ currentAccount?.loginCount }}</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="状态">
|
||||
<ElTag :type="getStatusTagType(currentAccount?.status || 'active')">
|
||||
{{ getStatusText(currentAccount?.status || 'active') }}
|
||||
</ElTag>
|
||||
</ElDescriptionsItem>
|
||||
</ElDescriptions>
|
||||
|
||||
<ElDivider>操作记录</ElDivider>
|
||||
<ArtTable :data="operationRecords" index max-height="300">
|
||||
<template #default>
|
||||
<ElTableColumn label="操作类型" prop="operationType" />
|
||||
<ElTableColumn label="操作描述" prop="description" />
|
||||
<ElTableColumn label="操作人" prop="operator" />
|
||||
<ElTableColumn label="操作时间" prop="operateTime" width="180" />
|
||||
</template>
|
||||
</ArtTable>
|
||||
</ElDialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
|
||||
defineOptions({ name: 'CustomerAccount' })
|
||||
|
||||
interface CustomerAccount {
|
||||
id: string
|
||||
username: string
|
||||
realName: string
|
||||
customerType: 'agent' | 'enterprise'
|
||||
organizationName: string
|
||||
phone: string
|
||||
email: string
|
||||
loginCount: number
|
||||
lastLoginTime: string
|
||||
status: 'active' | 'disabled' | 'locked'
|
||||
createTime: string
|
||||
}
|
||||
|
||||
// Mock 数据
|
||||
const mockData = ref<CustomerAccount[]>([
|
||||
{
|
||||
id: '1',
|
||||
username: 'agent001',
|
||||
realName: '张三',
|
||||
customerType: 'agent',
|
||||
organizationName: '华东区总代理',
|
||||
phone: '13800138001',
|
||||
email: 'zhangsan@example.com',
|
||||
loginCount: 328,
|
||||
lastLoginTime: '2026-01-09 08:30:00',
|
||||
status: 'active',
|
||||
createTime: '2026-01-01 10:00:00'
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
username: 'enterprise001',
|
||||
realName: '李四',
|
||||
customerType: 'enterprise',
|
||||
organizationName: '某某科技有限公司',
|
||||
phone: '13800138002',
|
||||
email: 'lisi@example.com',
|
||||
loginCount: 156,
|
||||
lastLoginTime: '2026-01-08 18:45:00',
|
||||
status: 'active',
|
||||
createTime: '2026-01-03 11:00:00'
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
username: 'agent002',
|
||||
realName: '王五',
|
||||
customerType: 'agent',
|
||||
organizationName: '江苏省代理',
|
||||
phone: '13800138003',
|
||||
email: 'wangwu@example.com',
|
||||
loginCount: 89,
|
||||
lastLoginTime: '2026-01-07 14:20:00',
|
||||
status: 'disabled',
|
||||
createTime: '2026-01-05 12:00:00'
|
||||
}
|
||||
])
|
||||
|
||||
const operationRecords = ref([
|
||||
{
|
||||
operationType: '重置密码',
|
||||
description: '管理员重置登录密码',
|
||||
operator: 'admin',
|
||||
operateTime: '2026-01-08 10:00:00'
|
||||
},
|
||||
{
|
||||
operationType: '解绑手机',
|
||||
description: '解绑原手机号并重新绑定',
|
||||
operator: 'admin',
|
||||
operateTime: '2026-01-06 15:30:00'
|
||||
}
|
||||
])
|
||||
|
||||
const searchQuery = ref('')
|
||||
const typeFilter = ref('')
|
||||
const statusFilter = ref('')
|
||||
const detailDialogVisible = ref(false)
|
||||
const currentAccount = ref<CustomerAccount | null>(null)
|
||||
|
||||
const filteredData = computed(() => {
|
||||
let data = mockData.value
|
||||
if (searchQuery.value) {
|
||||
const query = searchQuery.value.toLowerCase()
|
||||
data = data.filter(
|
||||
(item) =>
|
||||
item.username.toLowerCase().includes(query) ||
|
||||
item.realName.includes(query) ||
|
||||
item.phone.includes(query)
|
||||
)
|
||||
}
|
||||
if (typeFilter.value) {
|
||||
data = data.filter((item) => item.customerType === typeFilter.value)
|
||||
}
|
||||
if (statusFilter.value) {
|
||||
data = data.filter((item) => item.status === statusFilter.value)
|
||||
}
|
||||
return data
|
||||
})
|
||||
|
||||
const getStatusText = (status: string) => {
|
||||
const statusMap: Record<string, string> = {
|
||||
active: '正常',
|
||||
disabled: '禁用',
|
||||
locked: '已锁定'
|
||||
}
|
||||
return statusMap[status] || '未知'
|
||||
}
|
||||
|
||||
const getStatusTagType = (status: string) => {
|
||||
const typeMap: Record<string, string> = {
|
||||
active: 'success',
|
||||
disabled: 'info',
|
||||
locked: 'danger'
|
||||
}
|
||||
return typeMap[status] || 'info'
|
||||
}
|
||||
|
||||
const handleSearch = () => {
|
||||
// 搜索逻辑已通过 computed 实现
|
||||
}
|
||||
|
||||
const viewDetail = (row: CustomerAccount) => {
|
||||
currentAccount.value = row
|
||||
detailDialogVisible.value = true
|
||||
}
|
||||
|
||||
const unbindPhone = (row: CustomerAccount) => {
|
||||
ElMessageBox.confirm(`确定要解绑账号 ${row.username} 的手机号吗?`, '解绑确认', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
ElMessage.success('手机号解绑成功')
|
||||
})
|
||||
}
|
||||
|
||||
const resetPassword = (row: CustomerAccount) => {
|
||||
ElMessageBox.confirm(`确定要重置账号 ${row.username} 的密码吗?新密码将发送至其手机。`, '重置密码', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
ElMessage.success('密码重置成功,新密码已发送至手机')
|
||||
})
|
||||
}
|
||||
|
||||
const toggleStatus = (row: CustomerAccount) => {
|
||||
const action = row.status === 'active' ? '禁用' : '启用'
|
||||
ElMessageBox.confirm(`确定要${action}账号 ${row.username} 吗?`, `${action}确认`, {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
row.status = row.status === 'active' ? 'disabled' : 'active'
|
||||
ElMessage.success(`${action}成功`)
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.page-content {
|
||||
:deep(.el-descriptions__label) {
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user