Files
MoviePilot-Frontend/src/views/setting/AccountSettingAccount.vue
2023-09-22 15:11:52 +08:00

468 lines
12 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script lang="ts" setup>
import { useToast } from 'vue-toast-notification'
import { requiredValidator } from '@/@validators'
import api from '@/api'
import type { User } from '@/api/types'
import avatar1 from '@images/avatars/avatar-1.png'
const isNewPasswordVisible = ref(false)
const isConfirmPasswordVisible = ref(false)
const isPasswordVisible = ref(false)
const newPassword = ref('')
const confirmPassword = ref('')
// 提示框
const $toast = useToast()
const refInputEl = ref<HTMLElement>()
// 新增用户窗口
const addUserDialog = ref(false)
// 新增用户表单
const userForm = reactive({
name: '',
password: '',
email: '',
})
// 当前用户信息
const accountInfo = ref<User>({
id: 0,
name: '',
password: '',
email: '',
is_active: false,
is_superuser: false,
avatar: '',
})
// 所有用户信息
const allUsers = ref<User[]>([])
// changeAvatar function
function changeAvatar(file: Event) {
const fileReader = new FileReader()
const { files } = file.target as HTMLInputElement
if (files && files.length > 0) {
fileReader.readAsDataURL(files[0])
fileReader.onload = () => {
if (typeof fileReader.result === 'string') {
accountInfo.value.avatar = fileReader.result
saveAccountInfo()
}
}
}
}
// reset avatar image
function resetAvatar() {
accountInfo.value.avatar = avatar1
}
// 调用API加载当前用户数据
async function loadAccountInfo() {
try {
const user: User = await api.get('user/current')
accountInfo.value = user
if (!accountInfo.value.avatar)
accountInfo.value.avatar = avatar1
}
catch (error) {
console.log(error)
}
}
// 保存用户信息
async function saveAccountInfo() {
if (newPassword.value || confirmPassword.value) {
if (newPassword.value !== confirmPassword.value) {
$toast.error('两次输入的密码不一致')
return
}
accountInfo.value.password = newPassword.value
}
try {
const result: { [key: string]: any } = await api.put('user/', accountInfo.value)
if (result.success)
$toast.success('用户信息保存成功!')
else
$toast.error(`用户信息保存失败:${result.message}`)
}
catch (error) {
console.log(error)
}
}
// 调用API查询所有用户
async function loadAllUsers() {
try {
const result: User[] = await api.get('/user/')
allUsers.value = result
}
catch (error) {
console.log(error)
}
}
// 删除用户
async function deleteUser(user: User) {
try {
const result: { [key: string]: any } = await api.delete(`user/${user.name}`)
if (result.success) {
$toast.success('用户删除成功!')
loadAllUsers()
}
else {
$toast.error(`用户删除失败:${result.message}`)
}
}
catch (error) {
console.log(error)
}
}
// 冻结用户
async function deactivateUser(user: User) {
try {
user.is_active = !user.is_active
const result: { [key: string]: any } = await api.put('user/', user)
if (result.success) {
$toast.success('用户冻结成功!')
loadAllUsers()
}
else {
$toast.error(`用户冻结失败:${result.message}`)
}
}
catch (error) {
console.log(error)
}
}
// 新增用户
async function addUser() {
try {
const result: { [key: string]: any } = await api.post('user', userForm)
if (result.success) {
$toast.success('用户新增成功!')
loadAllUsers()
addUserDialog.value = false
}
else {
$toast.error(`用户新增失败:${result.message}`)
}
}
catch (error) {
console.log(error)
}
}
// 加载当前用户数据
onMounted(() => {
loadAccountInfo()
loadAllUsers()
})
</script>
<template>
<VRow>
<VCol cols="12">
<VCard title="个人信息">
<VCardText class="d-flex">
<!-- 👉 Avatar -->
<VAvatar
rounded="lg"
size="100"
class="me-6"
:image="accountInfo.avatar"
/>
<!-- 👉 Upload Photo -->
<form class="d-flex flex-column justify-center gap-5">
<div class="d-flex flex-wrap gap-2">
<VBtn
color="primary"
@click="refInputEl?.click()"
>
<VIcon
icon="mdi-cloud-upload-outline"
class="d-sm-none"
/>
<span class="d-none d-sm-block">上传头像</span>
</VBtn>
<input
ref="refInputEl"
type="file"
name="file"
accept=".jpeg,.png,.jpg,GIF"
hidden
@input="changeAvatar"
>
<VBtn
type="reset"
color="error"
variant="tonal"
@click="resetAvatar"
>
<span class="d-none d-sm-block">重置</span>
<VIcon
icon="mdi-refresh"
class="d-sm-none"
/>
</VBtn>
</div>
<p class="text-body-1 mb-0">
允许 JPGGIF PNG 格式 最大尺寸 800K
</p>
</form>
</VCardText>
<VDivider />
<VCardText>
<!-- 👉 Form -->
<VForm class="mt-6">
<VRow>
<!-- 👉 Name -->
<VCol
md="6"
cols="12"
>
<VTextField
v-model="accountInfo.name"
readonly
label="用户名"
/>
</VCol>
<!-- 👉 Email -->
<VCol
cols="12"
md="6"
>
<VTextField
v-model="accountInfo.email"
label="邮箱"
type="email"
/>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 new password -->
<VTextField
v-model="newPassword"
:type="isNewPasswordVisible ? 'text' : 'password'"
:append-inner-icon="
isNewPasswordVisible ? 'mdi-eye-off-outline' : 'mdi-eye-outline'
"
label="新密码"
autocomplete="new-password"
@click:append-inner="isNewPasswordVisible = !isNewPasswordVisible"
/>
</VCol>
<VCol
cols="12"
md="6"
>
<!-- 👉 confirm password -->
<VTextField
v-model="confirmPassword"
:type="isConfirmPasswordVisible ? 'text' : 'password'"
:append-inner-icon="
isConfirmPasswordVisible ? 'mdi-eye-off-outline' : 'mdi-eye-outline'
"
label="确认新密码"
@click:append-inner="
isConfirmPasswordVisible = !isConfirmPasswordVisible
"
/>
</VCol>
<!-- 👉 Form Actions -->
<VCol
cols="12"
class="d-flex flex-wrap gap-4"
>
<VBtn @click="saveAccountInfo">
保存
</VBtn>
</VCol>
</VRow>
</VForm>
</VCardText>
</VCard>
</VCol>
<VCol
v-if="accountInfo.is_superuser"
cols="12"
>
<!-- 👉 Accounts -->
<VCard title="所有用户">
<template #append>
<IconBtn @click.stop="addUserDialog = true">
<VIcon icon="mdi-plus" />
</IconBtn>
</template>
<VTable class="text-no-wrap">
<thead>
<tr>
<th scope="col">
用户名
</th>
<th scope="col">
邮箱
</th>
<th scope="col">
状态
</th>
<th scope="col">
管理员
</th>
<th
scope="col"
class="w-5"
/>
</tr>
</thead>
<tbody>
<tr
v-for="user in allUsers"
:key="user.name"
>
<td>
{{ user.name }}
</td>
<td>{{ user.email }}</td>
<td>
<VChip
v-if="user.is_active"
color="success"
text-color="white"
>
激活
</VChip>
<VChip
v-else
color="error"
text-color="white"
>
冻结
</VChip>
</td>
<td>{{ user.is_superuser ? "是" : "否" }}</td>
<td>
<IconBtn v-show="accountInfo.is_superuser && accountInfo.name != user.name">
<VIcon icon="mdi-dots-vertical" />
<VMenu
activator="parent"
close-on-content-click
>
<VList>
<VListItem
variant="plain"
@click="deactivateUser(user)"
>
<template #prepend>
<VIcon icon="mdi-lock" />
</template>
<VListItemTitle>
{{
user.is_active ? "冻结" : "解冻"
}}
</VListItemTitle>
</VListItem>
<VListItem
variant="plain"
base-color="error"
@click="deleteUser(user)"
>
<template #prepend>
<VIcon icon="mdi-delete" />
</template>
<VListItemTitle>删除</VListItemTitle>
</VListItem>
</VList>
</VMenu>
</IconBtn>
</td>
</tr>
</tbody>
</VTable>
</VCard>
</VCol>
</VRow>
<!-- 站点编辑弹窗 -->
<VDialog
v-model="addUserDialog"
max-width="50rem"
persistent
>
<!-- Dialog Content -->
<VCard title="新增用户">
<VCardText>
<VForm @submit.prevent="() => {}">
<VRow>
<VCol
cols="12"
md="6"
>
<VTextField
v-model="userForm.name"
label="用户名"
:rules="[requiredValidator]"
/>
</VCol>
<VCol
cols="12"
md="6"
>
<VTextField
v-model="userForm.password"
label="密码"
:rules="[requiredValidator]"
:type="isPasswordVisible ? 'text' : 'password'"
:append-inner-icon="
isPasswordVisible ? 'mdi-eye-off-outline' : 'mdi-eye-outline'
"
@click:append-inner="isPasswordVisible = !isPasswordVisible"
/>
</VCol>
<VCol
cols="12"
md="6"
>
<VTextField
v-model="userForm.email"
label="邮箱"
/>
</VCol>
</VRow>
</VForm>
</VCardText>
<VCardActions>
<VBtn @click="addUserDialog = false">
取消
</VBtn>
<VSpacer />
<VBtn @click="addUser">
确定
</VBtn>
</VCardActions>
</VCard>
</VDialog>
</template>