mirror of
https://github.com/jxxghp/MoviePilot-Frontend.git
synced 2026-09-05 23:56:42 +08:00
add user page
This commit is contained in:
@@ -414,3 +414,15 @@ export interface Context {
|
|||||||
// 种子信息
|
// 种子信息
|
||||||
torrent_info: TorrentInfo,
|
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>
|
<script lang="ts" setup>
|
||||||
import AccountSettingAccount from "@/views/account-setting/AccountSettingAccount.vue";
|
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 AccountSettingSystem from "@/views/account-setting/AccountSettingSystem.vue";
|
||||||
import { useRoute } from "vue-router";
|
import { useRoute } from "vue-router";
|
||||||
|
|
||||||
@@ -10,9 +9,8 @@ const activeTab = ref(route.params.tab);
|
|||||||
|
|
||||||
// tabs
|
// tabs
|
||||||
const tabs = [
|
const tabs = [
|
||||||
{ title: "账户", icon: "mdi-account-outline", tab: "account" },
|
{ title: "用户", icon: "mdi-account-outline", tab: "account" },
|
||||||
{ title: "安全", icon: "mdi-lock-open-outline", tab: "security" },
|
{ title: "系统", icon: "mdi-cog", tab: "system" },
|
||||||
{ title: "系统", icon: "mdi-bell-outline", tab: "system" },
|
|
||||||
];
|
];
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -32,11 +30,6 @@ const tabs = [
|
|||||||
<AccountSettingAccount />
|
<AccountSettingAccount />
|
||||||
</VWindowItem>
|
</VWindowItem>
|
||||||
|
|
||||||
<!-- Security -->
|
|
||||||
<VWindowItem value="security">
|
|
||||||
<AccountSettingSecurity />
|
|
||||||
</VWindowItem>
|
|
||||||
|
|
||||||
<!-- System -->
|
<!-- System -->
|
||||||
<VWindowItem value="system">
|
<VWindowItem value="system">
|
||||||
<AccountSettingSystem />
|
<AccountSettingSystem />
|
||||||
|
|||||||
@@ -1,30 +1,43 @@
|
|||||||
<script lang="ts" setup>
|
<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 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,
|
const $toast = useToast();
|
||||||
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 refInputEl = ref<HTMLElement>();
|
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
|
// changeAvatar function
|
||||||
const changeAvatar = (file: Event) => {
|
const changeAvatar = (file: Event) => {
|
||||||
@@ -35,72 +48,110 @@ const changeAvatar = (file: Event) => {
|
|||||||
fileReader.readAsDataURL(files[0]);
|
fileReader.readAsDataURL(files[0]);
|
||||||
fileReader.onload = () => {
|
fileReader.onload = () => {
|
||||||
if (typeof fileReader.result === "string")
|
if (typeof fileReader.result === "string")
|
||||||
accountDataLocal.value.avatarImg = fileReader.result;
|
accountInfo.value.avatar = fileReader.result;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// reset avatar image
|
// reset avatar image
|
||||||
const resetAvatar = () => {
|
const resetAvatar = () => {
|
||||||
accountDataLocal.value.avatarImg = accountData.avatarImg;
|
accountInfo.value.avatar = avatar1;
|
||||||
};
|
};
|
||||||
|
|
||||||
const timezones = [
|
// 调用API,加载当前用户数据
|
||||||
"(GMT-11:00) International Date Line West",
|
const loadAccountInfo = async () => {
|
||||||
"(GMT-11:00) Midway Island",
|
try {
|
||||||
"(GMT-10:00) Hawaii",
|
const user: User = await api.get(`user/current`);
|
||||||
"(GMT-09:00) Alaska",
|
accountInfo.value = user;
|
||||||
"(GMT-08:00) Pacific Time (US & Canada)",
|
if (!accountInfo.value.avatar) accountInfo.value.avatar = avatar1;
|
||||||
"(GMT-08:00) Tijuana",
|
} catch (error) {
|
||||||
"(GMT-07:00) Arizona",
|
console.log(error);
|
||||||
"(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",
|
|
||||||
];
|
|
||||||
|
|
||||||
const currencies = [
|
// 保存用户信息
|
||||||
"USD",
|
const saveAccountInfo = async () => {
|
||||||
"EUR",
|
if (newPassword.value || confirmPassword.value) {
|
||||||
"GBP",
|
if (newPassword.value !== confirmPassword.value) {
|
||||||
"AUD",
|
$toast.error("两次输入的密码不一致");
|
||||||
"BRL",
|
return;
|
||||||
"CAD",
|
}
|
||||||
"CNY",
|
accountInfo.value.password = newPassword.value;
|
||||||
"CZK",
|
}
|
||||||
"DKK",
|
try {
|
||||||
"HKD",
|
const result: { [key: string]: any } = await api.put(`user`, accountInfo.value);
|
||||||
"HUF",
|
if (result.success) {
|
||||||
"INR",
|
$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>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -109,12 +160,7 @@ const currencies = [
|
|||||||
<VCard title="个人信息">
|
<VCard title="个人信息">
|
||||||
<VCardText class="d-flex">
|
<VCardText class="d-flex">
|
||||||
<!-- 👉 Avatar -->
|
<!-- 👉 Avatar -->
|
||||||
<VAvatar
|
<VAvatar rounded="lg" size="100" class="me-6" :image="accountInfo.avatar" />
|
||||||
rounded="lg"
|
|
||||||
size="100"
|
|
||||||
class="me-6"
|
|
||||||
:image="accountDataLocal.avatarImg"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<!-- 👉 Upload Photo -->
|
<!-- 👉 Upload Photo -->
|
||||||
<form class="d-flex flex-column justify-center gap-5">
|
<form class="d-flex flex-column justify-center gap-5">
|
||||||
@@ -139,7 +185,7 @@ const currencies = [
|
|||||||
</VBtn>
|
</VBtn>
|
||||||
</div>
|
</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>
|
</form>
|
||||||
</VCardText>
|
</VCardText>
|
||||||
|
|
||||||
@@ -149,100 +195,47 @@ const currencies = [
|
|||||||
<!-- 👉 Form -->
|
<!-- 👉 Form -->
|
||||||
<VForm class="mt-6">
|
<VForm class="mt-6">
|
||||||
<VRow>
|
<VRow>
|
||||||
<!-- 👉 First Name -->
|
<!-- 👉 Name -->
|
||||||
<VCol md="6" cols="12">
|
<VCol md="6" cols="12">
|
||||||
<VTextField v-model="accountDataLocal.firstName" label="First Name" />
|
<VTextField readonly v-model="accountInfo.name" label="用户名" />
|
||||||
</VCol>
|
|
||||||
|
|
||||||
<!-- 👉 Last Name -->
|
|
||||||
<VCol md="6" cols="12">
|
|
||||||
<VTextField v-model="accountDataLocal.lastName" label="Last Name" />
|
|
||||||
</VCol>
|
</VCol>
|
||||||
|
|
||||||
<!-- 👉 Email -->
|
<!-- 👉 Email -->
|
||||||
<VCol cols="12" md="6">
|
<VCol cols="12" md="6">
|
||||||
|
<VTextField v-model="accountInfo.email" label="邮箱" type="email" />
|
||||||
|
</VCol>
|
||||||
|
|
||||||
|
<VCol cols="12" md="6">
|
||||||
|
<!-- 👉 new password -->
|
||||||
<VTextField
|
<VTextField
|
||||||
v-model="accountDataLocal.email"
|
v-model="newPassword"
|
||||||
label="E-mail"
|
:type="isNewPasswordVisible ? 'text' : 'password'"
|
||||||
type="email"
|
:append-inner-icon="
|
||||||
|
isNewPasswordVisible ? 'mdi-eye-off-outline' : 'mdi-eye-outline'
|
||||||
|
"
|
||||||
|
label="新密码"
|
||||||
|
@click:append-inner="isNewPasswordVisible = !isNewPasswordVisible"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
|
|
||||||
<!-- 👉 Organization -->
|
|
||||||
<VCol cols="12" md="6">
|
<VCol cols="12" md="6">
|
||||||
<VTextField v-model="accountDataLocal.org" label="Organization" />
|
<!-- 👉 confirm password -->
|
||||||
</VCol>
|
<VTextField
|
||||||
|
v-model="confirmPassword"
|
||||||
<!-- 👉 Phone -->
|
:type="isConfirmPasswordVisible ? 'text' : 'password'"
|
||||||
<VCol cols="12" md="6">
|
:append-inner-icon="
|
||||||
<VTextField v-model="accountDataLocal.phone" label="Phone Number" />
|
isConfirmPasswordVisible ? 'mdi-eye-off-outline' : 'mdi-eye-outline'
|
||||||
</VCol>
|
"
|
||||||
|
label="确认新密码"
|
||||||
<!-- 👉 Address -->
|
@click:append-inner="
|
||||||
<VCol cols="12" md="6">
|
isConfirmPasswordVisible = !isConfirmPasswordVisible
|
||||||
<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 }"
|
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
|
|
||||||
<!-- 👉 Form Actions -->
|
<!-- 👉 Form Actions -->
|
||||||
<VCol cols="12" class="d-flex flex-wrap gap-4">
|
<VCol cols="12" class="d-flex flex-wrap gap-4">
|
||||||
<VBtn>保存</VBtn>
|
<VBtn @click="saveAccountInfo">保存</VBtn>
|
||||||
|
|
||||||
<VBtn
|
|
||||||
color="secondary"
|
|
||||||
variant="tonal"
|
|
||||||
type="reset"
|
|
||||||
@click.prevent="resetForm"
|
|
||||||
>
|
|
||||||
重置
|
|
||||||
</VBtn>
|
|
||||||
</VCol>
|
</VCol>
|
||||||
</VRow>
|
</VRow>
|
||||||
</VForm>
|
</VForm>
|
||||||
@@ -250,19 +243,107 @@ const currencies = [
|
|||||||
</VCard>
|
</VCard>
|
||||||
</VCol>
|
</VCol>
|
||||||
|
|
||||||
<VCol cols="12">
|
<VCol cols="12" v-if="accountInfo.is_superuser">
|
||||||
<!-- 👉 Deactivate Account -->
|
<!-- 👉 Accounts -->
|
||||||
<VCard title="删除账户">
|
<VCard title="所有用户">
|
||||||
<VCardText>
|
<template #append>
|
||||||
<div>
|
<IconBtn @click.stop="addUserDialog = true">
|
||||||
<VCheckbox v-model="isAccountDeactivated" label="我确认删除我的账户信息" />
|
<VIcon icon="mdi-plus" />
|
||||||
</div>
|
</IconBtn>
|
||||||
|
</template>
|
||||||
<VBtn :disabled="!isAccountDeactivated" color="error" class="mt-3">
|
<VTable class="text-no-wrap">
|
||||||
删除账户
|
<thead>
|
||||||
</VBtn>
|
<tr>
|
||||||
</VCardText>
|
<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>
|
</VCard>
|
||||||
</VCol>
|
</VCol>
|
||||||
</VRow>
|
</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>
|
</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