Update confirmation dialog styles and props in UserProfile.vue, FileList.vue, PluginCard.vue, and dashboard.vue

This commit is contained in:
jxxghp
2024-05-06 17:37:47 +08:00
parent cad8964841
commit c44b20bae3
9 changed files with 179 additions and 531 deletions

View File

@@ -108,14 +108,6 @@ async function uninstallPlugin() {
const isConfirmed = await createConfirm({
title: '确认',
content: `是否确认卸载插件 ${props.plugin?.plugin_name} ?`,
confirmationText: '确认',
cancellationText: '取消',
dialogProps: {
maxWidth: '50rem',
},
confirmationButtonProps: {
variant: 'tonal',
},
})
if (!isConfirmed) return
@@ -229,14 +221,6 @@ async function resetPlugin() {
const isConfirmed = await createConfirm({
title: '确认',
content: `是否确认重置插件 ${props.plugin?.plugin_name} 的配置数据?`,
confirmationText: '确认',
cancellationText: '取消',
dialogProps: {
maxWidth: '50rem',
},
confirmationButtonProps: {
variant: 'tonal',
},
})
if (!isConfirmed) return

View File

@@ -43,16 +43,13 @@ const downloaded = ref<String[]>([])
async function getSiteIcon() {
try {
siteIcon.value = (await api.get(`site/icon/${torrent?.value?.site}`)).data.icon
}
catch (error) {
} catch (error) {
console.error(error)
}
}
// 询问并添加下载
async function handleAddDownload(_site: any = undefined,
_media: any = undefined,
_torrent: any = undefined) {
async function handleAddDownload(_site: any = undefined, _media: any = undefined, _torrent: any = undefined) {
if (!_media || !_torrent || !_site) {
_site = torrent.value?.site_name
_media = media.value
@@ -62,18 +59,9 @@ async function handleAddDownload(_site: any = undefined,
const isConfirmed = await createConfirm({
title: '确认',
content: `是否确认下载【${_site}${_torrent?.title} ?`,
confirmationText: '确认',
cancellationText: '取消',
dialogProps: {
maxWidth: '50rem',
},
confirmationButtonProps: {
variant: 'tonal',
},
})
if (!isConfirmed)
return
if (!isConfirmed) return
addDownload(_media, _torrent)
}
@@ -91,13 +79,11 @@ async function addDownload(_media: MediaInfo, _torrent: TorrentInfo) {
// 添加下载成功
$toast.success(`${_torrent?.site_name} ${_torrent?.title} 添加下载成功!`)
downloaded.value.push(_torrent?.enclosure || '')
}
else {
} else {
// 添加下载失败
$toast.error(`${_torrent?.site_name} ${_torrent?.title} 添加下载失败!`)
}
}
catch (error) {
} catch (error) {
console.error(error)
}
doneNProgress()
@@ -115,14 +101,10 @@ async function downloadTorrentFile() {
// 促销Chip类
function getVolumeFactorClass(downloadVolume: number, uploadVolume: number) {
if (downloadVolume === 0)
return 'text-white bg-lime-500'
else if (downloadVolume < 1)
return 'text-white bg-green-500'
else if (uploadVolume !== 1)
return 'text-white bg-sky-500'
else
return 'text-white bg-gray-500'
if (downloadVolume === 0) return 'text-white bg-lime-500'
else if (downloadVolume < 1) return 'text-white bg-green-500'
else if (uploadVolume !== 1) return 'text-white bg-sky-500'
else return 'text-white bg-gray-500'
}
// 装载时查询站点图标
@@ -138,15 +120,8 @@ onMounted(() => {
:variant="downloaded.includes(torrent?.enclosure || '') ? 'outlined' : 'elevated'"
@click="handleAddDownload"
>
<template
v-if="!showMoreTorrents"
#image
>
<VAvatar
class="absolute right-2 bottom-2 rounded"
variant="flat"
rounded="0"
>
<template v-if="!showMoreTorrents" #image>
<VAvatar class="absolute right-2 bottom-2 rounded" variant="flat" rounded="0">
<VImg :src="siteIcon" />
</VAvatar>
</template>
@@ -159,18 +134,10 @@ onMounted(() => {
<template #append>
<div class="me-n3">
<IconBtn>
<VIcon
icon="mdi-dots-vertical"
/>
<VMenu
activator="parent"
close-on-content-click
>
<VIcon icon="mdi-dots-vertical" />
<VMenu activator="parent" close-on-content-click>
<VList>
<VListItem
variant="plain"
@click="openTorrentDetail()"
>
<VListItem variant="plain" @click="openTorrentDetail()">
<template #prepend>
<VIcon icon="mdi-information" />
</template>
@@ -196,25 +163,11 @@ onMounted(() => {
{{ torrent?.title }}
</VCardText>
<VCardText>{{ torrent?.description }}</VCardText>
<VCardItem
v-if="torrent?.labels"
class="pb-3 pt-0 pe-12"
>
<VChip
v-if="torrent?.hit_and_run"
variant="elevated"
size="small"
class="me-1 mb-1 text-white bg-black"
>
<VCardItem v-if="torrent?.labels" class="pb-3 pt-0 pe-12">
<VChip v-if="torrent?.hit_and_run" variant="elevated" size="small" class="me-1 mb-1 text-white bg-black">
H&R
</VChip>
<VChip
v-if="torrent?.freedate_diff"
variant="elevated"
color="secondary"
size="small"
class="me-1 mb-1"
>
<VChip v-if="torrent?.freedate_diff" variant="elevated" color="secondary" size="small" class="me-1 mb-1">
{{ torrent?.freedate_diff }}
</VChip>
<VChip
@@ -227,51 +180,24 @@ onMounted(() => {
>
{{ label }}
</VChip>
<VChip
v-if="meta?.edition"
variant="elevated"
size="small"
class="me-1 mb-1 text-white bg-red-500"
>
<VChip v-if="meta?.edition" variant="elevated" size="small" class="me-1 mb-1 text-white bg-red-500">
{{ meta?.edition }}
</VChip>
<VChip
v-if="meta?.resource_pix"
variant="elevated"
size="small"
class="me-1 mb-1 text-white bg-red-500"
>
<VChip v-if="meta?.resource_pix" variant="elevated" size="small" class="me-1 mb-1 text-white bg-red-500">
{{ meta?.resource_pix }}
</VChip>
<VChip
v-if="meta?.video_encode"
variant="elevated"
size="small"
class="me-1 mb-1 text-white bg-orange-500"
>
<VChip v-if="meta?.video_encode" variant="elevated" size="small" class="me-1 mb-1 text-white bg-orange-500">
{{ meta?.video_encode }}
</VChip>
<VChip
v-if="torrent?.size"
variant="elevated"
size="small"
class="me-1 mb-1 text-white bg-yellow-500"
>
<VChip v-if="torrent?.size" variant="elevated" size="small" class="me-1 mb-1 text-white bg-yellow-500">
{{ formatFileSize(torrent?.size) }}
</VChip>
<VChip
v-if="meta?.resource_team"
variant="elevated"
size="small"
class="me-1 mb-1 text-white bg-cyan-500"
>
<VChip v-if="meta?.resource_team" variant="elevated" size="small" class="me-1 mb-1 text-white bg-cyan-500">
{{ meta?.resource_team }}
</VChip>
<VChip
v-if="torrent?.downloadvolumefactor !== 1 || torrent?.uploadvolumefactor !== 1"
:class="
getVolumeFactorClass(torrent?.downloadvolumefactor, torrent?.uploadvolumefactor)
"
:class="getVolumeFactorClass(torrent?.downloadvolumefactor, torrent?.uploadvolumefactor)"
variant="elevated"
size="small"
class="me-1 mb-1"
@@ -280,10 +206,7 @@ onMounted(() => {
</VChip>
</VCardItem>
<VCardActions>
<VBtn
v-if="props.more && props.more.length > 0"
@click.stop="showMoreTorrents = !showMoreTorrents"
>
<VBtn v-if="props.more && props.more.length > 0" @click.stop="showMoreTorrents = !showMoreTorrents">
<template #append>
<VIcon :icon="showMoreTorrents ? 'mdi-chevron-up' : 'mdi-chevron-down'" />
</template>
@@ -297,26 +220,12 @@ onMounted(() => {
<VChip
v-for="(item, index) in props.more"
:key="index"
@click.stop="
handleAddDownload(
item.torrent_info?.site_name,
item.media_info,
item.torrent_info,
)
"
@click.stop="handleAddDownload(item.torrent_info?.site_name, item.media_info, item.torrent_info)"
>
<template #append>
<VBadge color="primary" :content="`↑${item.torrent_info?.seeders}`" inline size="small" />
<VBadge
color="primary"
:content="`↑${item.torrent_info?.seeders}`"
inline
size="small"
/>
<VBadge
v-if="
item.torrent_info?.downloadvolumefactor !== 1
|| item.torrent_info?.uploadvolumefactor !== 1
"
v-if="item.torrent_info?.downloadvolumefactor !== 1 || item.torrent_info?.uploadvolumefactor !== 1"
:content="item.torrent_info?.volume_factor"
inline
size="small"

View File

@@ -5,7 +5,7 @@ import { useConfirm } from 'vuetify-use-dialog'
import { formatFileSize } from '@/@core/utils/formatters'
import api from '@/api'
import { doneNProgress, startNProgress } from '@/api/nprogress'
import type { Context, MediaInfo, TorrentInfo } from '@/api/types'
import type { Context, MediaInfo, TorrentInfo } from '@/api/types'
// 输入参数
const props = defineProps({
@@ -40,16 +40,13 @@ const downloaded = ref<String[]>([])
async function getSiteIcon() {
try {
siteIcon.value = (await api.get(`site/icon/${torrent?.value?.site}`)).data.icon
}
catch (error) {
} catch (error) {
console.error(error)
}
}
// 询问并添加下载
async function handleAddDownload(_site: any = undefined,
_media: any = undefined,
_torrent: any = undefined) {
async function handleAddDownload(_site: any = undefined, _media: any = undefined, _torrent: any = undefined) {
if (!_media || !_torrent || !_site) {
_site = torrent.value?.site_name
_media = media.value
@@ -59,18 +56,9 @@ async function handleAddDownload(_site: any = undefined,
const isConfirmed = await createConfirm({
title: '确认',
content: `是否确认下载【${_site}${_torrent?.title} ?`,
confirmationText: '确认',
cancellationText: '取消',
dialogProps: {
maxWidth: '50rem',
},
confirmationButtonProps: {
variant: 'tonal',
},
})
if (!isConfirmed)
return
if (!isConfirmed) return
addDownload(_media, _torrent)
}
@@ -88,13 +76,11 @@ async function addDownload(_media: MediaInfo, _torrent: TorrentInfo) {
// 添加下载成功
$toast.success(`${_torrent?.site_name} ${_torrent?.title} 添加下载成功!`)
downloaded.value.push(_torrent?.enclosure || '')
}
else {
} else {
// 添加下载失败
$toast.error(`${_torrent?.site_name} ${_torrent?.title} 添加下载失败!`)
}
}
catch (error) {
} catch (error) {
console.error(error)
}
doneNProgress()
@@ -112,14 +98,10 @@ async function downloadTorrentFile() {
// 促销Chip类
function getVolumeFactorClass(downloadVolume: number, uploadVolume: number) {
if (downloadVolume === 0)
return 'text-white bg-lime-500'
else if (downloadVolume < 1)
return 'text-white bg-green-500'
else if (uploadVolume !== 1)
return 'text-white bg-sky-500'
else
return 'text-white bg-gray-500'
if (downloadVolume === 0) return 'text-white bg-lime-500'
else if (downloadVolume < 1) return 'text-white bg-green-500'
else if (uploadVolume !== 1) return 'text-white bg-sky-500'
else return 'text-white bg-gray-500'
}
// 装载时查询站点图标
@@ -129,19 +111,9 @@ onMounted(() => {
</script>
<template>
<VListItem
@click="handleAddDownload"
:variant="downloaded.includes(torrent?.enclosure || '') ? 'outlined' : 'flat'"
>
<template
v-if="!showMoreTorrents"
#prepend
>
<VAvatar
class="rounded"
variant="flat"
@click.stop="openTorrentDetail"
>
<VListItem @click="handleAddDownload" :variant="downloaded.includes(torrent?.enclosure || '') ? 'outlined' : 'flat'">
<template v-if="!showMoreTorrents" #prepend>
<VAvatar class="rounded" variant="flat" @click.stop="openTorrentDetail">
<VImg :src="siteIcon" />
</VAvatar>
</template>
@@ -153,25 +125,11 @@ onMounted(() => {
<VListItemSubtitle>
{{ torrent?.description }}
</VListItemSubtitle>
<div
v-if="torrent?.labels"
class="pt-2"
>
<VChip
v-if="torrent?.hit_and_run"
variant="elevated"
size="small"
class="me-1 mb-1 text-white bg-black"
>
<div v-if="torrent?.labels" class="pt-2">
<VChip v-if="torrent?.hit_and_run" variant="elevated" size="small" class="me-1 mb-1 text-white bg-black">
H&R
</VChip>
<VChip
v-if="torrent?.freedate_diff"
variant="elevated"
color="secondary"
size="small"
class="me-1 mb-1"
>
<VChip v-if="torrent?.freedate_diff" variant="elevated" color="secondary" size="small" class="me-1 mb-1">
{{ torrent?.freedate_diff }}
</VChip>
<VChip
@@ -184,51 +142,24 @@ onMounted(() => {
>
{{ label }}
</VChip>
<VChip
v-if="meta?.edition"
variant="elevated"
size="small"
class="me-1 mb-1 text-white bg-red-500"
>
<VChip v-if="meta?.edition" variant="elevated" size="small" class="me-1 mb-1 text-white bg-red-500">
{{ meta?.edition }}
</VChip>
<VChip
v-if="meta?.resource_pix"
variant="elevated"
size="small"
class="me-1 mb-1 text-white bg-red-500"
>
<VChip v-if="meta?.resource_pix" variant="elevated" size="small" class="me-1 mb-1 text-white bg-red-500">
{{ meta?.resource_pix }}
</VChip>
<VChip
v-if="meta?.video_encode"
variant="elevated"
size="small"
class="me-1 mb-1 text-white bg-orange-500"
>
<VChip v-if="meta?.video_encode" variant="elevated" size="small" class="me-1 mb-1 text-white bg-orange-500">
{{ meta?.video_encode }}
</VChip>
<VChip
v-if="torrent?.size"
variant="elevated"
size="small"
class="me-1 mb-1 text-white bg-yellow-500"
>
<VChip v-if="torrent?.size" variant="elevated" size="small" class="me-1 mb-1 text-white bg-yellow-500">
{{ formatFileSize(torrent?.size) }}
</VChip>
<VChip
v-if="meta?.resource_team"
variant="elevated"
size="small"
class="me-1 mb-1 text-white bg-cyan-500"
>
<VChip v-if="meta?.resource_team" variant="elevated" size="small" class="me-1 mb-1 text-white bg-cyan-500">
{{ meta?.resource_team }}
</VChip>
<VChip
v-if="torrent?.downloadvolumefactor !== 1 || torrent?.uploadvolumefactor !== 1"
:class="
getVolumeFactorClass(torrent?.downloadvolumefactor, torrent?.uploadvolumefactor)
"
:class="getVolumeFactorClass(torrent?.downloadvolumefactor, torrent?.uploadvolumefactor)"
variant="elevated"
size="small"
class="me-1 mb-1"
@@ -239,18 +170,10 @@ onMounted(() => {
<template #append>
<div class="me-n3">
<IconBtn>
<VIcon
icon="mdi-dots-vertical"
/>
<VMenu
activator="parent"
close-on-content-click
>
<VIcon icon="mdi-dots-vertical" />
<VMenu activator="parent" close-on-content-click>
<VList>
<VListItem
variant="plain"
@click="openTorrentDetail()"
>
<VListItem variant="plain" @click="openTorrentDetail()">
<template #prepend>
<VIcon icon="mdi-information" />
</template>

View File

@@ -117,14 +117,6 @@ async function deleteItem(item: FileItem) {
const confirmed = await createConfirm({
title: '确认',
content: `是否确认删除${item.type === 'dir' ? '目录' : '文件'} ${item.basename}`,
confirmationText: '确认',
cancellationText: '取消',
dialogProps: {
maxWidth: '50rem',
},
cancellationButtonProps: {
variant: 'tonal',
},
})
if (confirmed) {

View File

@@ -60,22 +60,17 @@ async function getResourceList() {
try {
resourceDataList.value = await api.get(`site/resource/${props.site}`)
resourceLoading.value = false
}
catch (error) {
} catch (error) {
console.error(error)
}
}
// 促销Chip类
function getVolumeFactorClass(downloadVolume: number, uploadVolume: number) {
if (downloadVolume === 0)
return 'text-white bg-lime-500'
else if (downloadVolume < 1)
return 'text-white bg-green-500'
else if (uploadVolume !== 1)
return 'text-white bg-sky-500'
else
return 'text-white bg-gray-500'
if (downloadVolume === 0) return 'text-white bg-lime-500'
else if (downloadVolume < 1) return 'text-white bg-green-500'
else if (uploadVolume !== 1) return 'text-white bg-sky-500'
else return 'text-white bg-gray-500'
}
// 添加下载
@@ -83,18 +78,9 @@ async function addDownload(_torrent: any) {
const isConfirmed = await createConfirm({
title: '确认',
content: `是否确认下载【${_torrent.site_name}${_torrent?.title} ?`,
confirmationText: '确认',
cancellationText: '取消',
dialogProps: {
maxWidth: '50rem',
},
confirmationButtonProps: {
variant: 'tonal',
},
})
if (!isConfirmed)
return
if (!isConfirmed) return
startNProgress()
try {
@@ -103,13 +89,11 @@ async function addDownload(_torrent: any) {
if (result.success) {
// 添加下载成功
$toast.success(`${_torrent?.site_name} ${_torrent?.title} 添加下载成功!`)
}
else {
} else {
// 添加下载失败
$toast.error(`${_torrent?.site_name} ${_torrent?.title} 添加下载失败:${result.message || '未知错误'}`)
}
}
catch (error) {
} catch (error) {
console.error(error)
}
doneNProgress()
@@ -146,21 +130,10 @@ onMounted(() => {
<div class="text-sm my-1">
{{ item.description }}
</div>
<VChip
v-if="item.hit_and_run"
variant="elevated"
size="small"
class="me-1 mb-1 text-white bg-black"
>
<VChip v-if="item.hit_and_run" variant="elevated" size="small" class="me-1 mb-1 text-white bg-black">
H&R
</VChip>
<VChip
v-if="item.freedate_diff"
variant="elevated"
color="secondary"
size="small"
class="me-1 mb-1"
>
<VChip v-if="item.freedate_diff" variant="elevated" color="secondary" size="small" class="me-1 mb-1">
{{ item.freedate_diff }}
</VChip>
<VChip
@@ -175,9 +148,7 @@ onMounted(() => {
</VChip>
<VChip
v-if="item.downloadvolumefactor !== 1 || item.uploadvolumefactor !== 1"
:class="
getVolumeFactorClass(item.downloadvolumefactor, item.uploadvolumefactor)
"
:class="getVolumeFactorClass(item.downloadvolumefactor, item.uploadvolumefactor)"
variant="elevated"
size="small"
class="me-1 mb-1"
@@ -206,18 +177,10 @@ onMounted(() => {
<template #item.actions="{ item }">
<div class="me-n3">
<IconBtn>
<VIcon
icon="mdi-dots-vertical"
/>
<VMenu
activator="parent"
close-on-content-click
>
<VIcon icon="mdi-dots-vertical" />
<VMenu activator="parent" close-on-content-click>
<VList>
<VListItem
variant="plain"
@click="openTorrentDetail(item.page_url || '')"
>
<VListItem variant="plain" @click="openTorrentDetail(item.page_url || '')">
<template #prepend>
<VIcon icon="mdi-information" />
</template>
@@ -238,8 +201,6 @@ onMounted(() => {
</IconBtn>
</div>
</template>
<template #no-data>
没有数据
</template>
<template #no-data> 没有数据 </template>
</VDataTable>
</template>

View File

@@ -34,14 +34,6 @@ async function restart() {
const confirmed = await createConfirm({
title: '确认',
content: '确认重启系统吗?',
confirmationText: '确认',
cancellationText: '取消',
dialogProps: {
maxWidth: '30rem',
},
cancellationButtonProps: {
variant: 'tonal',
},
})
if (confirmed) {

View File

@@ -15,8 +15,8 @@ import '@core/scss/template/index.scss'
import '@layouts/styles/index.scss'
import '@styles/styles.scss'
import 'vue-toast-notification/dist/theme-bootstrap.css'
import { PerfectScrollbarPlugin } from 'vue3-perfect-scrollbar';
import 'vue3-perfect-scrollbar/style.css';
import { PerfectScrollbarPlugin } from 'vue3-perfect-scrollbar'
import 'vue3-perfect-scrollbar/style.css'
import DialogCloseBtn from '@/@core/components/DialogCloseBtn.vue'
import { fixArrayAt } from '@/@core/utils/compatibility'
@@ -30,7 +30,8 @@ loadFonts()
const app = createApp(App)
// 注册全局组件
app.component('VAceEditor', VAceEditor)
app
.component('VAceEditor', VAceEditor)
.component('VApexChart', VueApexCharts)
.component('VDialogCloseBtn', DialogCloseBtn)
@@ -42,7 +43,26 @@ app
.use(ToastPlugin, {
position: 'bottom-right',
})
.use(VuetifyUseDialog)
.use(VuetifyUseDialog, {
confirmDialog: {
dialogProps: {
maxWidth: '50rem',
},
confirmationButtonProps: {
variant: 'elevated',
color: 'primary',
class: 'me-3 px-5',
'prepend-icon': 'mdi-check',
},
cancellationButtonProps: {
variant: 'outlined',
color: 'secondary',
class: 'me-3',
},
confirmationText: '确认',
cancellationText: '取消',
},
})
.use(PerfectScrollbarPlugin)
.mount('#app')
.$nextTick(() => removeEl('#loading-bg'))

View File

@@ -112,8 +112,12 @@ function setDashboardConfig() {
</VCol>
</VRow>
<!-- 弹窗根据配置生成选项 -->
<VDialog v-model="dialog" max-width="40rem" scrollable :fullscreen="!display.mdAndUp.value">
<VCard title="设置仪表板">
<VDialog v-model="dialog" max-width="35rem" scrollable :fullscreen="!display.mdAndUp.value">
<VCard>
<VCardItem>
<VCardTitle>设置仪表板</VCardTitle>
</VCardItem>
<VDivider />
<VCardText>
<VRow>
<VCol v-for="(item, key) in dashboard_names" :key="key" cols="12" md="4" sm="4">
@@ -121,11 +125,17 @@ function setDashboardConfig() {
</VCol>
</VRow>
</VCardText>
<VCardActions>
<VBtn color="primary" @click="dialog = false"> 取消 </VBtn>
<VDivider />
<VCardText class="pt-5 text-end">
<VSpacer />
<VBtn color="primary" variant="tonal" @click="setDashboardConfig"> 保存 </VBtn>
</VCardActions>
<VBtn variant="outlined" color="secondary" class="me-4" @click="dialog = false"> 关闭 </VBtn>
<VBtn @click="setDashboardConfig">
<template #prepend>
<VIcon icon="mdi-content-save" />
</template>
保存
</VBtn>
</VCardText>
</VCard>
</VDialog>
</template>

View File

@@ -85,10 +85,8 @@ async function loadAccountInfo() {
const user: User = await api.get('user/current')
console.log(user)
accountInfo.value = user
if (!accountInfo.value.avatar)
accountInfo.value.avatar = avatar1
}
catch (error) {
if (!accountInfo.value.avatar) accountInfo.value.avatar = avatar1
} catch (error) {
console.log(error)
}
}
@@ -105,12 +103,9 @@ async function saveAccountInfo() {
}
try {
const result: { [key: string]: any } = await api.put('user/', accountInfo.value)
if (result.success)
$toast.success('用户信息保存成功!')
else
$toast.error(`用户信息保存失败:${result.message}`)
}
catch (error) {
if (result.success) $toast.success('用户信息保存成功!')
else $toast.error(`用户信息保存失败:${result.message}`)
} catch (error) {
console.log(error)
}
}
@@ -121,8 +116,7 @@ async function loadAllUsers() {
const result: User[] = await api.get('/user/')
allUsers.value = result
}
catch (error) {
} catch (error) {
console.log(error)
}
}
@@ -134,12 +128,10 @@ async function deleteUser(user: User) {
if (result.success) {
$toast.success('用户删除成功!')
loadAllUsers()
}
else {
} else {
$toast.error(`用户删除失败:${result.message}`)
}
}
catch (error) {
} catch (error) {
console.log(error)
}
}
@@ -153,12 +145,10 @@ async function deactivateUser(user: User) {
if (result.success) {
$toast.success('用户冻结成功!')
loadAllUsers()
}
else {
} else {
$toast.error(`用户冻结失败:${result.message}`)
}
}
catch (error) {
} catch (error) {
console.log(error)
}
}
@@ -175,12 +165,10 @@ async function addUser() {
$toast.success('用户新增成功!')
loadAllUsers()
addUserDialog.value = false
}
else {
} else {
$toast.error(`用户新增失败:${result.message}`)
}
}
catch (error) {
} catch (error) {
console.log(error)
}
}
@@ -194,12 +182,10 @@ async function getOtpUri() {
secret.value = result.data.secret
qrCode.value = result.data.uri
otpDialog.value = true
}
else {
} else {
$toast.error(`获取otp uri失败${result.message}`)
}
}
catch (error) {
} catch (error) {
console.log(error)
}
}
@@ -211,12 +197,10 @@ async function disableOtp() {
if (result.success) {
accountInfo.value.is_otp = false
$toast.success('关闭登录双重验证成功!')
}
else {
} else {
$toast.error(`关闭otp失败${result.message}`)
}
}
catch (error) {
} catch (error) {
console.log(error)
}
}
@@ -228,18 +212,19 @@ async function judgeOtpPassword() {
return
}
try {
const result: { [key: string]: any } = await api.post('user/otp/judge', { uri: otpUri.value, otpPassword: otpPassword.value })
const result: { [key: string]: any } = await api.post('user/otp/judge', {
uri: otpUri.value,
otpPassword: otpPassword.value,
})
if (result.success) {
$toast.success('开启登录双重验证成功!')
otpDialog.value = false
accountInfo.value.is_otp = true
}
else {
} else {
$toast.error(`开启otp失败${result.message}`)
}
}
catch (error) {
} catch (error) {
console.log(error)
}
}
@@ -258,23 +243,13 @@ onMounted(() => {
<VCard title="个人信息">
<VCardText class="d-flex">
<!-- 👉 Avatar -->
<VAvatar
rounded="lg"
size="100"
class="me-6"
:image="accountInfo.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"
/>
<VBtn color="primary" @click="refInputEl?.click()">
<VIcon icon="mdi-cloud-upload-outline" />
<span class="d-none d-sm-block ms-2">上传头像</span>
</VBtn>
@@ -285,17 +260,10 @@ onMounted(() => {
accept=".jpeg,.png,.jpg,GIF"
hidden
@input="changeAvatar"
>
/>
<VBtn
type="reset"
color="error"
variant="tonal"
@click="resetAvatar"
>
<VIcon
icon="mdi-refresh"
/>
<VBtn type="reset" color="error" variant="tonal" @click="resetAvatar">
<VIcon icon="mdi-refresh" />
<span class="d-none d-sm-block ms-2">重置</span>
</VBtn>
@@ -304,16 +272,12 @@ onMounted(() => {
variant="tonal"
@click.stop="accountInfo.is_otp ? disableOtp() : getOtpUri()"
>
<VIcon
icon="mdi-account-key"
/>
<span class="d-none d-sm-block ms-2">{{ accountInfo.is_otp ? "关闭验证" : "双重验证" }}</span>
<VIcon icon="mdi-account-key" />
<span class="d-none d-sm-block ms-2">{{ accountInfo.is_otp ? '关闭验证' : '双重验证' }}</span>
</VBtn>
</div>
<p class="text-body-1 mb-0">
允许 JPGGIF PNG 格式 最大尺寸 800K
</p>
<p class="text-body-1 mb-0">允许 JPGGIF PNG 格式 最大尺寸 800K</p>
</form>
</VCardText>
@@ -324,33 +288,16 @@ onMounted(() => {
<VForm class="mt-6">
<VRow>
<!-- 👉 Name -->
<VCol
md="6"
cols="12"
>
<VTextField
v-model="accountInfo.name"
readonly
label="用户名"
/>
<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 cols="12" md="6">
<VTextField v-model="accountInfo.email" label="邮箱" type="email" />
</VCol>
<VCol
cols="12"
md="6"
>
<VCol cols="12" md="6">
<!-- 👉 new password -->
<VTextField
v-model="newPassword"
@@ -362,32 +309,20 @@ onMounted(() => {
/>
</VCol>
<VCol
cols="12"
md="6"
>
<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'
"
:append-inner-icon="isConfirmPasswordVisible ? 'mdi-eye-off-outline' : 'mdi-eye-outline'"
label="确认新密码"
@click:append-inner="
isConfirmPasswordVisible = !isConfirmPasswordVisible
"
@click:append-inner="isConfirmPasswordVisible = !isConfirmPasswordVisible"
/>
</VCol>
<!-- 👉 Form Actions -->
<VCol
cols="12"
class="d-flex flex-wrap gap-4"
>
<VBtn @click="saveAccountInfo">
保存
</VBtn>
<VCol cols="12" class="d-flex flex-wrap gap-4">
<VBtn @click="saveAccountInfo"> 保存 </VBtn>
</VCol>
</VRow>
</VForm>
@@ -395,10 +330,7 @@ onMounted(() => {
</VCard>
</VCol>
<VCol
v-if="accountInfo.is_superuser"
cols="12"
>
<VCol v-if="accountInfo.is_superuser" cols="12">
<!-- 👉 Accounts -->
<VCard title="所有用户">
<template #append>
@@ -409,76 +341,38 @@ onMounted(() => {
<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"
/>
<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"
>
<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>
<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>{{ 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
>
<VMenu activator="parent" close-on-content-click>
<VList>
<VListItem
variant="plain"
@click="deactivateUser(user)"
>
<VListItem variant="plain" @click="deactivateUser(user)">
<template #prepend>
<VIcon icon="mdi-lock" />
</template>
<VListItemTitle>
{{
user.is_active ? "冻结" : "解冻"
}}
{{ user.is_active ? '冻结' : '解冻' }}
</VListItemTitle>
</VListItem>
<VListItem
variant="plain"
base-color="error"
@click="deleteUser(user)"
>
<VListItem variant="plain" base-color="error" @click="deleteUser(user)">
<template #prepend>
<VIcon icon="mdi-delete" />
</template>
@@ -495,85 +389,50 @@ onMounted(() => {
</VCol>
</VRow>
<!-- =弹窗 -->
<VDialog
v-model="addUserDialog"
max-width="50rem"
persistent
z-index="1010"
>
<VDialog v-model="addUserDialog" max-width="50rem" persistent z-index="1010">
<!-- Dialog Content -->
<VCard title="新增用户">
<VCardText>
<VForm @submit.prevent="() => {}">
<VRow>
<VCol
cols="12"
md="6"
>
<VTextField
v-model="userForm.name"
label="用户名"
:rules="[requiredValidator]"
/>
<VCol cols="12" md="6">
<VTextField v-model="userForm.name" label="用户名" :rules="[requiredValidator]" />
</VCol>
<VCol
cols="12"
md="6"
>
<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'
"
: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"
:rules="[requiredValidator]"
label="邮箱"
/>
<VCol cols="12" md="6">
<VTextField v-model="userForm.email" :rules="[requiredValidator]" label="邮箱" />
</VCol>
</VRow>
</VForm>
</VCardText>
<VCardActions>
<VBtn @click="addUserDialog = false">
取消
</VBtn>
<VBtn @click="addUserDialog = false"> 取消 </VBtn>
<VSpacer />
<VBtn @click="addUser">
确定
</VBtn>
<VBtn @click="addUser"> 确定 </VBtn>
</VCardActions>
</VCard>
</VDialog>
<!-- 双重验证弹窗 -->
<VDialog
v-model="otpDialog"
max-width="45rem"
persistent
z-index="1010"
>
<VDialog v-model="otpDialog" max-width="45rem" persistent z-index="1010">
<!-- 开启双重验证弹窗内容 -->
<VCard>
<DialogCloseBtn @click="otpDialog = false" />
<VCardText>
<h4 class="text-h4 text-center mb-6 mt-5">
登录双重验证
</h4><h5 class="text-h5 font-weight-medium mb-2">
身份验证器
</h5>
<h4 class="text-h4 text-center mb-6 mt-5">登录双重验证</h4>
<h5 class="text-h5 font-weight-medium mb-2">身份验证器</h5>
<p class="mb-6">
使用像Google AuthenticatorMicrosoft AuthenticatorAuthy或1Password这样的身份验证器应用程序扫描二维码它将为您生成一个6位数的代码供您在下方输入
使用像Google AuthenticatorMicrosoft
AuthenticatorAuthy或1Password这样的身份验证器应用程序扫描二维码它将为您生成一个6位数的代码供您在下方输入
</p>
<div class="my-6">
<QrcodeVue class="mx-auto" :value="qrCode" :size="200" max-width="25rem" />
@@ -597,14 +456,12 @@ onMounted(() => {
variant="outlined"
/>
<div class="d-flex justify-end flex-wrap gap-4">
<VBtn variant="outlined" color="secondary" @click="otpDialog = false">
取消
</VBtn>
<VBtn variant="outlined" color="secondary" @click="otpDialog = false"> 取消 </VBtn>
<VBtn @click="judgeOtpPassword">
确定
<template #append>
<template #prepend>
<VIcon icon="mdi-check" />
</template>
确定
</VBtn>
</div>
</VForm>