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:
sexygoat
2026-01-22 16:35:33 +08:00
commit 222e5bb11a
495 changed files with 145440 additions and 0 deletions

View File

@@ -0,0 +1,514 @@
<template>
<ArtTableFullScreen>
<div class="role-page" id="table-full-screen">
<!-- 搜索栏 -->
<ArtSearchBar
v-model:filter="searchForm"
:items="searchFormItems"
:show-expand="false"
@reset="handleReset"
@search="handleSearch"
></ArtSearchBar>
<ElCard shadow="never" class="art-table-card">
<!-- 表格头部 -->
<ArtTableHeader
:columnList="columnOptions"
v-model:columns="columnChecks"
@refresh="handleRefresh"
>
<template #left>
<ElButton @click="showDialog('add')">新增角色</ElButton>
</template>
</ArtTableHeader>
<!-- 表格 -->
<ArtTable
ref="tableRef"
row-key="ID"
:loading="loading"
:data="roleList"
:currentPage="pagination.page"
:pageSize="pagination.pageSize"
:total="pagination.total"
:marginTop="10"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
>
<template #default>
<ElTableColumn v-for="col in columns" :key="col.prop || col.type" v-bind="col" />
</template>
</ArtTable>
<!-- 新增/编辑对话框 -->
<ElDialog
v-model="dialogVisible"
:title="dialogType === 'add' ? '新增角色' : '编辑角色'"
width="30%"
>
<ElForm ref="formRef" :model="form" :rules="rules" label-width="120px">
<ElFormItem label="角色名称" prop="role_name">
<ElInput v-model="form.role_name" placeholder="请输入角色名称" />
</ElFormItem>
<ElFormItem label="角色描述" prop="role_desc">
<ElInput v-model="form.role_desc" type="textarea" :rows="3" placeholder="请输入角色描述" />
</ElFormItem>
<ElFormItem label="角色类型" prop="role_type">
<ElSelect v-model="form.role_type" placeholder="请选择角色类型" style="width: 100%">
<ElOption label="平台角色" :value="1" />
<ElOption label="客户角色" :value="2" />
</ElSelect>
</ElFormItem>
<ElFormItem label="状态">
<ElSwitch
v-model="form.status"
:active-value="CommonStatus.ENABLED"
:inactive-value="CommonStatus.DISABLED"
/>
</ElFormItem>
</ElForm>
<template #footer>
<div class="dialog-footer">
<ElButton @click="dialogVisible = false">取消</ElButton>
<ElButton type="primary" @click="handleSubmit(formRef)" :loading="submitLoading">
提交
</ElButton>
</div>
</template>
</ElDialog>
<!-- 分配权限对话框 -->
<ElDialog
v-model="permissionDialogVisible"
title="分配权限"
width="500px"
>
<ElCheckboxGroup v-model="selectedPermissions">
<div v-for="permission in allPermissions" :key="permission.ID" style="margin-bottom: 12px;">
<ElCheckbox :label="permission.ID">
{{ permission.perm_name }}
<ElTag :type="permission.perm_type === 1 ? '' : 'success'" size="small" style="margin-left: 8px;">
{{ permission.perm_type === 1 ? '菜单' : '按钮' }}
</ElTag>
</ElCheckbox>
</div>
</ElCheckboxGroup>
<template #footer>
<div class="dialog-footer">
<ElButton @click="permissionDialogVisible = false">取消</ElButton>
<ElButton type="primary" @click="handleAssignPermissions" :loading="permissionSubmitLoading">
提交
</ElButton>
</div>
</template>
</ElDialog>
</ElCard>
</div>
</ArtTableFullScreen>
</template>
<script setup lang="ts">
import { h } from 'vue'
import { RoleService, PermissionService } from '@/api/modules'
import { ElMessage, ElMessageBox, ElTag, ElCheckbox, ElCheckboxGroup, ElSwitch } from 'element-plus'
import type { FormInstance, FormRules } from 'element-plus'
import type { PlatformRole, Permission } from '@/types/api'
import type { SearchFormItem, SearchChangeParams } from '@/types'
import { useCheckedColumns } from '@/composables/useCheckedColumns'
import ArtButtonTable from '@/components/core/forms/ArtButtonTable.vue'
import { formatDateTime } from '@/utils/business/format'
import { CommonStatus, getStatusText } from '@/config/constants'
defineOptions({ name: 'Role' })
const dialogVisible = ref(false)
const permissionDialogVisible = ref(false)
const loading = ref(false)
const submitLoading = ref(false)
const permissionSubmitLoading = ref(false)
const tableRef = ref()
const currentRoleId = ref<number>(0)
const selectedPermissions = ref<number[]>([])
const originalPermissions = ref<number[]>([]) // 保存原始权限,用于对比
const allPermissions = ref<Permission[]>([])
// 搜索表单初始值
const initialSearchState = {
role_name: ''
}
// 搜索表单
const searchForm = reactive({ ...initialSearchState })
// 搜索表单配置
const searchFormItems: SearchFormItem[] = [
{
label: '角色名称',
prop: 'role_name',
type: 'input',
config: {
clearable: true,
placeholder: '请输入角色名称'
}
}
]
// 分页
const pagination = reactive({
page: 1,
pageSize: 20,
total: 0
})
// 列配置
const columnOptions = [
{ label: 'ID', prop: 'ID' },
{ label: '角色名称', prop: 'role_name' },
{ label: '角色描述', prop: 'role_desc' },
{ label: '角色类型', prop: 'role_type' },
{ label: '状态', prop: 'status' },
{ label: '创建时间', prop: 'CreatedAt' },
{ label: '操作', prop: 'operation' }
]
const formRef = ref<FormInstance>()
const rules = reactive<FormRules>({
role_name: [
{ required: true, message: '请输入角色名称', trigger: 'blur' },
{ min: 2, max: 50, message: '长度在 2 到 50 个字符', trigger: 'blur' }
],
role_desc: [{ max: 200, message: '描述不能超过200个字符', trigger: 'blur' }],
role_type: [{ required: true, message: '请选择角色类型', trigger: 'change' }]
})
const form = reactive<any>({
id: 0,
role_name: '',
role_desc: '',
role_type: 1,
status: CommonStatus.ENABLED
})
const roleList = ref<PlatformRole[]>([])
// 动态列配置
const { columnChecks, columns } = useCheckedColumns(() => [
{
prop: 'ID',
label: 'ID',
width: 80
},
{
prop: 'role_name',
label: '角色名称',
minWidth: 120
},
{
prop: 'role_desc',
label: '角色描述',
minWidth: 150
},
{
prop: 'role_type',
label: '角色类型',
width: 100,
formatter: (row: any) => {
return h(
ElTag,
{ type: row.role_type === 1 ? 'primary' : 'success' },
() => (row.role_type === 1 ? '平台角色' : '客户角色')
)
}
},
{
prop: 'status',
label: '状态',
width: 100,
formatter: (row: any) => {
return h(ElSwitch, {
modelValue: row.status,
activeValue: CommonStatus.ENABLED,
inactiveValue: CommonStatus.DISABLED,
activeText: getStatusText(CommonStatus.ENABLED),
inactiveText: getStatusText(CommonStatus.DISABLED),
inlinePrompt: true,
'onUpdate:modelValue': (val: number) => handleStatusChange(row, val)
})
}
},
{
prop: 'CreatedAt',
label: '创建时间',
width: 180,
formatter: (row: any) => formatDateTime(row.CreatedAt)
},
{
prop: 'operation',
label: '操作',
width: 180,
fixed: 'right',
formatter: (row: any) => {
return h('div', { style: 'display: flex; gap: 8px;' }, [
h(ArtButtonTable, {
icon: '&#xe72b;',
onClick: () => showPermissionDialog(row)
}),
h(ArtButtonTable, {
type: 'edit',
onClick: () => showDialog('edit', row)
}),
h(ArtButtonTable, {
type: 'delete',
onClick: () => deleteRole(row)
})
])
}
}
])
onMounted(() => {
getTableData()
loadAllPermissions()
})
// 加载所有权限列表
const loadAllPermissions = async () => {
try {
const res = await PermissionService.getPermissions({ page: 1, page_size: 1000 })
if (res.code === 0) {
allPermissions.value = res.data.items || []
}
} catch (error) {
console.error('获取权限列表失败:', error)
}
}
// 显示分配权限对话框
const showPermissionDialog = async (row: PlatformRole) => {
currentRoleId.value = row.ID
selectedPermissions.value = []
originalPermissions.value = []
try {
// 每次打开对话框时重新加载最新的权限列表
await loadAllPermissions()
// 加载当前角色的权限
const res = await RoleService.getRolePermissions(row.ID)
if (res.code === 0 || Array.isArray(res.data)) {
// 提取权限ID数组
const permissions = res.data || []
if (Array.isArray(permissions) && permissions.length > 0) {
// 如果返回的是权限对象数组提取ID
if (typeof permissions[0] === 'object' && 'ID' in permissions[0]) {
selectedPermissions.value = permissions.map((perm: any) => perm.ID)
} else {
// 如果返回的是ID数组
selectedPermissions.value = permissions
}
}
// 保存原始权限,用于后续对比
originalPermissions.value = [...selectedPermissions.value]
// 数据加载完成后再打开对话框
permissionDialogVisible.value = true
}
} catch (error) {
console.error('获取角色权限失败:', error)
}
}
// 提交分配权限
const handleAssignPermissions = async () => {
permissionSubmitLoading.value = true
try {
// 对比原始权限和当前选中的权限,找出需要新增和移除的权限
const addedPermissions = selectedPermissions.value.filter(
id => !originalPermissions.value.includes(id)
)
const removedPermissions = originalPermissions.value.filter(
id => !selectedPermissions.value.includes(id)
)
// 使用 Promise.all 并发执行新增和移除操作
const promises: Promise<any>[] = []
// 如果有新增的权限,调用分配接口
if (addedPermissions.length > 0) {
promises.push(RoleService.assignPermissions(currentRoleId.value, addedPermissions))
}
// 如果有移除的权限,调用移除接口
if (removedPermissions.length > 0) {
removedPermissions.forEach(permId => {
promises.push(RoleService.removePermission(currentRoleId.value, permId))
})
}
// 等待所有操作完成
if (promises.length > 0) {
await Promise.all(promises)
ElMessage.success('权限更新成功')
} else {
ElMessage.info('权限未发生变化')
}
permissionDialogVisible.value = false
} catch (error) {
console.error(error)
} finally {
permissionSubmitLoading.value = false
}
}
// 获取角色列表
const getTableData = async () => {
loading.value = true
try {
const params = {
page: pagination.page,
pageSize: pagination.pageSize,
role_name: searchForm.role_name || undefined
}
const res = await RoleService.getRoles(params)
if (res.code === 0) {
roleList.value = res.data.items || []
pagination.total = res.data.total || 0
}
} catch (error) {
console.error(error)
} finally {
loading.value = false
}
}
// 重置搜索
const handleReset = () => {
Object.assign(searchForm, { ...initialSearchState })
pagination.page = 1
getTableData()
}
// 搜索
const handleSearch = () => {
pagination.page = 1
getTableData()
}
// 刷新表格
const handleRefresh = () => {
getTableData()
}
// 处理表格分页变化
const handleSizeChange = (newPageSize: number) => {
pagination.pageSize = newPageSize
getTableData()
}
const handleCurrentChange = (newCurrentPage: number) => {
pagination.page = newCurrentPage
getTableData()
}
const dialogType = ref('add')
// 显示新增/编辑对话框
const showDialog = (type: string, row?: any) => {
dialogVisible.value = true
dialogType.value = type
if (type === 'edit' && row) {
form.id = row.ID
form.role_name = row.role_name
form.role_desc = row.role_desc
form.role_type = row.role_type
form.status = row.status
} else {
form.id = 0
form.role_name = ''
form.role_desc = ''
form.role_type = 1
form.status = CommonStatus.ENABLED
}
}
// 删除角色
const deleteRole = (row: any) => {
ElMessageBox.confirm(`确定删除角色 ${row.role_name} 吗?`, '删除确认', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'error'
})
.then(async () => {
try {
await RoleService.deleteRole(row.ID)
ElMessage.success('删除成功')
getTableData()
} catch (error) {
console.error(error)
}
})
.catch(() => {
// 用户取消删除
})
}
// 提交表单
const handleSubmit = async (formEl: FormInstance | undefined) => {
if (!formEl) return
await formEl.validate(async (valid) => {
if (valid) {
submitLoading.value = true
try {
const data = {
role_name: form.role_name,
role_desc: form.role_desc,
role_type: form.role_type,
status: form.status
}
if (dialogType.value === 'add') {
await RoleService.createRole(data)
ElMessage.success('新增成功')
} else {
await RoleService.updateRole(form.id, data)
ElMessage.success('修改成功')
}
dialogVisible.value = false
formEl.resetFields()
getTableData()
} catch (error) {
console.error(error)
} finally {
submitLoading.value = false
}
}
})
}
// 状态切换
const handleStatusChange = async (row: any, newStatus: number) => {
const oldStatus = row.status
// 先更新UI
row.status = newStatus
try {
await RoleService.updateRole(row.ID, { status: newStatus })
ElMessage.success('状态切换成功')
} catch (error) {
// 切换失败,恢复原状态
row.status = oldStatus
console.error(error)
}
}
</script>
<style lang="scss" scoped>
.role-page {
// 可以在这里添加角色页面特定样式
}
</style>