|
@@ -1,5 +1,5 @@
|
|
|
<script setup>
|
|
<script setup>
|
|
|
-import { ref } from 'vue'
|
|
|
|
|
|
|
+import { ref, computed } from 'vue'
|
|
|
import { useRouter } from 'vue-router'
|
|
import { useRouter } from 'vue-router'
|
|
|
import Button from 'primevue/button'
|
|
import Button from 'primevue/button'
|
|
|
import Menu from 'primevue/menu'
|
|
import Menu from 'primevue/menu'
|
|
@@ -17,33 +17,61 @@ import { useUserStore } from '@/stores/user'
|
|
|
import { resetPasswordApi } from '@/services/api'
|
|
import { resetPasswordApi } from '@/services/api'
|
|
|
import { zodResolver } from '@primevue/forms/resolvers/zod'
|
|
import { zodResolver } from '@primevue/forms/resolvers/zod'
|
|
|
import { z } from 'zod'
|
|
import { z } from 'zod'
|
|
|
|
|
+import { filterMenuByRole } from '@/utils/permission'
|
|
|
|
|
|
|
|
const router = useRouter()
|
|
const router = useRouter()
|
|
|
const sidebarVisible = ref(false)
|
|
const sidebarVisible = ref(false)
|
|
|
const userMenuRef = ref()
|
|
const userMenuRef = ref()
|
|
|
const toast = useToast()
|
|
const toast = useToast()
|
|
|
|
|
+const userStore = useUserStore()
|
|
|
|
|
+
|
|
|
const showUserMenu = (event) => {
|
|
const showUserMenu = (event) => {
|
|
|
userMenuRef.value.toggle(event)
|
|
userMenuRef.value.toggle(event)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-const navItems = [
|
|
|
|
|
|
|
+// 所有菜单项配置,包含角色权限
|
|
|
|
|
+const allNavItems = [
|
|
|
{
|
|
{
|
|
|
label: '首页',
|
|
label: '首页',
|
|
|
icon: 'pi pi-fw pi-home',
|
|
icon: 'pi pi-fw pi-home',
|
|
|
- name: 'dashboard'
|
|
|
|
|
|
|
+ name: 'dashboard',
|
|
|
|
|
+ roles: ['user', 'admin', 'channel', 'operator', 'mss', 'show']
|
|
|
},
|
|
},
|
|
|
{
|
|
{
|
|
|
label: '用户管理',
|
|
label: '用户管理',
|
|
|
icon: 'pi pi-fw pi-user',
|
|
icon: 'pi pi-fw pi-user',
|
|
|
- name: 'user'
|
|
|
|
|
|
|
+ name: 'user',
|
|
|
|
|
+ roles: ['admin']
|
|
|
},
|
|
},
|
|
|
{
|
|
{
|
|
|
label: '参数配置',
|
|
label: '参数配置',
|
|
|
icon: 'pi pi-fw pi-cog',
|
|
icon: 'pi pi-fw pi-cog',
|
|
|
- name: 'sys-config'
|
|
|
|
|
|
|
+ name: 'sys-config',
|
|
|
|
|
+ roles: ['admin']
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ label: '母菜单示例',
|
|
|
|
|
+ icon: 'pi pi-fw pi-list',
|
|
|
|
|
+ roles: ['admin'],
|
|
|
|
|
+ items: [
|
|
|
|
|
+ {
|
|
|
|
|
+ label: '子菜单示例',
|
|
|
|
|
+ icon: 'pi pi-fw pi-circle',
|
|
|
|
|
+ name: 'sub-menu',
|
|
|
|
|
+ roles: ['admin']
|
|
|
|
|
+ },
|
|
|
|
|
+ ]
|
|
|
}
|
|
}
|
|
|
]
|
|
]
|
|
|
|
|
|
|
|
|
|
+// 根据用户角色过滤菜单项
|
|
|
|
|
+const navItems = computed(() => {
|
|
|
|
|
+ const userRole = userStore.userInfo?.role
|
|
|
|
|
+ if (!userRole) return []
|
|
|
|
|
+
|
|
|
|
|
+ return filterMenuByRole(allNavItems, userRole)
|
|
|
|
|
+})
|
|
|
|
|
+
|
|
|
const userMenuItems = [
|
|
const userMenuItems = [
|
|
|
{
|
|
{
|
|
|
label: '修改密码',
|
|
label: '修改密码',
|
|
@@ -131,23 +159,12 @@ const handleResetPassword = async ({ valid, values }) => {
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
<div class="layout-header fixed top-0 left-0 right-0 w-full z-1 flex items-center">
|
|
<div class="layout-header fixed top-0 left-0 right-0 w-full z-1 flex items-center">
|
|
|
- <Button
|
|
|
|
|
- icon="pi pi-bars"
|
|
|
|
|
- severity="secondary"
|
|
|
|
|
- variant="text"
|
|
|
|
|
- @click="sidebarVisible = true"
|
|
|
|
|
- class="mr-4 lg:hidden!"
|
|
|
|
|
- />
|
|
|
|
|
|
|
+ <Button icon="pi pi-bars" severity="secondary" variant="text" @click="sidebarVisible = true"
|
|
|
|
|
+ class="mr-4 lg:hidden!" />
|
|
|
<img src="@/assets/logo.svg" alt="logo" class="logo" />
|
|
<img src="@/assets/logo.svg" alt="logo" class="logo" />
|
|
|
<div class="title flex-1 font-bold ml-4 text-xl">Admin</div>
|
|
<div class="title flex-1 font-bold ml-4 text-xl">Admin</div>
|
|
|
- <Button
|
|
|
|
|
- icon="pi pi-user"
|
|
|
|
|
- severity="secondary"
|
|
|
|
|
- @click="showUserMenu"
|
|
|
|
|
- aria-haspopup="true"
|
|
|
|
|
- aria-controls="overlay_menu"
|
|
|
|
|
- class="ml-2"
|
|
|
|
|
- />
|
|
|
|
|
|
|
+ <Button icon="pi pi-user" severity="secondary" @click="showUserMenu" aria-haspopup="true"
|
|
|
|
|
+ aria-controls="overlay_menu" class="ml-2" />
|
|
|
<Menu ref="userMenuRef" id="overlay_menu" :model="userMenuItems" :popup="true" />
|
|
<Menu ref="userMenuRef" id="overlay_menu" :model="userMenuItems" :popup="true" />
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
@@ -156,49 +173,30 @@ const handleResetPassword = async ({ valid, values }) => {
|
|
|
</Drawer>
|
|
</Drawer>
|
|
|
|
|
|
|
|
<Dialog v-model:visible="resetingPasswordData.visible" modal header="修改密码" :style="{ width: '25rem' }">
|
|
<Dialog v-model:visible="resetingPasswordData.visible" modal header="修改密码" :style="{ width: '25rem' }">
|
|
|
- <Form
|
|
|
|
|
- v-slot="$form"
|
|
|
|
|
- :resolver="resolver"
|
|
|
|
|
- :initialValues="resetingPasswordData"
|
|
|
|
|
- @submit="handleResetPassword"
|
|
|
|
|
- class="p-fluid"
|
|
|
|
|
- >
|
|
|
|
|
|
|
+ <Form v-slot="$form" :resolver="resolver" :initialValues="resetingPasswordData" @submit="handleResetPassword"
|
|
|
|
|
+ class="p-fluid">
|
|
|
<FloatLabel variant="on" class="mt-2">
|
|
<FloatLabel variant="on" class="mt-2">
|
|
|
<IconField>
|
|
<IconField>
|
|
|
<InputIcon class="pi pi-lock" />
|
|
<InputIcon class="pi pi-lock" />
|
|
|
- <Password
|
|
|
|
|
- id="password"
|
|
|
|
|
- name="password"
|
|
|
|
|
- v-model="resetingPasswordData.password"
|
|
|
|
|
- fluid
|
|
|
|
|
- toggleMask
|
|
|
|
|
- :feedback="false"
|
|
|
|
|
- autocomplete="off"
|
|
|
|
|
- />
|
|
|
|
|
|
|
+ <Password id="password" name="password" v-model="resetingPasswordData.password" fluid toggleMask
|
|
|
|
|
+ :feedback="false" autocomplete="off" />
|
|
|
</IconField>
|
|
</IconField>
|
|
|
<label for="password">新密码</label>
|
|
<label for="password">新密码</label>
|
|
|
</FloatLabel>
|
|
</FloatLabel>
|
|
|
<Message v-if="$form.password?.invalid" severity="error" size="small" variant="simple">{{
|
|
<Message v-if="$form.password?.invalid" severity="error" size="small" variant="simple">{{
|
|
|
$form.password.error?.message
|
|
$form.password.error?.message
|
|
|
- }}</Message>
|
|
|
|
|
|
|
+ }}</Message>
|
|
|
<FloatLabel variant="on" class="mt-4">
|
|
<FloatLabel variant="on" class="mt-4">
|
|
|
<IconField>
|
|
<IconField>
|
|
|
<InputIcon class="pi pi-lock" />
|
|
<InputIcon class="pi pi-lock" />
|
|
|
- <Password
|
|
|
|
|
- id="confirmPassword"
|
|
|
|
|
- name="confirmPassword"
|
|
|
|
|
- v-model="resetingPasswordData.confirmPassword"
|
|
|
|
|
- fluid
|
|
|
|
|
- toggleMask
|
|
|
|
|
- :feedback="false"
|
|
|
|
|
- autocomplete="off"
|
|
|
|
|
- />
|
|
|
|
|
|
|
+ <Password id="confirmPassword" name="confirmPassword" v-model="resetingPasswordData.confirmPassword" fluid
|
|
|
|
|
+ toggleMask :feedback="false" autocomplete="off" />
|
|
|
</IconField>
|
|
</IconField>
|
|
|
<label for="confirmPassword">重复新密码</label>
|
|
<label for="confirmPassword">重复新密码</label>
|
|
|
</FloatLabel>
|
|
</FloatLabel>
|
|
|
<Message v-if="$form.confirmPassword?.invalid" severity="error" size="small" variant="simple">{{
|
|
<Message v-if="$form.confirmPassword?.invalid" severity="error" size="small" variant="simple">{{
|
|
|
$form.confirmPassword.error?.message
|
|
$form.confirmPassword.error?.message
|
|
|
- }}</Message>
|
|
|
|
|
|
|
+ }}</Message>
|
|
|
<div class="field mt-4 text-right">
|
|
<div class="field mt-4 text-right">
|
|
|
<Button label="取消" severity="secondary" @click="resetingPasswordData.visible = false" />
|
|
<Button label="取消" severity="secondary" @click="resetingPasswordData.visible = false" />
|
|
|
<Button label="保存" type="submit" class="ml-4" />
|
|
<Button label="保存" type="submit" class="ml-4" />
|
|
@@ -213,6 +211,7 @@ const handleResetPassword = async ({ valid, values }) => {
|
|
|
height: 4rem;
|
|
height: 4rem;
|
|
|
background-color: var(--p-surface-0);
|
|
background-color: var(--p-surface-0);
|
|
|
padding: 0 2rem;
|
|
padding: 0 2rem;
|
|
|
|
|
+
|
|
|
.logo {
|
|
.logo {
|
|
|
width: 1.5rem;
|
|
width: 1.5rem;
|
|
|
height: 1.5rem;
|
|
height: 1.5rem;
|
|
@@ -242,9 +241,11 @@ const handleResetPassword = async ({ valid, values }) => {
|
|
|
.layout-header {
|
|
.layout-header {
|
|
|
padding: 0 1rem;
|
|
padding: 0 1rem;
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
.sidebar {
|
|
.sidebar {
|
|
|
display: none;
|
|
display: none;
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
.main-content {
|
|
.main-content {
|
|
|
margin-left: 0;
|
|
margin-left: 0;
|
|
|
width: 100%;
|
|
width: 100%;
|