🎨合并编译打包
This commit is contained in:
@@ -0,0 +1,95 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from "vue";
|
||||
import { FormInstance } from "element-plus";
|
||||
|
||||
// 声明 props 类型
|
||||
export interface ChangePwdFormProps {
|
||||
formInline: {
|
||||
id: string;
|
||||
originPassword: string;
|
||||
newPassword: string;
|
||||
confirmPassword: string;
|
||||
};
|
||||
}
|
||||
|
||||
// 声明 props 默认值
|
||||
// 推荐阅读:https://cn.vuejs.org/guide/typescript/composition-api.html#typing-component-props
|
||||
const props = withDefaults(defineProps<ChangePwdFormProps>(), {
|
||||
formInline: () => ({
|
||||
id: "",
|
||||
originPassword: "",
|
||||
newPassword: "",
|
||||
confirmPassword: ""
|
||||
})
|
||||
});
|
||||
|
||||
// vue 规定所有的 prop 都遵循着单向绑定原则,直接修改 prop 时,Vue 会抛出警告。此处的写法仅仅是为了消除警告。
|
||||
// 因为对一个 reactive 对象执行 ref,返回 Ref 对象的 value 值仍为传入的 reactive 对象,
|
||||
// 即 newFormInline === props.formInline 为 true,所以此处代码的实际效果,仍是直接修改 props.formInline。
|
||||
// 但该写法仅适用于 props.formInline 是一个对象类型的情况,原始类型需抛出事件
|
||||
// 推荐阅读:https://cn.vuejs.org/guide/components/props.html#one-way-data-flow
|
||||
const changePwdForm = ref(props.formInline);
|
||||
const changePwdFormRef = ref<FormInstance>();
|
||||
|
||||
function getUserChangePwdFormRef() {
|
||||
return changePwdFormRef.value;
|
||||
}
|
||||
|
||||
// 密码对比校验
|
||||
const passConfirm = (rule: any, value: any, callback: any) => {
|
||||
if (value === "") {
|
||||
callback(new Error("确认密码不能为空"));
|
||||
} else if (value !== changePwdForm.value.newPassword) {
|
||||
callback(new Error("两次密码不一致"));
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
|
||||
defineExpose({ getUserChangePwdFormRef });
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-form ref="changePwdFormRef" :model="changePwdForm" label-width="20%">
|
||||
<el-form-item
|
||||
prop="originPassword"
|
||||
label="原密码"
|
||||
:rules="[{ required: true, message: '原密码不能为空', trigger: 'blur' }]"
|
||||
>
|
||||
<el-input
|
||||
v-model="changePwdForm.originPassword"
|
||||
show-password
|
||||
class="!w-[220px]"
|
||||
placeholder="原密码"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
prop="newPassword"
|
||||
label="新密码"
|
||||
:rules="[
|
||||
{ required: true, message: '新密码不能为空', trigger: 'blur' },
|
||||
{ min: 6, message: '密码最低6位', trigger: 'blur' },
|
||||
{ max: 32, message: '密码最长32位', trigger: 'blur' }
|
||||
]"
|
||||
>
|
||||
<el-input
|
||||
v-model="changePwdForm.newPassword"
|
||||
show-password
|
||||
class="!w-[220px]"
|
||||
placeholder="新密码"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
prop="confirmPassword"
|
||||
label="确认密码"
|
||||
:rules="[{ validator: passConfirm, trigger: 'blur' }]"
|
||||
>
|
||||
<el-input
|
||||
v-model="changePwdForm.confirmPassword"
|
||||
show-password
|
||||
class="!w-[220px]"
|
||||
placeholder="确认密码"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</template>
|
102
web-src/src/layout/components/lay-navbar/component/user.vue
Normal file
102
web-src/src/layout/components/lay-navbar/component/user.vue
Normal file
@@ -0,0 +1,102 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from "vue";
|
||||
import { FormInstance } from "element-plus";
|
||||
|
||||
// 声明 props 类型
|
||||
export interface FormProps {
|
||||
formInline: {
|
||||
id: string;
|
||||
avatar: string;
|
||||
name: string;
|
||||
account: string;
|
||||
email: string;
|
||||
isAdmin: number;
|
||||
status: number;
|
||||
};
|
||||
}
|
||||
|
||||
// 声明 props 默认值
|
||||
// 推荐阅读:https://cn.vuejs.org/guide/typescript/composition-api.html#typing-component-props
|
||||
const props = withDefaults(defineProps<FormProps>(), {
|
||||
formInline: () => ({
|
||||
id: "",
|
||||
avatar: "",
|
||||
name: "",
|
||||
account: "",
|
||||
email: "",
|
||||
isAdmin: 0,
|
||||
status: 1
|
||||
})
|
||||
});
|
||||
|
||||
// vue 规定所有的 prop 都遵循着单向绑定原则,直接修改 prop 时,Vue 会抛出警告。此处的写法仅仅是为了消除警告。
|
||||
// 因为对一个 reactive 对象执行 ref,返回 Ref 对象的 value 值仍为传入的 reactive 对象,
|
||||
// 即 newFormInline === props.formInline 为 true,所以此处代码的实际效果,仍是直接修改 props.formInline。
|
||||
// 但该写法仅适用于 props.formInline 是一个对象类型的情况,原始类型需抛出事件
|
||||
// 推荐阅读:https://cn.vuejs.org/guide/components/props.html#one-way-data-flow
|
||||
const userEditForm = ref(props.formInline);
|
||||
const userEditFormRef = ref<FormInstance>();
|
||||
|
||||
function getUserEditFormRef() {
|
||||
return userEditFormRef.value;
|
||||
}
|
||||
|
||||
defineExpose({ getUserEditFormRef });
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-form ref="userEditFormRef" :model="userEditForm" label-width="20%">
|
||||
<el-form-item
|
||||
prop="name"
|
||||
label="名称"
|
||||
:rules="[{ required: true, message: '名称不能为空', trigger: 'blur' }]"
|
||||
>
|
||||
<el-input
|
||||
v-model="userEditForm.name"
|
||||
class="!w-[220px]"
|
||||
placeholder="名称"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
prop="account"
|
||||
label="账号"
|
||||
:rules="[{ required: true, message: '账号不能为空', trigger: 'blur' }]"
|
||||
>
|
||||
<el-input
|
||||
v-model="userEditForm.account"
|
||||
disabled
|
||||
class="!w-[220px]"
|
||||
placeholder="账号"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item prop="email" label="邮箱">
|
||||
<el-input
|
||||
v-model="userEditForm.email"
|
||||
class="!w-[220px]"
|
||||
placeholder="邮箱"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
prop="isAdmin"
|
||||
label="超管"
|
||||
:rules="[
|
||||
{ required: true, message: '是否为超管不能为空', trigger: 'blur' }
|
||||
]"
|
||||
>
|
||||
<el-select v-model="userEditForm.isAdmin" disabled class="!w-[220px]">
|
||||
<el-option label="否" :value="0" />
|
||||
<el-option label="是" :value="1" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
prop="status"
|
||||
label="状态"
|
||||
:rules="[{ required: true, message: '状态不能为空', trigger: 'blur' }]"
|
||||
>
|
||||
<el-select v-model="userEditForm.status" disabled class="!w-[220px]">
|
||||
<el-option label="禁用" :value="0" />
|
||||
<el-option label="启用" :value="1" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</template>
|
253
web-src/src/layout/components/lay-navbar/index.vue
Normal file
253
web-src/src/layout/components/lay-navbar/index.vue
Normal file
@@ -0,0 +1,253 @@
|
||||
<script setup lang="ts">
|
||||
import { useNav } from "@/layout/hooks/useNav";
|
||||
import LaySearch from "../lay-search/index.vue";
|
||||
import LayNotice from "../lay-notice/index.vue";
|
||||
import LayNavMix from "../lay-sidebar/NavMix.vue";
|
||||
import LaySidebarFullScreen from "../lay-sidebar/components/SidebarFullScreen.vue";
|
||||
import LaySidebarBreadCrumb from "../lay-sidebar/components/SidebarBreadCrumb.vue";
|
||||
import LaySidebarTopCollapse from "../lay-sidebar/components/SidebarTopCollapse.vue";
|
||||
import LogoutCircleRLine from "@iconify-icons/ri/logout-circle-r-line";
|
||||
import Setting from "@iconify-icons/ri/settings-3-line";
|
||||
import { h, ref } from "vue";
|
||||
import { addDialog } from "@/components/ReDialog/index";
|
||||
import {
|
||||
changePassword,
|
||||
editUser as editUserApi,
|
||||
getUser,
|
||||
userList
|
||||
} from "@/api/user";
|
||||
import { storageLocal } from "@pureadmin/utils";
|
||||
import { setUser, userKey } from "@/utils/auth";
|
||||
import forms, { type FormProps } from "./component/user.vue";
|
||||
import changePwdForms, {
|
||||
type ChangePwdFormProps
|
||||
} from "./component/change-password.vue";
|
||||
import { useRouter } from "vue-router";
|
||||
import useGetGlobalProperties from "@/hooks/useGetGlobalProperties";
|
||||
|
||||
const {
|
||||
layout,
|
||||
device,
|
||||
logout,
|
||||
onPanel,
|
||||
pureApp,
|
||||
username,
|
||||
userAvatar,
|
||||
avatarsStyle,
|
||||
toggleSideBar
|
||||
} = useNav();
|
||||
|
||||
const { $bus } = useGetGlobalProperties();
|
||||
|
||||
const router = useRouter();
|
||||
const userEditFormRef = ref();
|
||||
const changePwdFormRef = ref();
|
||||
// eslint-disable-next-line vue/valid-define-emits
|
||||
const emit = defineEmits();
|
||||
|
||||
// 打开用户信息编辑窗口
|
||||
const openUserInfoDialog = () => {
|
||||
const loginUser = storageLocal().getItem(userKey);
|
||||
addDialog({
|
||||
width: "20%",
|
||||
title: loginUser.name,
|
||||
contentRenderer: () => h(forms, { ref: userEditFormRef }),
|
||||
props: {
|
||||
formInline: {
|
||||
id: loginUser.id,
|
||||
avatar: loginUser.avatar,
|
||||
name: loginUser.name,
|
||||
account: loginUser.account,
|
||||
email: loginUser.email,
|
||||
isAdmin: loginUser.isAdmin,
|
||||
status: loginUser.status
|
||||
}
|
||||
},
|
||||
beforeSure: (done, { options }) => {
|
||||
const FormRef = userEditFormRef.value.getUserEditFormRef();
|
||||
FormRef.validate(valid => {
|
||||
if (!valid) return;
|
||||
editUserApi(options.props.formInline).then(res => {
|
||||
if (res.code === 200) {
|
||||
// 重新拉一下当前登陆用户信息
|
||||
getUser().then(res => {
|
||||
if (res.code === 200) {
|
||||
setUser(res.data);
|
||||
// 指定路由,刷新页面数据
|
||||
if (router.options.history.location === "/user/index") {
|
||||
userList({
|
||||
current: 1,
|
||||
size: 10
|
||||
}).then(res => {
|
||||
if (res.code === 200) {
|
||||
$bus.emit("userListData", res);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
done();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const openChangePasswordDialog = () => {
|
||||
const loginUser = storageLocal().getItem(userKey);
|
||||
addDialog({
|
||||
width: "20%",
|
||||
title: loginUser.name,
|
||||
contentRenderer: () => h(changePwdForms, { ref: changePwdFormRef }),
|
||||
props: {
|
||||
formInline: {
|
||||
id: loginUser.id,
|
||||
originPassword: "",
|
||||
newPassword: "",
|
||||
confirmPassword: ""
|
||||
}
|
||||
},
|
||||
beforeSure: (done, { options }) => {
|
||||
const FormRef = changePwdFormRef.value.getUserChangePwdFormRef();
|
||||
FormRef.validate(valid => {
|
||||
if (!valid) return;
|
||||
changePassword(options.props.formInline).then(res => {
|
||||
if (res.code === 200) {
|
||||
done();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="navbar bg-[#fff] shadow-sm shadow-[rgba(0,21,41,0.08)]">
|
||||
<LaySidebarTopCollapse
|
||||
v-if="device === 'mobile'"
|
||||
class="hamburger-container"
|
||||
:is-active="pureApp.sidebar.opened"
|
||||
@toggleClick="toggleSideBar"
|
||||
/>
|
||||
|
||||
<LaySidebarBreadCrumb
|
||||
v-if="layout !== 'mix' && device !== 'mobile'"
|
||||
class="breadcrumb-container"
|
||||
/>
|
||||
|
||||
<LayNavMix v-if="layout === 'mix'" />
|
||||
|
||||
<div v-if="layout === 'vertical'" class="vertical-header-right">
|
||||
<!-- 菜单搜索 -->
|
||||
<!-- <LaySearch id="header-search" />-->
|
||||
<!-- 全屏 -->
|
||||
<!-- <LaySidebarFullScreen id="full-screen" />-->
|
||||
<!-- 消息通知 -->
|
||||
<!-- <LayNotice id="header-notice" />-->
|
||||
<!-- 退出登录 -->
|
||||
<el-dropdown trigger="click">
|
||||
<span class="el-dropdown-link navbar-bg-hover select-none">
|
||||
<img :src="userAvatar" :style="avatarsStyle" />
|
||||
<p v-if="username" class="dark:text-white">{{ username }}</p>
|
||||
</span>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu class="logout">
|
||||
<el-dropdown-item @click="openUserInfoDialog">
|
||||
<IconifyIconOnline
|
||||
icon="eva:person-outline"
|
||||
style="margin: 5px"
|
||||
/>
|
||||
个人信息
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
<el-dropdown-menu class="logout">
|
||||
<el-dropdown-item @click="openChangePasswordDialog">
|
||||
<IconifyIconOnline
|
||||
icon="ic:baseline-lock-person"
|
||||
style="margin: 5px"
|
||||
/>
|
||||
修改密码
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
<el-dropdown-menu class="logout">
|
||||
<el-dropdown-item @click="logout">
|
||||
<IconifyIconOffline
|
||||
:icon="LogoutCircleRLine"
|
||||
style="margin: 5px"
|
||||
/>
|
||||
退出系统
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
<span
|
||||
class="set-icon navbar-bg-hover"
|
||||
title="打开系统配置"
|
||||
@click="onPanel"
|
||||
>
|
||||
<IconifyIconOffline :icon="Setting" />
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.navbar {
|
||||
width: 100%;
|
||||
height: 48px;
|
||||
overflow: hidden;
|
||||
|
||||
.hamburger-container {
|
||||
float: left;
|
||||
height: 100%;
|
||||
line-height: 48px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.vertical-header-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
min-width: 280px;
|
||||
height: 48px;
|
||||
color: #000000d9;
|
||||
|
||||
.el-dropdown-link {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-around;
|
||||
height: 48px;
|
||||
padding: 10px;
|
||||
color: #000000d9;
|
||||
cursor: pointer;
|
||||
|
||||
p {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
img {
|
||||
width: 52px;
|
||||
height: 52px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.breadcrumb-container {
|
||||
float: left;
|
||||
margin-left: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.logout {
|
||||
width: 120px;
|
||||
|
||||
::v-deep(.el-dropdown-menu__item) {
|
||||
display: inline-flex;
|
||||
flex-wrap: wrap;
|
||||
min-width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
Reference in New Issue
Block a user