Files
jk8_admin/web/src/views/backend/dashboard.vue
2026-06-05 14:47:22 +08:00

933 lines
32 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<div class="default-main bookkeeping-dashboard">
<section class="dashboard-grid">
<div class="dashboard-panel bank-panel">
<div class="panel-title">Available Bank Balance</div>
<div class="table-scroll">
<table class="bank-table">
<thead>
<tr>
<th>Bank Name</th>
<th>Current Balance</th>
<th>Transaction Breakdown</th>
<th>Safe Alert</th>
</tr>
</thead>
<tbody>
<tr v-for="bank in visibleBanks" :key="bank.id" :class="bank.rowClass" :style="{ backgroundColor: bank.labelColor }">
<td>
<strong>{{ bank.name }}</strong>
<small>{{ bank.account }}</small>
</td>
<td class="balance">AUD {{ money(bank.balance) }}</td>
<td>
<div class="bank-operate">
<el-button size="small" :icon="Coin" circle />
<el-button size="small" :icon="Switch" @click="openTransfer(bank)">Transfer</el-button>
<div class="breakdown">
<span><i class="dot income"></i> ({{ bank.depositCount }}) {{ money(bank.deposit) }}</span>
<span><i class="dot outcome"></i> ({{ bank.withdrawCount }}) {{ money(bank.withdraw) }}</span>
</div>
</div>
</td>
<td>
<el-tag size="small" effect="plain" :type="bank.alert ? 'danger' : 'info'">{{ bank.alert || 'No Alert' }}</el-tag>
</td>
</tr>
<tr v-if="!visibleBanks.length">
<td class="empty-banks" colspan="4">No bank data</td>
</tr>
</tbody>
</table>
</div>
<button v-if="banks.length > 4" class="more-info" type="button" @click="showAllBanks = !showAllBanks">
{{ showAllBanks ? 'show less' : 'more info' }}
<Icon :name="showAllBanks ? 'fa fa-caret-up' : 'fa fa-caret-down'" />
</button>
</div>
<aside class="summary-side">
<div class="dashboard-panel summary-panel">
<div class="panel-title">Customer Summary</div>
<dl>
<template v-for="item in summary" :key="item.label">
<dt>{{ item.label }}:</dt>
<dd>{{ item.value }}</dd>
</template>
</dl>
</div>
<el-button class="create-button" type="success" @click="openCreate">CREATE NEW TRANSACTION</el-button>
</aside>
</section>
<el-alert class="webhook-alert" type="warning" :closable="true" show-icon>
<template #title>
<strong>The Transaction Mode you currently set has the Webhook (JDK) feature enabled</strong>
</template>
<p>
Hint: If a transaction approved in the JK backend is not automatically recorded here within 10 minutes, click "Create New Transaction"
to record it manually.
</p>
<p>提示如果 JK 后台 Approved Transaction 在这里 10 分钟内没有自动记录请点击 "Create New Transaction" 手动记录</p>
</el-alert>
<section class="transaction-section">
<div class="filter-row">
<div class="date-filter">
<label>Start Date:</label>
<el-date-picker v-model="filters.startDate" type="date" value-format="YYYY-MM-DD" />
<label>End Date:</label>
<el-date-picker v-model="filters.endDate" type="date" value-format="YYYY-MM-DD" />
<el-button @click="search">Search</el-button>
<el-button @click="setToday">Today</el-button>
</div>
<div class="totals">
<span>Date of data: {{ dateRangeText }}</span>
<b
>Total Deposit / IN: <em>AUD {{ money(totalDeposit) }}</em></b
>
<b
>Total Withdraw / OUT: <em>AUD {{ money(totalWithdraw) }}</em></b
>
</div>
</div>
<el-table :data="transactions" border size="small" class="transaction-table" :row-class-name="transactionRowClass">
<el-table-column prop="createdBy" label="Created by" width="110" />
<el-table-column prop="createdTime" label="Created Time" width="170" />
<el-table-column prop="category" label="Category" width="100" />
<el-table-column prop="username" label="Username" width="110" />
<el-table-column prop="remark" label="Remark" min-width="205" />
<el-table-column prop="bank" label="Bank" min-width="165" />
<el-table-column prop="type" label="Type" width="95" />
<el-table-column label="Amount (AUD)" width="130" align="right">
<template #default="{ row }">
<strong :class="row.flow === 'in' ? 'amount-in' : 'amount-out'">{{ money(row.amount) }}</strong>
</template>
</el-table-column>
<el-table-column prop="label" label="Label" width="100" />
<el-table-column label="Game Ticket" min-width="140">
<template #default="{ row }">
<div v-for="(ticket, index) in row.ticket" :key="`${ticket}-${index}`">{{ ticket }}</div>
</template>
</el-table-column>
<el-table-column label="Action" fixed="right" width="180">
<template #default="{ row }">
<el-button link type="primary" size="small" @click="openHistory(row)">history</el-button>
<el-button link type="primary" size="small" @click="openEdit(row)">edit</el-button>
<el-button link type="danger" size="small" @click="removeTransaction(row)">delete</el-button>
</template>
</el-table-column>
</el-table>
<div class="pagination">
<el-pagination
v-model:current-page="transactionPage.currentPage"
background
layout="prev, pager, next"
:total="transactionPage.count"
:page-size="transactionPage.pageSize"
@current-change="onTransactionPageChange"
/>
</div>
</section>
<el-dialog v-model="transactionDialog.visible" :title="transactionDialog.title" width="680px">
<el-form :model="transactionForm" label-width="145px">
<el-form-item label="Date & Time">
<el-date-picker v-model="transactionForm.time" type="datetime" value-format="YYYY-MM-DD HH:mm:ss" />
<el-radio-group v-model="transactionForm.timeMode" class="inline-mode">
<el-radio value="Auto">Auto</el-radio>
<el-radio value="Manual">Manual</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="Category">
<el-select v-model="transactionForm.category">
<el-option label="Customer" :value="1" />
<el-option label="Other Adjust" :value="2" />
</el-select>
</el-form-item>
<el-form-item label="Type">
<el-radio-group v-model="transactionForm.type">
<el-radio-button :value="1">Deposit</el-radio-button>
<el-radio-button :value="2">Withdraw</el-radio-button>
<el-radio-button :value="3">IN</el-radio-button>
<el-radio-button :value="4">OUT</el-radio-button>
</el-radio-group>
</el-form-item>
<el-form-item label="Username">
<el-input v-model="transactionForm.username" :disabled="transactionDialog.mode === 'edit'" placeholder="e.g. PLAYER001" />
</el-form-item>
<el-form-item label="Remark">
<el-input v-model="transactionForm.remark" placeholder="(optional)" />
</el-form-item>
<el-form-item label="Amount (AUD)">
<el-input-number v-model="transactionForm.amount" :min="0" :precision="2" />
</el-form-item>
<el-form-item label="Bank">
<el-select v-model="transactionForm.bank" placeholder="- (optional) -">
<el-option v-for="bank in banks" :key="bank.id" :label="bank.name" :value="bank.id" />
</el-select>
</el-form-item>
<el-form-item label="Label">
<el-select v-model="transactionForm.label" placeholder="- (optional) -">
<el-option label="First Deposit" :value="1" />
<el-option label="Unclaim" :value="2" />
</el-select>
</el-form-item>
<el-form-item label="Game Ticket Auto">
<el-checkbox v-model="transactionForm.ticketAuto">Auto generate ticket</el-checkbox>
</el-form-item>
</el-form>
<template #footer>
<el-button type="primary" :loading="transactionDialog.loading" @click="submitTransaction">CONFIRM</el-button>
<el-button @click="transactionDialog.visible = false">CANCEL</el-button>
</template>
</el-dialog>
<el-dialog v-model="historyDialog.visible" title="Transaction Edit History" width="720px">
<p class="dialog-note">Transaction edit history is only kept for the most recent 12 months.</p>
<p class="dialog-note">Transaction 的编辑历史记录仅保存最近 12 个月</p>
<el-table v-loading="historyDialog.loading" :data="historyDialog.rows" border size="small" empty-text="No Record">
<el-table-column prop="id" label="Tx ID" width="100" />
<el-table-column prop="editedBy" label="Edit By" width="120" />
<el-table-column prop="editedTime" label="Edit Time" width="170" />
<el-table-column prop="changes" label="Changes (Old → New)" />
</el-table>
<template #footer>
<el-button @click="historyDialog.visible = false">CANCEL</el-button>
</template>
</el-dialog>
<el-dialog v-model="transferDialog.visible" title="Bank Transfer" width="560px">
<el-form :model="transferForm" label-width="125px">
<el-form-item label="Transfer (From)">
<el-input v-model="transferForm.fromName" disabled />
</el-form-item>
<el-form-item label="Transfer (To)">
<el-select v-model="transferForm.bankTo" placeholder="- Select Bank -">
<el-option
v-for="bank in banks"
:key="bank.id"
:label="bank.name"
:value="bank.id"
:disabled="bank.id === transferForm.bankFrom"
/>
</el-select>
</el-form-item>
<el-form-item label="Amount">
<el-input-number v-model="transferForm.money" :min="0" :precision="2" />
</el-form-item>
<el-form-item label="Remark">
<el-input v-model="transferForm.remark" placeholder="(optional)" />
</el-form-item>
</el-form>
<template #footer>
<el-button type="primary" :loading="transferDialog.loading" @click="submitBankTransfer">CONFIRM</el-button>
<el-button @click="transferDialog.visible = false">CANCEL</el-button>
</template>
</el-dialog>
</div>
</template>
<script setup lang="ts">
import { Coin, Switch } from '@element-plus/icons-vue'
import { computed, onMounted, reactive, ref } from 'vue'
import { bankTransact, delTransact, editTransact, index as getDashboard, logHistory, newTransact } from '/@/api/backend/dashboard'
import type { DashboardTransactPayload } from '/@/api/backend/dashboard'
defineOptions({
name: 'dashboard',
})
interface Bank {
id: number | string
name: string
account: string
balance: number
deposit: number
withdraw: number
depositCount: number
withdrawCount: number
alert?: string
rowClass?: string
labelColor?: string
}
interface DashboardBank {
id: number | string
bank_name?: string
bank_account?: string
balance?: number | string
current_balance?: number | string
tx_in?: number | string
tx_out?: number | string
fund_in?: number | string
fund_out?: number | string
total_fund_in?: number | string
total_fund_out?: number | string
count_fund_in?: number | string
count_fund_out?: number | string
deposit_count?: number | string
withdraw_count?: number | string
safe_alert?: number | string
status?: number | string
label_color?: string
}
interface Transaction {
id: number
createdBy: string
createdTime: string
category: string
username: string
remark: string
bank: string
type: string
flow: 'in' | 'out'
amount: number
label: string
ticket: string[]
}
interface DashboardScoreLog {
game_type_text?: string
money_log_id?: number | string
game_type?: number | string
score?: number | string
}
interface DashboardTransaction {
id: number
user_id?: number | string
money?: number | string
before?: number | string
after?: number | string
type?: number | string
transaction_id?: string
created_by?: string
memo?: string
create_time?: number | string
bank_id?: number | string
category?: number | string
user_name?: string
bank_name?: string
label?: number | string
scoreLog?: DashboardScoreLog[]
}
interface DashboardTransactionPage {
count?: number | string
current_page?: number | string
last_page?: number | string
list?: DashboardTransaction[]
total_deposit?: number | string
total_withdraw?: number | string
}
interface DashboardCustomerSummary {
total_deposit?: number | string
total_withdraw?: number | string
count_deposit?: number | string
count_withdraw?: number | string
active_player?: number | string
first_deposit?: number | string
unclaim_amount?: number | string
unclaim_receipt?: number | string
}
interface HistoryRow {
id: number | string
editedBy: string
editedTime: string
changes: string
}
const today = () => {
const date = new Date()
const pad = (value: number) => value.toString().padStart(2, '0')
return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}`
}
const money = (value: number) =>
Number(value).toLocaleString('en-AU', {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})
const safeAlertLabels: Record<string, string> = {
'1': 'Hourly Alert',
'2': 'Daily Alert',
'3': 'Weekly Alert',
'4': 'Monthly Alert',
'5': 'Yearly Alert',
'6': 'Lifetime Alert',
}
const toNumber = (value: unknown) => {
const number = Number(value)
return Number.isFinite(number) ? number : 0
}
const formatDateTime = (value: unknown) => {
if (typeof value === 'string' && value.trim() && !Number.isFinite(Number(value))) {
return value
}
const timestamp = toNumber(value)
if (!timestamp) return ''
const date = new Date(timestamp * 1000)
const pad = (number: number) => number.toString().padStart(2, '0')
return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} ${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`
}
const mapBank = (bank: DashboardBank): Bank => {
const fundIn = toNumber(bank.total_fund_in ?? bank.fund_in)
const fundOut = toNumber(bank.total_fund_out ?? bank.fund_out)
const safeAlert = String(bank.safe_alert ?? '0')
return {
id: bank.id,
name: bank.bank_name || '-',
account: bank.bank_account || '',
balance: toNumber(bank.current_balance ?? bank.balance ?? fundIn - fundOut),
deposit: fundIn,
withdraw: fundOut,
depositCount: toNumber(bank.count_fund_in ?? bank.deposit_count ?? bank.tx_in),
withdrawCount: toNumber(bank.count_fund_out ?? bank.withdraw_count ?? bank.tx_out),
alert: safeAlertLabels[safeAlert],
rowClass: String(bank.status ?? '1') === '0' ? 'bank-muted' : '',
labelColor: bank.label_color || '',
}
}
const banks = ref<Bank[]>([])
const transactions = ref<Transaction[]>([])
const customerSummary = reactive({
totalDeposit: 0,
totalWithdraw: 0,
countDeposit: 0,
countWithdraw: 0,
activePlayer: 0,
firstDeposit: 0,
unclaimAmount: 0,
unclaimReceipt: 0,
})
const filters = reactive({
startDate: today(),
endDate: today(),
})
const transactionPage = reactive({
count: 0,
currentPage: 1,
lastPage: 1,
pageSize: 10,
})
const showAllBanks = ref(false)
const visibleBanks = computed(() => (showAllBanks.value ? banks.value : banks.value.slice(0, 4)))
const totalDeposit = computed(() => customerSummary.totalDeposit)
const totalWithdraw = computed(() => customerSummary.totalWithdraw)
const dateRangeText = computed(() => {
const start = new Date(`${filters.startDate}T00:00:00`)
const end = new Date(`${filters.endDate}T00:00:00`)
const diffDays = Math.max(1, Math.floor((end.getTime() - start.getTime()) / 86400000) + 1)
return `${filters.startDate} to ${filters.endDate} (${diffDays} ${diffDays === 1 ? 'Day' : 'Days'})`
})
const summary = computed(() => [
{ label: 'Total Deposit / IN', value: `AUD ${money(totalDeposit.value)}` },
{ label: 'Total Withdraw / OUT', value: `AUD ${money(totalWithdraw.value)}` },
{ label: 'Deposit Count', value: customerSummary.countDeposit },
{ label: 'Withdraw Count', value: customerSummary.countWithdraw },
{ label: 'Active Player', value: customerSummary.activePlayer },
{ label: 'First Deposit Player', value: customerSummary.firstDeposit },
{ label: 'Unclaimed Amount', value: `AUD ${money(customerSummary.unclaimAmount)}` },
{ label: 'Unclaimed Receipt', value: customerSummary.unclaimReceipt },
])
const transactionDialog = reactive<{ visible: boolean; title: string; loading: boolean; mode: 'create' | 'edit'; editId: number | string | '' }>({
visible: false,
title: 'Create New Transaction',
loading: false,
mode: 'create',
editId: '',
})
const transactionForm = reactive({
time: '',
timeMode: 'Auto',
category: 1,
type: 1,
username: '',
remark: '',
amount: 0,
bank: '',
label: '',
ticketAuto: true,
})
const historyDialog = reactive({
visible: false,
loading: false,
rows: [] as HistoryRow[],
})
const transferDialog = reactive({ visible: false, loading: false })
const transferForm = reactive<{ bankFrom: Bank['id'] | ''; fromName: string; bankTo: Bank['id'] | ''; money: number; remark: string }>({
bankFrom: '',
fromName: '',
bankTo: '',
money: 0,
remark: '',
})
const typeLabels: Record<string, string> = {
'1': 'Deposit',
'2': 'Withdraw',
'3': 'IN',
'4': 'OUT',
}
const categoryLabels: Record<string, string> = {
'1': 'Customer',
'2': 'Other Adjust',
}
const transactionLabelTexts: Record<string, string> = {
'1': 'First Deposit',
'2': 'Unclaim',
}
const categoryValues: Record<string, number> = {
Customer: 1,
'Other Adjust': 2,
}
const typeValues: Record<string, number> = {
Deposit: 1,
Withdraw: 2,
IN: 3,
OUT: 4,
}
const labelValues: Record<string, number> = {
'First Deposit': 1,
Unclaim: 2,
}
const mapTransaction = (transaction: DashboardTransaction): Transaction => {
const type = String(transaction.type ?? '')
return {
id: transaction.id,
createdBy: transaction.created_by || '',
createdTime: formatDateTime(transaction.create_time),
category: categoryLabels[String(transaction.category ?? '')] || '',
username: transaction.user_name || '',
remark: transaction.memo || '',
bank: transaction.bank_name || '',
type: typeLabels[type] || type,
flow: ['1', '3'].includes(type) ? 'in' : 'out',
amount: toNumber(transaction.money),
label: transactionLabelTexts[String(transaction.label ?? '')] || '',
ticket: Array.isArray(transaction.scoreLog)
? transaction.scoreLog.map((score) => `${score.game_type_text || ''} : ${score.score ?? ''}`).filter((score) => score.trim() !== ':')
: [],
}
}
const loadDashboard = (page = transactionPage.currentPage) => {
return getDashboard({
start: filters.startDate,
end: filters.endDate,
page,
}).then((res) => {
const bankData = Array.isArray(res.data.bank) ? res.data.bank : res.data.bank?.list
banks.value = Array.isArray(bankData) ? bankData.map(mapBank) : []
const transactionData = res.data.transaction as DashboardTransactionPage | undefined
const transactionList = Array.isArray(transactionData?.list) ? transactionData.list : []
transactions.value = transactionList.map(mapTransaction)
transactionPage.count = toNumber(transactionData?.count)
transactionPage.currentPage = toNumber(transactionData?.current_page) || page
transactionPage.lastPage = toNumber(transactionData?.last_page) || 1
transactionPage.pageSize = transactionPage.lastPage > 0 ? Math.max(1, Math.ceil(transactionPage.count / transactionPage.lastPage)) : 10
const customerData = res.data.customer as DashboardCustomerSummary | undefined
customerSummary.totalDeposit = toNumber(customerData?.total_deposit)
customerSummary.totalWithdraw = toNumber(customerData?.total_withdraw)
customerSummary.countDeposit = toNumber(customerData?.count_deposit)
customerSummary.countWithdraw = toNumber(customerData?.count_withdraw)
customerSummary.activePlayer = toNumber(customerData?.active_player)
customerSummary.firstDeposit = toNumber(customerData?.first_deposit)
customerSummary.unclaimAmount = toNumber(customerData?.unclaim_amount)
customerSummary.unclaimReceipt = toNumber(customerData?.unclaim_receipt)
})
}
const resetTransactionForm = () => {
Object.assign(transactionForm, {
time: '',
timeMode: 'Auto',
category: 1,
type: 1,
username: '',
remark: '',
amount: 0,
bank: '',
label: '',
ticketAuto: true,
})
}
const openCreate = () => {
resetTransactionForm()
transactionDialog.title = 'Create New Transaction'
transactionDialog.mode = 'create'
transactionDialog.editId = ''
transactionDialog.visible = true
}
const openEdit = (row: Transaction) => {
Object.assign(transactionForm, {
time: row.createdTime,
timeMode: 'Manual',
category: categoryValues[row.category] || 1,
type: typeValues[row.type] || 1,
username: row.username,
remark: row.remark,
amount: row.amount,
bank: banks.value.find((bank) => bank.name === row.bank)?.id || '',
label: labelValues[row.label] || '',
ticketAuto: row.ticket.length > 0,
})
transactionDialog.title = `Edit Transaction #${row.id}`
transactionDialog.mode = 'edit'
transactionDialog.editId = row.id
transactionDialog.visible = true
}
const transactionTimestamp = () => {
if (transactionForm.timeMode === 'Auto' || !transactionForm.time) {
return Math.floor(Date.now() / 1000)
}
const date = new Date(transactionForm.time.replace(' ', 'T'))
const timestamp = Math.floor(date.getTime() / 1000)
return Number.isFinite(timestamp) ? timestamp : Math.floor(Date.now() / 1000)
}
const buildTransactPayload = (): DashboardTransactPayload => ({
create_time: transactionTimestamp(),
category: transactionForm.category,
type: transactionForm.type,
user_name: transactionForm.username,
memo: transactionForm.remark,
money: transactionForm.amount,
bank_id: transactionForm.bank,
label: transactionForm.label,
game_ticket: transactionForm.ticketAuto ? 1 : 0,
})
const submitTransaction = () => {
transactionDialog.loading = true
const request =
transactionDialog.mode === 'edit' && transactionDialog.editId !== ''
? editTransact({ ...buildTransactPayload(), id: transactionDialog.editId })
: newTransact(buildTransactPayload())
request
.then(() => {
transactionDialog.visible = false
transactionPage.currentPage = 1
return loadDashboard(1)
})
.finally(() => {
transactionDialog.loading = false
})
}
const stringifyChange = (history: Record<string, unknown>) => {
if (history.bank_befter !== undefined || history.bank_after !== undefined) {
return `Bank:${history.bank_befter ?? ''}${history.bank_after ?? ''}`
}
return JSON.stringify(history)
}
const mapHistory = (history: Record<string, unknown>): HistoryRow => ({
id: (history.id || history.money_log_id || '') as number | string,
editedBy: String(history.admin_name || ''),
editedTime: formatDateTime(history.create_time),
changes: stringifyChange(history),
})
const openHistory = (row: Transaction) => {
historyDialog.visible = true
historyDialog.loading = true
historyDialog.rows = []
logHistory({ id: row.id })
.then((res) => {
const data = Array.isArray(res.data) ? res.data : res.data?.list
historyDialog.rows = Array.isArray(data) ? data.map((item) => mapHistory(item as Record<string, unknown>)) : []
})
.finally(() => {
historyDialog.loading = false
})
}
const openTransfer = (bank: Bank) => {
Object.assign(transferForm, { bankFrom: bank.id, fromName: bank.name, bankTo: '', money: 0, remark: '' })
transferDialog.visible = true
}
const submitBankTransfer = () => {
transferDialog.loading = true
bankTransact({
money: transferForm.money,
bank_from: transferForm.bankFrom,
bank_to: transferForm.bankTo,
remark: transferForm.remark,
})
.then(() => {
transferDialog.visible = false
return loadDashboard()
})
.finally(() => {
transferDialog.loading = false
})
}
const removeTransaction = (row: Transaction) => {
delTransact({ id: row.id }).then(() => {
return loadDashboard(transactionPage.currentPage)
})
}
const setToday = () => {
filters.startDate = today()
filters.endDate = today()
transactionPage.currentPage = 1
loadDashboard(1).catch(() => {
// Request errors are displayed by the shared Axios interceptor.
})
}
const search = () => {
transactionPage.currentPage = 1
loadDashboard(1).catch(() => {
// Request errors are displayed by the shared Axios interceptor.
})
}
const onTransactionPageChange = (page: number) => {
loadDashboard(page).catch(() => {
// Request errors are displayed by the shared Axios interceptor.
})
}
const transactionRowClass = ({ row }: { row: Transaction }) => (row.flow === 'in' ? 'transaction-in' : 'transaction-out')
onMounted(() => {
loadDashboard().catch(() => {
// Request errors are displayed by the shared Axios interceptor.
})
})
</script>
<style scoped lang="scss">
.bookkeeping-dashboard {
color: var(--el-text-color-primary);
font-size: 13px;
}
.dashboard-grid {
display: grid;
grid-template-columns: minmax(0, 1fr) 310px;
gap: 16px;
}
.dashboard-panel,
.transaction-section {
border: 1px solid var(--el-border-color);
border-radius: 4px;
background: var(--el-bg-color);
}
.panel-title {
padding: 11px 14px;
border-bottom: 1px solid var(--el-border-color);
background: var(--el-fill-color-light);
color: var(--el-text-color-primary);
font-size: 15px;
font-weight: 700;
}
.table-scroll {
overflow-x: auto;
}
.bank-table {
width: 100%;
border-collapse: collapse;
th,
td {
padding: 9px 12px;
border-bottom: 1px solid var(--el-border-color-lighter);
text-align: left;
vertical-align: middle;
}
th {
color: var(--el-text-color-secondary);
font-size: 12px;
white-space: nowrap;
}
strong,
small {
display: block;
}
small {
margin-top: 3px;
color: var(--el-text-color-secondary);
}
.balance {
color: var(--el-color-primary);
font-weight: 700;
white-space: nowrap;
}
.empty-banks {
padding: 24px;
color: var(--el-text-color-secondary);
text-align: center;
}
}
.bank-muted {
background: var(--el-fill-color-lighter);
}
.bank-operate {
display: flex;
align-items: center;
gap: 7px;
}
.breakdown {
display: flex;
flex-wrap: wrap;
gap: 8px;
color: var(--el-text-color-regular);
white-space: nowrap;
}
.dot {
font-style: normal;
font-weight: 700;
}
.income,
.amount-in {
color: var(--el-color-success);
}
.outcome,
.amount-out {
color: var(--el-color-danger);
}
.more-info {
display: block;
width: 100%;
padding: 9px;
border: 0;
background: transparent;
color: var(--el-color-primary);
cursor: pointer;
}
.summary-side {
display: flex;
flex-direction: column;
gap: 14px;
}
.summary-panel dl {
display: grid;
grid-template-columns: 1fr auto;
gap: 0;
margin: 0;
}
.summary-panel dt,
.summary-panel dd {
margin: 0;
padding: 8px 12px;
border-bottom: 1px solid var(--el-border-color-lighter);
}
.summary-panel dd {
color: var(--el-color-primary);
font-weight: 700;
text-align: right;
}
.create-button {
width: 100%;
min-height: 44px;
font-weight: 700;
}
.webhook-alert {
margin: 16px 0;
p {
margin: 6px 0 0;
line-height: 1.5;
}
}
.filter-row {
display: flex;
justify-content: space-between;
gap: 16px;
padding: 12px;
border-bottom: 1px solid var(--el-border-color);
}
.date-filter,
.totals {
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 8px;
}
.date-filter :deep(.el-date-editor) {
width: 145px;
}
.totals {
justify-content: flex-end;
em {
color: var(--el-color-primary);
font-style: normal;
}
}
.transaction-table :deep(.transaction-in) {
--el-table-tr-bg-color: var(--el-color-success-light-9);
}
.transaction-table :deep(.transaction-out) {
--el-table-tr-bg-color: var(--el-color-danger-light-9);
}
.pagination {
display: flex;
justify-content: flex-end;
padding: 12px;
}
.inline-mode {
margin-left: 12px;
}
.dialog-note {
margin: 0 0 8px;
color: var(--el-text-color-secondary);
}
:deep(.el-dialog__body) {
padding-top: 12px;
}
:deep(.el-form-item .el-select),
:deep(.el-form-item .el-input) {
width: 100%;
}
@media screen and (max-width: 1100px) {
.dashboard-grid {
grid-template-columns: 1fr;
}
}
@media screen and (max-width: 720px) {
.filter-row {
flex-direction: column;
}
.totals {
justify-content: flex-start;
}
.bank-table {
min-width: 760px;
}
}
</style>