feat: 优化后台界面与报表样式

This commit is contained in:
wchino
2026-06-11 15:32:22 +08:00
parent 064ba727e0
commit 9b10f21663
37 changed files with 5746 additions and 610 deletions

View File

@@ -256,35 +256,41 @@ const getPlaceholder = (placeholder: string | string[] | undefined, key = 0, def
width: 100%;
max-width: 100%;
background-color: var(--ba-bg-color-overlay);
border: 1px solid var(--ba-border-color);
border: 1px solid var(--ba-border-color-soft);
border-bottom: none;
padding: 13px 15px;
padding: 16px;
font-size: 14px;
border-radius: var(--ba-radius-panel) var(--ba-radius-panel) 0 0;
.com-search-col {
display: flex;
align-items: center;
min-height: 40px;
padding-top: 8px;
color: var(--el-text-color-regular);
font-size: 13px;
}
.com-search-col-label {
width: 33.33%;
padding: 0 15px;
padding: 0 12px;
text-align: right;
overflow: hidden;
white-space: nowrap;
color: var(--el-text-color-secondary);
font-weight: 600;
}
.com-search-col-input {
padding: 0 15px;
padding: 0 12px;
width: 66.66%;
}
.com-search-col-input-range {
display: flex;
align-items: center;
padding: 0 15px;
padding: 0 12px;
width: 66.66%;
.range-separator {
padding: 0 5px;
padding: 0 8px;
color: var(--el-text-color-secondary);
font-size: 12px;
}
}
}
@@ -297,4 +303,28 @@ const getPlaceholder = (placeholder: string | string[] | undefined, key = 0, def
.w83 {
width: 83.5% !important;
}
@media screen and (max-width: 768px) {
.table-com-search {
padding: 12px;
.com-search-col {
display: block;
}
.com-search-col-label,
.com-search-col-input,
.com-search-col-input-range,
.w16,
.w83 {
width: 100% !important;
padding: 0;
text-align: left;
}
.com-search-col-label {
margin-bottom: 6px;
}
}
.pl-20 {
padding-left: 0;
}
}
</style>

View File

@@ -176,42 +176,51 @@ const onChangeShowColumn = (value: string | number | boolean, field: string) =>
box-sizing: border-box;
display: flex;
align-items: center;
gap: 10px;
width: 100%;
max-width: 100%;
background-color: var(--ba-bg-color-overlay);
border: 1px solid var(--ba-border-color);
border: 1px solid var(--ba-border-color-soft);
border-bottom: none;
padding: 13px 15px;
padding: 14px 16px;
font-size: 14px;
border-radius: var(--ba-radius-panel) var(--ba-radius-panel) 0 0;
.table-header-operate-text {
margin-left: 6px;
}
:deep(.el-button) {
min-height: 32px;
}
}
.btns-ml-12 + .btns-ml-12 {
margin-left: 12px;
margin-left: 0;
}
.table-search {
display: flex;
align-items: center;
gap: 10px;
margin-left: auto;
.quick-search {
width: auto;
width: 240px;
}
}
.table-search-button-group {
display: flex;
margin-left: 12px;
border: 1px solid var(--el-border-color);
border-radius: var(--el-border-radius-base);
margin-left: 0;
border: 1px solid var(--ba-border-color-soft);
border-radius: var(--ba-radius-control);
overflow: hidden;
background: var(--ba-bg-color-soft);
button:focus,
button:active {
background-color: var(--ba-bg-color-overlay);
}
button:hover {
background-color: var(--el-color-info-light-7);
background-color: var(--el-color-primary-light-9);
}
.table-search-button-item {
height: 30px;
height: 32px;
width: 34px;
border: none;
border-radius: 0;
}
@@ -219,7 +228,7 @@ const onChangeShowColumn = (value: string | number | boolean, field: string) =>
margin: 0;
}
.right-border {
border-right: 1px solid var(--el-border-color);
border-right: 1px solid var(--ba-border-color-soft);
}
}
@@ -240,4 +249,19 @@ html.dark {
}
}
}
@media screen and (max-width: 768px) {
.table-header {
flex-wrap: wrap;
align-items: stretch;
padding: 12px;
}
.table-search {
width: 100%;
margin-left: 0;
.quick-search {
width: 100%;
}
}
}
</style>

View File

