chore(web): 收口前几轮后台改动(品牌 / 媒资上传 / 轮播图 / 类型管理 / 操作日志 / App 用户)

为什么:这些改动在前几轮就已完成并在 :3005 验证过,但一直留在工作区没有入库。
下一步 3-4 的四个页面依赖其中的公共样式入口(src/styles/business/index.css)与
api/admin 的公共类型(PageResult 等),如果不先收口,新页面的提交就会夹带旧改动,
既说不清 diff 边界,也让 index.css 这类多轮共用的文件长期处于半提交状态。

本提交只做归档,不改行为:
- 品牌与接口基址改成 FruitShorts / 短剧服务(.env.*、README、vite 代理)
- 去掉前端 mock 数据目录(src/data、src/permissions、stores/auth),改为走真实接口
- 新增 api/admin 下的接口封装与 src/utils/uploader.ts(媒资分片直传)
- 新增/改造:轮播图管理、类型管理、操作日志、App 用户管理、后台布局与公共样式
This commit is contained in:
Vicky committed 2026-10-15 13:28:01 +08:00
1 parent 40546bfd01
commit 44c2c46a68
59 files changed
+4082 -982

No files matched your search

@@ -1,54 +1,77 @@
import { computed, nextTick, ref, watch } from 'vue'
import { ElMessage } from 'element-plus'
import { roleData } from '../../../data/roles'
import { customMenus } from '../../../data/customMenus'
import { permissionGroups } from '../../../permissions/permissions'
import { useAuth } from '../../../stores/auth'
import { useRbac } from '../../../stores/rbac'
import { computed, nextTick, onMounted, ref, watch } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import {
createRoleApi,
deleteRoleApi,
listPermissionGroupsApi,
listRoleCategoriesApi,
listRolesApi,
updateRolePermissionsApi
} from '@/api/admin/role'
import { useUserStoreWithOut } from '@/store/modules/user'
import { useRbac } from '@/stores/rbac'
export function useRolePermissionEditor() {
const { rolePermissions, updateRolePermission, createRole } = useRbac()
const { currentRole } = useAuth()
const selectedRole = ref(roleData[0].name)
const userStore = useUserStoreWithOut()
const { can } = useRbac()
const roleData = ref([])
const categories = ref([])
const permissionGroups = ref([])
const selectedRoleId = ref(null)
const dialogVisible = ref(false)
const newRoleName = ref('')
const newRoleCategoryId = ref(undefined)
const treeRef = ref()
const syncingTree = ref(false)
const permissionTree = computed(() => {
const groups = permissionGroups.map((group) => ({
const permissionTree = computed(() =>
permissionGroups.value.map((group) => ({
id: `group-${group.name}`,
label: group.name,
children: group.items.map((permission) => ({ id: permission, label: permission, permission }))
children: group.items.map((item) => ({
id: item.code,
label: item.name,
permission: item.code
}))
}))
if (customMenus.length) {
groups.push({
id: 'group-custom-menus',
label: '自定义菜单',
children: customMenus.map((menu) => ({
id: menu.permission,
label: menu.title,
permission: menu.permission
}))
})
}
return groups
})
const currentLevel = computed(
() => roleData.find((role) => role.name === currentRole.value)?.level || 1
)
const selectedRoleRecord = computed(() =>
roleData.find((role) => role.name === selectedRole.value)
)
const canEditSelectedRole = computed(
() => (selectedRoleRecord.value?.level || 1) >= currentLevel.value
)
const selectedRoleRecord = computed(() =>
roleData.value.find((role) => role.id === selectedRoleId.value)
)
const selectedRole = computed(() => selectedRoleRecord.value?.name || '—')
const currentLevel = computed(() => {
const code = userStore.getUserInfo?.role
return roleData.value.find((role) => role.code === code)?.level ?? 1
})
const canEditSelectedRole = computed(
() =>
can('system:role') &&
!!selectedRoleRecord.value &&
selectedRoleRecord.value.level >= currentLevel.value
)
async function load() {
const [rolesRes, groupsRes, categoriesRes] = await Promise.all([
listRolesApi(),
listPermissionGroupsApi(),
listRoleCategoriesApi()
])
roleData.value = rolesRes.data || []
permissionGroups.value = groupsRes.data || []
categories.value = categoriesRes.data || []
if (!selectedRoleId.value && roleData.value.length) {
selectedRoleId.value = roleData.value[0].id
}
}
watch(
selectedRole,
async (role) => {
[selectedRoleId, permissionGroups],
async () => {
syncingTree.value = true
await nextTick()
treeRef.value?.setCheckedKeys(rolePermissions[role] || [])
treeRef.value?.setCheckedKeys(selectedRoleRecord.value?.permissions || [])
await nextTick()
syncingTree.value = false
},
@@ -56,36 +79,72 @@ export function useRolePermissionEditor() {
)
function selectRole(row) {
selectedRole.value = row.name
selectedRoleId.value = row.id
}
function handleCheck(data, checked) {
if (!syncingTree.value && data.permission && canEditSelectedRole.value) {
updateRolePermission(selectedRole.value, data.permission, checked)
}
function readCheckedCodes() {
const checked = treeRef.value?.getCheckedKeys() || []
return checked.filter((key) => typeof key === 'string' && !key.startsWith('group-'))
}
function addRole() {
/** 勾选即保存:把整棵树的权限码提交给后端覆盖。 */
async function handleCheck() {
if (syncingTree.value || !canEditSelectedRole.value) return
const role = selectedRoleRecord.value
if (!role) return
const codes = readCheckedCodes()
await updateRolePermissionsApi(role.id, codes)
role.permissions = codes
ElMessage.success('权限已保存')
}
async function addRole() {
const name = newRoleName.value.trim()
if (!name) return ElMessage.warning('请输入角色名称')
if (roleData.some((role) => role.name === name)) return ElMessage.warning('角色名称已存在')
roleData.push({
id: Date.now(),
if (roleData.value.some((role) => role.name === name)) {
return ElMessage.warning('角色名称已存在')
}
await createRoleApi({
name,
description: '自定义权限角色',
memberCount: 0,
category: '自定义角色',
categoryId: newRoleCategoryId.value || categories.value[0]?.id || 0,
level: currentLevel.value + 1,
status: '启用'
description: '自定义角色',
permissions: []
})
createRole(name)
selectedRole.value = name
newRoleName.value = ''
newRoleCategoryId.value = undefined
dialogVisible.value = false
ElMessage.success('角色已创建')
await load()
const created = roleData.value.find((role) => role.name === name)
if (created) selectedRoleId.value = created.id
}
async function removeRole() {
const role = selectedRoleRecord.value
if (!role) return ElMessage.warning('请先选择角色')
if (role.builtin) return ElMessage.warning('内置角色不可删除')
try {
await ElMessageBox.confirm(`确定删除角色「${role.name}」吗?`, '删除确认', {
confirmButtonText: '确认删除',
cancelButtonText: '取消',
type: 'warning',
confirmButtonClass: 'el-button--danger'
})
} catch {
return
}
await deleteRoleApi(role.id)
ElMessage.success('角色已删除')
selectedRoleId.value = null
await load()
}
onMounted(load)
return {
roleData,
rolePermissions,
categories,
selectedRole,
selectedRoleRecord,
canEditSelectedRole,
@@ -93,8 +152,10 @@ export function useRolePermissionEditor() {
treeRef,
dialogVisible,
newRoleName,
newRoleCategoryId,
selectRole,
handleCheck,
addRole
addRole,
removeRole
}
}