mirror of
https://github.com/jxxghp/MoviePilot-Frontend.git
synced 2026-05-19 18:59:31 +08:00
add user page
This commit is contained in:
@@ -414,3 +414,15 @@ export interface Context {
|
||||
// 种子信息
|
||||
torrent_info: TorrentInfo,
|
||||
}
|
||||
|
||||
|
||||
// 用户信息
|
||||
export interface User {
|
||||
id: number,
|
||||
name: string,
|
||||
password: string,
|
||||
email: string,
|
||||
is_active: boolean,
|
||||
is_superuser: boolean,
|
||||
avatar: string,
|
||||
};
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<script lang="ts" setup>
|
||||
import AccountSettingAccount from "@/views/account-setting/AccountSettingAccount.vue";
|
||||
import AccountSettingSecurity from "@/views/account-setting/AccountSettingSecurity.vue";
|
||||
import AccountSettingSystem from "@/views/account-setting/AccountSettingSystem.vue";
|
||||
import { useRoute } from "vue-router";
|
||||
|
||||
@@ -10,9 +9,8 @@ const activeTab = ref(route.params.tab);
|
||||
|
||||
// tabs
|
||||
const tabs = [
|
||||
{ title: "账户", icon: "mdi-account-outline", tab: "account" },
|
||||
{ title: "安全", icon: "mdi-lock-open-outline", tab: "security" },
|
||||
{ title: "系统", icon: "mdi-bell-outline", tab: "system" },
|
||||
{ title: "用户", icon: "mdi-account-outline", tab: "account" },
|
||||
{ title: "系统", icon: "mdi-cog", tab: "system" },
|
||||
];
|
||||
</script>
|
||||
|
||||
@@ -32,11 +30,6 @@ const tabs = [
|
||||
<AccountSettingAccount />
|
||||
</VWindowItem>
|
||||
|
||||
<!-- Security -->
|
||||
<VWindowItem value="security">
|
||||
<AccountSettingSecurity />
|
||||
</VWindowItem>
|
||||
|
||||
<!-- System -->
|
||||
<VWindowItem value="system">
|
||||
<AccountSettingSystem />
|
||||
|
||||
@@ -1,30 +1,43 @@
|
||||
<script lang="ts" setup>
|
||||
import { requiredValidator } from "@/@validators";
|
||||
import api from "@/api";
|
||||
import { User } from "@/api/types";
|
||||
import avatar1 from "@images/avatars/avatar-1.png";
|
||||
import { useToast } from "vue-toast-notification";
|
||||
const isNewPasswordVisible = ref(false);
|
||||
const isConfirmPasswordVisible = ref(false);
|
||||
const isPasswordVisible = ref(false);
|
||||
const newPassword = ref("");
|
||||
const confirmPassword = ref("");
|
||||
|
||||
const accountData = {
|
||||
avatarImg: avatar1,
|
||||
firstName: "john",
|
||||
lastName: "Doe",
|
||||
email: "johnDoe@example.com",
|
||||
org: "ThemeSelection",
|
||||
phone: "+1 (917) 543-9876",
|
||||
address: "123 Main St, New York, NY 10001",
|
||||
state: "New York",
|
||||
zip: "10001",
|
||||
country: "USA",
|
||||
language: "English",
|
||||
timezone: "(GMT-11:00) International Date Line West",
|
||||
currency: "USD",
|
||||
};
|
||||
// 提示框
|
||||
const $toast = useToast();
|
||||
|
||||
const refInputEl = ref<HTMLElement>();
|
||||
|
||||
const accountDataLocal = ref(structuredClone(accountData));
|
||||
const isAccountDeactivated = ref(false);
|
||||
// 新增用户窗口
|
||||
const addUserDialog = ref(false);
|
||||
|
||||
const resetForm = () => {
|
||||
accountDataLocal.value = structuredClone(accountData);
|
||||
};
|
||||
// 新增用户表单
|
||||
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
|
||||
const changeAvatar = (file: Event) => {
|
||||
@@ -35,72 +48,110 @@ const changeAvatar = (file: Event) => {
|
||||
fileReader.readAsDataURL(files[0]);
|
||||
fileReader.onload = () => {
|
||||
if (typeof fileReader.result === "string")
|
||||
accountDataLocal.value.avatarImg = fileReader.result;
|
||||
accountInfo.value.avatar = fileReader.result;
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
// reset avatar image
|
||||
const resetAvatar = () => {
|
||||
accountDataLocal.value.avatarImg = accountData.avatarImg;
|
||||
accountInfo.value.avatar = avatar1;
|
||||
};
|
||||
|
||||
const timezones = [
|
||||
"(GMT-11:00) International Date Line West",
|
||||
"(GMT-11:00) Midway Island",
|
||||
"(GMT-10:00) Hawaii",
|
||||
"(GMT-09:00) Alaska",
|
||||
"(GMT-08:00) Pacific Time (US & Canada)",
|
||||
"(GMT-08:00) Tijuana",
|
||||
"(GMT-07:00) Arizona",
|
||||
"(GMT-07:00) Chihuahua",
|
||||
"(GMT-07:00) La Paz",
|
||||
"(GMT-07:00) Mazatlan",
|
||||
"(GMT-07:00) Mountain Time (US & Canada)",
|
||||
"(GMT-06:00) Central America",
|
||||
"(GMT-06:00) Central Time (US & Canada)",
|
||||
"(GMT-06:00) Guadalajara",
|
||||
"(GMT-06:00) Mexico City",
|
||||
"(GMT-06:00) Monterrey",
|
||||
"(GMT-06:00) Saskatchewan",
|
||||
"(GMT-05:00) Bogota",
|
||||
"(GMT-05:00) Eastern Time (US & Canada)",
|
||||
"(GMT-05:00) Indiana (East)",
|
||||
"(GMT-05:00) Lima",
|
||||
"(GMT-05:00) Quito",
|
||||
"(GMT-04:00) Atlantic Time (Canada)",
|
||||
"(GMT-04:00) Caracas",
|
||||
"(GMT-04:00) La Paz",
|
||||
"(GMT-04:00) Santiago",
|
||||
"(GMT-03:30) Newfoundland",
|
||||
"(GMT-03:00) Brasilia",
|
||||
"(GMT-03:00) Buenos Aires",
|
||||
"(GMT-03:00) Georgetown",
|
||||
"(GMT-03:00) Greenland",
|
||||
"(GMT-02:00) Mid-Atlantic",
|
||||
"(GMT-01:00) Azores",
|
||||
"(GMT-01:00) Cape Verde Is.",
|
||||
"(GMT+00:00) Casablanca",
|
||||
"(GMT+00:00) Dublin",
|
||||
"(GMT+00:00) Edinburgh",
|
||||
"(GMT+00:00) Lisbon",
|
||||
"(GMT+00:00) London",
|
||||
];
|
||||
// 调用API,加载当前用户数据
|
||||
const loadAccountInfo = async () => {
|
||||
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);
|
||||
}
|
||||
};
|
||||
|
||||
const currencies = [
|
||||
"USD",
|
||||
"EUR",
|
||||
"GBP",
|
||||
"AUD",
|
||||
"BRL",
|
||||
"CAD",
|
||||
"CNY",
|
||||
"CZK",
|
||||
"DKK",
|
||||
"HKD",
|
||||
"HUF",
|
||||
"INR",
|
||||
];
|
||||
// 保存用户信息
|
||||
const saveAccountInfo = async () => {
|
||||
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,查询所有用户
|
||||
const loadAllUsers = async () => {
|
||||
try {
|
||||
const result: User[] = await api.get(`/user`);
|
||||
allUsers.value = result;
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
};
|
||||
|
||||
// 删除用户
|
||||
const deleteUser = async (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);
|
||||
}
|
||||
};
|
||||
|
||||
// 冻结用户
|
||||
const deactivateUser = async (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);
|
||||
}
|
||||
};
|
||||
|
||||
// 新增用户
|
||||
const addUser = async () => {
|
||||
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>
|
||||
@@ -109,12 +160,7 @@ const currencies = [
|
||||
<VCard title="个人信息">
|
||||
<VCardText class="d-flex">
|
||||
<!-- 👉 Avatar -->
|
||||
<VAvatar
|
||||
rounded="lg"
|
||||
size="100"
|
||||
class="me-6"
|
||||
:image="accountDataLocal.avatarImg"
|
||||
/>
|
||||
<VAvatar rounded="lg" size="100" class="me-6" :image="accountInfo.avatar" />
|
||||
|
||||
<!-- 👉 Upload Photo -->
|
||||
<form class="d-flex flex-column justify-center gap-5">
|
||||
@@ -139,7 +185,7 @@ const currencies = [
|
||||
</VBtn>
|
||||
</div>
|
||||
|
||||
<p class="text-body-1 mb-0">允许 JPG, GIF 或 PNG 格式, 最大尽寸 800K。</p>
|
||||
<p class="text-body-1 mb-0">允许 JPG、GIF 或 PNG 格式, 最大尽寸 800K。</p>
|
||||
</form>
|
||||
</VCardText>
|
||||
|
||||
@@ -149,100 +195,47 @@ const currencies = [
|
||||
<!-- 👉 Form -->
|
||||
<VForm class="mt-6">
|
||||
<VRow>
|
||||
<!-- 👉 First Name -->
|
||||
<!-- 👉 Name -->
|
||||
<VCol md="6" cols="12">
|
||||
<VTextField v-model="accountDataLocal.firstName" label="First Name" />
|
||||
</VCol>
|
||||
|
||||
<!-- 👉 Last Name -->
|
||||
<VCol md="6" cols="12">
|
||||
<VTextField v-model="accountDataLocal.lastName" label="Last Name" />
|
||||
<VTextField readonly v-model="accountInfo.name" 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="accountDataLocal.email"
|
||||
label="E-mail"
|
||||
type="email"
|
||||
v-model="newPassword"
|
||||
:type="isNewPasswordVisible ? 'text' : 'password'"
|
||||
:append-inner-icon="
|
||||
isNewPasswordVisible ? 'mdi-eye-off-outline' : 'mdi-eye-outline'
|
||||
"
|
||||
label="新密码"
|
||||
@click:append-inner="isNewPasswordVisible = !isNewPasswordVisible"
|
||||
/>
|
||||
</VCol>
|
||||
|
||||
<!-- 👉 Organization -->
|
||||
<VCol cols="12" md="6">
|
||||
<VTextField v-model="accountDataLocal.org" label="Organization" />
|
||||
</VCol>
|
||||
|
||||
<!-- 👉 Phone -->
|
||||
<VCol cols="12" md="6">
|
||||
<VTextField v-model="accountDataLocal.phone" label="Phone Number" />
|
||||
</VCol>
|
||||
|
||||
<!-- 👉 Address -->
|
||||
<VCol cols="12" md="6">
|
||||
<VTextField v-model="accountDataLocal.address" label="Address" />
|
||||
</VCol>
|
||||
|
||||
<!-- 👉 State -->
|
||||
<VCol cols="12" md="6">
|
||||
<VTextField v-model="accountDataLocal.state" label="State" />
|
||||
</VCol>
|
||||
|
||||
<!-- 👉 Zip Code -->
|
||||
<VCol cols="12" md="6">
|
||||
<VTextField v-model="accountDataLocal.zip" label="Zip Code" />
|
||||
</VCol>
|
||||
|
||||
<!-- 👉 Country -->
|
||||
<VCol cols="12" md="6">
|
||||
<VSelect
|
||||
v-model="accountDataLocal.country"
|
||||
label="Country"
|
||||
:items="['USA', 'Canada', 'UK', 'India', 'Australia']"
|
||||
/>
|
||||
</VCol>
|
||||
|
||||
<!-- 👉 Language -->
|
||||
<VCol cols="12" md="6">
|
||||
<VSelect
|
||||
v-model="accountDataLocal.language"
|
||||
label="Language"
|
||||
:items="['English', 'Spanish', 'Arabic', 'Hindi', 'Urdu']"
|
||||
/>
|
||||
</VCol>
|
||||
|
||||
<!-- 👉 Timezone -->
|
||||
<VCol cols="12" md="6">
|
||||
<VSelect
|
||||
v-model="accountDataLocal.timezone"
|
||||
label="Timezone"
|
||||
:items="timezones"
|
||||
:menu-props="{ maxHeight: 200 }"
|
||||
/>
|
||||
</VCol>
|
||||
|
||||
<!-- 👉 Currency -->
|
||||
<VCol cols="12" md="6">
|
||||
<VSelect
|
||||
v-model="accountDataLocal.currency"
|
||||
label="Currency"
|
||||
:items="currencies"
|
||||
:menu-props="{ maxHeight: 200 }"
|
||||
<!-- 👉 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>保存</VBtn>
|
||||
|
||||
<VBtn
|
||||
color="secondary"
|
||||
variant="tonal"
|
||||
type="reset"
|
||||
@click.prevent="resetForm"
|
||||
>
|
||||
重置
|
||||
</VBtn>
|
||||
<VBtn @click="saveAccountInfo">保存</VBtn>
|
||||
</VCol>
|
||||
</VRow>
|
||||
</VForm>
|
||||
@@ -250,19 +243,107 @@ const currencies = [
|
||||
</VCard>
|
||||
</VCol>
|
||||
|
||||
<VCol cols="12">
|
||||
<!-- 👉 Deactivate Account -->
|
||||
<VCard title="删除账户">
|
||||
<VCardText>
|
||||
<div>
|
||||
<VCheckbox v-model="isAccountDeactivated" label="我确认删除我的账户信息" />
|
||||
</div>
|
||||
|
||||
<VBtn :disabled="!isAccountDeactivated" color="error" class="mt-3">
|
||||
删除账户
|
||||
</VBtn>
|
||||
</VCardText>
|
||||
<VCol cols="12" v-if="accountInfo.is_superuser">
|
||||
<!-- 👉 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"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="user in allUsers" :key="user.name">
|
||||
<td>
|
||||
{{ user.name }}
|
||||
</td>
|
||||
<td>{{ user.email }}</td>
|
||||
<td>
|
||||
<VChip color="success" text-color="white" v-if="user.is_active"
|
||||
>激活</VChip
|
||||
>
|
||||
<VChip color="error" text-color="white" v-else>冻结</VChip>
|
||||
</td>
|
||||
<td>{{ user.is_superuser ? "是" : "否" }}</td>
|
||||
<td>
|
||||
<IconBtn v-show="!user.is_superuser">
|
||||
<VIcon icon="mdi-dots-vertical" />
|
||||
<VMenu activator="parent">
|
||||
<VList>
|
||||
<VListItem variant="plain" @click.stop="deactivateUser(user)">
|
||||
<template #prepend>
|
||||
<VIcon icon="mdi-lock"></VIcon>
|
||||
</template>
|
||||
<VListItemTitle>{{
|
||||
user.is_active ? "冻结" : "解冻"
|
||||
}}</VListItemTitle>
|
||||
</VListItem>
|
||||
<VListItem
|
||||
variant="plain"
|
||||
base-color="error"
|
||||
@click.stop="deleteUser(user)"
|
||||
>
|
||||
<template #prepend>
|
||||
<VIcon icon="mdi-delete"></VIcon>
|
||||
</template>
|
||||
<VListItemTitle>删除</VListItemTitle>
|
||||
</VListItem>
|
||||
</VList>
|
||||
</VMenu>
|
||||
</IconBtn>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</VTable>
|
||||
</VCard>
|
||||
</VCol>
|
||||
</VRow>
|
||||
<!-- 站点编辑弹窗 -->
|
||||
<VDialog v-model="addUserDialog" max-width="800" 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>
|
||||
|
||||
@@ -1,183 +0,0 @@
|
||||
<script lang="ts" setup>
|
||||
const isCurrentPasswordVisible = ref(false);
|
||||
const isNewPasswordVisible = ref(false);
|
||||
const isConfirmPasswordVisible = ref(false);
|
||||
const currentPassword = ref("12345678");
|
||||
const newPassword = ref("87654321");
|
||||
const confirmPassword = ref("87654321");
|
||||
|
||||
const recentDevices = [
|
||||
{
|
||||
browser: "Chrome on Windows",
|
||||
device: "HP Spectre 360",
|
||||
location: "New York, NY",
|
||||
recentActivity: "28 Apr 2022, 18:20",
|
||||
deviceIcon: { icon: "mdi-microsoft-windows", color: "primary" },
|
||||
},
|
||||
{
|
||||
browser: "Chrome on iPhone",
|
||||
device: "iPhone 12x",
|
||||
location: "Los Angeles, CA",
|
||||
recentActivity: "20 Apr 2022, 10:20",
|
||||
deviceIcon: { icon: "mdi-cellphone", color: "error" },
|
||||
},
|
||||
{
|
||||
browser: "Chrome on Android",
|
||||
device: "Oneplus 9 Pro",
|
||||
location: "San Francisco, CA",
|
||||
recentActivity: "16 Apr 2022, 04:20",
|
||||
deviceIcon: { icon: "mdi-android", color: "success" },
|
||||
},
|
||||
{
|
||||
browser: "Chrome on MacOS",
|
||||
device: "Apple iMac",
|
||||
location: "New York, NY",
|
||||
recentActivity: "28 Apr 2022, 18:20",
|
||||
deviceIcon: { icon: "mdi-apple", color: "secondary" },
|
||||
},
|
||||
{
|
||||
browser: "Chrome on Windows",
|
||||
device: "HP Spectre 360",
|
||||
location: "Los Angeles, CA",
|
||||
recentActivity: "20 Apr 2022, 10:20",
|
||||
deviceIcon: { icon: "mdi-microsoft-windows", color: "primary" },
|
||||
},
|
||||
{
|
||||
browser: "Chrome on Android",
|
||||
device: "Oneplus 9 Pro",
|
||||
location: "San Francisco, CA",
|
||||
recentActivity: "16 Apr 2022, 04:20",
|
||||
deviceIcon: { icon: "mdi-android", color: "success" },
|
||||
},
|
||||
];
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<VRow>
|
||||
<!-- SECTION: Change Password -->
|
||||
<VCol cols="12">
|
||||
<VCard title="修改密码">
|
||||
<VForm>
|
||||
<VCardText>
|
||||
<!-- 👉 Current Password -->
|
||||
<VRow class="mb-3">
|
||||
<VCol cols="12" md="6">
|
||||
<!-- 👉 current password -->
|
||||
<VTextField
|
||||
v-model="currentPassword"
|
||||
:type="isCurrentPasswordVisible ? 'text' : 'password'"
|
||||
:append-inner-icon="
|
||||
isCurrentPasswordVisible ? 'mdi-eye-off-outline' : 'mdi-eye-outline'
|
||||
"
|
||||
label="原密码"
|
||||
@click:append-inner="
|
||||
isCurrentPasswordVisible = !isCurrentPasswordVisible
|
||||
"
|
||||
/>
|
||||
</VCol>
|
||||
</VRow>
|
||||
|
||||
<!-- 👉 New Password -->
|
||||
<VRow>
|
||||
<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="新密码"
|
||||
@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>
|
||||
</VRow>
|
||||
</VCardText>
|
||||
|
||||
<!-- 👉 Action Buttons -->
|
||||
<VCardText class="d-flex flex-wrap gap-4">
|
||||
<VBtn> 保存 </VBtn>
|
||||
|
||||
<VBtn type="reset" color="secondary" variant="tonal"> 重置 </VBtn>
|
||||
</VCardText>
|
||||
</VForm>
|
||||
</VCard>
|
||||
</VCol>
|
||||
<!-- !SECTION -->
|
||||
|
||||
<VCol cols="12">
|
||||
<!-- SECTION: Create an API key -->
|
||||
<VCard title="安全密钥">
|
||||
<VRow>
|
||||
<!-- 👉 Choose API Key -->
|
||||
<VCol cols="12" md="5" order-md="0" order="1">
|
||||
<VCardText>
|
||||
<VForm @submit.prevent="() => {}">
|
||||
<VRow>
|
||||
<!-- 👉 Name the API Key -->
|
||||
<VCol cols="12">
|
||||
<VTextField label="Name the API key" />
|
||||
</VCol>
|
||||
|
||||
<!-- 👉 Create Key Button -->
|
||||
<VCol cols="12">
|
||||
<VBtn type="submit" block> 保存 </VBtn>
|
||||
</VCol>
|
||||
</VRow>
|
||||
</VForm>
|
||||
</VCardText>
|
||||
</VCol>
|
||||
</VRow>
|
||||
</VCard>
|
||||
<!-- !SECTION -->
|
||||
</VCol>
|
||||
|
||||
<!-- SECTION Recent Devices -->
|
||||
<VCol cols="12">
|
||||
<!-- 👉 Table -->
|
||||
<VCard title="最近活跃设备">
|
||||
<VTable class="text-no-wrap">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">浏览器</th>
|
||||
<th scope="col">设备</th>
|
||||
<th scope="col">地点</th>
|
||||
<th scope="col">最后活动时间</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="device in recentDevices" :key="device.recentActivity">
|
||||
<td>
|
||||
<VIcon
|
||||
start
|
||||
:icon="device.deviceIcon.icon"
|
||||
:color="device.deviceIcon.color"
|
||||
/>
|
||||
{{ device.browser }}
|
||||
</td>
|
||||
<td>{{ device.device }}</td>
|
||||
<td>{{ device.location }}</td>
|
||||
<td>{{ device.recentActivity }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</VTable>
|
||||
</VCard>
|
||||
</VCol>
|
||||
<!-- !SECTION -->
|
||||
</VRow>
|
||||
</template>
|
||||
Reference in New Issue
Block a user