@@ -230,17 +230,42 @@ defineExpose({
</script>
<style scoped lang="scss">
.ba-data-table {
border-radius: 0 0 var(--ba-radius-panel) var(--ba-radius-panel);
overflow: hidden;
}
.ba-data-table :deep(.table-header-cell) .cell {
color: var(--el-text-color-primary);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-weight: 700;
}
.ba-data-table :deep(.cell) {
line-height: 1.45;
}
.ba-data-table :deep(.el-table__empty-text) {
padding: 18px 0;
color: var(--el-text-color-secondary);
font-weight: 500;
}
.table-pagination {
box-sizing: border-box;
width: 100%;
max-width: 100%;
background-color: var(--ba-bg-color-overlay);
padding: 13px 15px;
padding: 14px 16px;
border: 1px solid var(--ba-border-color-soft);
border-top: none;
border-radius: 0 0 var(--ba-radius-panel) var(--ba-radius-panel);
display: flex;
justify-content: flex-end;
}
@media screen and (max-width: 768px) {
.table-pagination {
justify-content: center;
padding: 12px;
}
}
</style>

View File

@@ -41,6 +41,9 @@ export default {
'Current Balance': 'Current Balance',
'Transaction Breakdown': 'Transaction Breakdown',
'Safe Alert': 'Safe Alert',
'Fast Calculation': 'Fast Calculation',
'Calculation placeholder': 'e.g. +10+50-20',
'Fast Calculation Support': 'Fast Calculation Support',
Transfer: 'Transfer',
'No Alert': 'No Alert',
'No bank data': 'No bank data',

View File

@@ -41,6 +41,9 @@ export default {
'Current Balance': '当前余额',
'Transaction Breakdown': '交易明细',
'Safe Alert': '安全提醒',
'Fast Calculation': '快速计算',
'Calculation placeholder': 'e.g. +10+50-20',
'Fast Calculation Support': 'Fast Calculation Support',
Transfer: '转账',
'No Alert': '无提醒',
'No bank data': '暂无银行数据',

View File

@@ -28,12 +28,15 @@ const menuWidth = computed(() => config.menuWidth())
<style scoped lang="scss">
.layout-aside-Default:not(.shrink) {
background: var(--ba-bg-color-overlay);
margin: 16px 0 16px 16px;
height: calc(100% - 32px);
box-shadow: var(--el-box-shadow-light);
border-radius: var(--el-border-radius-base);
margin: var(--ba-main-space) 0 var(--ba-main-space) var(--ba-main-space);
height: calc(100% - (var(--ba-main-space) * 2));
border: 1px solid var(--ba-border-color-soft);
box-shadow: var(--ba-shadow-card);
border-radius: var(--ba-radius-panel);
overflow: hidden;
transition: width 0.3s ease;
transition:
width 0.26s ease,
box-shadow 0.26s ease;
width: v-bind(menuWidth);
}
.layout-aside-Default.shrink,
@@ -43,7 +46,8 @@ const menuWidth = computed(() => config.menuWidth())
margin: 0;
height: 100%;
overflow: hidden;
transition: width 0.3s ease;
border-right: 1px solid var(--ba-border-color-soft);
transition: width 0.26s ease;
width: v-bind(menuWidth);
}
.shrink {

View File

@@ -49,29 +49,47 @@ const onMenuCollapse = function () {
<style scoped lang="scss">
.layout-logo {
width: 100%;
height: 50px;
height: 56px;
display: flex;
align-items: center;
justify-content: center;
box-sizing: border-box;
padding: 10px;
padding: 10px 12px;
background: v-bind('config.layout.layoutMode != "Streamline" ? config.getColorVal("menuTopBarBackground"):"transparent"');
border-bottom: 1px solid var(--ba-border-color-soft);
}
.logo-img {
width: 28px;
width: 30px;
height: 30px;
object-fit: contain;
}
.website-name {
display: block;
width: 180px;
padding-left: 4px;
font-size: var(--el-font-size-extra-large);
font-weight: 600;
padding-left: 8px;
font-size: 17px;
font-weight: 700;
letter-spacing: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.fold {
margin-left: auto;
display: inline-flex;
align-items: center;
justify-content: center;
width: 30px;
height: 30px;
border-radius: var(--ba-radius-control);
cursor: pointer;
transition:
background-color 0.2s ease,
transform 0.2s ease;
&:hover {
background: var(--el-color-primary-light-9);
transform: translateX(-1px);
}
}
.unfold {
margin: 0 auto;

View File

@@ -53,11 +53,11 @@ const menuTitle = (menu: RouteRecordRaw) => {
const reportMenuPaths: Record<string, string> = {
'Annual Report': '/user/moneyLog/annualReport',
'年度报表': '/user/moneyLog/annualReport',
年度报表: '/user/moneyLog/annualReport',
'Daily Report': '/user/moneyLog/dailyReport',
'日报表': '/user/moneyLog/dailyReport',
日报表: '/user/moneyLog/dailyReport',
'Customer Report': '/user/moneyLog/customerReport',
'客户报表': '/user/moneyLog/customerReport',
客户报表: '/user/moneyLog/customerReport',
}
const onClickMenuItem = (menu: RouteRecordRaw) => {
@@ -96,8 +96,8 @@ const onClickSubMenu = (menu: RouteRecordRaw) => {
.el-sub-menu .icon,
.el-menu-item .icon {
vertical-align: middle;
margin-right: 5px;
width: 24px;
margin-right: 8px;
width: 22px;
text-align: center;
flex-shrink: 0;
}
@@ -106,5 +106,6 @@ const onClickSubMenu = (menu: RouteRecordRaw) => {
}
.el-menu-item.is-active {
background-color: v-bind('config.getColorVal("menuActiveBackground")');
box-shadow: inset 3px 0 0 v-bind('config.getColorVal("menuActiveColor")');
}
</style>

View File

@@ -71,6 +71,7 @@ onBeforeRouteUpdate((to) => {
.vertical-menus-scrollbar {
height: v-bind(verticalMenusScrollbarHeight);
background-color: v-bind('config.getColorVal("menuBackground")');
padding: 4px 0 10px;
}
.layouts-menu-vertical {
border: 0;

View File

@@ -29,46 +29,63 @@ const onMenuCollapse = () => {
<style lang="scss" scoped>
.nav-bar {
display: flex;
height: 50px;
margin: 20px var(--ba-main-space) 0 var(--ba-main-space);
height: 52px;
margin: var(--ba-main-space) var(--ba-main-space) 0 var(--ba-main-space);
gap: 12px;
:deep(.nav-tabs) {
display: flex;
height: 100%;
position: relative;
min-width: 0;
.ba-nav-tab {
display: flex;
align-items: center;
justify-content: center;
padding: 0 20px;
padding: 0 18px;
cursor: pointer;
z-index: 1;
user-select: none;
opacity: 0.7;
opacity: 0.78;
color: v-bind('config.getColorVal("headerBarTabColor")');
border-radius: var(--ba-radius-control);
transition:
opacity 0.2s ease,
color 0.2s ease,
background-color 0.2s ease;
.close-icon {
padding: 2px;
margin: 2px 0 0 4px;
}
.close-icon:hover {
background: var(--ba-color-primary-light);
color: var(--el-border-color) !important;
background: var(--el-color-primary-light-8);
color: var(--el-color-primary) !important;
border-radius: 50%;
}
&.active {
color: v-bind('config.getColorVal("headerBarTabActiveColor")');
font-weight: 600;
}
&:hover {
opacity: 1;
background: v-bind('config.getColorVal("headerBarHoverBackground")');
}
}
.nav-tabs-active-box {
position: absolute;
height: 40px;
border-radius: var(--el-border-radius-base);
height: 38px;
top: 7px;
border-radius: var(--ba-radius-control);
background-color: v-bind('config.getColorVal("headerBarTabActiveBackground")');
box-shadow: var(--el-box-shadow-light);
transition: all 0.2s;
-webkit-transition: all 0.2s;
border: 1px solid var(--ba-border-color-soft);
box-shadow: var(--ba-shadow-card);
transition:
transform 0.2s ease,
width 0.2s ease,
left 0.2s ease;
-webkit-transition:
transform 0.2s ease,
width 0.2s ease,
left 0.2s ease;
}
}
}
@@ -76,6 +93,7 @@ const onMenuCollapse = () => {
width: 100%;
background-color: v-bind('config.getColorVal("headerBarBackground")');
margin: 0;
border-bottom: 1px solid var(--ba-border-color-soft);
.unfold {
align-self: center;
padding-left: var(--ba-main-space);

View File

@@ -231,8 +231,9 @@ const onClearCache = (type: string) => {
<style scoped lang="scss">
.nav-menus.Default:not(.shrink) {
border-radius: var(--el-border-radius-base);
box-shadow: var(--el-box-shadow-light);
border: 1px solid var(--ba-border-color-soft);
border-radius: var(--ba-radius-panel);
box-shadow: var(--ba-shadow-card);
}
.reload-hot-server-content {
font-size: var(--el-font-size-small);
@@ -250,35 +251,52 @@ const onClearCache = (type: string) => {
height: 100%;
margin-left: auto;
background-color: v-bind('configStore.getColorVal("headerBarBackground")');
overflow: hidden;
.nav-menu-item {
height: 100%;
width: 40px;
width: 44px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition:
background-color 0.2s ease,
transform 0.18s ease;
.nav-menu-icon {
box-sizing: content-box;
color: v-bind('configStore.getColorVal("headerBarTabColor")');
}
&:hover {
transform: translateY(-1px);
.icon {
animation: twinkle 0.3s ease-in-out;
animation: twinkle 0.24s ease-in-out;
}
}
&:active {
transform: translateY(0);
}
}
.admin-info {
display: flex;
height: 100%;
padding: 0 10px;
padding: 0 14px 0 10px;
align-items: center;
cursor: pointer;
user-select: none;
color: v-bind('configStore.getColorVal("headerBarTabColor")');
transition: background-color 0.2s ease;
:deep(.el-avatar) {
border: 2px solid var(--ba-border-color-soft);
background: var(--ba-bg-color-soft);
}
}
.admin-name {
padding-left: 6px;
max-width: 128px;
padding-left: 8px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
font-weight: 600;
}
.nav-menu-item:hover,
.admin-info:hover,
@@ -294,22 +312,34 @@ const onClearCache = (type: string) => {
display: flex;
justify-content: center;
flex-wrap: wrap;
padding-top: 10px;
padding-top: 12px;
:deep(.el-avatar) {
border: 3px solid var(--ba-border-color-soft);
}
.admin-info-other {
display: block;
width: 100%;
text-align: center;
padding: 10px 0;
padding: 12px 0 8px;
.admin-info-name {
font-size: var(--el-font-size-large);
font-weight: 700;
}
.admin-info-lasttime {
margin-top: 4px;
color: var(--el-text-color-secondary);
font-size: var(--el-font-size-small);
}
}
}
.admin-info-footer {
padding: 10px 0;
padding: 12px;
margin: 0 -12px -12px -12px;
display: flex;
justify-content: space-around;
justify-content: flex-end;
gap: 8px;
border-top: 1px solid var(--ba-border-color-soft);
background: var(--ba-bg-color-soft);
}
.pt2 {
padding-top: 2px;

View File

@@ -101,5 +101,6 @@ watch(
width: 100%;
position: relative;
overflow: hidden;
background: linear-gradient(180deg, rgba(255, 255, 255, 0.58), rgba(255, 255, 255, 0) 220px), var(--ba-bg-color);
}
</style>

View File

@@ -55,10 +55,15 @@ onUnmounted(() => {
display: flex;
align-items: center;
justify-content: center;
border: 1px solid var(--ba-border-color-soft);
border-radius: var(--ba-radius-panel);
background: linear-gradient(90deg, transparent, rgba(37, 99, 235, 0.05), transparent), var(--ba-bg-color-overlay);
box-shadow: var(--ba-shadow-card);
}
.loading-footer {
display: flex;
align-items: center;
justify-content: center;
margin-top: 16px;
}
</style>

View File

@@ -15,11 +15,11 @@ export const useConfig = defineStore(
isDark: false,
// 侧边栏
menuBackground: ['#ffffff', '#1d1e1f'],
menuColor: ['#303133', '#CFD3DC'],
menuActiveBackground: ['#ffffff', '#1d1e1f'],
menuActiveColor: ['#409eff', '#3375b9'],
menuTopBarBackground: ['#fcfcfc', '#1d1e1f'],
menuBackground: ['#ffffff', '#182131'],
menuColor: ['#334155', '#cbd5e1'],
menuActiveBackground: ['#eef4ff', '#162846'],
menuActiveColor: ['#2563eb', '#8eadf5'],
menuTopBarBackground: ['#ffffff', '#182131'],
menuWidth: 260,
menuDefaultIcon: 'fa fa-circle-o',
menuCollapse: false,
@@ -27,11 +27,11 @@ export const useConfig = defineStore(
menuShowTopBar: true,
// 顶栏
headerBarTabColor: ['#000000', '#CFD3DC'],
headerBarTabActiveBackground: ['#ffffff', '#1d1e1f'],
headerBarTabActiveColor: ['#000000', '#409EFF'],
headerBarBackground: ['#ffffff', '#1d1e1f'],
headerBarHoverBackground: ['#f5f5f5', '#18222c'],
headerBarTabColor: ['#475569', '#cbd5e1'],
headerBarTabActiveBackground: ['#ffffff', '#182131'],
headerBarTabActiveColor: ['#172033', '#8eadf5'],
headerBarBackground: ['#ffffff', '#182131'],
headerBarHoverBackground: ['#eef4ff', '#162846'],
})
const lang: Lang = reactive({

View File

@@ -3,7 +3,6 @@
margin: 0;
padding: 0;
box-sizing: border-box;
outline: none !important;
}
html,
@@ -14,14 +13,17 @@ body,
width: 100%;
height: 100%;
font-family:
Helvetica Neue,
Helvetica,
ui-sans-serif,
system-ui,
-apple-system,
BlinkMacSystemFont,
Segoe UI,
PingFang SC,
Hiragino Sans GB,
Microsoft YaHei,
SimSun,
sans-serif;
font-weight: 400;
line-height: 1.5;
-webkit-font-smoothing: antialiased;
-webkit-tap-highlight-color: transparent;
background-color: var(--ba-bg-color);
@@ -29,6 +31,36 @@ body,
font-size: var(--el-font-size-base);
}
html {
text-rendering: optimizeLegibility;
}
html,
body {
overscroll-behavior: none;
}
button,
input,
textarea,
select {
font: inherit;
}
a {
color: inherit;
text-decoration: none;
}
:focus-visible {
outline: 2px solid rgba(37, 99, 235, 0.38);
outline-offset: 2px;
}
::selection {
background: rgba(37, 99, 235, 0.18);
}
// 阿里 iconfont Symbol引用css
.iconfont-icon {
width: 1em;
@@ -51,7 +83,7 @@ body,
}
.default-main {
margin: var(--ba-main-space) var(--ba-main-space) 60px var(--ba-main-space);
margin: var(--ba-main-space) var(--ba-main-space) 64px var(--ba-main-space);
}
.zoom-handle {
position: absolute;
@@ -64,10 +96,10 @@ body,
.block-help {
display: block;
width: 100%;
color: #909399;
color: var(--ba-text-muted);
font-size: 13px;
line-height: 16px;
padding-top: 5px;
line-height: 1.45;
padding-top: 6px;
}
/* 表格顶部菜单-s */
@@ -84,7 +116,9 @@ body,
/* 鼠标置入浮动效果-s */
.suspension {
transition: all 0.3s ease;
transition:
transform 0.24s ease,
box-shadow 0.24s ease;
}
.suspension:hover {
-webkit-transform: translateY(-4px) scale(1.02);
@@ -92,20 +126,23 @@ body,
-ms-transform: translateY(-4px) scale(1.02);
-o-transform: translateY(-4px) scale(1.02);
transform: translateY(-4px) scale(1.02);
-webkit-box-shadow: 0 14px 24px rgba(0, 0, 0, 0.2);
box-shadow: 0 14px 24px rgba(0, 0, 0, 0.2);
-webkit-box-shadow: 0 16px 34px rgba(15, 23, 42, 0.14);
box-shadow: 0 16px 34px rgba(15, 23, 42, 0.14);
z-index: 2147483600;
border-radius: 6px;
border-radius: var(--ba-radius-panel);
}
/* 鼠标置入浮动效果-e */
/* 表格-s */
.ba-table-box {
border-radius: var(--el-border-radius-round);
border-radius: var(--ba-radius-panel);
overflow: hidden;
background: var(--ba-bg-color-overlay);
box-shadow: var(--ba-shadow-card);
}
.ba-table-alert {
background-color: var(--el-fill-color-darker) !important;
border: 1px solid var(--ba-boder-color);
background-color: var(--ba-bg-color-soft) !important;
border: 1px solid var(--ba-border-color);
border-bottom: 0;
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
@@ -115,28 +152,31 @@ body,
/* 新增/编辑表单-s */
.ba-operate-dialog {
overflow: hidden;
border-radius: var(--el-border-radius-base);
padding-bottom: 52px;
border-radius: var(--ba-radius-panel);
padding-bottom: 58px;
}
.ba-operate-dialog .el-dialog__header {
border-bottom: 1px solid var(--ba-bg-color);
border-bottom: 1px solid var(--ba-border-color-soft);
padding: 18px 22px;
.el-dialog__headerbtn {
top: 4px;
top: 8px;
}
}
.ba-operate-dialog .el-dialog__body {
height: 58vh;
padding: 20px 22px;
}
.ba-operate-dialog .el-dialog__footer {
padding: 10px var(--el-dialog-padding-primary);
box-shadow: var(--el-box-shadow);
padding: 12px 22px;
box-shadow: 0 -10px 24px rgba(15, 23, 42, 0.06);
position: absolute;
width: 100%;
bottom: 0;
left: 0;
background: var(--ba-bg-color-overlay);
}
.ba-operate-form {
padding-top: 20px;
padding-top: 16px;
}
/* 新增/编辑表单-e */
@@ -145,9 +185,10 @@ body,
position: fixed;
top: 0;
left: 0;
height: 100vh;
height: 100dvh;
width: 100vw;
background-color: rgba(0, 0, 0, 0.5);
background-color: rgba(15, 23, 42, 0.48);
backdrop-filter: blur(2px);
z-index: 2147483599;
}
/* 全局遮罩-e */
@@ -169,7 +210,9 @@ body,
.slide-left-enter-active,
.slide-left-leave-active {
will-change: transform;
transition: all 0.3s ease;
transition:
transform 0.22s ease,
opacity 0.22s ease;
}
// slide-right
.slide-right-enter-from {
@@ -194,9 +237,9 @@ body,
min-height: calc(100vh - 120px);
}
.user-views {
padding-left: 15px;
padding-left: var(--ba-main-space);
.user-views-card {
margin-bottom: 15px;
margin-bottom: var(--ba-main-space);
}
}
.ba-aside-drawer {
@@ -226,6 +269,9 @@ body,
.xs-hidden {
display: none;
}
.default-main {
margin: 12px 12px 48px 12px;
}
}
@media screen and (max-width: 1024px) {
.ba-operate-dialog {

View File

@@ -6,8 +6,10 @@
$bg-color: () !default;
$bg-color: map.merge(
(
'': #141414,
'overlay': #1d1e1f,
'': #101722,
'overlay': #182131,
'soft': #111c2a,
'muted': #0d1420,
),
$bg-color
);
@@ -16,7 +18,9 @@ $bg-color: map.merge(
$border-color: () !default;
$border-color: map.merge(
(
'': #4c4d4f,
'': #2d3a4c,
'soft': #263244,
'strong': #3a4a61,
),
$border-color
);
@@ -24,4 +28,22 @@ $border-color: map.merge(
html.dark {
@include set-component-css-var('bg-color', $bg-color);
@include set-component-css-var('border-color', $border-color);
--ba-surface-soft: #111c2a;
--ba-surface-muted: #0d1420;
--ba-text-muted: #94a3b8;
--ba-shadow-soft: 0 18px 44px rgba(0, 0, 0, 0.34);
--ba-shadow-card: 0 12px 30px rgba(0, 0, 0, 0.28);
--el-color-primary: #5b86f0;
--el-color-primary-light-3: #406fcf;
--el-color-primary-light-5: #2e4f93;
--el-color-primary-light-7: #203b6f;
--el-color-primary-light-8: #1b315a;
--el-color-primary-light-9: #162846;
--el-color-primary-dark-2: #8eadf5;
--el-text-color-primary: #e5edf7;
--el-text-color-regular: #cbd5e1;
--el-text-color-secondary: #94a3b8;
--el-fill-color-light: #111c2a;
--el-fill-color-lighter: #0d1420;
}

View File

@@ -1,19 +1,189 @@
.el-menu {
user-select: none;
padding: 8px;
background: transparent;
.el-sub-menu__title:hover {
background-color: var(--el-color-primary-light-9) !important;
}
.el-menu-item,
.el-sub-menu__title {
height: 42px;
margin: 3px 0;
border-radius: var(--ba-radius-control);
line-height: 42px;
transition:
background-color 0.2s ease,
color 0.2s ease,
transform 0.2s ease;
}
.el-menu-item:hover,
.el-sub-menu__title:hover {
transform: translateX(2px);
}
.el-menu-item.is-active {
background: var(--el-color-primary-light-9);
color: var(--el-color-primary);
font-weight: 600;
}
}
.el-table {
--el-table-border-color: var(--ba-border-color);
--el-table-header-bg-color: var(--ba-bg-color-soft);
--el-table-header-text-color: var(--el-text-color-primary);
--el-table-row-hover-bg-color: var(--el-color-primary-light-9);
color: var(--el-text-color-regular);
font-variant-numeric: tabular-nums;
.el-table__cell {
padding: 9px 0;
}
.el-table__header th {
font-weight: 600;
letter-spacing: 0.01em;
}
.el-table__empty-block {
min-height: 180px;
}
.el-table__empty-text {
color: var(--el-text-color-secondary);
}
}
.el-card {
border: none;
border-radius: var(--ba-radius-panel);
box-shadow: var(--ba-shadow-card);
.el-card__header {
border-bottom: 1px solid var(--el-border-color-extra-light);
border-bottom: 1px solid var(--ba-border-color-soft);
padding: 14px 18px;
font-weight: 600;
}
.el-card__body {
padding: 18px;
}
}
.el-button {
border-radius: var(--ba-radius-control);
font-weight: 600;
transition:
transform 0.18s ease,
box-shadow 0.18s ease,
background-color 0.18s ease,
border-color 0.18s ease;
&:hover {
transform: translateY(-1px);
}
&:active {
transform: translateY(0);
}
&.is-link:hover,
&.is-text:hover,
&.is-circle:hover {
transform: none;
}
}
.el-button--primary:not(.is-link, .is-text, .is-plain) {
box-shadow: 0 8px 18px rgba(37, 99, 235, 0.18);
}
.el-button--danger:not(.is-link, .is-text, .is-plain) {
box-shadow: 0 8px 18px rgba(220, 63, 77, 0.14);
}
.el-input__wrapper,
.el-textarea__inner,
.el-select__wrapper {
border-radius: var(--ba-radius-control);
box-shadow: 0 0 0 1px var(--ba-border-color-soft) inset;
transition:
box-shadow 0.2s ease,
background-color 0.2s ease;
}
.el-input__wrapper:hover,
.el-textarea__inner:hover,
.el-select__wrapper:hover {
box-shadow: 0 0 0 1px var(--ba-border-color-strong) inset;
}
.el-input__wrapper.is-focus,
.el-textarea__inner:focus,
.el-select__wrapper.is-focused {
box-shadow:
0 0 0 1px var(--el-color-primary) inset,
0 0 0 3px rgba(37, 99, 235, 0.12);
}
.el-date-editor.el-input__wrapper {
border-radius: var(--ba-radius-control);
}
.el-dialog {
border-radius: var(--ba-radius-panel);
box-shadow: 0 24px 70px rgba(15, 23, 42, 0.2);
overflow: hidden;
}
.el-dialog__header {
margin-right: 0;
padding: 18px 22px;
border-bottom: 1px solid var(--ba-border-color-soft);
}
.el-dialog__title {
color: var(--el-text-color-primary);
font-weight: 700;
}
.el-dialog__body {
padding: 20px 22px;
}
.el-dialog__footer {
padding: 14px 22px 18px;
background: var(--ba-bg-color-soft);
border-top: 1px solid var(--ba-border-color-soft);
}
.el-overlay {
backdrop-filter: blur(2px);
}
.el-popper,
.el-dropdown__popper,
.el-popover.el-popper {
border: 1px solid var(--ba-border-color-soft);
border-radius: var(--ba-radius-panel);
box-shadow: var(--ba-shadow-soft);
}
.el-dropdown-menu {
padding: 6px;
}
.el-dropdown-menu__item {
border-radius: 6px;
}
.el-pagination {
font-variant-numeric: tabular-nums;
}
.el-pagination.is-background .el-pager li,
.el-pagination.is-background .btn-prev,
.el-pagination.is-background .btn-next {
border-radius: 7px;
}
.el-tag {
border-radius: 6px;
font-weight: 600;
}
.el-alert {
border-radius: var(--ba-radius-panel);
}
.el-divider__text.is-center {
@@ -38,7 +208,7 @@
}
}
.el-textarea__inner {
padding: 5px 11px;
padding: 7px 11px;
}
.datetime-picker {
height: 32px;
@@ -55,14 +225,14 @@
height: 5px;
}
&::-webkit-scrollbar-thumb {
background: #eaeaea;
background: #d5dde8;
border-radius: var(--el-border-radius-base);
box-shadow: none;
-webkit-box-shadow: none;
}
&:hover {
&::-webkit-scrollbar-thumb:hover {
background: #c8c9cc;
background: #aeb9c8;
}
}
}
@@ -70,7 +240,7 @@
.el-overlay-dialog,
.ba-scroll-style {
scrollbar-width: thin;
scrollbar-color: #c8c9cc #eaeaea;
scrollbar-color: #aeb9c8 #d5dde8;
}
}
/* dialog 滚动条样式优化结束 >>> */

View File

@@ -2,4 +2,5 @@
@use '/@/styles/element';
@use '/@/styles/var';
@use '/@/styles/dark';
@use '/@/styles/report-table';
@use '/@/styles/markdown';

View File

@@ -0,0 +1,276 @@
.admin-report-page {
color: var(--el-text-color-primary);
font-size: 14px;
font-variant-numeric: tabular-nums;
}
.admin-report-tabs {
display: flex;
width: fit-content;
max-width: 100%;
gap: 4px;
margin-bottom: var(--ba-main-space);
padding: 4px;
overflow-x: auto;
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);
button {
min-height: 34px;
padding: 0 18px;
border: 0;
border-radius: var(--ba-radius-control);
background: transparent;
color: var(--el-text-color-regular);
font-weight: 600;
white-space: nowrap;
cursor: pointer;
transition:
color 0.18s ease,
background-color 0.18s ease,
box-shadow 0.18s ease;
&:hover {
background: var(--el-color-primary-light-9);
color: var(--el-color-primary);
}
&.active {
background: var(--el-color-primary);
color: var(--el-color-white);
box-shadow: 0 8px 18px rgba(37, 99, 235, 0.18);
}
}
}
.admin-report-filter {
margin-bottom: var(--ba-main-space);
overflow: hidden;
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);
}
.admin-report-heading {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
min-height: 46px;
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-weight: 700;
strong {
display: inline-flex;
align-items: center;
min-height: 24px;
padding: 0 10px;
border-radius: 999px;
background: var(--el-color-success-light-9);
color: var(--el-color-success);
font-size: 12px;
font-weight: 700;
}
}
.admin-report-filter-content {
padding: 16px;
background: var(--ba-bg-color-overlay);
}
.admin-report-filter-content .filter-item,
.admin-report-filter-content .date-fields {
display: flex;
align-items: center;
gap: 8px;
}
.admin-report-filter-content label {
color: var(--el-text-color-secondary);
font-weight: 600;
white-space: nowrap;
}
.admin-report-filter-content .el-date-editor,
.admin-report-filter-content .el-input,
.admin-report-filter-content .el-select {
width: 200px;
}
.admin-report-actions {
display: flex;
align-items: center;
gap: 8px;
.el-button {
min-width: 92px;
margin: 0;
}
}
.admin-report-summary {
margin: 0 0 10px;
color: var(--el-text-color-regular);
font-size: 13px;
strong {
color: var(--el-text-color-primary);
font-weight: 700;
}
}
.admin-report-table-wrap {
overflow-x: auto;
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);
}
.admin-report-table {
width: 100%;
border-collapse: separate;
border-spacing: 0;
color: var(--el-text-color-regular);
font-size: 13px;
line-height: 1.45;
font-variant-numeric: tabular-nums;
th,
td {
height: 42px;
padding: 10px 12px;
border-right: 1px solid var(--ba-border-color-soft);
border-bottom: 1px solid var(--ba-border-color-soft);
background: var(--ba-bg-color-overlay);
text-align: left;
vertical-align: middle;
white-space: nowrap;
}
th:last-child,
td:last-child {
border-right: 0;
}
thead th {
border-bottom-color: var(--ba-border-color-strong);
background: var(--ba-bg-color-soft);
color: var(--el-text-color-primary);
font-weight: 700;
small {
display: block;
margin-top: 2px;
color: var(--el-text-color-secondary);
font-size: 11px;
font-weight: 500;
}
}
tbody tr:last-child td {
border-bottom: 0;
}
tbody tr:hover td {
background: var(--el-color-primary-light-9);
}
.date-cell,
.reward-cell {
background: var(--ba-bg-color-soft);
}
.deposit-value,
.positive-value {
color: var(--el-color-success);
font-weight: 700;
}
.withdraw-value,
.negative-value {
color: var(--el-color-danger);
font-weight: 700;
}
.unclaim-value {
color: var(--el-color-primary);
font-weight: 700;
}
.empty-row {
height: 150px;
color: var(--el-text-color-secondary);
text-align: center;
}
}
.admin-report-footer {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
margin-top: 12px;
padding: 12px 16px;
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);
color: var(--el-text-color-secondary);
font-size: 13px;
}
.admin-report-page-size {
display: flex;
align-items: center;
gap: 8px;
.el-select {
width: 96px;
}
}
html.dark {
.admin-report-heading strong {
background: rgba(22, 138, 91, 0.18);
}
}
@media (max-width: 768px) {
.admin-report-tabs {
width: 100%;
}
.admin-report-filter-content {
padding: 12px;
}
.admin-report-filter-content .filter-item,
.admin-report-filter-content .date-fields {
align-items: flex-start;
flex-direction: column;
}
.admin-report-filter-content .el-date-editor,
.admin-report-filter-content .el-input,
.admin-report-filter-content .el-select {
width: 100%;
}
.admin-report-actions,
.admin-report-footer {
align-items: stretch;
flex-direction: column;
}
.admin-report-actions .el-button,
.admin-report-footer .el-pagination {
width: 100%;
}
}

View File

@@ -2,15 +2,24 @@
@use 'mixins' as *;
// 后台主体窗口左右间距
$main-space: 16px;
$primary-light: #3f6ad8;
$main-space: 18px;
$primary-light: #2563eb;
$surface-soft: #eef3f8;
$surface-muted: #f7f9fc;
$text-muted: #64748b;
$shadow-soft: 0 14px 34px rgba(15, 23, 42, 0.08);
$shadow-card: 0 10px 24px rgba(15, 23, 42, 0.06);
$radius-panel: 10px;
$radius-control: 7px;
// --ba-background
$bg-color: () !default;
$bg-color: map.merge(
(
'': #f5f5f5,
'': #eef3f8,
'overlay': #ffffff,
'soft': $surface-muted,
'muted': $surface-soft,
),
$bg-color
);
@@ -19,7 +28,9 @@ $bg-color: map.merge(
$border-color: () !default;
$border-color: map.merge(
(
'': #f6f6f6,
'': #d8e0ea,
'soft': #e8eef5,
'strong': #c7d2de,
),
$border-color
);
@@ -27,6 +38,38 @@ $border-color: map.merge(
:root {
@include set-css-var-value('main-space', $main-space);
@include set-css-var-value('color-primary-light', $primary-light);
@include set-css-var-value('surface-soft', $surface-soft);
@include set-css-var-value('surface-muted', $surface-muted);
@include set-css-var-value('text-muted', $text-muted);
@include set-css-var-value('shadow-soft', $shadow-soft);
@include set-css-var-value('shadow-card', $shadow-card);
@include set-css-var-value('radius-panel', $radius-panel);
@include set-css-var-value('radius-control', $radius-control);
@include set-component-css-var('bg-color', $bg-color);
@include set-component-css-var('border-color', $border-color);
--el-color-primary: #2563eb;
--el-color-primary-light-3: #5b86f0;
--el-color-primary-light-5: #8eadf5;
--el-color-primary-light-7: #c7d7fb;
--el-color-primary-light-8: #dbe6fd;
--el-color-primary-light-9: #eef4ff;
--el-color-primary-dark-2: #1d4ed8;
--el-color-success: #168a5b;
--el-color-success-light-9: #ecfdf5;
--el-color-warning: #b7791f;
--el-color-warning-light-9: #fff7ed;
--el-color-danger: #dc3f4d;
--el-color-danger-light-9: #fff1f2;
--el-color-info: #64748b;
--el-border-radius-base: #{$radius-control};
--el-border-radius-small: 5px;
--el-border-radius-round: #{$radius-panel};
--el-box-shadow-light: #{$shadow-card};
--el-box-shadow: #{$shadow-soft};
--el-text-color-primary: #172033;
--el-text-color-regular: #334155;
--el-text-color-secondary: #64748b;
--el-fill-color-light: #f7f9fc;
--el-fill-color-lighter: #fbfcfe;
}

View File

@@ -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>

View File

@@ -18,10 +18,10 @@
<div class="login">
<div class="login-box">
<div class="head">
<img src="~assets/login-header.png" alt="" />
<img src="~assets/login-header.png" alt="login" />
</div>
<div class="form">
<img class="profile-avatar" :src="fullUrl('/static/images/avatar.png')" alt="" />
<img class="profile-avatar" :src="fullUrl('/static/images/avatar.png')" alt="avatar" />
<div class="content">
<el-form @keyup.enter="onSubmitPre()" ref="formRef" :rules="rules" size="large" :model="form">
<el-form-item prop="username">
@@ -52,14 +52,7 @@
</el-form-item>
<el-checkbox v-model="form.keep" :label="t('login.Hold session')" size="default"></el-checkbox>
<el-form-item>
<el-button
:loading="state.submitLoading"
class="submit-button"
round
type="primary"
size="large"
@click="onSubmitPre()"
>
<el-button :loading="state.submitLoading" class="submit-button" type="primary" size="large" @click="onSubmitPre()">
{{ t('login.Sign in') }}
</el-button>
</el-form-item>
@@ -173,13 +166,26 @@ const onSubmit = (captchaInfo = '') => {
<style scoped lang="scss">
.switch-language {
position: fixed;
top: 20px;
right: 20px;
z-index: 1;
top: 22px;
right: 22px;
z-index: 2;
display: flex;
align-items: center;
justify-content: center;
width: 42px;
height: 42px;
border: 1px solid rgba(148, 163, 184, 0.28);
border-radius: var(--ba-radius-panel);
background: rgba(255, 255, 255, 0.76);
box-shadow: var(--ba-shadow-card);
backdrop-filter: blur(10px);
}
.bubble {
overflow: hidden;
background: url(/@/assets/bg.jpg) repeat;
background:
linear-gradient(135deg, rgba(238, 243, 248, 0.94), rgba(226, 232, 240, 0.9)),
url(/@/assets/bg.jpg) repeat;
background-blend-mode: screen;
}
.form-item-icon {
height: auto;
@@ -189,21 +195,25 @@ const onSubmit = (captchaInfo = '') => {
top: 0;
display: flex;
width: 100vw;
height: 100vh;
min-height: 100dvh;
align-items: center;
justify-content: center;
.login-box {
overflow: hidden;
width: 430px;
width: min(430px, calc(100vw - 32px));
padding: 0;
background: var(--ba-bg-color-overlay);
margin-bottom: 80px;
margin-bottom: 64px;
border: 1px solid rgba(216, 224, 234, 0.8);
border-radius: 16px;
box-shadow: 0 26px 72px rgba(15, 23, 42, 0.18);
}
.head {
background: #ccccff;
background: linear-gradient(135deg, rgba(37, 99, 235, 0.16), rgba(15, 23, 42, 0.02)), var(--ba-bg-color-soft);
border-bottom: 1px solid var(--ba-border-color-soft);
img {
display: block;
width: 430px;
width: 100%;
margin: 0 auto;
user-select: none;
}
@@ -213,22 +223,27 @@ const onSubmit = (captchaInfo = '') => {
.profile-avatar {
display: block;
position: absolute;
height: 100px;
width: 100px;
border-radius: 50%;
border: 4px solid var(--ba-bg-color-overlay);
top: -50px;
right: calc(50% - 50px);
height: 92px;
width: 92px;
border-radius: 18px;
border: 5px solid var(--ba-bg-color-overlay);
top: -46px;
right: calc(50% - 46px);
z-index: 2;
user-select: none;
box-shadow: 0 12px 30px rgba(15, 23, 42, 0.16);
}
.content {
padding: 100px 40px 40px 40px;
padding: 92px 40px 40px 40px;
:deep(.el-form-item) {
margin-bottom: 20px;
}
}
.submit-button {
width: 100%;
letter-spacing: 2px;
font-weight: 300;
min-height: 44px;
letter-spacing: 0;
font-weight: 700;
margin-top: 15px;
--el-button-bg-color: var(--el-color-primary);
}
@@ -241,9 +256,17 @@ const onSubmit = (captchaInfo = '') => {
align-items: center;
justify-content: center;
.login-box {
width: 340px;
width: calc(100vw - 28px);
margin-top: 0;
margin-bottom: 0;
}
.form .content {
padding: 86px 24px 28px 24px;
}
}
.switch-language {
top: 14px;
right: 14px;
}
}
.chang-lang :deep(.el-dropdown-menu__item) {
@@ -257,15 +280,24 @@ const onSubmit = (captchaInfo = '') => {
// 暗黑样式
@at-root .dark {
.bubble {
background: url(/@/assets/bg-dark.jpg) repeat;
background:
linear-gradient(135deg, rgba(16, 23, 34, 0.92), rgba(24, 33, 49, 0.88)),
url(/@/assets/bg-dark.jpg) repeat;
background-blend-mode: multiply;
}
.switch-language {
background: rgba(24, 33, 49, 0.76);
border-color: rgba(58, 74, 97, 0.85);
}
.login {
.login-box {
background: #161b22;
background: #182131;
border-color: rgba(58, 74, 97, 0.86);
}
.head {
background: linear-gradient(135deg, rgba(91, 134, 240, 0.14), rgba(13, 20, 32, 0.18)), #111c2a;
img {
filter: brightness(61%);
filter: brightness(72%);
}
}
.form {

View File

@@ -1,8 +1,8 @@
<template>
<div class="default-main customer-report">
<section class="report-filter">
<header>{{ t('user.moneyLog.customerReport.title') }}</header>
<div class="filter-content">
<div class="default-main admin-report-page customer-report">
<section class="admin-report-filter report-filter">
<header class="admin-report-heading">{{ t('user.moneyLog.customerReport.title') }}</header>
<div class="admin-report-filter-content filter-content">
<div class="filter-item">
<label>{{ t('user.moneyLog.customerReport.startDate') }}:</label>
<el-date-picker v-model="filters.start" type="date" value-format="YYYY-MM-DD" clearable />
@@ -17,26 +17,22 @@
</div>
<div class="filter-item">
<label>{{ t('user.moneyLog.customerReport.loseRebate') }}:</label>
<el-input
v-model="filters.loseRebate"
:placeholder="t('user.moneyLog.customerReport.rebatePlaceholder')"
clearable
/>
<el-input v-model="filters.loseRebate" :placeholder="t('user.moneyLog.customerReport.rebatePlaceholder')" clearable />
</div>
<div class="filter-actions">
<div class="admin-report-actions filter-actions">
<el-button type="primary" @click="search">{{ t('user.moneyLog.customerReport.search') }}</el-button>
<el-button @click="clear">{{ t('user.moneyLog.customerReport.clear') }}</el-button>
</div>
</div>
</section>
<div class="date-summary">
<div class="admin-report-summary date-summary">
<strong>{{ t('user.moneyLog.customerReport.dateOfData') }}:</strong>
{{ dateSummary }}
</div>
<div v-loading="loading" class="report-table-wrap">
<table class="report-table" :style="{ minWidth: `${tableMinWidth}px` }">
<div v-loading="loading" class="admin-report-table-wrap report-table-wrap">
<table class="admin-report-table report-table" :style="{ minWidth: `${tableMinWidth}px` }">
<thead>
<tr>
<th rowspan="2" class="date-column">{{ t('user.moneyLog.customerReport.registerDate') }} </th>
@@ -83,7 +79,7 @@
</table>
</div>
<div class="table-footer">
<div class="admin-report-footer table-footer">
<div>{{ t('user.moneyLog.customerReport.totalRecords', { total }) }}</div>
<el-pagination
v-model:current-page="currentPage"
@@ -94,7 +90,7 @@
:page-count="lastPage"
@current-change="loadReport"
/>
<div class="page-size">
<div class="admin-report-page-size page-size">
<span>{{ t('user.moneyLog.customerReport.show') }}</span>
<el-select v-model="pageSize" @change="changePageSize">
<el-option v-for="size in pageSizes" :key="size" :label="size" :value="size" />
@@ -269,48 +265,20 @@ onMounted(loadReport)
<style scoped lang="scss">
.customer-report {
--report-dark: #333;
--report-border: #d8d8d8;
--report-reward: #e5e5e5;
--report-surface: var(--el-bg-color);
padding: 20px;
color: var(--el-text-color-primary);
font-size: 14px;
}
.report-filter {
margin-bottom: 20px;
border: 1px solid var(--report-border);
background: var(--report-surface);
header {
min-height: 37px;
padding: 9px 8px;
background: var(--report-dark);
color: #fff;
font-weight: 700;
}
padding: var(--ba-main-space);
}
.filter-content {
display: grid;
grid-template-columns: 335px 335px;
gap: 6px 18px;
min-height: 120px;
padding: 13px 20px;
grid-template-columns: repeat(2, minmax(280px, 350px));
gap: 12px 18px;
}
.filter-item {
display: flex;
align-items: center;
gap: 8px;
label {
width: 115px;
width: 118px;
flex-shrink: 0;
font-weight: 700;
text-align: right;
white-space: nowrap;
}
:deep(.el-date-editor),
@@ -321,53 +289,11 @@ onMounted(loadReport)
.filter-actions {
grid-column: 2;
display: flex;
gap: 16px;
.el-button {
width: 160px;
margin: 0;
}
}
.date-summary {
margin-bottom: 8px;
}
.report-table-wrap {
overflow-x: auto;
justify-content: flex-start;
}
.report-table {
width: 100%;
min-width: 2500px;
border-collapse: collapse;
th,
td {
height: 38px;
padding: 8px;
border: 1px solid var(--report-border);
text-align: left;
white-space: nowrap;
}
thead th {
border-color: #eee;
background: var(--report-dark);
color: #fff;
font-weight: 700;
vertical-align: middle;
small {
display: block;
font-size: 11px;
}
}
tbody td {
background: var(--report-surface);
}
.date-column {
width: 225px;
@@ -381,10 +307,6 @@ onMounted(loadReport)
min-width: 250px;
}
.reward-cell {
background: var(--report-reward);
}
.username-cell {
display: flex;
align-items: center;
@@ -392,55 +314,19 @@ onMounted(loadReport)
}
.user-icon {
color: var(--el-text-color-primary);
}
.deposit-value,
.positive-value {
color: #35ad78;
}
.withdraw-value,
.negative-value {
color: #e05a55;
}
.empty-row {
height: 150px;
color: var(--el-text-color-secondary);
text-align: center;
}
}
.table-footer {
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 8px;
margin-top: 18px;
}
.page-size {
display: flex;
align-items: center;
gap: 8px;
.el-select {
width: 90px;
}
}
@at-root html.dark {
.customer-report {
--report-dark: #2b2b2b;
--report-border: var(--el-border-color);
--report-reward: #333;
:deep(.el-pagination) {
margin-left: auto;
}
}
@media (max-width: 768px) {
.customer-report {
padding: 14px;
padding: 12px;
}
.filter-content {
@@ -449,8 +335,7 @@ onMounted(loadReport)
}
.filter-item {
align-items: flex-start;
flex-direction: column;
display: flex;
label {
width: auto;
@@ -459,6 +344,7 @@ onMounted(loadReport)
}
.filter-actions {
grid-column: auto;
width: 100%;
}
}

View File

@@ -1,27 +1,15 @@
<template>
<div class="default-main daily-report">
<section class="report-filter">
<header>{{ t('user.moneyLog.dailyReport.title') }}</header>
<div class="filter-content">
<div class="default-main admin-report-page daily-report">
<section class="admin-report-filter report-filter">
<header class="admin-report-heading">{{ t('user.moneyLog.dailyReport.title') }}</header>
<div class="admin-report-filter-content filter-content">
<div class="date-fields">
<label>{{ t('user.moneyLog.dailyReport.startDate') }}:</label>
<el-date-picker
v-model="filters.start"
type="date"
value-format="YYYY-MM-DD"
:clearable="false"
@change="loadReport"
/>
<el-date-picker v-model="filters.start" type="date" value-format="YYYY-MM-DD" :clearable="false" @change="loadReport" />
<label>{{ t('user.moneyLog.dailyReport.endDate') }}:</label>
<el-date-picker
v-model="filters.end"
type="date"
value-format="YYYY-MM-DD"
:clearable="false"
@change="loadReport"
/>
<el-date-picker v-model="filters.end" type="date" value-format="YYYY-MM-DD" :clearable="false" @change="loadReport" />
</div>
<div class="period-tabs">
<div class="admin-report-tabs period-tabs">
<button
v-for="period in periods"
:key="period.value"
@@ -35,8 +23,8 @@
</div>
</section>
<div v-loading="loading" class="report-table-wrap">
<table class="report-table" :style="{ minWidth: `${tableMinWidth}px` }">
<div v-loading="loading" class="admin-report-table-wrap report-table-wrap">
<table class="admin-report-table report-table" :style="{ minWidth: `${tableMinWidth}px` }">
<thead>
<tr>
<th rowspan="2" class="date-column">{{ t('user.moneyLog.dailyReport.date') }}</th>
@@ -233,117 +221,33 @@ onMounted(loadReport)
<style scoped lang="scss">
.daily-report {
--report-dark: #333;
--report-border: #ddd;
--report-strong-border: #999;
--report-muted: #ddd;
--report-surface: var(--el-bg-color);
padding: 20px;
color: var(--el-text-color-primary);
font-size: 14px;
}
.report-filter {
margin-bottom: 20px;
border: 1px solid var(--report-border);
background: var(--report-surface);
header {
min-height: 37px;
padding: 9px 8px;
background: var(--report-dark);
color: #fff;
font-weight: 700;
}
padding: var(--ba-main-space);
}
.filter-content {
min-height: 92px;
padding: 13px;
display: flex;
align-items: flex-start;
flex-direction: column;
gap: 12px;
}
.date-fields {
display: flex;
align-items: center;
gap: 8px;
margin-bottom: 5px;
label {
font-weight: 700;
white-space: nowrap;
}
:deep(.el-date-editor) {
width: 200px;
margin-right: 0;
}
}
.period-tabs {
display: flex;
width: fit-content;
overflow: hidden;
border: 1px solid #999;
border-radius: 3px;
margin-bottom: 0;
box-shadow: none;
button {
width: 100px;
height: 32px;
border: 0;
border-right: 1px solid #999;
background: var(--el-fill-color-light);
color: var(--el-text-color-primary);
cursor: pointer;
&:last-child {
border-right: 0;
}
&:hover {
background: var(--el-fill-color);
}
&.active {
background: #bbb;
color: #000;
}
min-width: 96px;
}
}
.report-table-wrap {
overflow-x: auto;
}
.report-table {
width: 100%;
min-width: 2516px;
border-collapse: collapse;
th,
td {
height: 38px;
padding: 8px;
border: 1px solid var(--report-border);
text-align: left;
white-space: nowrap;
}
thead {
background: var(--report-dark);
color: #fff;
th {
border-color: #eee;
background: var(--report-dark);
font-weight: 700;
vertical-align: middle;
}
}
tbody td {
background: var(--report-surface);
color: var(--el-text-color-primary);
}
.date-column,
.date-cell {
@@ -352,9 +256,7 @@ onMounted(loadReport)
.date-cell,
.reward-cell {
border-color: var(--report-strong-border);
background: var(--report-muted);
color: #333;
font-weight: 600;
}
.single-column {
@@ -372,56 +274,15 @@ onMounted(loadReport)
.reward-heading {
min-width: 230px;
}
.deposit-value,
.positive-value {
color: #3cb371;
}
.withdraw-value,
.negative-value {
color: #ff5349;
}
.unclaim-value {
color: #9457c5;
}
.empty-row {
height: 150px;
background: var(--report-surface);
color: var(--el-text-color-secondary);
text-align: center;
}
}
@at-root html.dark {
.daily-report {
--report-dark: #2b2b2b;
--report-border: var(--el-border-color);
--report-strong-border: #606266;
--report-muted: #333;
}
.period-tabs button.active {
background: #555;
color: #fff;
}
.report-table .date-cell,
.report-table .reward-cell {
color: var(--el-text-color-primary);
}
}
@media (max-width: 768px) {
.daily-report {
padding: 14px;
padding: 12px;
}
.date-fields {
align-items: flex-start;
flex-direction: column;
width: 100%;
}
}
</style>

View File

@@ -1,6 +1,6 @@
<template>
<div class="default-main submitted-rewards">
<nav class="reward-tabs" :aria-label="t('user.submittedReward.Promotion type')">
<div class="default-main admin-report-page submitted-rewards">
<nav class="admin-report-tabs reward-tabs" :aria-label="t('user.submittedReward.Promotion type')">
<button
v-for="tab in promotionTabs"
:key="tab.value"
@@ -12,13 +12,13 @@
</button>
</nav>
<section class="reward-panel">
<header class="panel-heading">
<section class="admin-report-filter reward-panel">
<header class="admin-report-heading panel-heading">
<span>{{ t('user.submittedReward.Submitted Rewards') }} ({{ activePromotionLabel }})</span>
<strong>{{ t('user.submittedReward.Running') }}</strong>
</header>
<div class="filter-panel">
<div class="admin-report-filter-content filter-panel">
<div class="filter-item">
<label>{{ t('user.submittedReward.Start Date') }}:</label>
<el-date-picker v-model="filters.start" type="date" value-format="YYYY-MM-DD" :clearable="false" />
@@ -45,20 +45,20 @@
@keyup.enter="search"
/>
</div>
<div class="filter-actions">
<el-button @click="search">{{ t('Search') }}</el-button>
<div class="admin-report-actions filter-actions">
<el-button type="primary" @click="search">{{ t('Search') }}</el-button>
<el-button @click="clearFilters">{{ t('user.submittedReward.Clear') }}</el-button>
</div>
</div>
</section>
<div class="date-summary">
<div class="admin-report-summary date-summary">
<strong>{{ t('user.submittedReward.Date of data') }}:</strong>
{{ dateRangeText }}
</div>
<div v-loading="loading" class="reward-table-wrap">
<table class="reward-table">
<div v-loading="loading" class="admin-report-table-wrap reward-table-wrap">
<table class="admin-report-table reward-table">
<thead>
<tr>
<th class="submitted-time">{{ t('user.submittedReward.Submitted Time') }}</th>
@@ -76,20 +76,10 @@
<td>{{ row.status }}</td>
<td>
<div v-if="row.statusCode === 0" class="action-buttons">
<el-button
type="success"
size="small"
:loading="actionLoadingId === row.id"
@click="confirmReward(row, 1)"
>
<el-button type="success" size="small" :loading="actionLoadingId === row.id" @click="confirmReward(row, 1)">
{{ t('user.submittedReward.Agree') }}
</el-button>
<el-button
type="danger"
size="small"
:loading="actionLoadingId === row.id"
@click="confirmReward(row, 2)"
>
<el-button type="danger" size="small" :loading="actionLoadingId === row.id" @click="confirmReward(row, 2)">
{{ t('user.submittedReward.Reject') }}
</el-button>
</div>
@@ -102,7 +92,7 @@
</table>
</div>
<div class="table-footer">
<div class="admin-report-footer table-footer">
<span>{{ t('user.submittedReward.Total records', { total: pagination.total }) }}</span>
<el-pagination
v-if="pagination.total > 0"
@@ -295,87 +285,38 @@ onMounted(() => {
<style scoped lang="scss">
.submitted-rewards {
--reward-dark: #333;
--reward-border: #ddd;
--reward-approved: #dcfbc9;
padding: 20px;
color: var(--el-text-color-primary);
font-size: 14px;
padding: var(--ba-main-space);
}
.reward-tabs {
display: flex;
width: fit-content;
margin-bottom: 20px;
overflow: hidden;
border: 1px solid #999;
border-radius: 4px;
button {
min-width: 136px;
height: 31px;
padding: 5px 30px;
border: 0;
border-right: 1px solid #999;
background: var(--el-fill-color-light);
color: var(--el-text-color-primary);
cursor: pointer;
&:last-child {
min-width: 99px;
border-right: 0;
}
&:hover {
background: var(--el-fill-color);
}
&.active {
background: #bbb;
color: #000;
min-width: 112px;
}
}
}
.reward-panel {
border: 1px solid var(--reward-border);
}
.panel-heading {
display: flex;
align-items: center;
gap: 12px;
min-height: 40px;
padding: 10px;
background: var(--reward-dark);
color: #fff;
font-weight: 700;
justify-content: flex-start;
strong {
color: #57e31a;
margin-left: 4px;
}
}
.filter-panel {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 8px 28px;
min-height: 88px;
padding: 14px 40px;
background: var(--el-bg-color);
flex-wrap: wrap;
gap: 12px 24px;
}
.filter-item {
display: flex;
align-items: center;
gap: 12px;
label {
min-width: 76px;
text-align: right;
font-weight: 700;
white-space: nowrap;
}
:deep(.el-date-editor),
@@ -386,48 +327,13 @@ onMounted(() => {
}
.filter-actions {
display: flex;
gap: 8px;
.el-button {
width: 100px;
margin: 0;
min-width: 92px;
}
}
.date-summary {
margin: 20px 0 8px;
}
.reward-table-wrap {
min-height: 120px;
overflow-x: auto;
}
.reward-table {
width: 100%;
min-width: 850px;
border-collapse: collapse;
th,
td {
height: 37px;
padding: 8px;
border: 1px solid var(--reward-border);
text-align: left;
white-space: nowrap;
}
th {
background: var(--reward-dark);
color: #fff;
font-weight: 700;
}
tbody tr {
background: var(--reward-approved);
color: #333;
}
.submitted-time {
width: 150px;
@@ -444,13 +350,6 @@ onMounted(() => {
.action {
width: 180px;
}
.empty-row {
height: 90px;
background: var(--el-bg-color);
color: var(--el-text-color-secondary);
text-align: center;
}
}
.action-buttons {
@@ -462,34 +361,9 @@ onMounted(() => {
}
}
.table-footer {
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 8px;
margin-top: 18px;
}
@at-root html.dark {
.submitted-rewards {
--reward-dark: #2b2b2b;
--reward-border: var(--el-border-color);
--reward-approved: #29482b;
}
.reward-tabs button.active {
background: #555;
color: #fff;
}
.reward-table tbody tr {
color: var(--el-text-color-primary);
}
}
@media (max-width: 900px) {
.submitted-rewards {
padding: 14px;
padding: 12px;
}
.reward-tabs {
@@ -502,7 +376,8 @@ onMounted(() => {
}
.filter-panel {
padding: 14px;
align-items: flex-start;
flex-direction: column;
}
}
</style>