mirror of
https://github.com/jxxghp/MoviePilot-Frontend.git
synced 2026-05-28 03:39:46 +08:00
更新国际化支持
This commit is contained in:
@@ -4,6 +4,10 @@ import api from '@/api'
|
||||
import personIcon from '@images/misc/person.png'
|
||||
import type { Person } from '@/api/types'
|
||||
import NoDataFound from '@/components/NoDataFound.vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
// 国际化
|
||||
const { t } = useI18n()
|
||||
|
||||
// 输入参数
|
||||
const personProps = defineProps({
|
||||
@@ -67,7 +71,7 @@ function getPersonImage() {
|
||||
function getAlsoKnownAs() {
|
||||
if (!personDetail.value?.also_known_as) return ''
|
||||
if (personProps.source === 'themoviedb') {
|
||||
return '别名:' + personDetail.value.also_known_as.join('、')
|
||||
return t('person.alias') + personDetail.value.also_known_as.join('、')
|
||||
} else {
|
||||
return personDetail.value.also_known_as.join(',')
|
||||
}
|
||||
@@ -81,7 +85,7 @@ function getPersonCreditsPath() {
|
||||
} else if (personProps.source === 'bangumi') {
|
||||
apipath = 'bangumi'
|
||||
}
|
||||
return `/browse/${apipath}/person/credits/${personDetail.value.id}?title=参演作品`
|
||||
return `/browse/${apipath}/person/credits/${personDetail.value.id}?title=${t('person.credits')}`
|
||||
}
|
||||
|
||||
// 参演作品API路径
|
||||
@@ -136,7 +140,7 @@ onBeforeMount(() => {
|
||||
<div>
|
||||
<div class="slider-header">
|
||||
<RouterLink :to="getPersonCreditsPath()" class="slider-title">
|
||||
<span>参演作品</span>
|
||||
<span>{{ t('person.credits') }}</span>
|
||||
<VIcon icon="mdi-arrow-right-circle-outline" class="ms-1" />
|
||||
</RouterLink>
|
||||
</div>
|
||||
@@ -146,7 +150,7 @@ onBeforeMount(() => {
|
||||
<NoDataFound
|
||||
v-if="!personDetail.id && isRefreshed"
|
||||
error-code="500"
|
||||
error-title="出错啦!"
|
||||
error-description="无法获取到媒体信息,请检查网络连接。"
|
||||
:error-title="t('error.title')"
|
||||
:error-description="t('error.networkError')"
|
||||
/>
|
||||
</template>
|
||||
|
||||
@@ -12,6 +12,10 @@ import { isNullOrEmptyObject } from '@/@core/utils'
|
||||
import { getPluginTabs } from '@/router/i18n-menu'
|
||||
import PluginMarketSettingDialog from '@/components/dialog/PluginMarketSettingDialog.vue'
|
||||
import { useDynamicButton } from '@/composables/useDynamicButton'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
// 国际化
|
||||
const { t } = useI18n()
|
||||
|
||||
const route = useRoute()
|
||||
|
||||
@@ -37,13 +41,13 @@ const activeSort = ref(null)
|
||||
const orderConfig = ref<{ id: string }[]>([])
|
||||
|
||||
// 排序选项
|
||||
const sortOptions = [
|
||||
{ title: '热门', value: 'count' },
|
||||
{ title: '插件名称', value: 'plugin_name' },
|
||||
{ title: '作者', value: 'plugin_author' },
|
||||
{ title: '插件仓库', value: 'repo_url' },
|
||||
{ title: '最新发布', value: 'add_time' },
|
||||
]
|
||||
const sortOptions = computed(() => [
|
||||
{ title: t('plugin.sort.popular'), value: 'count' },
|
||||
{ title: t('plugin.sort.name'), value: 'plugin_name' },
|
||||
{ title: t('plugin.sort.author'), value: 'plugin_author' },
|
||||
{ title: t('plugin.sort.repository'), value: 'repo_url' },
|
||||
{ title: t('plugin.sort.latest'), value: 'add_time' },
|
||||
])
|
||||
|
||||
// 加载中
|
||||
const loading = ref(false)
|
||||
@@ -105,7 +109,7 @@ const $toast = useToast()
|
||||
const progressDialog = ref(false)
|
||||
|
||||
// 进度框文本
|
||||
const progressText = ref('正在安装插件...')
|
||||
const progressText = ref(t('plugin.installingPlugin'))
|
||||
|
||||
// 过滤表单
|
||||
const filterForm = reactive({
|
||||
@@ -217,7 +221,7 @@ async function installPlugin(item: Plugin) {
|
||||
try {
|
||||
// 显示等待提示框
|
||||
progressDialog.value = true
|
||||
progressText.value = `正在安装 ${item?.plugin_name} v${item?.plugin_version} ...`
|
||||
progressText.value = t('plugin.installing', { name: item?.plugin_name, version: item?.plugin_version })
|
||||
|
||||
const result: { [key: string]: any } = await api.get(`plugin/install/${item?.id}`, {
|
||||
params: {
|
||||
@@ -230,12 +234,12 @@ async function installPlugin(item: Plugin) {
|
||||
progressDialog.value = false
|
||||
|
||||
if (result.success) {
|
||||
$toast.success(`插件 ${item?.plugin_name} 安装成功!`)
|
||||
$toast.success(t('plugin.installSuccess', { name: item?.plugin_name }))
|
||||
|
||||
// 刷新
|
||||
refreshData()
|
||||
} else {
|
||||
$toast.error(`插件 ${item?.plugin_name} 安装失败:${result.message}`)
|
||||
$toast.error(t('plugin.installFailed', { name: item?.plugin_name, message: result.message }))
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
|
||||
@@ -2,6 +2,10 @@
|
||||
import api from '@/api'
|
||||
import { FileItem, StorageConf, TransferDirectoryConf } from '@/api/types'
|
||||
import FileBrowser from '@/components/FileBrowser.vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
// 国际化
|
||||
const { t } = useI18n()
|
||||
|
||||
const endpoints = {
|
||||
list: {
|
||||
@@ -179,7 +183,7 @@ onMounted(() => {
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.file-browser-view {
|
||||
height: 100%;
|
||||
position: relative;
|
||||
block-size: 100%;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -6,6 +6,11 @@ import UserCard from '@/components/cards/UserCard.vue'
|
||||
import UserAddEditDialog from '@/components/dialog/UserAddEditDialog.vue'
|
||||
import { useDisplay } from 'vuetify'
|
||||
import { useDynamicButton } from '@/composables/useDynamicButton'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
// 国际化
|
||||
const { t } = useI18n()
|
||||
|
||||
// APP
|
||||
const display = useDisplay()
|
||||
const appMode = inject('pwaMode') && display.mdAndDown.value
|
||||
@@ -68,7 +73,7 @@ useDynamicButton({
|
||||
|
||||
<template>
|
||||
<!-- 页面标题 -->
|
||||
<VPageContentTitle title="用户管理" />
|
||||
<VPageContentTitle :title="t('user.management')" />
|
||||
<div class="card-list-container">
|
||||
<!-- 加载中提示 -->
|
||||
<LoadingBanner v-if="!isRefreshed" class="mt-12" />
|
||||
@@ -87,7 +92,7 @@ useDynamicButton({
|
||||
|
||||
<!-- 无数据提示 -->
|
||||
<div v-if="allUsers.length === 0 && isRefreshed">
|
||||
<NoDataFound error-code="404" error-title="没有用户" error-description="点击添加用户卡片添加用户" />
|
||||
<NoDataFound error-code="404" :error-title="t('user.noUsers')" :error-description="t('user.clickToAddUser')" />
|
||||
</div>
|
||||
|
||||
<!-- 新增用户按钮 -->
|
||||
|
||||
@@ -7,6 +7,10 @@ import type { User } from '@/api/types'
|
||||
import avatar1 from '@images/avatars/avatar-1.png'
|
||||
import { useDisplay } from 'vuetify'
|
||||
import { useUserStore } from '@/stores'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
// 国际化
|
||||
const { t } = useI18n()
|
||||
|
||||
// 显示器宽度
|
||||
const display = useDisplay()
|
||||
@@ -73,19 +77,19 @@ function changeAvatar(file: Event) {
|
||||
const maxSize = 800 * 1024
|
||||
// 检查文件是否为图片
|
||||
if (!allowedTypes.includes(selectedFile.type)) {
|
||||
$toast.error('上传的文件不符合要求,请重新选择头像')
|
||||
$toast.error(t('profile.avatarFormatError'))
|
||||
return
|
||||
}
|
||||
// 检查文件大小
|
||||
if (selectedFile.size > maxSize) {
|
||||
$toast.error('文件大小不得大于800KB')
|
||||
$toast.error(t('profile.avatarSizeError'))
|
||||
return
|
||||
}
|
||||
fileReader.readAsDataURL(selectedFile)
|
||||
fileReader.onload = () => {
|
||||
if (typeof fileReader.result === 'string') {
|
||||
currentAvatar.value = fileReader.result
|
||||
$toast.success('新头像上传成功,待保存后生效!')
|
||||
$toast.success(t('profile.avatarUploadSuccess'))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -94,13 +98,13 @@ function changeAvatar(file: Event) {
|
||||
// 重置默认头像
|
||||
function resetDefaultAvatar() {
|
||||
currentAvatar.value = avatar1
|
||||
$toast.success('已重置为默认头像,待保存后生效!')
|
||||
$toast.success(t('profile.resetAvatarSuccess'))
|
||||
}
|
||||
|
||||
// 还原当前头像
|
||||
function restoreCurrentAvatar() {
|
||||
currentAvatar.value = accountInfo.value.avatar
|
||||
$toast.success('已还原当前使用头像!')
|
||||
$toast.success(t('profile.restoreAvatarSuccess'))
|
||||
}
|
||||
|
||||
// 加载当前用户信息
|
||||
@@ -121,16 +125,16 @@ async function fetchUserInfo() {
|
||||
// 保存用户信息
|
||||
async function saveAccountInfo() {
|
||||
if (isSaving.value) {
|
||||
$toast.error('正在保存中,请稍后...')
|
||||
$toast.error(t('profile.savingInProgress'))
|
||||
return
|
||||
}
|
||||
if (!currentUserName.value) {
|
||||
$toast.error('用户名不能为空')
|
||||
$toast.error(t('profile.usernameRequired'))
|
||||
return
|
||||
}
|
||||
if (newPassword.value || confirmPassword.value) {
|
||||
if (newPassword.value !== confirmPassword.value) {
|
||||
$toast.error('两次输入的密码不一致')
|
||||
$toast.error(t('profile.passwordMismatch'))
|
||||
return
|
||||
}
|
||||
accountInfo.value.password = newPassword.value
|
||||
@@ -157,11 +161,11 @@ async function saveAccountInfo() {
|
||||
|
||||
if (result.success) {
|
||||
if (oldUserName !== currentUserName.value) {
|
||||
$toast.success(`【${oldUserName}】更名【${currentUserName.value}】,用户信息保存成功!`)
|
||||
$toast.success(t('profile.usernameChangeSuccess', { oldName: oldUserName, newName: currentUserName.value }))
|
||||
// 更新本地用户名显示
|
||||
userStore.setUserName(currentUserName.value)
|
||||
} else {
|
||||
$toast.success('用户信息保存成功!')
|
||||
$toast.success(t('profile.saveSuccess'))
|
||||
}
|
||||
// 更新本地头像显示
|
||||
if (oldAvatar !== currentAvatar.value) {
|
||||
@@ -169,9 +173,15 @@ async function saveAccountInfo() {
|
||||
}
|
||||
} else {
|
||||
if (oldAvatar !== currentAvatar.value) {
|
||||
$toast.error(`【${oldUserName}】更名【${currentUserName.value}】,信息保存失败:${result.message}!`)
|
||||
$toast.error(
|
||||
t('profile.saveFailedWithNameChange', {
|
||||
oldName: oldUserName,
|
||||
newName: currentUserName.value,
|
||||
message: result.message,
|
||||
}),
|
||||
)
|
||||
} else {
|
||||
$toast.error(`用户信息保存失败:${result.message}!`)
|
||||
$toast.error(t('profile.saveFailed', { message: result.message }))
|
||||
}
|
||||
// 失败缓存值还原
|
||||
currentUserName.value = accountInfo.value.name
|
||||
@@ -195,7 +205,7 @@ async function getOtpUri() {
|
||||
qrCode.value = result.data.uri
|
||||
otpDialog.value = true
|
||||
} else {
|
||||
$toast.error(`获取otp uri失败:${result.message}!`)
|
||||
$toast.error(t('profile.otpGenerateFailed', { message: result.message }))
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
@@ -208,9 +218,9 @@ async function disableOtp() {
|
||||
const result: { [key: string]: any } = await api.post('user/otp/disable')
|
||||
if (result.success) {
|
||||
accountInfo.value.is_otp = false
|
||||
$toast.success('关闭登录双重验证成功!')
|
||||
$toast.success(t('profile.otpDisableSuccess'))
|
||||
} else {
|
||||
$toast.error(`关闭otp失败:${result.message}!`)
|
||||
$toast.error(t('profile.otpDisableFailed', { message: result.message }))
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
@@ -220,7 +230,7 @@ async function disableOtp() {
|
||||
// 启用Otp
|
||||
async function judgeOtpPassword() {
|
||||
if (!otpPassword.value) {
|
||||
$toast.error('请填写6位验证码')
|
||||
$toast.error(t('profile.otpCodeRequired'))
|
||||
return
|
||||
}
|
||||
try {
|
||||
@@ -230,11 +240,11 @@ async function judgeOtpPassword() {
|
||||
})
|
||||
|
||||
if (result.success) {
|
||||
$toast.success('开启登录双重验证成功!')
|
||||
$toast.success(t('profile.otpEnableSuccess'))
|
||||
otpDialog.value = false
|
||||
accountInfo.value.is_otp = true
|
||||
} else {
|
||||
$toast.error(`开启otp失败:${result.message}!`)
|
||||
$toast.error(t('profile.otpEnableFailed', { message: result.message }))
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
@@ -259,7 +269,7 @@ watch(
|
||||
<div>
|
||||
<VRow>
|
||||
<VCol cols="12">
|
||||
<VCard title="个人信息">
|
||||
<VCard :title="t('profile.personalInfo')">
|
||||
<VCardText class="flex">
|
||||
<!-- 👉 Avatar -->
|
||||
<VAvatar rounded="lg" size="100" class="me-6" :image="currentAvatar" />
|
||||
@@ -269,7 +279,7 @@ watch(
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<VBtn color="primary" @click="refInputEl?.click()">
|
||||
<VIcon icon="mdi-cloud-upload-outline" />
|
||||
<span v-if="display.mdAndUp.value" class="ms-2">上传新头像</span>
|
||||
<span v-if="display.mdAndUp.value" class="ms-2">{{ t('profile.uploadNewAvatar') }}</span>
|
||||
</VBtn>
|
||||
|
||||
<input
|
||||
@@ -283,12 +293,12 @@ watch(
|
||||
|
||||
<VBtn type="reset" color="info" variant="tonal" @click="restoreCurrentAvatar">
|
||||
<VIcon icon="mdi-refresh" />
|
||||
<span v-if="display.mdAndUp.value" class="ms-2">重置</span>
|
||||
<span v-if="display.mdAndUp.value" class="ms-2">{{ t('common.reset') }}</span>
|
||||
</VBtn>
|
||||
|
||||
<VBtn type="reset" color="error" variant="tonal" @click="resetDefaultAvatar">
|
||||
<VIcon icon="mdi-image-sync-outline" />
|
||||
<span v-if="display.mdAndUp.value" class="ms-2">默认</span>
|
||||
<span v-if="display.mdAndUp.value" class="ms-2">{{ t('common.default') }}</span>
|
||||
</VBtn>
|
||||
|
||||
<VBtn
|
||||
@@ -298,12 +308,12 @@ watch(
|
||||
>
|
||||
<VIcon icon="mdi-account-key" />
|
||||
<span v-if="display.mdAndUp.value" class="ms-2">{{
|
||||
accountInfo.is_otp ? '关闭双重验证' : '开启双重验证'
|
||||
accountInfo.is_otp ? t('profile.disableTwoFactor') : t('profile.enableTwoFactor')
|
||||
}}</span>
|
||||
</VBtn>
|
||||
</div>
|
||||
|
||||
<p class="text-body-1 mb-0">允许 JPG、PNG、GIF、WEBP 格式, 最大尺寸 800KB。</p>
|
||||
<p class="text-body-1 mb-0">{{ t('profile.avatarFormatTip') }}</p>
|
||||
</form>
|
||||
</VCardText>
|
||||
|
||||
@@ -312,10 +322,16 @@ watch(
|
||||
<VForm class="mt-6">
|
||||
<VRow>
|
||||
<VCol cols="12" md="6">
|
||||
<VTextField v-model="currentUserName" density="comfortable" readonly label="用户名" />
|
||||
<VTextField v-model="currentUserName" density="comfortable" readonly :label="t('user.username')" />
|
||||
</VCol>
|
||||
<VCol cols="12" md="6">
|
||||
<VTextField v-model="accountInfo.email" density="comfortable" clearable label="邮箱" type="email" />
|
||||
<VTextField
|
||||
v-model="accountInfo.email"
|
||||
density="comfortable"
|
||||
clearable
|
||||
:label="t('user.email')"
|
||||
type="email"
|
||||
/>
|
||||
</VCol>
|
||||
<VCol cols="12" md="6">
|
||||
<VTextField
|
||||
@@ -324,7 +340,7 @@ watch(
|
||||
:type="isNewPasswordVisible ? 'text' : 'password'"
|
||||
:append-inner-icon="isNewPasswordVisible ? 'mdi-eye-off-outline' : 'mdi-eye-outline'"
|
||||
clearable
|
||||
label="密码"
|
||||
:label="t('user.password')"
|
||||
autocomplete=""
|
||||
@click:append-inner="isNewPasswordVisible = !isNewPasswordVisible"
|
||||
/>
|
||||
@@ -337,7 +353,7 @@ watch(
|
||||
:type="isConfirmPasswordVisible ? 'text' : 'password'"
|
||||
:append-inner-icon="isConfirmPasswordVisible ? 'mdi-eye-off-outline' : 'mdi-eye-outline'"
|
||||
clearable
|
||||
label="确认密码"
|
||||
:label="t('user.confirmPassword')"
|
||||
@click:append-inner="isConfirmPasswordVisible = !isConfirmPasswordVisible"
|
||||
/>
|
||||
</VCol>
|
||||
@@ -346,14 +362,14 @@ watch(
|
||||
v-model="accountInfo.nickname"
|
||||
density="comfortable"
|
||||
clearable
|
||||
label="昵称"
|
||||
placeholder="显示昵称,优先于用户名显示"
|
||||
:label="t('profile.nickname')"
|
||||
:placeholder="t('profile.nicknamePlaceholder')"
|
||||
/>
|
||||
</VCol>
|
||||
</VRow>
|
||||
|
||||
<VDivider class="my-10">
|
||||
<span>账号绑定</span>
|
||||
<span>{{ t('profile.accountBinding') }}</span>
|
||||
</VDivider>
|
||||
|
||||
<VRow>
|
||||
@@ -362,7 +378,7 @@ watch(
|
||||
v-model="accountInfo.settings.wechat_userid"
|
||||
density="comfortable"
|
||||
clearable
|
||||
label="微信用户"
|
||||
:label="t('profile.wechatUser')"
|
||||
/>
|
||||
</VCol>
|
||||
<VCol cols="12" md="6">
|
||||
@@ -370,7 +386,7 @@ watch(
|
||||
v-model="accountInfo.settings.telegram_userid"
|
||||
density="comfortable"
|
||||
clearable
|
||||
label="Telegram用户"
|
||||
:label="t('profile.telegramUser')"
|
||||
/>
|
||||
</VCol>
|
||||
<VCol cols="12" md="6">
|
||||
@@ -378,7 +394,7 @@ watch(
|
||||
v-model="accountInfo.settings.slack_userid"
|
||||
density="comfortable"
|
||||
clearable
|
||||
label="Slack用户"
|
||||
:label="t('profile.slackUser')"
|
||||
/>
|
||||
</VCol>
|
||||
<VCol cols="12" md="6">
|
||||
@@ -386,7 +402,7 @@ watch(
|
||||
v-model="accountInfo.settings.vocechat_userid"
|
||||
density="comfortable"
|
||||
clearable
|
||||
label="VoceChat用户"
|
||||
:label="t('profile.vocechatUser')"
|
||||
/>
|
||||
</VCol>
|
||||
<VCol cols="12" md="6">
|
||||
@@ -394,7 +410,7 @@ watch(
|
||||
v-model="accountInfo.settings.synologychat_userid"
|
||||
density="comfortable"
|
||||
clearable
|
||||
label="SynologyChat用户"
|
||||
:label="t('profile.synologychatUser')"
|
||||
/>
|
||||
</VCol>
|
||||
<VCol cols="12" md="6">
|
||||
@@ -402,7 +418,7 @@ watch(
|
||||
v-model="accountInfo.settings.douban_userid"
|
||||
density="comfortable"
|
||||
clearable
|
||||
label="豆瓣用户"
|
||||
:label="t('profile.doubanUser')"
|
||||
/>
|
||||
</VCol>
|
||||
</VRow>
|
||||
@@ -410,8 +426,8 @@ watch(
|
||||
<!-- 👉 Form Actions -->
|
||||
<VCol cols="12" class="d-flex flex-wrap gap-4">
|
||||
<VBtn @click="saveAccountInfo" :disabled="isSaving">
|
||||
<span v-if="isSaving">保存中...</span>
|
||||
<span v-else>保存</span>
|
||||
<span v-if="isSaving">{{ t('common.saving') }}...</span>
|
||||
<span v-else>{{ t('common.save') }}</span>
|
||||
</VBtn>
|
||||
</VCol>
|
||||
</VRow>
|
||||
@@ -427,40 +443,33 @@ watch(
|
||||
<VCard>
|
||||
<VDialogCloseBtn @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">{{ t('profile.twoFactorAuthentication') }}</h4>
|
||||
<h5 class="text-h5 font-weight-medium mb-2">{{ t('profile.authenticatorApp') }}</h5>
|
||||
<p class="mb-6">
|
||||
使用像Google Authenticator、Microsoft
|
||||
Authenticator、Authy或1Password这样的身份验证器应用程序,扫描二维码。它将为您生成一个6位数的代码,供您在下方输入。
|
||||
{{ t('profile.authenticatorAppDescription') }}
|
||||
</p>
|
||||
<div class="my-6">
|
||||
<QrcodeVue class="mx-auto" :value="qrCode" :size="200" max-width="25rem" />
|
||||
</div>
|
||||
<VAlert
|
||||
:title="secret"
|
||||
variant="tonal"
|
||||
type="warning"
|
||||
class="my-4"
|
||||
text="如果您在使用二维码时遇到困难,请在您的应用程序中选择手动输入以上代码。"
|
||||
>
|
||||
<VAlert :title="secret" variant="tonal" type="warning" class="my-4" :text="t('profile.secretKeyTip')">
|
||||
<template #prepend />
|
||||
</VAlert>
|
||||
<VForm>
|
||||
<VTextField
|
||||
v-model="otpPassword"
|
||||
type="text"
|
||||
label="输入验证码以确认开启双重验证"
|
||||
:label="t('profile.enterVerificationCode')"
|
||||
autocomplete=""
|
||||
class="mb-8"
|
||||
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"> {{ t('common.cancel') }} </VBtn>
|
||||
<VBtn @click="judgeOtpPassword">
|
||||
<template #prepend>
|
||||
<VIcon icon="mdi-check" />
|
||||
</template>
|
||||
确定
|
||||
{{ t('common.confirm') }}
|
||||
</VBtn>
|
||||
</div>
|
||||
</VForm>
|
||||
|
||||
Reference in New Issue
Block a user