This commit is contained in:
@@ -40,12 +40,26 @@
|
||||
</q-list>
|
||||
</pn-scroll-list>
|
||||
</pn-page-card>
|
||||
|
||||
<pn-small-dialog
|
||||
v-model="showStopUsingDialog"
|
||||
icon="mdi-close-box-outline"
|
||||
color="negative"
|
||||
title="account__stop_using_dialog_title"
|
||||
message1="account__stop_using_dialog_message1"
|
||||
message2="account__stop_using_dialog_message2"
|
||||
mainBtnLabel="account__stop_using_dialog_btn_ok"
|
||||
@clickMainBtn="onConfirmStopUsing"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import { computed, ref, inject } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { useAuthStore } from 'stores/auth'
|
||||
import type { WebApp } from '@twa-dev/types'
|
||||
|
||||
const tg = inject('tg') as WebApp
|
||||
|
||||
const router = useRouter()
|
||||
const authStore = useAuthStore()
|
||||
@@ -68,16 +82,22 @@
|
||||
{ id: 5, name: 'account__company_data', icon: 'mdi-account-group-outline', description: 'account__company_data_description', pathName: 'your_company' },
|
||||
{ id: 6, name: 'account__settings', icon: 'mdi-cog-outline', description: 'account__settings_description', iconColor: 'info', pathName: 'settings' },
|
||||
{ id: 7, name: 'account__support', icon: 'mdi-lifebuoy', description: 'account__support_description', iconColor: 'info', pathName: 'support' },
|
||||
{ id: 9, name: 'account__terms_of_use', icon: 'mdi-book-open-variant-outline', description: '', iconColor: 'grey', pathName: 'terms' },
|
||||
{ id: 10, name: 'account__privacy', icon: 'mdi-lock-outline', description: '', iconColor: 'grey', pathName: 'privacy' }
|
||||
{ id: 8, name: 'account__terms_of_use', icon: 'mdi-book-open-variant-outline', description: '', iconColor: 'grey', pathName: 'terms' },
|
||||
{ id: 9, name: 'account__privacy', icon: 'mdi-lock-outline', description: '', iconColor: 'grey', pathName: 'privacy' },
|
||||
{ id: 10, name: 'account__stop_using', icon: 'mdi-close-box-outline', description: 'account__stop_using_description', iconColor: 'negative', pathName: 'stop_using' }
|
||||
]))
|
||||
|
||||
const displayItems = computed(() => (
|
||||
items.value.filter((item: ItemList) => !('display' in item) || item.display === true)
|
||||
))
|
||||
|
||||
const showStopUsingDialog = ref(false)
|
||||
async function goTo (path: string) {
|
||||
await router.push({ name: path })
|
||||
if (path !== 'stop_using') {
|
||||
await router.push({ name: path })
|
||||
} else {
|
||||
showStopUsingDialog.value = true
|
||||
}
|
||||
}
|
||||
|
||||
async function logout () {
|
||||
@@ -85,6 +105,10 @@
|
||||
await router.push({ name: 'login' })
|
||||
}
|
||||
|
||||
function onConfirmStopUsing () {
|
||||
tg.close()
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<pn-scroll-list>
|
||||
<div
|
||||
v-if="chat"
|
||||
class="flex column items-center q-pa-md q-pb-lg"
|
||||
class="flex column items-center q-pa-md"
|
||||
>
|
||||
<pn-auto-avatar
|
||||
:img="chat.logo"
|
||||
@@ -25,17 +25,23 @@
|
||||
size="100px"
|
||||
rounded
|
||||
/>
|
||||
<div
|
||||
class="flex row items-start justify-center q-pt-md text-bold"
|
||||
align="center"
|
||||
>
|
||||
{{ chat.name }}
|
||||
</div>
|
||||
<div
|
||||
v-if="chat.description"
|
||||
class="flex row items-start justify-center q-pt-md"
|
||||
class="flex row items-start justify-center text-caption"
|
||||
align="center"
|
||||
>
|
||||
{{ chat.description }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<q-list separator v-if="chatUsers.length!==0">
|
||||
<q-item-label header>
|
||||
{{ $t('chat_page__members') + ' (' + chatUsers.length +')' }}
|
||||
<q-item-label class="text-caption text-bold q-py-none" header>
|
||||
{{ $t('chat_card__members') + ' (' + chatUsers.length +')' }}
|
||||
</q-item-label>
|
||||
<q-item
|
||||
v-for="item in chatUsers"
|
||||
@@ -61,7 +67,7 @@
|
||||
</q-badge>
|
||||
{{item.section1}}
|
||||
</q-item-label>
|
||||
<q-item-label lines="1" caption v-if="item.section3">
|
||||
<q-item-label lines="2" caption v-if="item.section3">
|
||||
{{item.section3}}
|
||||
</q-item-label>
|
||||
<q-item-label caption lines="2">
|
||||
@@ -113,9 +119,9 @@
|
||||
|
||||
watch(() => chatsStore.isInit, initChat)
|
||||
|
||||
function onSubmit () {
|
||||
async function onSubmit () {
|
||||
if (chat.value && chat.value.invite_link) tg.openTelegramLink(chat.value.invite_link)
|
||||
if (chat && chat.value) chatsStore.getChatUsers(chat.value.id)
|
||||
if (chat.value) await chatsStore.getChatUsers(chat.value.id)
|
||||
}
|
||||
|
||||
const users = usersStore.getUsers
|
||||
@@ -123,8 +129,8 @@
|
||||
const { userSection } = useUserSection()
|
||||
|
||||
const chatUsers = computed(() => {
|
||||
if (!chat || !chat.value) return []
|
||||
const idSet = new Set(chat.value.chat_users)
|
||||
if (!chat.value) return []
|
||||
const idSet = new Set(chat.value.users)
|
||||
const arr = users.filter(el => idSet.has(el.id))
|
||||
return arr.map(el => ({
|
||||
...el,
|
||||
|
||||
@@ -5,18 +5,23 @@
|
||||
title="company_edit__title_card"
|
||||
btnText="company_edit__btn"
|
||||
@update="updateCompany"
|
||||
>
|
||||
<template #myCompany v-if="company.is_own">
|
||||
<div class="q-mb-md flex w100 justify-center">
|
||||
<div class="flex items-center text-amber-10">
|
||||
<q-icon name="star" class="q-pr-xs"/>
|
||||
{{ $t('company_edit__my_company') }}
|
||||
>
|
||||
<template #myCompany v-if="company.is_own">
|
||||
<div class="q-mb-md flex w100 justify-center">
|
||||
<div class="flex items-center text-amber-10">
|
||||
<q-icon name="star" class="q-pr-xs"/>
|
||||
{{ $t('company_edit__my_company') }}
|
||||
</div>
|
||||
<div class="flex column">
|
||||
<div class="text-caption" align="center" style="white-space: pre-wrap;">
|
||||
{{ $t('company_edit__my_company_hint') }}
|
||||
</div>
|
||||
<div class="text-caption text-primary cursor-pointer" align="center" @click="goMyCompany">
|
||||
{{ $t('company_edit__my_company_href') }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-caption" align="center" style="white-space: pre-wrap;">
|
||||
{{ $t('company_edit__my_company_hint') }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
</company-block>
|
||||
</template>
|
||||
|
||||
@@ -50,6 +55,10 @@
|
||||
if (isInit) initCompany()
|
||||
})
|
||||
|
||||
async function goMyCompany () {
|
||||
await router.push({ name: 'your_company' })
|
||||
}
|
||||
|
||||
async function updateCompany(companyData: CompanyParams) {
|
||||
if (companyId.value) {
|
||||
await companiesStore.update(companyId.value, companyData)
|
||||
|
||||
@@ -1,17 +1,27 @@
|
||||
<template>
|
||||
<pn-page-card>
|
||||
<template #title>
|
||||
{{$t('company__mask')}}
|
||||
<q-btn
|
||||
v-if="!checkIsDirty(mask, originalMask)"
|
||||
@click = "updateCompanies()"
|
||||
flat round
|
||||
icon="mdi-check"
|
||||
/>
|
||||
{{$t('mask__title')}}
|
||||
</template>
|
||||
|
||||
<template #footer>
|
||||
<q-btn
|
||||
rounded color="primary"
|
||||
class="w100 q-mt-md q-mb-xs fix-disabled-btn"
|
||||
@click="updateCompanies()"
|
||||
>
|
||||
{{ $t('mask__btn_ok') }}
|
||||
</q-btn>
|
||||
</template>
|
||||
|
||||
<pn-scroll-list>
|
||||
<template #card-body-header>
|
||||
<div style="min-height: var(--top-raduis);"/>
|
||||
<div class="q-ma-sm q-pa-sm bg-grey-11 flex no-wrap items-center" style="border-radius: var(--top-raduis);">
|
||||
<q-icon name="mdi-information q-pr-xs" size="sm" color="primary"/>
|
||||
<span class="text-caption">
|
||||
{{ $t('mask__info_block')}}
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
<q-table
|
||||
flat
|
||||
@@ -48,7 +58,6 @@
|
||||
</q-th>
|
||||
</q-tr>
|
||||
</template>
|
||||
|
||||
|
||||
<template #body="props">
|
||||
<q-tr :props="props">
|
||||
@@ -144,7 +153,6 @@
|
||||
<q-card-section class="q-pt-sm">
|
||||
<p>{{ $t('mask__help_message1')}}</p>
|
||||
<p>{{ $t('mask__help_message2')}}</p>
|
||||
<p>{{ $t('mask__help_message3')}}</p>
|
||||
</q-card-section>
|
||||
</q-card>
|
||||
</q-dialog>
|
||||
@@ -152,9 +160,8 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { ref, computed, watch } from 'vue'
|
||||
import { useCompaniesStore } from 'stores/companies'
|
||||
import type { CompanyMask } from 'types/Company'
|
||||
import { useRouter } from 'vue-router'
|
||||
|
||||
const showDialogHelp = ref<boolean>(false)
|
||||
@@ -164,6 +171,12 @@
|
||||
const companies = computed(() => companiesStore.companies)
|
||||
const companiesMask = computed(() => companiesStore.companiesMask)
|
||||
|
||||
const tableColumns = [
|
||||
{ name: 'checkbox', field: 'checkbox', label: 'checkbox', sortable: false, style: "width: 10%" },
|
||||
{ name: 'name', field: 'name', align: 'left' as const, label: 'name', sortable: false, style: "width: 45% !important; text-wrap: auto" },
|
||||
{ name: 'visible', field: 'visible', align: 'center' as const, label: 'visible', sortable: false, style: "width: 45% !important" }
|
||||
]
|
||||
|
||||
interface DisplayCompany {
|
||||
id: number
|
||||
name: string
|
||||
@@ -174,15 +187,36 @@
|
||||
hasFocus: boolean
|
||||
}
|
||||
|
||||
//
|
||||
const displayCompanies = ref<DisplayCompany[]>([])
|
||||
const originalMask = ref<CompanyMask[]>([])
|
||||
|
||||
const tableColumns = [
|
||||
{ name: 'checkbox', field: 'checkbox', label: 'checkbox', sortable: false, style: "width: 10%" },
|
||||
{ name: 'name', field: 'name', align: 'left' as const, label: 'name', sortable: false, style: "width: 45% !important; text-wrap: auto" },
|
||||
{ name: 'visible', field: 'visible', align: 'center' as const, label: 'visible', sortable: false, style: "width: 45% !important" }
|
||||
]
|
||||
watch(
|
||||
() => [companies.value, companiesMask.value],
|
||||
() => {
|
||||
updateDisplayCompanies()
|
||||
},
|
||||
{ immediate: true, deep: true }
|
||||
)
|
||||
|
||||
function updateDisplayCompanies() {
|
||||
if (companies.value.length === 0 || companiesMask.value.length === 0) return
|
||||
|
||||
displayCompanies.value = companies.value
|
||||
.filter(el => !el.is_own)
|
||||
.map(el => ({
|
||||
id: el.id,
|
||||
name: el.name,
|
||||
logo: el.logo ?? '',
|
||||
masked: companiesStore.checkCompanyMasked(el.id),
|
||||
company_list: getList(el.id),
|
||||
isMenuOpen: false,
|
||||
hasFocus: false
|
||||
}))
|
||||
}
|
||||
|
||||
function getList (companyId: number) {
|
||||
const company = companiesMask.value.find(el => Number(el.company_id) === companyId)
|
||||
return company?.company_list ?? []
|
||||
}
|
||||
|
||||
const mask = computed(() => displayCompanies.value
|
||||
.filter(el => el.masked)
|
||||
@@ -193,8 +227,8 @@
|
||||
.sort((a, b) => compareNumbers(a.company_id, b.company_id))
|
||||
)
|
||||
|
||||
function getCompanyById(id: number) {
|
||||
return companies.value.find(c => c.id === id);
|
||||
function getCompanyById (id: number) {
|
||||
return companies.value.find(c => c.id === id)
|
||||
}
|
||||
|
||||
function companiesSelect (id :number) {
|
||||
@@ -220,61 +254,6 @@
|
||||
return a < b ? -1 : 1
|
||||
}
|
||||
|
||||
function checkIsDirty (arr1: CompanyMask[], arr2: CompanyMask[]): boolean {
|
||||
// Проверка длины массивов
|
||||
if (arr1.length !== arr2.length) return false
|
||||
|
||||
// Функция для нормализации объекта в строковый ключ
|
||||
const getKey = (obj: { company_id: number; company_list: number[] }): string => {
|
||||
const sortedList = [...obj.company_list].sort(compareNumbers)
|
||||
return `${obj.company_id}|${sortedList.join(',')}`
|
||||
}
|
||||
|
||||
// Создаем Map для подсчета объектов в первом массиве
|
||||
const countMap = new Map<string, number>()
|
||||
for (const item of arr1) {
|
||||
const key = getKey(item)
|
||||
countMap.set(key, (countMap.get(key) || 0) + 1)
|
||||
}
|
||||
|
||||
// Проверяем объекты второго массива
|
||||
for (const item of arr2) {
|
||||
const key = getKey(item)
|
||||
const count = countMap.get(key) || 0
|
||||
|
||||
if (count === 0) return false
|
||||
|
||||
countMap.set(key, count - 1)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
|
||||
function getList (companyId: number) {
|
||||
const company = companiesMask.value.find(el => el.company_id === companyId)
|
||||
return company?.company_list ?? []
|
||||
}
|
||||
|
||||
displayCompanies.value = companies.value
|
||||
.filter(el => !el.is_own)
|
||||
.map(el => ({
|
||||
id: el.id,
|
||||
name: el.name,
|
||||
logo: el?.logo ?? '',
|
||||
masked: companiesStore.checkCompanyMasked(el.id),
|
||||
company_list: getList(el.id),
|
||||
isMenuOpen: false,
|
||||
hasFocus: false
|
||||
}))
|
||||
|
||||
console.log(companies.value)
|
||||
console.log(displayCompanies.value)
|
||||
|
||||
originalMask.value = [ ...(companiesMask.value.sort((a, b) => compareNumbers(a.company_id, b.company_id))) ]
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@@ -306,4 +285,3 @@
|
||||
color: 'brand';
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
@@ -74,8 +74,9 @@
|
||||
<q-btn
|
||||
@click="sendAuth()"
|
||||
color="primary"
|
||||
class="w100 q-my-md"
|
||||
class="w100 q-my-md fix-disabled-btn"
|
||||
:disabled="!isEmailValid || !isPasswordValid"
|
||||
:unelevated="!isEmailValid || !isPasswordValid"
|
||||
>
|
||||
{{$t('login__sign_in')}}
|
||||
</q-btn>
|
||||
|
||||
@@ -69,17 +69,29 @@
|
||||
{{item.description}}
|
||||
</q-item-label>
|
||||
</q-item-section>
|
||||
<q-item-section side class="text-caption ">
|
||||
<div class="flex items-center column">
|
||||
<div class="flex items-center">
|
||||
<q-icon name="mdi-chat-outline"/>
|
||||
<span>{{ item.chat_count }} </span>
|
||||
<q-item-section side top>
|
||||
<q-item-label lines="1" class="text-caption">
|
||||
<div class="flex items-center">
|
||||
<div class="flex items-center">
|
||||
<q-icon name="mdi-chat-outline"/>
|
||||
<span>{{ item.chat_count }} </span>
|
||||
</div>
|
||||
<div class="flex items-center q-ml-xs">
|
||||
<q-icon name="mdi-account-outline"/>
|
||||
<span>{{ item.user_count }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center">
|
||||
<q-icon name="mdi-account-outline"/>
|
||||
<span>{{ item.user_count }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</q-item-label>
|
||||
<q-item-label>
|
||||
<q-btn
|
||||
color="grey"
|
||||
size="sm"
|
||||
dense flat round
|
||||
icon="mdi-pencil"
|
||||
class="q-ml-sm"
|
||||
@click.stop="editProject(item.id)"
|
||||
/>
|
||||
</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
</q-slide-item>
|
||||
@@ -225,6 +237,10 @@
|
||||
await router.push({ name: 'chats', params: { id }})
|
||||
}
|
||||
|
||||
async function editProject (id: number) {
|
||||
await router.push({ name: 'project_info', params: { id }})
|
||||
}
|
||||
|
||||
async function goAccount () {
|
||||
await router.push({ name: 'account' })
|
||||
}
|
||||
|
||||
104
src/pages/account/AgreementsPage.vue
Normal file
104
src/pages/account/AgreementsPage.vue
Normal file
@@ -0,0 +1,104 @@
|
||||
<template>
|
||||
<pn-page-card>
|
||||
<template #title>
|
||||
{{$t('agreements__title')}}
|
||||
</template>
|
||||
<pn-scroll-list>
|
||||
<div class="flex column w100 q-pa-md q-gutter-y-md">
|
||||
<div>
|
||||
{{ $t('agreements__description') }}
|
||||
</div>
|
||||
<div class="flex items-center no-wrap">
|
||||
<q-checkbox v-model="agreement" val="1" class="q-pr-sm"/>
|
||||
<span>
|
||||
{{ $t('agreements__checkbox_agreement_terms') + ' ' }}
|
||||
<span
|
||||
@click="router.push({ name: 'terms' })"
|
||||
class="cursor-pointer"
|
||||
style="text-decoration: underline;"
|
||||
>
|
||||
{{ $t('agreements__checkbox_agreement_terms_doc') }}
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="flex items-center no-wrap">
|
||||
<q-checkbox v-model="agreement" val="2" class="q-pr-sm"/>
|
||||
<span>
|
||||
{{ $t('agreements__checkbox_agreement_consent') + ' ' }}
|
||||
<span
|
||||
@click="router.push({ name: 'consent' })"
|
||||
class="cursor-pointer"
|
||||
style="text-decoration: underline;"
|
||||
>
|
||||
{{ $t('agreements__checkbox_agreement_consent_doc') }}
|
||||
</span>
|
||||
{{ ' ' + $t('agreements__checkbox_agreement_privacy') + ' ' }}
|
||||
<span
|
||||
@click="router.push({ name: 'consent' })"
|
||||
class="cursor-pointer"
|
||||
style="text-decoration: underline;"
|
||||
>
|
||||
{{ $t('agreements__checkbox_agreement_privacy_doc') }}
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</pn-scroll-list>
|
||||
<template #footer>
|
||||
<div class="flex no-wrap justify-center w100 q-gutter-x-md q-pb-md">
|
||||
<q-btn
|
||||
flat
|
||||
color="negative"
|
||||
no-caps
|
||||
style="opacity: 0.6"
|
||||
@click="onDecline"
|
||||
class="col"
|
||||
>
|
||||
<div class="flex column items-center">
|
||||
{{ $t('agreements__btn_decline') }}
|
||||
<span class="text-caption">
|
||||
{{ $t('agreements__btn_decline_description') }}
|
||||
</span>
|
||||
</div>
|
||||
</q-btn>
|
||||
<q-btn
|
||||
color="primary"
|
||||
no-caps
|
||||
:disable="agreement.length !== 2"
|
||||
@click="onSubmit"
|
||||
class="fix-disabled-btn col"
|
||||
>
|
||||
{{$t('agreements__btn_agree')}}
|
||||
</q-btn>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
</pn-page-card>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, inject } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { useAuthStore } from 'stores/auth'
|
||||
import type { WebApp } from '@twa-dev/types'
|
||||
const tg = inject('tg') as WebApp
|
||||
|
||||
function onDecline () {
|
||||
tg.close()
|
||||
}
|
||||
|
||||
|
||||
const authStore = useAuthStore()
|
||||
const router = useRouter()
|
||||
|
||||
async function onSubmit () {
|
||||
await authStore.termsAccepted()
|
||||
await router.push({ name: 'projects' })
|
||||
}
|
||||
|
||||
const agreement = ref([])
|
||||
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
||||
10
src/pages/account/ConsentPage.vue
Normal file
10
src/pages/account/ConsentPage.vue
Normal file
@@ -0,0 +1,10 @@
|
||||
<template>
|
||||
<doc-block type="consent"/>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import docBlock from 'components/docBlock.vue'
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
||||
@@ -52,12 +52,17 @@
|
||||
</div>
|
||||
|
||||
<q-btn
|
||||
@click="editProject"
|
||||
flat round
|
||||
color="white"
|
||||
icon="edit"
|
||||
size="md"
|
||||
/>
|
||||
@click="toProjects"
|
||||
flat
|
||||
no-caps
|
||||
dense
|
||||
class="q-ml-xl"
|
||||
>
|
||||
<span class="flex items-center no-wrap text-caption">
|
||||
{{ $t('header__to_projects') }}
|
||||
<q-icon name="mdi-chevron-right"/>
|
||||
</span>
|
||||
</q-btn>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -93,9 +98,8 @@
|
||||
expandProjectInfo.value = !expandProjectInfo.value
|
||||
}
|
||||
|
||||
async function editProject () {
|
||||
if (currentProjectId.value)
|
||||
await router.push({ name: 'project_info', params: { id: currentProjectId.value } })
|
||||
async function toProjects () {
|
||||
await router.push({ name: 'projects' })
|
||||
}
|
||||
|
||||
interface sizeParams {
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
<q-item-label lines="1" class="text-bold" v-if="item.section1">
|
||||
{{item.section1}}
|
||||
</q-item-label>
|
||||
<q-item-label lines="1" caption v-if="item.section3">
|
||||
<q-item-label lines="2" caption v-if="item.section3">
|
||||
{{item.section3}}
|
||||
</q-item-label>
|
||||
<q-item-label caption lines="2">
|
||||
@@ -172,7 +172,7 @@
|
||||
<q-item-label lines="1" class="text-bold" v-if="item.section1">
|
||||
{{item.section1}}
|
||||
</q-item-label>
|
||||
<q-item-label lines="1" caption v-if="item.section3">
|
||||
<q-item-label lines="2" caption v-if="item.section3">
|
||||
{{item.section3}}
|
||||
</q-item-label>
|
||||
<q-item-label caption lines="2">
|
||||
|
||||
Reference in New Issue
Block a user