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:
498
src/views/account-management/enterprise-customer/index.vue
Normal file
498
src/views/account-management/enterprise-customer/index.vue
Normal file
@@ -0,0 +1,498 @@
|
||||
<template>
|
||||
<div class="page-content">
|
||||
<!-- 搜索和操作区 -->
|
||||
<ElRow :gutter="12">
|
||||
<ElCol :xs="24" :sm="12" :lg="6">
|
||||
<ElInput v-model="searchQuery" placeholder="企业名称/联系人" clearable />
|
||||
</ElCol>
|
||||
<ElCol :xs="24" :sm="12" :lg="6">
|
||||
<ElSelect v-model="statusFilter" placeholder="状态筛选" clearable style="width: 100%">
|
||||
<ElOption label="全部" value="" />
|
||||
<ElOption label="正常" value="active" />
|
||||
<ElOption label="禁用" value="disabled" />
|
||||
<ElOption label="待审核" value="pending" />
|
||||
</ElSelect>
|
||||
</ElCol>
|
||||
<ElCol :xs="24" :sm="12" :lg="6">
|
||||
<ElSelect v-model="roleFilter" placeholder="角色筛选" clearable style="width: 100%">
|
||||
<ElOption label="全部" value="" />
|
||||
<ElOption label="企业管理员" value="admin" />
|
||||
<ElOption label="企业用户" value="user" />
|
||||
</ElSelect>
|
||||
</ElCol>
|
||||
<ElCol :xs="24" :sm="12" :lg="6" class="el-col2">
|
||||
<ElButton v-ripple @click="handleSearch">搜索</ElButton>
|
||||
<ElButton v-ripple type="primary" @click="showDialog('add')">新增企业客户</ElButton>
|
||||
</ElCol>
|
||||
</ElRow>
|
||||
|
||||
<!-- 企业客户列表 -->
|
||||
<ArtTable :data="filteredData" index style="margin-top: 20px">
|
||||
<template #default>
|
||||
<ElTableColumn label="企业名称" prop="enterpriseName" min-width="180" show-overflow-tooltip />
|
||||
<ElTableColumn label="统一社会信用代码" prop="creditCode" width="180" />
|
||||
<ElTableColumn label="联系人" prop="contactPerson" width="120" />
|
||||
<ElTableColumn label="联系电话" prop="contactPhone" width="130" />
|
||||
<ElTableColumn label="所属角色" prop="roleName" width="120">
|
||||
<template #default="scope">
|
||||
<ElTag>{{ scope.row.roleName }}</ElTag>
|
||||
</template>
|
||||
</ElTableColumn>
|
||||
<ElTableColumn label="卡片数量" prop="cardCount" width="100">
|
||||
<template #default="scope">
|
||||
<span style="color: var(--el-color-primary)">{{ scope.row.cardCount }}</span>
|
||||
</template>
|
||||
</ElTableColumn>
|
||||
<ElTableColumn label="设备数量" prop="deviceCount" width="100">
|
||||
<template #default="scope">
|
||||
<span style="color: var(--el-color-success)">{{ scope.row.deviceCount }}</span>
|
||||
</template>
|
||||
</ElTableColumn>
|
||||
<ElTableColumn label="账户余额" prop="balance" width="120">
|
||||
<template #default="scope"> ¥{{ scope.row.balance.toFixed(2) }} </template>
|
||||
</ElTableColumn>
|
||||
<ElTableColumn label="状态" prop="status" width="100">
|
||||
<template #default="scope">
|
||||
<ElTag v-if="scope.row.status === 'active'" type="success">正常</ElTag>
|
||||
<ElTag v-else-if="scope.row.status === 'disabled'" type="danger">禁用</ElTag>
|
||||
<ElTag v-else-if="scope.row.status === 'pending'" type="warning">待审核</ElTag>
|
||||
</template>
|
||||
</ElTableColumn>
|
||||
<ElTableColumn label="创建时间" prop="createTime" width="180" />
|
||||
<ElTableColumn fixed="right" label="操作" width="260">
|
||||
<template #default="scope">
|
||||
<el-button link :icon="View" @click="viewDetail(scope.row)">详情</el-button>
|
||||
<el-button link @click="showDialog('edit', scope.row)">编辑</el-button>
|
||||
<el-button
|
||||
link
|
||||
:type="scope.row.status === 'active' ? 'warning' : 'success'"
|
||||
@click="toggleStatus(scope.row)"
|
||||
>
|
||||
{{ scope.row.status === 'active' ? '禁用' : '启用' }}
|
||||
</el-button>
|
||||
<el-button link type="danger" @click="handleDelete(scope.row)">删除</el-button>
|
||||
</template>
|
||||
</ElTableColumn>
|
||||
</template>
|
||||
</ArtTable>
|
||||
|
||||
<!-- 新增/编辑企业客户对话框 -->
|
||||
<ElDialog
|
||||
v-model="dialogVisible"
|
||||
:title="dialogType === 'add' ? '新增企业客户' : '编辑企业客户'"
|
||||
width="700px"
|
||||
align-center
|
||||
>
|
||||
<ElForm ref="formRef" :model="form" :rules="rules" label-width="140px">
|
||||
<ElDivider content-position="left">企业信息</ElDivider>
|
||||
|
||||
<ElFormItem label="企业名称" prop="enterpriseName">
|
||||
<ElInput v-model="form.enterpriseName" placeholder="请输入企业名称" />
|
||||
</ElFormItem>
|
||||
|
||||
<ElFormItem label="统一社会信用代码" prop="creditCode">
|
||||
<ElInput v-model="form.creditCode" placeholder="请输入统一社会信用代码" maxlength="18" />
|
||||
</ElFormItem>
|
||||
|
||||
<ElFormItem label="企业地址" prop="address">
|
||||
<ElInput v-model="form.address" placeholder="请输入企业地址" />
|
||||
</ElFormItem>
|
||||
|
||||
<ElFormItem label="营业执照" prop="businessLicense">
|
||||
<ElUpload
|
||||
:action="uploadUrl"
|
||||
:limit="1"
|
||||
list-type="picture-card"
|
||||
accept="image/*"
|
||||
>
|
||||
<el-icon><Plus /></el-icon>
|
||||
</ElUpload>
|
||||
<div style="color: var(--el-text-color-secondary); font-size: 12px">支持 JPG、PNG 格式</div>
|
||||
</ElFormItem>
|
||||
|
||||
<ElDivider content-position="left">联系人信息</ElDivider>
|
||||
|
||||
<ElFormItem label="联系人" prop="contactPerson">
|
||||
<ElInput v-model="form.contactPerson" placeholder="请输入联系人姓名" />
|
||||
</ElFormItem>
|
||||
|
||||
<ElFormItem label="联系电话" prop="contactPhone">
|
||||
<ElInput v-model="form.contactPhone" placeholder="请输入联系电话" maxlength="11" />
|
||||
</ElFormItem>
|
||||
|
||||
<ElFormItem label="联系邮箱" prop="contactEmail">
|
||||
<ElInput v-model="form.contactEmail" placeholder="请输入联系邮箱" />
|
||||
</ElFormItem>
|
||||
|
||||
<ElDivider content-position="left">账号配置</ElDivider>
|
||||
|
||||
<ElFormItem label="登录账号" prop="username">
|
||||
<ElInput v-model="form.username" placeholder="请输入登录账号" :disabled="dialogType === 'edit'" />
|
||||
<div style="color: var(--el-text-color-secondary); font-size: 12px">只能登录企业端</div>
|
||||
</ElFormItem>
|
||||
|
||||
<ElFormItem v-if="dialogType === 'add'" label="登录密码" prop="password">
|
||||
<ElInput v-model="form.password" type="password" placeholder="请输入登录密码" show-password />
|
||||
</ElFormItem>
|
||||
|
||||
<ElFormItem label="分配角色" prop="roleId">
|
||||
<ElSelect v-model="form.roleId" placeholder="请选择客户角色" style="width: 100%">
|
||||
<ElOption
|
||||
v-for="role in availableRoles"
|
||||
:key="role.id"
|
||||
:label="role.roleName"
|
||||
:value="role.id"
|
||||
/>
|
||||
</ElSelect>
|
||||
<div style="color: var(--el-text-color-secondary); font-size: 12px">角色决定企业客户的能力边界</div>
|
||||
</ElFormItem>
|
||||
|
||||
<ElFormItem label="初始余额" prop="initialBalance">
|
||||
<ElInputNumber v-model="form.initialBalance" :min="0" :precision="2" style="width: 100%" />
|
||||
<span style="margin-left: 8px">元</span>
|
||||
</ElFormItem>
|
||||
|
||||
<ElFormItem label="备注" prop="remark">
|
||||
<ElInput v-model="form.remark" type="textarea" :rows="3" placeholder="请输入备注信息" />
|
||||
</ElFormItem>
|
||||
|
||||
<ElFormItem label="状态">
|
||||
<ElSwitch v-model="form.status" active-value="active" inactive-value="disabled" />
|
||||
</ElFormItem>
|
||||
</ElForm>
|
||||
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<ElButton @click="dialogVisible = false">取消</ElButton>
|
||||
<ElButton type="primary" @click="handleSubmit(formRef)">提交</ElButton>
|
||||
</div>
|
||||
</template>
|
||||
</ElDialog>
|
||||
|
||||
<!-- 企业详情对话框 -->
|
||||
<ElDialog v-model="detailDialogVisible" title="企业客户详情" width="800px" align-center>
|
||||
<ElDescriptions :column="2" border>
|
||||
<ElDescriptionsItem label="企业名称">{{ currentDetail.enterpriseName }}</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="统一社会信用代码">{{ currentDetail.creditCode }}</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="联系人">{{ currentDetail.contactPerson }}</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="联系电话">{{ currentDetail.contactPhone }}</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="联系邮箱">{{ currentDetail.contactEmail }}</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="企业地址" :span="2">{{ currentDetail.address }}</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="所属角色">
|
||||
<ElTag>{{ currentDetail.roleName }}</ElTag>
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="登录账号">{{ currentDetail.username }}</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="卡片数量">
|
||||
<span style="color: var(--el-color-primary)">{{ currentDetail.cardCount }}</span>
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="设备数量">
|
||||
<span style="color: var(--el-color-success)">{{ currentDetail.deviceCount }}</span>
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="账户余额">¥{{ currentDetail.balance.toFixed(2) }}</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="状态">
|
||||
<ElTag v-if="currentDetail.status === 'active'" type="success">正常</ElTag>
|
||||
<ElTag v-else-if="currentDetail.status === 'disabled'" type="danger">禁用</ElTag>
|
||||
<ElTag v-else-if="currentDetail.status === 'pending'" type="warning">待审核</ElTag>
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="创建时间">{{ currentDetail.createTime }}</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="最后登录">{{ currentDetail.lastLoginTime || '未登录' }}</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="备注" :span="2">{{ currentDetail.remark || '无' }}</ElDescriptionsItem>
|
||||
</ElDescriptions>
|
||||
|
||||
<template #footer>
|
||||
<ElButton @click="detailDialogVisible = false">关闭</ElButton>
|
||||
</template>
|
||||
</ElDialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { View, Plus } from '@element-plus/icons-vue'
|
||||
import type { FormInstance, FormRules } from 'element-plus'
|
||||
|
||||
defineOptions({ name: 'EnterpriseCustomer' })
|
||||
|
||||
interface EnterpriseCustomer {
|
||||
id: string
|
||||
enterpriseName: string
|
||||
creditCode: string
|
||||
address: string
|
||||
contactPerson: string
|
||||
contactPhone: string
|
||||
contactEmail: string
|
||||
username: string
|
||||
roleId: string
|
||||
roleName: string
|
||||
cardCount: number
|
||||
deviceCount: number
|
||||
balance: number
|
||||
status: 'active' | 'disabled' | 'pending'
|
||||
createTime: string
|
||||
lastLoginTime?: string
|
||||
remark?: string
|
||||
}
|
||||
|
||||
const searchQuery = ref('')
|
||||
const statusFilter = ref('')
|
||||
const roleFilter = ref('')
|
||||
const dialogVisible = ref(false)
|
||||
const detailDialogVisible = ref(false)
|
||||
const dialogType = ref<'add' | 'edit'>('add')
|
||||
const formRef = ref<FormInstance>()
|
||||
const uploadUrl = ref('/api/upload/image')
|
||||
|
||||
const form = reactive({
|
||||
enterpriseName: '',
|
||||
creditCode: '',
|
||||
address: '',
|
||||
businessLicense: '',
|
||||
contactPerson: '',
|
||||
contactPhone: '',
|
||||
contactEmail: '',
|
||||
username: '',
|
||||
password: '',
|
||||
roleId: '',
|
||||
initialBalance: 0,
|
||||
remark: '',
|
||||
status: 'active'
|
||||
})
|
||||
|
||||
const rules = reactive<FormRules>({
|
||||
enterpriseName: [{ required: true, message: '请输入企业名称', trigger: 'blur' }],
|
||||
creditCode: [
|
||||
{ required: true, message: '请输入统一社会信用代码', trigger: 'blur' },
|
||||
{ len: 18, message: '统一社会信用代码为18位', trigger: 'blur' }
|
||||
],
|
||||
contactPerson: [{ required: true, message: '请输入联系人', trigger: 'blur' }],
|
||||
contactPhone: [
|
||||
{ required: true, message: '请输入联系电话', trigger: 'blur' },
|
||||
{ pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号', trigger: 'blur' }
|
||||
],
|
||||
contactEmail: [
|
||||
{ required: true, message: '请输入联系邮箱', trigger: 'blur' },
|
||||
{ type: 'email', message: '请输入正确的邮箱格式', trigger: 'blur' }
|
||||
],
|
||||
username: [{ required: true, message: '请输入登录账号', trigger: 'blur' }],
|
||||
password: [
|
||||
{ required: true, message: '请输入登录密码', trigger: 'blur' },
|
||||
{ min: 6, max: 20, message: '密码长度为6-20位', trigger: 'blur' }
|
||||
],
|
||||
roleId: [{ required: true, message: '请选择客户角色', trigger: 'change' }]
|
||||
})
|
||||
|
||||
const availableRoles = ref([
|
||||
{ id: '1', roleName: '企业管理员' },
|
||||
{ id: '2', roleName: '企业用户' },
|
||||
{ id: '3', roleName: '企业财务' }
|
||||
])
|
||||
|
||||
const mockData = ref<EnterpriseCustomer[]>([
|
||||
{
|
||||
id: '1',
|
||||
enterpriseName: '深圳市科技有限公司',
|
||||
creditCode: '91440300MA5DXXXX01',
|
||||
address: '深圳市南山区科技园',
|
||||
contactPerson: '张经理',
|
||||
contactPhone: '13800138000',
|
||||
contactEmail: 'zhang@company.com',
|
||||
username: 'shenzhen_tech',
|
||||
roleId: '1',
|
||||
roleName: '企业管理员',
|
||||
cardCount: 500,
|
||||
deviceCount: 450,
|
||||
balance: 15000.00,
|
||||
status: 'active',
|
||||
createTime: '2026-01-01 10:00:00',
|
||||
lastLoginTime: '2026-01-09 09:30:00',
|
||||
remark: '重要客户'
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
enterpriseName: '北京智能制造有限公司',
|
||||
creditCode: '91110000MA5DXXXX02',
|
||||
address: '北京市海淀区中关村',
|
||||
contactPerson: '李总监',
|
||||
contactPhone: '13900139000',
|
||||
contactEmail: 'li@manufacturing.com',
|
||||
username: 'beijing_smart',
|
||||
roleId: '1',
|
||||
roleName: '企业管理员',
|
||||
cardCount: 800,
|
||||
deviceCount: 750,
|
||||
balance: 28000.00,
|
||||
status: 'active',
|
||||
createTime: '2026-01-03 14:00:00',
|
||||
lastLoginTime: '2026-01-09 08:15:00'
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
enterpriseName: '上海物联网科技公司',
|
||||
creditCode: '91310000MA5DXXXX03',
|
||||
address: '上海市浦东新区张江高科技园区',
|
||||
contactPerson: '王主管',
|
||||
contactPhone: '13700137000',
|
||||
contactEmail: 'wang@iot-shanghai.com',
|
||||
username: 'shanghai_iot',
|
||||
roleId: '2',
|
||||
roleName: '企业用户',
|
||||
cardCount: 300,
|
||||
deviceCount: 280,
|
||||
balance: 8500.00,
|
||||
status: 'pending',
|
||||
createTime: '2026-01-08 16:00:00'
|
||||
}
|
||||
])
|
||||
|
||||
const currentDetail = ref<EnterpriseCustomer>({
|
||||
id: '',
|
||||
enterpriseName: '',
|
||||
creditCode: '',
|
||||
address: '',
|
||||
contactPerson: '',
|
||||
contactPhone: '',
|
||||
contactEmail: '',
|
||||
username: '',
|
||||
roleId: '',
|
||||
roleName: '',
|
||||
cardCount: 0,
|
||||
deviceCount: 0,
|
||||
balance: 0,
|
||||
status: 'active',
|
||||
createTime: ''
|
||||
})
|
||||
|
||||
const filteredData = computed(() => {
|
||||
let data = mockData.value
|
||||
|
||||
if (searchQuery.value) {
|
||||
data = data.filter(
|
||||
(item) =>
|
||||
item.enterpriseName.includes(searchQuery.value) ||
|
||||
item.contactPerson.includes(searchQuery.value) ||
|
||||
item.contactPhone.includes(searchQuery.value)
|
||||
)
|
||||
}
|
||||
|
||||
if (statusFilter.value) {
|
||||
data = data.filter((item) => item.status === statusFilter.value)
|
||||
}
|
||||
|
||||
if (roleFilter.value) {
|
||||
data = data.filter((item) => item.roleId === roleFilter.value)
|
||||
}
|
||||
|
||||
return data
|
||||
})
|
||||
|
||||
const handleSearch = () => {}
|
||||
|
||||
const showDialog = (type: 'add' | 'edit', row?: EnterpriseCustomer) => {
|
||||
dialogType.value = type
|
||||
dialogVisible.value = true
|
||||
if (type === 'edit' && row) {
|
||||
Object.assign(form, {
|
||||
enterpriseName: row.enterpriseName,
|
||||
creditCode: row.creditCode,
|
||||
address: row.address,
|
||||
contactPerson: row.contactPerson,
|
||||
contactPhone: row.contactPhone,
|
||||
contactEmail: row.contactEmail,
|
||||
username: row.username,
|
||||
roleId: row.roleId,
|
||||
initialBalance: row.balance,
|
||||
remark: row.remark,
|
||||
status: row.status
|
||||
})
|
||||
} else {
|
||||
Object.assign(form, {
|
||||
enterpriseName: '',
|
||||
creditCode: '',
|
||||
address: '',
|
||||
businessLicense: '',
|
||||
contactPerson: '',
|
||||
contactPhone: '',
|
||||
contactEmail: '',
|
||||
username: '',
|
||||
password: '',
|
||||
roleId: '',
|
||||
initialBalance: 0,
|
||||
remark: '',
|
||||
status: 'active'
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const handleSubmit = async (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return
|
||||
await formEl.validate((valid) => {
|
||||
if (valid) {
|
||||
if (dialogType.value === 'add') {
|
||||
const selectedRole = availableRoles.value.find((r) => r.id === form.roleId)
|
||||
mockData.value.unshift({
|
||||
id: Date.now().toString(),
|
||||
enterpriseName: form.enterpriseName,
|
||||
creditCode: form.creditCode,
|
||||
address: form.address,
|
||||
contactPerson: form.contactPerson,
|
||||
contactPhone: form.contactPhone,
|
||||
contactEmail: form.contactEmail,
|
||||
username: form.username,
|
||||
roleId: form.roleId,
|
||||
roleName: selectedRole?.roleName || '',
|
||||
cardCount: 0,
|
||||
deviceCount: 0,
|
||||
balance: form.initialBalance,
|
||||
status: form.status as any,
|
||||
createTime: new Date().toLocaleString('zh-CN'),
|
||||
remark: form.remark
|
||||
})
|
||||
ElMessage.success('企业客户创建成功')
|
||||
} else {
|
||||
ElMessage.success('企业客户更新成功')
|
||||
}
|
||||
dialogVisible.value = false
|
||||
formEl.resetFields()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const viewDetail = (row: EnterpriseCustomer) => {
|
||||
currentDetail.value = { ...row }
|
||||
detailDialogVisible.value = true
|
||||
}
|
||||
|
||||
const toggleStatus = (row: EnterpriseCustomer) => {
|
||||
const action = row.status === 'active' ? '禁用' : '启用'
|
||||
ElMessageBox.confirm(`确定${action}该企业客户吗?`, `${action}确认`, {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
row.status = row.status === 'active' ? 'disabled' : 'active'
|
||||
ElMessage.success(`${action}成功`)
|
||||
})
|
||||
}
|
||||
|
||||
const handleDelete = (row: EnterpriseCustomer) => {
|
||||
ElMessageBox.confirm('删除后无法恢复,确定要删除该企业客户吗?', '删除确认', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'error'
|
||||
}).then(() => {
|
||||
const index = mockData.value.findIndex((item) => item.id === row.id)
|
||||
if (index !== -1) mockData.value.splice(index, 1)
|
||||
ElMessage.success('删除成功')
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.page-content {
|
||||
:deep(.el-upload-list--picture-card .el-upload-list__item) {
|
||||
width: 148px;
|
||||
height: 148px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user