feat: 优化后台界面与报表样式
This commit is contained in:
@@ -14,7 +14,12 @@
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="bank in visibleBanks" :key="bank.id" :class="bank.rowClass" :style="{ backgroundColor: bank.labelColor }">
|
||||
<tr
|
||||
v-for="bank in visibleBanks"
|
||||
:key="bank.id"
|
||||
:class="[bank.rowClass, { 'bank-colored': bank.labelColor }]"
|
||||
:style="bankRowStyle(bank)"
|
||||
>
|
||||
<td>
|
||||
<strong>{{ bank.name }}</strong>
|
||||
<small>{{ bank.account }}</small>
|
||||
@@ -22,11 +27,50 @@
|
||||
<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)">{{ t('dashboard.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>
|
||||
<input :id="calculatorToggleId(bank)" class="calculator-toggle" type="checkbox" />
|
||||
<div class="bank-operate-main">
|
||||
<label
|
||||
class="calculator-button"
|
||||
:for="calculatorToggleId(bank)"
|
||||
:aria-label="t('dashboard.Fast Calculation')"
|
||||
:title="t('dashboard.Fast Calculation')"
|
||||
>
|
||||
<Icon name="fa fa-calculator" size="13" color="currentColor" />
|
||||
</label>
|
||||
<el-button size="small" :icon="Switch" @click="openTransfer(bank)">
|
||||
{{ t('dashboard.Transfer') }}
|
||||
</el-button>
|
||||
<div class="breakdown">
|
||||
<div class="breakdown-row breakdown-in">
|
||||
<span class="breakdown-label">入</span>
|
||||
<span class="breakdown-count">({{ bank.depositCount }})</span>
|
||||
<strong>{{ money(bank.deposit) }}</strong>
|
||||
</div>
|
||||
<div class="breakdown-row breakdown-out">
|
||||
<span class="breakdown-label">出</span>
|
||||
<span class="breakdown-count">({{ bank.withdrawCount }})</span>
|
||||
<strong>{{ money(bank.withdraw) }}</strong>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bank-calculator">
|
||||
<div class="calculator-formula">
|
||||
<el-input
|
||||
v-model="calculatorFormulas[calculatorKey(bank)]"
|
||||
class="calculator-input"
|
||||
size="small"
|
||||
:placeholder="t('dashboard.Calculation placeholder')"
|
||||
clearable
|
||||
/>
|
||||
<span class="calculator-equals">=</span>
|
||||
<strong class="calculator-result" :class="{ 'is-invalid': isCalculatorInvalid(bank) }">
|
||||
{{ calculatorResultText(bank) }}
|
||||
</strong>
|
||||
</div>
|
||||
<div class="calculator-hint">
|
||||
{{ t('dashboard.Fast Calculation Support') }}
|
||||
<span>+ − × ÷</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
@@ -240,7 +284,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Coin, Switch } from '@element-plus/icons-vue'
|
||||
import { Switch } from '@element-plus/icons-vue'
|
||||
import { computed, onMounted, reactive, ref } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { bankTransact, delTransact, editTransact, index as getDashboard, logHistory, newTransact } from '/@/api/backend/dashboard'
|
||||
@@ -357,6 +401,11 @@ interface HistoryRow {
|
||||
bankAfter: string
|
||||
}
|
||||
|
||||
interface CalculationResult {
|
||||
valid: boolean
|
||||
value: number
|
||||
}
|
||||
|
||||
const today = () => {
|
||||
const date = new Date()
|
||||
const pad = (value: number) => value.toString().padStart(2, '0')
|
||||
@@ -374,6 +423,249 @@ const toNumber = (value: unknown) => {
|
||||
return Number.isFinite(number) ? number : 0
|
||||
}
|
||||
|
||||
const normalizeCalculationFormula = (value: string) =>
|
||||
value
|
||||
.replace(/,/g, '')
|
||||
.replace(/乘以/g, '*')
|
||||
.replace(/除以/g, '/')
|
||||
.replace(/加/g, '+')
|
||||
.replace(/减/g, '-')
|
||||
.replace(/[+]/g, '+')
|
||||
.replace(/[-–—]/g, '-')
|
||||
.replace(/[×*xX]/g, '*')
|
||||
.replace(/[÷/]/g, '/')
|
||||
.replace(/\s+/g, '')
|
||||
.replace(/^=/, '')
|
||||
|
||||
const evaluateExpression = (expression: string) => {
|
||||
let index = 0
|
||||
|
||||
const parseNumber = () => {
|
||||
const matched = expression.slice(index).match(/^(?:\d+(?:\.\d*)?|\.\d+)/)
|
||||
if (!matched) return null
|
||||
|
||||
index += matched[0].length
|
||||
return Number(matched[0])
|
||||
}
|
||||
|
||||
const parseFactor = (): number | null => {
|
||||
const char = expression[index]
|
||||
|
||||
if (char === '+') {
|
||||
index += 1
|
||||
return parseFactor()
|
||||
}
|
||||
|
||||
if (char === '-') {
|
||||
index += 1
|
||||
const value = parseFactor()
|
||||
return value === null ? null : -value
|
||||
}
|
||||
|
||||
if (char === '(') {
|
||||
index += 1
|
||||
const value = parseExpression()
|
||||
|
||||
if (value === null || expression[index] !== ')') return null
|
||||
|
||||
index += 1
|
||||
return value
|
||||
}
|
||||
|
||||
return parseNumber()
|
||||
}
|
||||
|
||||
const parseTerm = (): number | null => {
|
||||
let value = parseFactor()
|
||||
if (value === null) return null
|
||||
|
||||
while (expression[index] === '*' || expression[index] === '/') {
|
||||
const operator = expression[index]
|
||||
index += 1
|
||||
const right = parseFactor()
|
||||
|
||||
if (right === null) return null
|
||||
|
||||
value = operator === '*' ? value * right : right === 0 ? Number.NaN : value / right
|
||||
}
|
||||
|
||||
return value
|
||||
}
|
||||
|
||||
function parseExpression(): number | null {
|
||||
let value = parseTerm()
|
||||
if (value === null) return null
|
||||
|
||||
while (expression[index] === '+' || expression[index] === '-') {
|
||||
const operator = expression[index]
|
||||
index += 1
|
||||
const right = parseTerm()
|
||||
|
||||
if (right === null) return null
|
||||
|
||||
value = operator === '+' ? value + right : value - right
|
||||
}
|
||||
|
||||
return value
|
||||
}
|
||||
|
||||
const result = parseExpression()
|
||||
|
||||
return result !== null && index === expression.length && Number.isFinite(result) ? result : null
|
||||
}
|
||||
|
||||
const calculateBalancePreview = (balance: number, formula: string): CalculationResult => {
|
||||
const normalized = normalizeCalculationFormula(formula)
|
||||
|
||||
if (!normalized) {
|
||||
return { valid: true, value: balance }
|
||||
}
|
||||
|
||||
if (!/^[\d+\-*/().]+$/.test(normalized)) {
|
||||
return { valid: false, value: balance }
|
||||
}
|
||||
|
||||
const expression = /^[+\-*/]/.test(normalized) ? `${balance}${normalized}` : `${balance}+${normalized}`
|
||||
const value = evaluateExpression(expression)
|
||||
|
||||
return value === null ? { valid: false, value: balance } : { valid: true, value }
|
||||
}
|
||||
|
||||
const clampRgbChannel = (value: number) => Math.min(255, Math.max(0, Math.round(value)))
|
||||
|
||||
const parseColorChannel = (value: string) => {
|
||||
const number = Number(value)
|
||||
return Number.isFinite(number) ? clampRgbChannel(number) : 0
|
||||
}
|
||||
|
||||
const parseAlphaChannel = (value?: string) => {
|
||||
if (value === undefined) return 1
|
||||
|
||||
const number = Number(value)
|
||||
return Number.isFinite(number) ? Math.min(1, Math.max(0, number)) : 1
|
||||
}
|
||||
|
||||
const parseColorToRgb = (value: string) => {
|
||||
const color = value.trim()
|
||||
const hex = color.match(/^#([0-9a-f]{3}|[0-9a-f]{6}|[0-9a-f]{8})$/i)
|
||||
|
||||
if (hex) {
|
||||
const raw = hex[1]
|
||||
const full =
|
||||
raw.length === 3
|
||||
? raw
|
||||
.split('')
|
||||
.map((char) => char + char)
|
||||
.join('')
|
||||
: raw
|
||||
|
||||
return {
|
||||
r: Number.parseInt(full.slice(0, 2), 16),
|
||||
g: Number.parseInt(full.slice(2, 4), 16),
|
||||
b: Number.parseInt(full.slice(4, 6), 16),
|
||||
a: full.length === 8 ? Number.parseInt(full.slice(6, 8), 16) / 255 : 1,
|
||||
}
|
||||
}
|
||||
|
||||
const rgb = color.match(/^rgba?\(\s*([\d.]+)\s*,\s*([\d.]+)\s*,\s*([\d.]+)(?:\s*,\s*([\d.]+))?\s*\)$/i)
|
||||
|
||||
if (!rgb) return null
|
||||
|
||||
return {
|
||||
r: parseColorChannel(rgb[1]),
|
||||
g: parseColorChannel(rgb[2]),
|
||||
b: parseColorChannel(rgb[3]),
|
||||
a: parseAlphaChannel(rgb[4]),
|
||||
}
|
||||
}
|
||||
|
||||
const rgbToCss = (rgb: { r: number; g: number; b: number }) => `rgb(${clampRgbChannel(rgb.r)}, ${clampRgbChannel(rgb.g)}, ${clampRgbChannel(rgb.b)})`
|
||||
|
||||
const mixRgb = (source: { r: number; g: number; b: number }, target: { r: number; g: number; b: number }, targetWeight: number) => ({
|
||||
r: source.r * (1 - targetWeight) + target.r * targetWeight,
|
||||
g: source.g * (1 - targetWeight) + target.g * targetWeight,
|
||||
b: source.b * (1 - targetWeight) + target.b * targetWeight,
|
||||
})
|
||||
|
||||
const polishedBankRowColor = (backgroundColor: string) => {
|
||||
const rgb = parseColorToRgb(backgroundColor)
|
||||
if (!rgb) return backgroundColor
|
||||
|
||||
const blended = {
|
||||
r: rgb.r * rgb.a + 255 * (1 - rgb.a),
|
||||
g: rgb.g * rgb.a + 255 * (1 - rgb.a),
|
||||
b: rgb.b * rgb.a + 255 * (1 - rgb.a),
|
||||
}
|
||||
const max = Math.max(blended.r, blended.g, blended.b)
|
||||
const min = Math.min(blended.r, blended.g, blended.b)
|
||||
|
||||
if (max - min < 18) {
|
||||
return rgbToCss(mixRgb(blended, { r: 241, g: 245, b: 249 }, 0.72))
|
||||
}
|
||||
|
||||
if (blended.g >= blended.r && blended.g >= blended.b) {
|
||||
return '#d9f3e5'
|
||||
}
|
||||
|
||||
if (blended.r >= blended.g && blended.r >= blended.b) {
|
||||
return '#f8dde2'
|
||||
}
|
||||
|
||||
return '#dbeafe'
|
||||
}
|
||||
|
||||
const srgbToLinear = (channel: number) => {
|
||||
const value = channel / 255
|
||||
return value <= 0.03928 ? value / 12.92 : Math.pow((value + 0.055) / 1.055, 2.4)
|
||||
}
|
||||
|
||||
const relativeLuminance = (rgb: { r: number; g: number; b: number }) =>
|
||||
0.2126 * srgbToLinear(rgb.r) + 0.7152 * srgbToLinear(rgb.g) + 0.0722 * srgbToLinear(rgb.b)
|
||||
|
||||
const contrastRatio = (first: number, second: number) => {
|
||||
const lighter = Math.max(first, second)
|
||||
const darker = Math.min(first, second)
|
||||
return (lighter + 0.05) / (darker + 0.05)
|
||||
}
|
||||
|
||||
const readableTextColor = (backgroundColor: string) => {
|
||||
const rgb = parseColorToRgb(backgroundColor)
|
||||
if (!rgb) return '#172033'
|
||||
|
||||
const blended = {
|
||||
r: rgb.r * rgb.a + 255 * (1 - rgb.a),
|
||||
g: rgb.g * rgb.a + 255 * (1 - rgb.a),
|
||||
b: rgb.b * rgb.a + 255 * (1 - rgb.a),
|
||||
}
|
||||
const luminance = relativeLuminance(blended)
|
||||
const slateContrast = contrastRatio(luminance, relativeLuminance({ r: 23, g: 32, b: 51 }))
|
||||
const whiteContrast = contrastRatio(luminance, 1)
|
||||
const darkContrast = contrastRatio(luminance, 0)
|
||||
|
||||
if (slateContrast >= 4.5 || slateContrast >= whiteContrast) {
|
||||
return '#172033'
|
||||
}
|
||||
|
||||
return whiteContrast >= darkContrast ? '#ffffff' : '#000000'
|
||||
}
|
||||
|
||||
const bankRowStyle = (bank: Bank): Record<string, string> => {
|
||||
if (!bank.labelColor) return {}
|
||||
|
||||
const backgroundColor = polishedBankRowColor(bank.labelColor)
|
||||
const textColor = readableTextColor(backgroundColor)
|
||||
const isLightText = textColor === '#ffffff'
|
||||
|
||||
return {
|
||||
backgroundColor,
|
||||
'--bank-row-text-color': textColor,
|
||||
'--bank-row-muted-color': isLightText ? 'rgba(255, 255, 255, 0.82)' : 'rgba(0, 0, 0, 0.72)',
|
||||
'--bank-row-border-color': isLightText ? 'rgba(255, 255, 255, 0.24)' : 'rgba(0, 0, 0, 0.16)',
|
||||
'--bank-row-control-bg': isLightText ? 'rgba(255, 255, 255, 0.16)' : 'rgba(255, 255, 255, 0.72)',
|
||||
'--bank-row-control-hover-bg': isLightText ? 'rgba(255, 255, 255, 0.26)' : 'rgba(255, 255, 255, 0.88)',
|
||||
}
|
||||
}
|
||||
|
||||
const formatDateTime = (value: unknown) => {
|
||||
if (typeof value === 'string' && value.trim() && !Number.isFinite(Number(value))) {
|
||||
return value
|
||||
@@ -435,6 +727,7 @@ const transactionPage = reactive({
|
||||
pageSize: 10,
|
||||
})
|
||||
const showAllBanks = ref(false)
|
||||
const calculatorFormulas = reactive<Record<string, string>>({})
|
||||
const visibleBanks = computed(() => (showAllBanks.value ? banks.value : banks.value.slice(0, 4)))
|
||||
const totalDeposit = computed(() => transactionTotals.totalDeposit)
|
||||
const totalWithdraw = computed(() => transactionTotals.totalWithdraw)
|
||||
@@ -460,6 +753,19 @@ const summary = computed(() => [
|
||||
{ label: t('dashboard.Unclaimed Receipt'), value: customerSummary.unclaimReceipt },
|
||||
])
|
||||
|
||||
const calculatorKey = (bank: Bank) => String(bank.id)
|
||||
|
||||
const calculatorToggleId = (bank: Bank) => `bank-calculator-${calculatorKey(bank).replace(/[^a-zA-Z0-9_-]/g, '-')}`
|
||||
|
||||
const calculatorResult = (bank: Bank) => calculateBalancePreview(bank.balance, calculatorFormulas[calculatorKey(bank)] || '')
|
||||
|
||||
const isCalculatorInvalid = (bank: Bank) => !calculatorResult(bank).valid
|
||||
|
||||
const calculatorResultText = (bank: Bank) => {
|
||||
const result = calculatorResult(bank)
|
||||
return result.valid ? `AUD ${money(result.value)}` : 'AUD --'
|
||||
}
|
||||
|
||||
const transactionDialog = reactive<{ visible: boolean; loading: boolean; mode: 'create' | 'edit'; editId: number | string | '' }>({
|
||||
visible: false,
|
||||
loading: false,
|
||||
@@ -753,25 +1059,29 @@ onMounted(() => {
|
||||
.bookkeeping-dashboard {
|
||||
color: var(--el-text-color-primary);
|
||||
font-size: 13px;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
.dashboard-grid {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr) 310px;
|
||||
gap: 16px;
|
||||
gap: var(--ba-main-space);
|
||||
}
|
||||
.dashboard-panel,
|
||||
.transaction-section {
|
||||
border: 1px solid var(--el-border-color);
|
||||
border-radius: 4px;
|
||||
background: var(--el-bg-color);
|
||||
border: 1px solid var(--ba-border-color-soft);
|
||||
border-radius: var(--ba-radius-panel);
|
||||
background: var(--ba-bg-color-overlay);
|
||||
box-shadow: var(--ba-shadow-card);
|
||||
overflow: hidden;
|
||||
}
|
||||
.panel-title {
|
||||
padding: 11px 14px;
|
||||
border-bottom: 1px solid var(--el-border-color);
|
||||
background: var(--el-fill-color-light);
|
||||
padding: 13px 16px;
|
||||
border-bottom: 1px solid var(--ba-border-color-soft);
|
||||
background: var(--ba-bg-color-soft);
|
||||
color: var(--el-text-color-primary);
|
||||
font-size: 15px;
|
||||
font-weight: 700;
|
||||
line-height: 1.4;
|
||||
}
|
||||
.table-scroll {
|
||||
overflow-x: auto;
|
||||
@@ -781,16 +1091,68 @@ onMounted(() => {
|
||||
border-collapse: collapse;
|
||||
th,
|
||||
td {
|
||||
padding: 9px 12px;
|
||||
border-bottom: 1px solid var(--el-border-color-lighter);
|
||||
padding: 11px 14px;
|
||||
border-bottom: 1px solid var(--ba-border-color-soft);
|
||||
text-align: left;
|
||||
vertical-align: middle;
|
||||
}
|
||||
th {
|
||||
color: var(--el-text-color-secondary);
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
background: var(--ba-bg-color-soft);
|
||||
white-space: nowrap;
|
||||
}
|
||||
tbody tr {
|
||||
transition: background-color 0.18s ease;
|
||||
}
|
||||
tbody tr:hover:not(.bank-colored) {
|
||||
background: var(--el-color-primary-light-9);
|
||||
}
|
||||
tbody tr.bank-colored {
|
||||
color: var(--bank-row-text-color);
|
||||
text-shadow: none;
|
||||
|
||||
td {
|
||||
border-bottom-color: var(--bank-row-border-color);
|
||||
}
|
||||
|
||||
small {
|
||||
color: var(--bank-row-muted-color);
|
||||
}
|
||||
|
||||
.balance {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
:deep(.el-button) {
|
||||
--el-button-text-color: var(--bank-row-text-color);
|
||||
--el-button-bg-color: var(--bank-row-control-bg);
|
||||
--el-button-border-color: var(--bank-row-border-color);
|
||||
--el-button-hover-text-color: var(--bank-row-text-color);
|
||||
--el-button-hover-bg-color: var(--bank-row-control-hover-bg);
|
||||
--el-button-hover-border-color: var(--bank-row-border-color);
|
||||
--el-button-active-text-color: var(--bank-row-text-color);
|
||||
--el-button-active-bg-color: var(--bank-row-control-hover-bg);
|
||||
--el-button-active-border-color: var(--bank-row-border-color);
|
||||
}
|
||||
|
||||
.calculator-button {
|
||||
color: var(--bank-row-text-color);
|
||||
border-color: var(--bank-row-border-color);
|
||||
background: var(--bank-row-control-bg);
|
||||
&:hover,
|
||||
&:focus-visible {
|
||||
background: var(--bank-row-control-hover-bg);
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.el-tag) {
|
||||
color: var(--bank-row-text-color);
|
||||
border-color: var(--bank-row-border-color);
|
||||
background: var(--bank-row-control-bg);
|
||||
}
|
||||
}
|
||||
strong,
|
||||
small {
|
||||
display: block;
|
||||
@@ -815,15 +1177,164 @@ onMounted(() => {
|
||||
}
|
||||
.bank-operate {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 7px;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 8px;
|
||||
:deep(.el-button.is-circle) {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
}
|
||||
}
|
||||
.breakdown {
|
||||
.bank-operate-main {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
}
|
||||
.calculator-toggle {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
.calculator-toggle:not(:checked) ~ .bank-calculator {
|
||||
display: none;
|
||||
}
|
||||
.calculator-toggle:checked + .bank-operate-main .calculator-button {
|
||||
border-color: var(--el-color-primary);
|
||||
background: var(--el-color-primary-light-9);
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
.calculator-toggle:focus-visible + .bank-operate-main .calculator-button {
|
||||
outline: 2px solid var(--el-color-primary-light-5);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
.calculator-button {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
padding: 0;
|
||||
border: 1px solid var(--el-border-color);
|
||||
border-radius: 50%;
|
||||
background: var(--el-bg-color);
|
||||
color: var(--el-text-color-regular);
|
||||
cursor: pointer;
|
||||
line-height: 1;
|
||||
transition:
|
||||
background-color 0.18s ease,
|
||||
border-color 0.18s ease,
|
||||
color 0.18s ease;
|
||||
&:hover,
|
||||
&:focus-visible {
|
||||
border-color: var(--el-color-primary);
|
||||
color: var(--el-color-primary);
|
||||
background: var(--el-color-primary-light-9);
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
.bank-calculator {
|
||||
width: min(300px, 100%);
|
||||
padding: 6px 8px 5px;
|
||||
border: 1px solid var(--ba-border-color-soft);
|
||||
border-radius: 2px;
|
||||
background: rgba(255, 255, 255, 0.94);
|
||||
box-shadow: 0 6px 14px rgba(15, 23, 42, 0.08);
|
||||
color: var(--el-text-color-primary);
|
||||
}
|
||||
.calculator-formula {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
}
|
||||
.calculator-input {
|
||||
flex: 1 1 125px;
|
||||
min-width: 112px;
|
||||
:deep(.el-input__wrapper) {
|
||||
min-height: 28px;
|
||||
border-radius: 0;
|
||||
box-shadow: 0 0 0 1px rgba(148, 163, 184, 0.72) inset;
|
||||
}
|
||||
}
|
||||
.calculator-equals {
|
||||
color: var(--el-text-color-primary);
|
||||
font-size: 15px;
|
||||
font-weight: 700;
|
||||
line-height: 1;
|
||||
}
|
||||
.calculator-result {
|
||||
min-width: 92px;
|
||||
color: var(--el-text-color-primary);
|
||||
font-size: 15px;
|
||||
font-weight: 700;
|
||||
line-height: 1;
|
||||
white-space: nowrap;
|
||||
&.is-invalid {
|
||||
color: var(--el-color-danger);
|
||||
}
|
||||
}
|
||||
.calculator-hint {
|
||||
margin-top: 3px;
|
||||
color: var(--el-text-color-regular);
|
||||
font-size: 11px;
|
||||
line-height: 1.2;
|
||||
span {
|
||||
margin-left: 4px;
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
}
|
||||
.breakdown {
|
||||
display: inline-flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
min-width: 118px;
|
||||
padding: 5px 8px;
|
||||
border: 1px solid rgba(148, 163, 184, 0.28);
|
||||
border-radius: 4px;
|
||||
background: rgba(248, 250, 252, 0.94);
|
||||
color: var(--el-text-color-regular);
|
||||
box-shadow: 0 1px 0 rgba(15, 23, 42, 0.04);
|
||||
}
|
||||
.breakdown-row {
|
||||
display: grid;
|
||||
grid-template-columns: 18px auto minmax(0, 1fr);
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
line-height: 1.15;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.breakdown-label {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
border-radius: 3px;
|
||||
color: #ffffff;
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
}
|
||||
.breakdown-count {
|
||||
color: var(--el-text-color-secondary);
|
||||
font-weight: 600;
|
||||
}
|
||||
.breakdown-in {
|
||||
color: var(--el-color-success);
|
||||
.breakdown-label {
|
||||
background: var(--el-color-success);
|
||||
}
|
||||
}
|
||||
.breakdown-out {
|
||||
color: var(--el-color-danger);
|
||||
.breakdown-label {
|
||||
background: var(--el-color-danger);
|
||||
}
|
||||
}
|
||||
.dot {
|
||||
font-style: normal;
|
||||
@@ -840,16 +1351,22 @@ onMounted(() => {
|
||||
.more-info {
|
||||
display: block;
|
||||
width: 100%;
|
||||
padding: 9px;
|
||||
padding: 11px;
|
||||
border: 0;
|
||||
background: transparent;
|
||||
border-top: 1px solid var(--ba-border-color-soft);
|
||||
background: var(--ba-bg-color-soft);
|
||||
color: var(--el-color-primary);
|
||||
cursor: pointer;
|
||||
font-weight: 700;
|
||||
transition: background-color 0.18s ease;
|
||||
&:hover {
|
||||
background: var(--el-color-primary-light-9);
|
||||
}
|
||||
}
|
||||
.summary-side {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 14px;
|
||||
gap: var(--ba-main-space);
|
||||
}
|
||||
.summary-panel dl {
|
||||
display: grid;
|
||||
@@ -860,8 +1377,12 @@ onMounted(() => {
|
||||
.summary-panel dt,
|
||||
.summary-panel dd {
|
||||
margin: 0;
|
||||
padding: 8px 12px;
|
||||
border-bottom: 1px solid var(--el-border-color-lighter);
|
||||
padding: 10px 14px;
|
||||
border-bottom: 1px solid var(--ba-border-color-soft);
|
||||
}
|
||||
.summary-panel dt {
|
||||
color: var(--el-text-color-secondary);
|
||||
font-weight: 600;
|
||||
}
|
||||
.summary-panel dd {
|
||||
color: var(--el-color-primary);
|
||||
@@ -870,11 +1391,12 @@ onMounted(() => {
|
||||
}
|
||||
.create-button {
|
||||
width: 100%;
|
||||
min-height: 44px;
|
||||
min-height: 46px;
|
||||
font-weight: 700;
|
||||
}
|
||||
.webhook-alert {
|
||||
margin: 16px 0;
|
||||
margin: var(--ba-main-space) 0;
|
||||
border: 1px solid rgba(183, 121, 31, 0.24);
|
||||
p {
|
||||
margin: 6px 0 0;
|
||||
line-height: 1.5;
|
||||
@@ -883,9 +1405,10 @@ onMounted(() => {
|
||||
.filter-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 16px;
|
||||
padding: 12px;
|
||||
border-bottom: 1px solid var(--el-border-color);
|
||||
gap: 14px;
|
||||
padding: 14px 16px;
|
||||
border-bottom: 1px solid var(--ba-border-color-soft);
|
||||
background: var(--ba-bg-color-soft);
|
||||
}
|
||||
.date-filter,
|
||||
.totals {
|
||||
@@ -895,10 +1418,18 @@ onMounted(() => {
|
||||
gap: 8px;
|
||||
}
|
||||
.date-filter :deep(.el-date-editor) {
|
||||
width: 145px;
|
||||
width: 150px;
|
||||
}
|
||||
.date-filter label {
|
||||
color: var(--el-text-color-secondary);
|
||||
font-weight: 600;
|
||||
}
|
||||
.totals {
|
||||
justify-content: flex-end;
|
||||
color: var(--el-text-color-regular);
|
||||
b {
|
||||
font-weight: 700;
|
||||
}
|
||||
em {
|
||||
color: var(--el-color-primary);
|
||||
font-style: normal;
|
||||
@@ -913,7 +1444,9 @@ onMounted(() => {
|
||||
.pagination {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
padding: 12px;
|
||||
padding: 14px 16px;
|
||||
border-top: 1px solid var(--ba-border-color-soft);
|
||||
background: var(--ba-bg-color-soft);
|
||||
}
|
||||
.inline-mode {
|
||||
margin-left: 12px;
|
||||
@@ -923,7 +1456,7 @@ onMounted(() => {
|
||||
color: var(--el-text-color-secondary);
|
||||
}
|
||||
:deep(.el-dialog__body) {
|
||||
padding-top: 12px;
|
||||
padding-top: 16px;
|
||||
}
|
||||
:deep(.el-form-item .el-select),
|
||||
:deep(.el-form-item .el-input) {
|
||||
@@ -938,11 +1471,24 @@ onMounted(() => {
|
||||
.filter-row {
|
||||
flex-direction: column;
|
||||
}
|
||||
.date-filter {
|
||||
align-items: stretch;
|
||||
label {
|
||||
width: 100%;
|
||||
}
|
||||
:deep(.el-date-editor),
|
||||
:deep(.el-button) {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
.totals {
|
||||
justify-content: flex-start;
|
||||
}
|
||||
.bank-table {
|
||||
min-width: 760px;
|
||||
}
|
||||
.pagination {
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user