feat: 优化统一规则逻辑
This commit is contained in:
@@ -1,5 +1,10 @@
|
||||
<script setup>
|
||||
<script setup>
|
||||
import { computed, reactive, ref, watch } from 'vue'
|
||||
import {
|
||||
DEFAULT_TRANSACTION_CATEGORY,
|
||||
TRANSACTION_CATEGORIES,
|
||||
getCategoryLabel,
|
||||
} from '../config/transactionCategories.js'
|
||||
import { useTransactionStore } from '../stores/transactions'
|
||||
import { useUiStore } from '../stores/ui'
|
||||
import EchoInput from '../components/EchoInput.vue'
|
||||
@@ -14,7 +19,7 @@ const dragStartY = ref(0)
|
||||
const dragging = ref(false)
|
||||
const dragOffset = ref(0)
|
||||
|
||||
const categories = ['Food', 'Transport', 'Health', 'Groceries', 'Entertainment', 'Income', 'Uncategorized']
|
||||
const categories = TRANSACTION_CATEGORIES
|
||||
|
||||
const toDatetimeLocal = (value) => {
|
||||
const date = value ? new Date(value) : new Date()
|
||||
@@ -26,7 +31,7 @@ const form = reactive({
|
||||
type: 'expense',
|
||||
amount: '',
|
||||
merchant: '',
|
||||
category: 'Food',
|
||||
category: DEFAULT_TRANSACTION_CATEGORY,
|
||||
note: '',
|
||||
date: toDatetimeLocal(),
|
||||
})
|
||||
@@ -35,7 +40,7 @@ const resetForm = () => {
|
||||
form.type = 'expense'
|
||||
form.amount = ''
|
||||
form.merchant = ''
|
||||
form.category = 'Food'
|
||||
form.category = DEFAULT_TRANSACTION_CATEGORY
|
||||
form.note = ''
|
||||
form.date = toDatetimeLocal()
|
||||
}
|
||||
@@ -146,8 +151,6 @@ const sheetStyle = computed(() => {
|
||||
transition: dragging.value ? 'none' : 'transform 0.2s ease-out',
|
||||
}
|
||||
})
|
||||
|
||||
transactionStore.ensureInitialized()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -169,10 +172,10 @@ transactionStore.ensureInitialized()
|
||||
<div class="flex items-center justify-between mb-6">
|
||||
<div>
|
||||
<p class="text-xs text-stone-400 font-bold uppercase tracking-widest">
|
||||
{{ editingId ? '编辑记录' : '记录新消费' }}
|
||||
{{ editingId ? '编辑记录' : '记录新流水' }}
|
||||
</p>
|
||||
<h3 class="text-xl font-extrabold text-stone-800">
|
||||
{{ editingId ? '更新本地账本' : '快速入账' }}
|
||||
{{ editingId ? '更新本地账本' : '快速记一笔' }}
|
||||
</h3>
|
||||
</div>
|
||||
<button class="text-stone-400 text-sm font-bold" @click="closePanel">关闭</button>
|
||||
@@ -214,7 +217,7 @@ transactionStore.ensureInitialized()
|
||||
input-class="text-2xl font-bold"
|
||||
>
|
||||
<template #prefix>
|
||||
<span class="text-stone-400 text-sm">¥</span>
|
||||
<span class="text-stone-400 text-sm">楼</span>
|
||||
</template>
|
||||
</EchoInput>
|
||||
|
||||
@@ -229,16 +232,16 @@ transactionStore.ensureInitialized()
|
||||
<div class="mt-2 flex gap-2 overflow-x-auto hide-scrollbar pb-1">
|
||||
<button
|
||||
v-for="category in categories"
|
||||
:key="category"
|
||||
:key="category.value"
|
||||
class="px-4 py-2 rounded-2xl text-xs font-bold border transition"
|
||||
:class="
|
||||
form.category === category
|
||||
form.category === category.value
|
||||
? 'bg-gradient-warm text-white border-transparent'
|
||||
: 'bg-stone-50 text-stone-500 border-stone-100'
|
||||
"
|
||||
@click="form.category = category"
|
||||
@click="form.category = category.value"
|
||||
>
|
||||
{{ category }}
|
||||
{{ getCategoryLabel(category.value) }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -258,7 +261,7 @@ transactionStore.ensureInitialized()
|
||||
v-model="form.note"
|
||||
rows="2"
|
||||
class="mt-2 w-full rounded-2xl border border-stone-200 px-4 py-3 text-sm focus:outline-none focus:border-stone-400 resize-none"
|
||||
placeholder="可记录口味、心情或健康状态"
|
||||
placeholder="可记录口味、心情或其他背景信息"
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
@@ -272,7 +275,7 @@ transactionStore.ensureInitialized()
|
||||
:disabled="saving"
|
||||
@click="submitForm"
|
||||
>
|
||||
{{ saving ? '保存中...' : editingId ? '保存修改' : '立即入账' }}
|
||||
{{ saving ? '保存中...' : editingId ? '保存修改' : '立即记账' }}
|
||||
</button>
|
||||
|
||||
<button
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<script setup>
|
||||
<script setup>
|
||||
import { computed, onMounted, onBeforeUnmount } from 'vue'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { App } from '@capacitor/app'
|
||||
@@ -7,6 +7,8 @@ import { useTransactionStore } from '../stores/transactions'
|
||||
import { useTransactionEntry } from '../composables/useTransactionEntry'
|
||||
import { useSettingsStore } from '../stores/settings'
|
||||
import { useUiStore } from '../stores/ui'
|
||||
import { getCategoryLabel, getCategoryMeta } from '../config/transactionCategories.js'
|
||||
import { bootstrapApp } from '../services/appBootstrap.js'
|
||||
|
||||
const router = useRouter()
|
||||
const transactionStore = useTransactionStore()
|
||||
@@ -20,10 +22,10 @@ const { notifications, confirmNotification, dismissNotification, processingId, s
|
||||
let appStateListener = null
|
||||
|
||||
onMounted(async () => {
|
||||
await transactionStore.ensureInitialized()
|
||||
await bootstrapApp()
|
||||
await syncNotifications()
|
||||
try {
|
||||
// App 从后台回到前台时,自动刷新本地账本和通知队列
|
||||
// App 浠庡悗鍙板洖鍒板墠鍙版椂锛岃嚜鍔ㄥ埛鏂版湰鍦拌处鏈拰閫氱煡闃熷垪
|
||||
appStateListener = await App.addListener('appStateChange', async ({ isActive }) => {
|
||||
if (isActive) {
|
||||
await transactionStore.hydrateTransactions()
|
||||
@@ -31,8 +33,7 @@ onMounted(async () => {
|
||||
}
|
||||
})
|
||||
} catch (error) {
|
||||
// Web 环境或插件不可用时忽略
|
||||
console.warn('[notifications] appStateChange listener failed', error)
|
||||
// Web 鐜鎴栨彃浠朵笉鍙敤鏃跺拷鐣? console.warn('[notifications] appStateChange listener failed', error)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -59,7 +60,7 @@ const periodStart = computed(() => {
|
||||
const cycle = budgetResetCycle.value
|
||||
const now = new Date()
|
||||
if (cycle === 'weekly') {
|
||||
const day = now.getDay() || 7 // 周一=1,周日=7
|
||||
const day = now.getDay() || 7 // 鍛ㄤ竴=1锛屽懆鏃?7
|
||||
const diff = day - 1
|
||||
return new Date(now.getFullYear(), now.getMonth(), now.getDate() - diff)
|
||||
}
|
||||
@@ -76,7 +77,7 @@ const periodStart = computed(() => {
|
||||
if (!settingsStore.budgetCustomStartDate) return null
|
||||
return new Date(settingsStore.budgetCustomStartDate)
|
||||
}
|
||||
// none:不限制周期
|
||||
// none锛氫笉闄愬埗鍛ㄦ湡
|
||||
return null
|
||||
})
|
||||
|
||||
@@ -108,9 +109,9 @@ const remainingBudget = computed(() =>
|
||||
const todayIncomeValue = computed(() => todaysIncome.value || 0)
|
||||
const todayExpenseValue = computed(() => Math.abs(todaysExpense.value || 0))
|
||||
|
||||
const formatCurrency = (value) => `¥ ${Math.abs(value || 0).toFixed(2)}`
|
||||
const formatCurrency = (value) => `楼 ${Math.abs(value || 0).toFixed(2)}`
|
||||
const formatAmount = (value) =>
|
||||
`${value >= 0 ? '+' : '-'} ¥${Math.abs(value || 0).toFixed(2)}`
|
||||
`${value >= 0 ? '+' : '-'} 楼${Math.abs(value || 0).toFixed(2)}`
|
||||
const formatTime = (value) =>
|
||||
new Intl.DateTimeFormat('zh-CN', { hour: '2-digit', minute: '2-digit' }).format(
|
||||
new Date(value),
|
||||
@@ -118,18 +119,6 @@ const formatTime = (value) =>
|
||||
|
||||
const recentTransactions = computed(() => latestTransactions.value.slice(0, 4))
|
||||
|
||||
const categoryMeta = {
|
||||
Food: { icon: 'ph-bowl-food', bg: 'bg-orange-100', color: 'text-orange-600' },
|
||||
Transport: { icon: 'ph-taxi', bg: 'bg-blue-100', color: 'text-blue-600' },
|
||||
Health: { icon: 'ph-heartbeat', bg: 'bg-rose-100', color: 'text-rose-600' },
|
||||
Groceries: { icon: 'ph-basket', bg: 'bg-amber-100', color: 'text-amber-600' },
|
||||
Income: { icon: 'ph-wallet', bg: 'bg-emerald-100', color: 'text-emerald-600' },
|
||||
Uncategorized: { icon: 'ph-note-pencil', bg: 'bg-stone-100', color: 'text-stone-500' },
|
||||
default: { icon: 'ph-note-pencil', bg: 'bg-stone-100', color: 'text-stone-500' },
|
||||
}
|
||||
|
||||
const getCategoryMeta = (category) => categoryMeta[category] || categoryMeta.default
|
||||
|
||||
const goSettings = () => {
|
||||
router.push({ name: 'settings' })
|
||||
}
|
||||
@@ -155,7 +144,7 @@ const openTransactionDetail = (tx) => {
|
||||
<p class="text-xs text-stone-400 font-bold tracking-wider">
|
||||
{{ currentDayLabel }}
|
||||
</p>
|
||||
<h1 class="text-2xl font-extrabold text-stone-800">今日概览</h1>
|
||||
<h1 class="text-2xl font-extrabold text-stone-800">浠婃棩姒傝</h1>
|
||||
</div>
|
||||
<div
|
||||
class="w-10 h-10 rounded-full bg-orange-50 p-0.5 cursor-pointer border border-orange-100"
|
||||
@@ -181,7 +170,7 @@ const openTransactionDetail = (tx) => {
|
||||
<div class="relative z-10 flex flex-col h-full justify-between">
|
||||
<div>
|
||||
<div class="flex items-center justify-between">
|
||||
<p class="text-white/80 text-xs font-bold tracking-widest uppercase">本月结余</p>
|
||||
<p class="text-white/80 text-xs font-bold tracking-widest uppercase">鏈湀缁撲綑</p>
|
||||
<button
|
||||
class="w-8 h-8 rounded-full bg-white/20 flex items-center justify-center backdrop-blur-md hover:bg-white/30 transition"
|
||||
>
|
||||
@@ -196,13 +185,13 @@ const openTransactionDetail = (tx) => {
|
||||
<div class="flex gap-8">
|
||||
<div>
|
||||
<p class="text-white/70 text-xs mb-0.5 flex items-center gap-1">
|
||||
<i class="ph-bold ph-arrow-down-left" /> 收入
|
||||
<i class="ph-bold ph-arrow-down-left" /> 鏀跺叆
|
||||
</p>
|
||||
<p class="font-bold text-lg">{{ formatCurrency(totalIncome) }}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-white/70 text-xs mb-0.5 flex items-center gap-1">
|
||||
<i class="ph-bold ph-arrow-up-right" /> 支出
|
||||
<i class="ph-bold ph-arrow-up-right" /> 鏀嚭
|
||||
</p>
|
||||
<p class="font-bold text-lg">{{ formatCurrency(Math.abs(totalExpense)) }}</p>
|
||||
</div>
|
||||
@@ -212,7 +201,7 @@ const openTransactionDetail = (tx) => {
|
||||
|
||||
<div class="bg-white rounded-2xl p-4 shadow-sm border border-stone-100">
|
||||
<div class="flex justify-between items-center mb-2">
|
||||
<span class="text-xs font-bold text-stone-500">本期预算</span>
|
||||
<span class="text-xs font-bold text-stone-500">鏈湡棰勭畻</span>
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="text-xs font-bold text-stone-800">
|
||||
{{ Math.round(budgetUsage * 100) }}%
|
||||
@@ -221,8 +210,7 @@ const openTransactionDetail = (tx) => {
|
||||
class="text-[10px] text-stone-400 underline-offset-2 hover:text-stone-600"
|
||||
@click="goSettings"
|
||||
>
|
||||
去设置
|
||||
</button>
|
||||
鍘昏缃? </button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-full bg-stone-100 rounded-full h-2.5 overflow-hidden">
|
||||
@@ -232,8 +220,8 @@ const openTransactionDetail = (tx) => {
|
||||
/>
|
||||
</div>
|
||||
<div class="flex justify-between mt-2 text-[10px] text-stone-400">
|
||||
<span>已用 {{ formatCurrency(Math.abs(periodExpense)) }}</span>
|
||||
<span>剩余 {{ formatCurrency(remainingBudget) }}</span>
|
||||
<span>宸茬敤 {{ formatCurrency(Math.abs(periodExpense)) }}</span>
|
||||
<span>鍓╀綑 {{ formatCurrency(remainingBudget) }}</span>
|
||||
</div>
|
||||
<div class="flex justify-between mt-1 text-[10px] text-stone-400">
|
||||
<span>
|
||||
@@ -252,18 +240,17 @@ const openTransactionDetail = (tx) => {
|
||||
class="text-[10px] text-stone-400 underline-offset-2 hover:text-stone-600"
|
||||
@click="goSettings"
|
||||
>
|
||||
去设置
|
||||
</button>
|
||||
鍘昏缃? </button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-2 gap-3">
|
||||
<div class="bg-white rounded-2xl p-4 border border-stone-100 shadow-sm">
|
||||
<p class="text-xs text-stone-400 mb-2">今日收入</p>
|
||||
<p class="text-xs text-stone-400 mb-2">浠婃棩鏀跺叆</p>
|
||||
<p class="text-xl font-bold text-emerald-600">{{ formatCurrency(todayIncomeValue) }}</p>
|
||||
</div>
|
||||
<div class="bg-white rounded-2xl p-4 border border-stone-100 shadow-sm">
|
||||
<p class="text-xs text-stone-400 mb-2">今日支出</p>
|
||||
<p class="text-xs text-stone-400 mb-2">浠婃棩鏀嚭</p>
|
||||
<p class="text-xl font-bold text-rose-600">- {{ formatCurrency(todayExpenseValue) }}</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -275,7 +262,7 @@ const openTransactionDetail = (tx) => {
|
||||
class="text-xs font-bold text-gradient-warm"
|
||||
@click="goList"
|
||||
>
|
||||
查看全部
|
||||
鏌ョ湅鍏ㄩ儴
|
||||
</button>
|
||||
</div>
|
||||
<div v-if="recentTransactions.length" class="space-y-4">
|
||||
@@ -298,7 +285,7 @@ const openTransactionDetail = (tx) => {
|
||||
<div>
|
||||
<p class="font-bold text-stone-800 text-sm">{{ tx.merchant }}</p>
|
||||
<p class="text-[11px] text-stone-400">
|
||||
{{ formatTime(tx.date) }} · {{ tx.category }}
|
||||
{{ formatTime(tx.date) }} 路 {{ getCategoryLabel(tx.category) }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -313,15 +300,15 @@ const openTransactionDetail = (tx) => {
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="text-sm text-stone-400 text-center py-4">
|
||||
还没有记录,快去添加第一笔消费吧~
|
||||
杩樻病鏈夎褰曪紝蹇幓娣诲姞绗竴绗旀秷璐瑰惂锝?
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="space-y-3">
|
||||
<div class="flex justify-between items-end">
|
||||
<h3 class="font-bold text-lg text-stone-700">待确认通知</h3>
|
||||
<h3 class="font-bold text-lg text-stone-700">寰呯‘璁ら€氱煡</h3>
|
||||
<span class="text-[10px] bg-orange-100 text-orange-600 px-2 py-1 rounded-full font-bold">
|
||||
自动捕获
|
||||
鑷姩鎹曡幏
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -341,7 +328,7 @@ const openTransactionDetail = (tx) => {
|
||||
</div>
|
||||
<div class="flex-1">
|
||||
<h4 class="font-bold text-stone-800 text-sm">
|
||||
{{ notif.channel }} · {{ formatTime(notif.createdAt) }}
|
||||
{{ notif.channel }} 路 {{ formatTime(notif.createdAt) }}
|
||||
</h4>
|
||||
<p class="text-xs text-stone-400 leading-relaxed">
|
||||
{{ notif.text }}
|
||||
@@ -353,19 +340,19 @@ const openTransactionDetail = (tx) => {
|
||||
:disabled="processingId === notif.id"
|
||||
@click="confirmNotification(notif.id)"
|
||||
>
|
||||
入账
|
||||
鍏ヨ处
|
||||
</button>
|
||||
<button
|
||||
class="text-[11px] text-stone-400"
|
||||
@click="dismissNotification(notif.id)"
|
||||
>
|
||||
忽略
|
||||
蹇界暐
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="text-sm text-stone-400 text-center py-4 border border-dashed border-stone-100 rounded-2xl">
|
||||
暂无新通知,享受慢生活 ☕️
|
||||
鏆傛棤鏂伴€氱煡锛屼韩鍙楁參鐢熸椿 鈽曪笍
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -383,8 +370,8 @@ const openTransactionDetail = (tx) => {
|
||||
<i class="ph-fill ph-robot text-xl" />
|
||||
</div>
|
||||
<div class="z-10">
|
||||
<h4 class="font-bold text-stone-700">AI 顾问</h4>
|
||||
<p class="text-xs text-stone-400 mt-1">分析我的消费习惯</p>
|
||||
<h4 class="font-bold text-stone-700">AI 椤鹃棶</h4>
|
||||
<p class="text-xs text-stone-400 mt-1">鍒嗘瀽鎴戠殑娑堣垂涔犳儻</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -400,10 +387,13 @@ const openTransactionDetail = (tx) => {
|
||||
<i class="ph-fill ph-carrot text-xl" />
|
||||
</div>
|
||||
<div class="z-10">
|
||||
<h4 class="font-bold text-stone-700">热量管理</h4>
|
||||
<p class="text-xs text-stone-400 mt-1">今日剩余 750 kcal</p>
|
||||
<h4 class="font-bold text-stone-700">鐑噺绠$悊</h4>
|
||||
<p class="text-xs text-stone-400 mt-1">浠婃棩鍓╀綑 750 kcal</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<script setup>
|
||||
<script setup>
|
||||
import { computed, ref } from 'vue'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { CATEGORY_CHIPS, getCategoryLabel } from '../config/transactionCategories.js'
|
||||
import { useTransactionStore } from '../stores/transactions'
|
||||
import { useUiStore } from '../stores/ui'
|
||||
|
||||
@@ -8,17 +9,7 @@ const transactionStore = useTransactionStore()
|
||||
const uiStore = useUiStore()
|
||||
const { groupedTransactions, filters, loading } = storeToRefs(transactionStore)
|
||||
const deletingId = ref('')
|
||||
|
||||
const categoryChips = [
|
||||
{ label: '全部', value: 'all', icon: 'ph-asterisk' },
|
||||
{ label: '餐饮', value: 'Food', icon: 'ph-bowl-food' },
|
||||
{ label: '通勤', value: 'Transport', icon: 'ph-taxi' },
|
||||
{ label: '健康', value: 'Health', icon: 'ph-heartbeat' },
|
||||
{ label: '买菜', value: 'Groceries', icon: 'ph-basket' },
|
||||
{ label: '娱乐', value: 'Entertainment', icon: 'ph-game-controller' },
|
||||
{ label: '收入', value: 'Income', icon: 'ph-wallet' },
|
||||
{ label: '其他', value: 'Uncategorized', icon: 'ph-dots-three-outline' },
|
||||
]
|
||||
const categoryChips = CATEGORY_CHIPS
|
||||
|
||||
const setCategory = (value) => {
|
||||
filters.value.category = value
|
||||
@@ -29,7 +20,7 @@ const toggleTodayOnly = () => {
|
||||
}
|
||||
|
||||
const formatAmount = (value) =>
|
||||
`${value >= 0 ? '+' : '-'} ¥${Math.abs(value || 0).toFixed(2)}`
|
||||
`${value >= 0 ? '+' : '-'} 楼${Math.abs(value || 0).toFixed(2)}`
|
||||
|
||||
const formatTime = (value) =>
|
||||
new Intl.DateTimeFormat('zh-CN', { hour: '2-digit', minute: '2-digit' }).format(
|
||||
@@ -53,8 +44,6 @@ const handleDelete = async (item) => {
|
||||
}
|
||||
|
||||
const hasData = computed(() => groupedTransactions.value.length > 0)
|
||||
|
||||
transactionStore.ensureInitialized()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -114,7 +103,7 @@ transactionStore.ensureInitialized()
|
||||
<div>
|
||||
<h4 class="text-sm font-bold text-stone-500">{{ group.label }}</h4>
|
||||
<p class="text-[10px] text-stone-400">
|
||||
支出 ¥{{ group.totalExpense.toFixed(2) }}
|
||||
支出 楼{{ group.totalExpense.toFixed(2) }}
|
||||
</p>
|
||||
</div>
|
||||
<span class="text-xs text-stone-300 font-bold">{{ group.dayKey }}</span>
|
||||
@@ -145,10 +134,10 @@ transactionStore.ensureInitialized()
|
||||
<p class="text-xs text-stone-400 mt-0.5">
|
||||
{{ formatTime(item.date) }}
|
||||
<span v-if="item.category" class="text-stone-300">
|
||||
· {{ item.category }}
|
||||
路 {{ getCategoryLabel(item.category) }}
|
||||
</span>
|
||||
<span v-if="item.note" class="text-stone-300">
|
||||
· {{ item.note }}
|
||||
路 {{ item.note }}
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -3,6 +3,7 @@ import { computed, onMounted, ref } from 'vue'
|
||||
import NotificationBridge, { isNativeNotificationBridgeAvailable } from '../lib/notificationBridge'
|
||||
import { useSettingsStore } from '../stores/settings'
|
||||
import EchoInput from '../components/EchoInput.vue'
|
||||
import { BUDGET_CATEGORY_OPTIONS } from '../config/transactionCategories.js'
|
||||
|
||||
// 从 Vite 注入的版本号(来源于 package.json),用于在设置页展示
|
||||
// eslint-disable-next-line no-undef
|
||||
@@ -458,3 +459,4 @@ onMounted(() => {
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user