generated from VLADIMIR/template_frontend
updates
This commit is contained in:
@@ -0,0 +1,173 @@
|
||||
<script setup lang="ts">
|
||||
import { NAlert, NSpin } from 'naive-ui'
|
||||
import { onMounted, ref } from 'vue'
|
||||
import { RouterLink } from 'vue-router'
|
||||
|
||||
import { getAuthClient } from '@/api/auth_client'
|
||||
import HeaderText from '@/components/HeaderText.vue'
|
||||
|
||||
const props = defineProps<{
|
||||
kind: 'terms' | 'privacy' | 'consent'
|
||||
}>()
|
||||
|
||||
const titles: Record<string, string> = {
|
||||
terms: 'Пользовательское соглашение',
|
||||
privacy: 'Политика конфиденциальности',
|
||||
consent: 'Согласие на обработку персональных данных',
|
||||
}
|
||||
|
||||
const text = ref('')
|
||||
const loading = ref(true)
|
||||
const error = ref('')
|
||||
|
||||
onMounted(async () => {
|
||||
const client = getAuthClient()
|
||||
const fetchers = {
|
||||
terms: () => client.GetTerms({}),
|
||||
privacy: () => client.GetPrivacy({}),
|
||||
consent: () => client.GetConsent({}),
|
||||
}
|
||||
|
||||
try {
|
||||
const res = await fetchers[props.kind]()
|
||||
if (res.error) {
|
||||
error.value = res.error
|
||||
} else {
|
||||
text.value = res.text || ''
|
||||
}
|
||||
} catch (err) {
|
||||
error.value = err instanceof Error ? err.message : 'Не удалось загрузить документ'
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="center-block-custom">
|
||||
<div class="width700 doc-page">
|
||||
<h1 class="doc-title">
|
||||
<HeaderText>{{ titles[props.kind] }}</HeaderText>
|
||||
</h1>
|
||||
|
||||
<div v-if="loading" class="doc-loading">
|
||||
<n-spin size="large" />
|
||||
</div>
|
||||
|
||||
<n-alert v-else-if="error" type="error" :show-icon="false" class="doc-error">
|
||||
{{ error }}
|
||||
</n-alert>
|
||||
|
||||
<div v-else class="paper-block">
|
||||
<div class="paper">
|
||||
<p class="doc-text">{{ text }}</p>
|
||||
</div>
|
||||
<div class="paper-shadow paper-shadow-left"></div>
|
||||
<div class="paper-shadow paper-shadow-right"></div>
|
||||
</div>
|
||||
|
||||
<div class="docs-links">
|
||||
Смотрите также:
|
||||
<RouterLink to="/user-agreement" class="docs-link">Пользовательское соглашение</RouterLink>
|
||||
·
|
||||
<RouterLink to="/privacy-policy" class="docs-link">Политика конфиденциальности</RouterLink>
|
||||
·
|
||||
<RouterLink to="/consent" class="docs-link">Согласие на обработку персональных данных</RouterLink>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.doc-page {
|
||||
padding-top: 30px;
|
||||
}
|
||||
|
||||
.doc-title {
|
||||
margin: 0 0 24px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.doc-loading {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
padding: 80px 0;
|
||||
}
|
||||
|
||||
.doc-error {
|
||||
margin: 0 auto;
|
||||
max-width: 600px;
|
||||
}
|
||||
|
||||
.paper-block {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.paper {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
padding: 32px 28px;
|
||||
border-radius: 3px;
|
||||
background-image: url('@/assets/images/paper_white.jpg');
|
||||
background-size: cover;
|
||||
color: #1d1d1d;
|
||||
box-shadow: 0 0 12px rgb(0 0 0 / 70%);
|
||||
max-height: 60vh;
|
||||
overflow-y: auto;
|
||||
scrollbar-width: none;
|
||||
}
|
||||
|
||||
.paper::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.doc-text {
|
||||
font-family: 'font_old_typer';
|
||||
font-size: 17px;
|
||||
line-height: 1.65;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
text-align: justify;
|
||||
}
|
||||
|
||||
.paper-shadow {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 3px;
|
||||
background-image: url('@/assets/images/paper_white.jpg');
|
||||
background-size: cover;
|
||||
}
|
||||
|
||||
.paper-shadow-left {
|
||||
left: 0;
|
||||
transform: rotate(-2.2deg);
|
||||
filter: brightness(60%);
|
||||
box-shadow: 0 0 10px rgb(0 0 0 / 60%);
|
||||
}
|
||||
|
||||
.paper-shadow-right {
|
||||
right: 0;
|
||||
transform: rotate(2.2deg);
|
||||
filter: brightness(75%);
|
||||
box-shadow: 0 0 10px rgb(0 0 0 / 60%);
|
||||
}
|
||||
|
||||
.docs-links {
|
||||
margin-top: 28px;
|
||||
padding-bottom: 20px;
|
||||
text-align: center;
|
||||
font-size: 15px;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.docs-link {
|
||||
color: #45aa89;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.docs-link:hover {
|
||||
color: #63e2b7;
|
||||
}
|
||||
</style>
|
||||
@@ -24,7 +24,8 @@ const message = useMessage()
|
||||
const username = ref('')
|
||||
const email = ref('')
|
||||
const password = ref('')
|
||||
const approval = ref(false)
|
||||
const acceptTerms = ref(false)
|
||||
const acceptPrivacy = ref(false)
|
||||
|
||||
const options = computed(() => {
|
||||
return ['@mail.ru', '@yandex.ru', '@gmail.com'].map((suffix) => {
|
||||
@@ -48,15 +49,22 @@ async function signin() {
|
||||
}
|
||||
|
||||
async function signup() {
|
||||
if (!approval.value) {
|
||||
if (!acceptTerms.value || !acceptPrivacy.value) {
|
||||
return
|
||||
}
|
||||
const res = await authStore.signup(username.value, email.value)
|
||||
const res = await authStore.signup(
|
||||
username.value,
|
||||
email.value,
|
||||
acceptTerms.value,
|
||||
acceptPrivacy.value,
|
||||
)
|
||||
if (!res && authStore.error != null) {
|
||||
message.error(authStore.error)
|
||||
return
|
||||
}
|
||||
message.info('Пароль отправлен на почту')
|
||||
acceptTerms.value = false
|
||||
acceptPrivacy.value = false
|
||||
}
|
||||
|
||||
async function sendNewPassword() {
|
||||
@@ -101,16 +109,23 @@ async function sendNewPassword() {
|
||||
autocomplete: 'disabled',
|
||||
}" :options="options" placeholder="detective@mail.ru" clearable />
|
||||
<div class="form-label">
|
||||
<n-checkbox v-model:checked="approval">
|
||||
Я согласен с
|
||||
<a href="/user-agreement" target="_blank" class="docs-link">пользовательским соглашением</a><br />
|
||||
и
|
||||
<a href="/privacy-policy" target="_blank" class="docs-link">соглашением о персональных данных</a>
|
||||
<n-checkbox v-model:checked="acceptTerms">
|
||||
Я принимаю условия
|
||||
<a href="/user-agreement" target="_blank" class="docs-link">пользовательского соглашения</a>
|
||||
</n-checkbox>
|
||||
</div>
|
||||
<div class="form-label">
|
||||
<n-checkbox v-model:checked="acceptPrivacy">
|
||||
Я даю согласие на обработку
|
||||
<a href="/privacy-policy" target="_blank" class="docs-link">персональных данных</a>
|
||||
</n-checkbox>
|
||||
</div>
|
||||
<div class="form-button-wrapper">
|
||||
<div class="form-label">
|
||||
<n-button @click="signup" :disabled="username.length == 0 || password.length == 0 || !approval">
|
||||
<n-button
|
||||
@click="signup"
|
||||
:disabled="username.length == 0 || email.length == 0 || !acceptTerms || !acceptPrivacy"
|
||||
>
|
||||
<span v-if="!authStore.isLoading"> Регистрация </span>
|
||||
<span v-else> Подождите... </span>
|
||||
</n-button>
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
<script setup lang="ts">
|
||||
import { NAlert } from 'naive-ui'
|
||||
import { NAlert, NButton, NInput, NModal, useMessage } from 'naive-ui'
|
||||
import { ref } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
|
||||
import { getAuthClient } from '@/api/auth_client'
|
||||
import HeaderMenu from '@/components/HeaderMenu.vue'
|
||||
import { useAuthStore } from '@/stores/auth'
|
||||
|
||||
const authStore = useAuthStore()
|
||||
const router = useRouter()
|
||||
const message = useMessage()
|
||||
const client = getAuthClient()
|
||||
|
||||
const permissions = ref<string[]>([])
|
||||
@@ -208,6 +211,22 @@ function getDetectiveTip(): Tip {
|
||||
|
||||
const tip = getDetectiveTip()
|
||||
|
||||
// ===== Удаление аккаунта =====
|
||||
const showDeleteDialog = ref(false)
|
||||
const deletePassword = ref('')
|
||||
const deleteError = ref('')
|
||||
|
||||
async function confirmDeleteAccount() {
|
||||
deleteError.value = ''
|
||||
const res = await authStore.deleteAccount(deletePassword.value)
|
||||
if (!res) {
|
||||
deleteError.value = authStore.error || 'Не удалось удалить аккаунт'
|
||||
return
|
||||
}
|
||||
message.success('Аккаунт и персональные данные удалены')
|
||||
showDeleteDialog.value = false
|
||||
router.push('/login')
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -218,10 +237,93 @@ const tip = getDetectiveTip()
|
||||
</n-alert>
|
||||
|
||||
<p>Доброго времени суток, {{ authStore.username }}.</p>
|
||||
|
||||
<div class="danger-zone">
|
||||
<div class="danger-zone-text">
|
||||
<div class="danger-zone-title">Удаление аккаунта</div>
|
||||
<div class="danger-zone-hint">
|
||||
Удаление необратимо: аккаунт и все персональные данные будут стёрты (ст. 21 152-ФЗ).
|
||||
</div>
|
||||
</div>
|
||||
<n-button type="error" ghost @click="showDeleteDialog = true">Удалить аккаунт</n-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<n-modal
|
||||
v-model:show="showDeleteDialog"
|
||||
preset="card"
|
||||
title="Удаление аккаунта"
|
||||
class="delete-modal"
|
||||
>
|
||||
<p class="delete-modal-text">
|
||||
Это действие необратимо. Для подтверждения удаления введите пароль от учётной записи —
|
||||
он понадобится, чтобы убедиться, что аккаунт удаляет его владелец.
|
||||
</p>
|
||||
<n-input
|
||||
v-model:value="deletePassword"
|
||||
type="password"
|
||||
placeholder="Пароль для подтверждения"
|
||||
@keyup.enter="confirmDeleteAccount"
|
||||
/>
|
||||
<p v-if="deleteError" class="delete-modal-error">{{ deleteError }}</p>
|
||||
<div class="delete-modal-buttons">
|
||||
<n-button @click="showDeleteDialog = false">Отмена</n-button>
|
||||
<n-button
|
||||
type="error"
|
||||
:disabled="deletePassword.length == 0"
|
||||
:loading="authStore.isLoading"
|
||||
@click="confirmDeleteAccount"
|
||||
>
|
||||
Удалить навсегда
|
||||
</n-button>
|
||||
</div>
|
||||
</n-modal>
|
||||
|
||||
<HeaderMenu active="office" />
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
<style scoped>
|
||||
.danger-zone {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 16px;
|
||||
margin-top: 40px;
|
||||
padding: 20px;
|
||||
background-color: #222;
|
||||
border: 1px solid rgb(224 108 117 / 40%);
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.danger-zone-title {
|
||||
font-size: 17px;
|
||||
font-weight: 600;
|
||||
color: #e06c75;
|
||||
}
|
||||
|
||||
.danger-zone-hint {
|
||||
margin-top: 6px;
|
||||
font-size: 14px;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.delete-modal-text {
|
||||
margin: 0 0 16px;
|
||||
line-height: 1.5;
|
||||
color: #bbb;
|
||||
}
|
||||
|
||||
.delete-modal-error {
|
||||
margin: 12px 0 0;
|
||||
color: #e06c75;
|
||||
}
|
||||
|
||||
.delete-modal-buttons {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 12px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user