--
2.47.2
From 56f005404dda7e9f6a29774136cafe3271c62f7c Mon Sep 17 00:00:00 2001
From: shanshi <3316401569@qq.com>
Date: Wed, 21 Oct 2026 15:37:50 +0800
Subject: [PATCH 3/7] =?UTF-8?q?=E8=B4=A2=E5=8A=A1=E5=8F=B0=E8=B4=A6?=
=?UTF-8?q?=EF=BC=9A=E5=A2=9E=E5=8A=A0=E8=B4=A6=E6=88=B7=E5=BC=82=E5=B8=B8?=
=?UTF-8?q?=E7=8B=AC=E7=AB=8B=E5=BE=85=E5=8A=9E?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../Controllers/Business/FinanceLedger.php | 148 ++++++++++++++
...00_create_finance_ledger_account_todos.php | 39 ++++
.../pages/financial-ledger/ledger.blade.php | 5 +-
.../unavailable-balance-tasks.blade.php | 183 ++++++++++++++++++
routes/admin.php | 8 +
.../FinanceLedgerAccountStatusTest.php | 129 ++++++++++++
6 files changed, 511 insertions(+), 1 deletion(-)
create mode 100644 database/migrations/2026_10_21_150000_create_finance_ledger_account_todos.php
create mode 100644 resources/views/business/pages/financial-ledger/unavailable-balance-tasks.blade.php
diff --git a/app/Http/Controllers/Business/FinanceLedger.php b/app/Http/Controllers/Business/FinanceLedger.php
index a328b41f..fc202ee3 100644
--- a/app/Http/Controllers/Business/FinanceLedger.php
+++ b/app/Http/Controllers/Business/FinanceLedger.php
@@ -24,6 +24,154 @@ class FinanceLedger extends Controller
return $template->download();
}
+ public function unavailableBalanceTasks(Request $request)
+ {
+ $connection = DB::connection((string) config('upstream_mirrors.connection', 'business_mirror'));
+ $abnormalStatuses = array_values(array_diff(
+ array_keys(FinanceLedgerAccountStatus::OPTIONS),
+ [FinanceLedgerAccountStatus::NORMAL],
+ ));
+ $query = $connection->table('fin_accounts')->whereIn(
+ 'account_short',
+ $connection->table('fin_account_statuses')
+ ->select('account_short')
+ ->whereIn('status', $abnormalStatuses),
+ );
+ $keyword = trim((string) $request->query('pending_q', ''));
+
+ if ($keyword !== '') {
+ $query->where(function ($builder) use ($keyword): void {
+ foreach (['company_name', 'account_short', 'account_no', 'owner'] as $column) {
+ $builder->orWhere($column, 'like', '%' . $keyword . '%');
+ }
+ });
+ }
+
+ $summaryQuery = clone $query;
+ $summary = [
+ 'count' => (clone $query)->count(),
+ 'balance' => round((float) (clone $summaryQuery)->sum('current_balance'), 2),
+ 'blocked' => round((float) (clone $summaryQuery)->sum('blocked_amount'), 2),
+ 'daily_available' => round((float) (clone $summaryQuery)->sum('daily_available'), 2),
+ ];
+
+ $rows = $query
+ ->orderByDesc('blocked_amount')
+ ->orderBy('company_name')
+ ->orderBy('account_short')
+ ->paginate(50, ['*'], 'pending_page')
+ ->withQueryString();
+
+ $accountShorts = $rows->pluck('account_short')->filter()->unique()->values()->all();
+ $statuses = $accountShorts === []
+ ? collect()
+ : $connection->table('fin_account_statuses')
+ ->whereIn('account_short', $accountShorts)
+ ->pluck('status', 'account_short');
+ $typeOverrides = $accountShorts === []
+ ? collect()
+ : $connection->table('fin_account_type_overrides')
+ ->whereIn('account_short', $accountShorts)
+ ->pluck('account_type', 'account_short');
+ $todos = $accountShorts === []
+ ? collect()
+ : $connection->table('fin_account_todos')
+ ->whereIn('account_short', $accountShorts)
+ ->get()
+ ->keyBy('account_short');
+
+ $rows->getCollection()->transform(static function ($row) use ($statuses, $typeOverrides, $todos) {
+ $row->account_status = FinanceLedgerAccountStatus::normalize($statuses[$row->account_short] ?? null);
+ if ($typeOverrides->has($row->account_short)) {
+ $row->account_type = (string) ($typeOverrides->get($row->account_short) ?? '');
+ }
+ $todo = $todos->get($row->account_short);
+ $row->todo_abnormal_reason = $todo->abnormal_reason ?? null;
+ $row->todo_handling_method = $todo->handling_method ?? null;
+ $row->todo_owner_name = $todo->owner_name ?? null;
+ $row->todo_started_at = $todo->started_at ?? null;
+ $row->todo_completed_at = $todo->completed_at ?? null;
+ $row->todo_current_progress = $todo->current_progress ?? null;
+
+ return $row;
+ });
+
+ return view('business.pages.financial-ledger.unavailable-balance-tasks', [
+ 'rows' => $rows,
+ 'summary' => $summary,
+ 'keyword' => $keyword,
+ 'backUrl' => route('business.pages.show', [
+ 'section' => 'financial-operations',
+ 'page' => 'ledger',
+ ]) . '?tab=accounts',
+ 'canEdit' => FinanceLedgerAccess::canEditAccountStatus(user()),
+ 'handlingMethods' => [
+ 'online' => '线上',
+ 'offline' => '线下',
+ ],
+ ]);
+ }
+
+ public function updateAccountTodo(Request $request)
+ {
+ abort_unless(FinanceLedgerAccess::canEditAccountStatus(user()), 403, '只有财务台账管理员可以修改待办事项。');
+
+ $data = $request->validate([
+ 'account_short' => ['required', 'string', 'max:100'],
+ 'abnormal_reason' => ['nullable', 'string', 'max:1000'],
+ 'handling_method' => ['nullable', 'string', Rule::in(['online', 'offline'])],
+ 'owner_name' => ['nullable', 'string', 'max:100'],
+ 'started_at' => ['nullable', 'date'],
+ 'completed_at' => ['nullable', 'date'],
+ 'current_progress' => ['nullable', 'string', 'max:100'],
+ ]);
+
+ $accountShort = trim((string) $data['account_short']);
+ $connection = DB::connection((string) config('upstream_mirrors.connection', 'business_mirror'));
+ abort_unless(
+ $connection->table('fin_accounts')->where('account_short', $accountShort)->exists(),
+ 422,
+ '账户简称不存在,请刷新待办清单后重试。',
+ );
+
+ $abnormalStatuses = array_values(array_diff(
+ array_keys(FinanceLedgerAccountStatus::OPTIONS),
+ [FinanceLedgerAccountStatus::NORMAL],
+ ));
+ $status = $connection->table('fin_account_statuses')
+ ->where('account_short', $accountShort)
+ ->value('status');
+ abort_unless(
+ $status !== null && in_array(FinanceLedgerAccountStatus::normalize((string) $status), $abnormalStatuses, true),
+ 422,
+ '账户当前已不是异常状态,无需继续填写待办。',
+ );
+
+ $now = now();
+ $values = [
+ 'abnormal_reason' => $data['abnormal_reason'] ?? null,
+ 'handling_method' => $data['handling_method'] ?? null,
+ 'owner_name' => $data['owner_name'] ?? null,
+ 'started_at' => $data['started_at'] ?? null,
+ 'completed_at' => $data['completed_at'] ?? null,
+ 'current_progress' => $data['current_progress'] ?? null,
+ 'updated_by' => (string) user()->name,
+ 'updated_at' => $now,
+ ];
+ $todos = $connection->table('fin_account_todos');
+ $existing = $todos->where('account_short', $accountShort)->exists();
+ if ($existing) {
+ $todos->where('account_short', $accountShort)->update($values);
+ } else {
+ $todos->insert($values + [
+ 'account_short' => $accountShort,
+ 'created_at' => $now,
+ ]);
+ }
+
+ return redirect()->back()->with('success', '待办事项已保存');
+ }
+
public function updateMapping(Request $request, int $id, FinanceLedgerCalculator $calculator): JsonResponse
{
abort_unless(FinanceLedgerAccess::canEditMapping(user()), 403, '只有财务台账管理员可以修改账户映射。');
diff --git a/database/migrations/2026_10_21_150000_create_finance_ledger_account_todos.php b/database/migrations/2026_10_21_150000_create_finance_ledger_account_todos.php
new file mode 100644
index 00000000..62f548b7
--- /dev/null
+++ b/database/migrations/2026_10_21_150000_create_finance_ledger_account_todos.php
@@ -0,0 +1,39 @@
+connection());
+ if (! $schema->hasTable('fin_accounts') || $schema->hasTable('fin_account_todos')) {
+ return;
+ }
+
+ $schema->create('fin_account_todos', function (Blueprint $table): void {
+ $table->bigIncrements('id');
+ $table->string('account_short', 100)->unique('fin_account_todos_account_unique');
+ $table->text('abnormal_reason')->nullable();
+ $table->string('handling_method', 20)->nullable();
+ $table->string('owner_name', 100)->nullable();
+ $table->dateTime('started_at')->nullable();
+ $table->dateTime('completed_at')->nullable();
+ $table->string('current_progress', 100)->nullable();
+ $table->string('updated_by', 100)->nullable();
+ $table->timestamps();
+ });
+ }
+
+ public function down(): void
+ {
+ Schema::connection($this->connection())->dropIfExists('fin_account_todos');
+ }
+};
diff --git a/resources/views/business/pages/financial-ledger/ledger.blade.php b/resources/views/business/pages/financial-ledger/ledger.blade.php
index 20fc2295..01b9c906 100644
--- a/resources/views/business/pages/financial-ledger/ledger.blade.php
+++ b/resources/views/business/pages/financial-ledger/ledger.blade.php
@@ -88,6 +88,9 @@
.ledger-summary__item { display:flex; flex-direction:column; justify-content:center; gap:.2rem; min-width:0; min-height:3.4rem; padding:.6rem .8rem; border:1px solid var(--vip-border, #D8D2C7); border-radius:.55rem; background:var(--vip-content-accent-soft, #F7F0DE); box-shadow:0 1px 2px rgba(16,24,40,.04); }
.ledger-summary__item--available { background:#f3f7ee; }
.ledger-summary__item--blocked { background:#fff5f5; }
+ .ledger-summary__item--link { color:inherit; text-decoration:none; transition:border-color .15s ease, box-shadow .15s ease; }
+ .ledger-summary__item--link:hover { border-color:rgba(184,0,0,.42); box-shadow:0 2px 6px rgba(184,0,0,.08); }
+ .ledger-summary__item--link:focus-visible { outline:2px solid var(--vip-danger, #B80000); outline-offset:2px; }
.ledger-summary__item span { overflow:hidden; color:var(--vip-content-muted, #595959); font-size:.68rem; line-height:1.35; text-overflow:ellipsis; white-space:nowrap; }
.ledger-summary__item b { overflow:hidden; color:var(--vip-content-ink, #171717); font-size:.92rem; font-variant-numeric:tabular-nums; line-height:1.35; text-overflow:ellipsis; white-space:nowrap; }
.ledger-note { padding:.65rem .9rem; margin-bottom:.85rem; border:1px solid var(--vip-border, #D8D2C7); border-radius:.55rem; background:#fff; color:var(--vip-content-muted, #595959); font-size:.72rem; line-height:1.6; }
@@ -228,7 +231,7 @@
diff --git a/resources/views/business/pages/financial-ledger/unavailable-balance-tasks.blade.php b/resources/views/business/pages/financial-ledger/unavailable-balance-tasks.blade.php
new file mode 100644
index 00000000..b8327042
--- /dev/null
+++ b/resources/views/business/pages/financial-ledger/unavailable-balance-tasks.blade.php
@@ -0,0 +1,183 @@
+@php
+ $money = static fn ($value): string => $value === null || $value === '' ? '' : number_format((float) $value, 2);
+@endphp
+
+
+ 账户异常待办
+
+
+
+
+
+
+
+
+
待处理账户{{ number_format((int) ($summary['count'] ?? 0)) }}
+
不可用金额合计{{ $money($summary['blocked'] ?? 0) }}
+
可用金额合计{{ $money($summary['balance'] ?? 0) }}
+
每日可出金额合计{{ $money($summary['daily_available'] ?? 0) }}
+
+
+ @if (session('success'))
+ check_circle{{ session('success') }}
+ @endif
+ @if ($errors->any())
+ error{{ $errors->first() }}
+ @endif
+
+
+
+
+
+ @if ($rows->total() > 0)
+
+ @endif
+
+
+
diff --git a/routes/admin.php b/routes/admin.php
index 34d6f88e..cc3013ff 100644
--- a/routes/admin.php
+++ b/routes/admin.php
@@ -697,6 +697,14 @@ Route::post('workspace/financial-ledger/mapping/payable/apply', [\App\Http\Contr
->middleware('business.page:financial-operations,ledger')
->name('business.financial-ledger.mapping.payable.apply');
+Route::get('workspace/financial-operations/ledger/unavailable-balances', [\App\Http\Controllers\Business\FinanceLedger::class, 'unavailableBalanceTasks'])
+ ->middleware('business.page:financial-operations,ledger')
+ ->name('business.financial-ledger.unavailable-balances');
+
+Route::post('workspace/financial-ledger/accounts/todo', [\App\Http\Controllers\Business\FinanceLedger::class, 'updateAccountTodo'])
+ ->middleware('business.page:financial-operations,ledger')
+ ->name('business.financial-ledger.accounts.todo');
+
Route::get('workspace/financial-ledger/{page?}/{tab?}', function (Request $request, ?string $page = null, ?string $tab = null) {
abort_unless($page === null || $page === 'ledger', 404);
diff --git a/tests/Feature/Business/FinanceLedgerAccountStatusTest.php b/tests/Feature/Business/FinanceLedgerAccountStatusTest.php
index 39d2598a..bff4536c 100644
--- a/tests/Feature/Business/FinanceLedgerAccountStatusTest.php
+++ b/tests/Feature/Business/FinanceLedgerAccountStatusTest.php
@@ -35,6 +35,20 @@ class FinanceLedgerAccountStatusTest extends FeatureTestCase
]);
}
+ private function unavailableBalancesUrl(): string
+ {
+ return route('business.financial-ledger.unavailable-balances', [
+ 'company_id' => $this->company->id,
+ ]);
+ }
+
+ private function accountTodoUrl(): string
+ {
+ return route('business.financial-ledger.accounts.todo', [
+ 'company_id' => $this->company->id,
+ ]);
+ }
+
private function statusUrl(): string
{
return route('business.financial-ledger.accounts.status', [
@@ -107,12 +121,122 @@ class FinanceLedgerAccountStatusTest extends FeatureTestCase
->assertSee('恢复默认')
->assertSee('可用金额合计')
->assertSee('不可用金额合计')
+ ->assertSee('href="' . $this->unavailableBalancesUrl() . '"', false)
+ ->assertSee('查看账户异常待办')
->assertSee('可用金额 − 不可用金额')
->assertSee('未填写')
->assertDontSee('data-account-filter-state', false)
->assertSee('value="normal" selected', false);
}
+ public function test_unavailable_balance_tasks_page_is_separate_and_only_lists_blocked_accounts(): void
+ {
+ $connection = DB::connection('business_mirror');
+ $accounts = [
+ [
+ 'company_name' => '待办测试公司',
+ 'account_short' => '待办测试账户',
+ 'account_no' => '6222000000000099',
+ 'account_type' => '公户',
+ 'bank_name' => '待办测试银行',
+ 'bank_city_branch' => '上海 / 待办测试支行',
+ 'current_balance' => 100,
+ 'blocked_amount' => 25.5,
+ 'daily_available' => 74.5,
+ 'owner' => '待办负责人',
+ 'next_action' => '核实冻结原因',
+ 'remark' => '独立待办备注',
+ ],
+ [
+ 'company_name' => '零金额测试公司',
+ 'account_short' => '零金额测试账户',
+ 'current_balance' => 100,
+ 'blocked_amount' => 0,
+ 'daily_available' => 100,
+ ],
+ [
+ 'company_name' => '正常状态测试公司',
+ 'account_short' => '正常状态测试账户',
+ 'current_balance' => 100,
+ 'blocked_amount' => 50,
+ 'daily_available' => 50,
+ ],
+ ];
+ foreach ($accounts as $account) {
+ $connection->table('fin_accounts')->insert($account);
+ }
+ $connection->table('fin_account_statuses')->insert([
+ [
+ 'account_short' => '待办测试账户',
+ 'status' => 'limited',
+ 'updated_by' => 'Test User',
+ ],
+ [
+ 'account_short' => '正常状态测试账户',
+ 'status' => 'normal',
+ 'updated_by' => 'Test User',
+ ],
+ ]);
+ $connection->table('fin_account_type_overrides')->insert([
+ 'account_short' => '待办测试账户',
+ 'account_type' => '网商银行',
+ 'updated_by' => 'Test User',
+ ]);
+
+ $this->loginAs()->get($this->unavailableBalancesUrl())
+ ->assertOk()
+ ->assertSee('账户异常待办')
+ ->assertSee('不与其他页面的待办事项合并')
+ ->assertSee('待办测试公司')
+ ->assertSee('data-pending-account="待办测试账户"', false)
+ ->assertSee('不可用金额合计')
+ ->assertSee('25.50')
+ ->assertSee('限额')
+ ->assertSee('网商银行')
+ ->assertSee('6222000000000099')
+ ->assertSee('待办测试银行')
+ ->assertSee('上海 / 待办测试支行')
+ ->assertSee('异常原因')
+ ->assertSee('办理方式')
+ ->assertSee('当前进度')
+ ->assertDontSee('data-pending-account="正常状态测试账户"', false)
+ ->assertDontSee('data-pending-account="零金额测试账户"', false);
+
+ $this->loginAs()->post($this->accountTodoUrl(), [
+ 'account_short' => '待办测试账户',
+ 'abnormal_reason' => '账户冻结',
+ 'handling_method' => 'online',
+ 'owner_name' => '待办负责人',
+ 'started_at' => '2026-10-21T09:00',
+ 'completed_at' => '2026-10-21T11:30',
+ 'current_progress' => '处理中',
+ ])->assertRedirect();
+
+ $this->assertDatabaseHas('fin_account_todos', [
+ 'account_short' => '待办测试账户',
+ 'abnormal_reason' => '账户冻结',
+ 'handling_method' => 'online',
+ 'owner_name' => '待办负责人',
+ 'current_progress' => '处理中',
+ 'updated_by' => 'Test User',
+ ], 'business_mirror');
+
+ $this->loginAs()->get($this->unavailableBalancesUrl())
+ ->assertOk()
+ ->assertSee('账户冻结')
+ ->assertSee('待办负责人')
+ ->assertSee('处理中');
+
+ $this->loginAs()->get($this->unavailableBalancesUrl() . '?pending_q=待办负责人')
+ ->assertOk()
+ ->assertSee('data-pending-account="待办测试账户"', false);
+
+ $this->loginAs()->get($this->unavailableBalancesUrl() . '?pending_q=不存在')
+ ->assertOk()
+ ->assertSee('当前没有账户异常待办')
+ ->assertDontSee('data-pending-account="待办测试账户"', false);
+ }
+
public function test_financial_ledger_tab_forms_submit_to_query_tab_routes(): void
{
$pageUrl = $this->pageUrl();
@@ -406,7 +530,12 @@ class FinanceLedgerAccountStatusTest extends FeatureTestCase
'account_short' => '测试账户',
'account_should_keep' => '否',
])->assertForbidden();
+ $this->withExceptionHandling()->loginAs($viewer)->post($this->accountTodoUrl(), [
+ 'account_short' => '测试账户',
+ 'current_progress' => '处理中',
+ ])->assertForbidden();
$this->assertDatabaseCount('fin_account_statuses', 0, 'business_mirror');
+ $this->assertDatabaseCount('fin_account_todos', 0, 'business_mirror');
}
}
--
2.47.2
From ac89af9d32525113acf60868fd985b12647c0b16 Mon Sep 17 00:00:00 2001
From: shanshi <3316401569@qq.com>
Date: Wed, 21 Oct 2026 15:54:53 +0800
Subject: [PATCH 4/7] =?UTF-8?q?=E8=B4=A2=E5=8A=A1=E5=8F=B0=E8=B4=A6?=
=?UTF-8?q?=EF=BC=9A=E5=AE=8C=E5=96=84=E5=BC=82=E5=B8=B8=E5=BE=85=E5=8A=9E?=
=?UTF-8?q?=E7=AD=9B=E9=80=89=E6=8E=92=E5=BA=8F=E5=92=8C=E5=AE=8C=E6=88=90?=
=?UTF-8?q?=E8=81=94=E5=8A=A8?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../Controllers/Business/FinanceLedger.php | 231 +++++++++++++-----
...status_to_finance_ledger_account_todos.php | 39 +++
.../unavailable-balance-tasks.blade.php | 135 ++++++++--
.../FinanceLedgerAccountStatusTest.php | 86 ++++++-
4 files changed, 398 insertions(+), 93 deletions(-)
create mode 100644 database/migrations/2026_10_21_160000_add_completion_status_to_finance_ledger_account_todos.php
diff --git a/app/Http/Controllers/Business/FinanceLedger.php b/app/Http/Controllers/Business/FinanceLedger.php
index fc202ee3..d8a1f773 100644
--- a/app/Http/Controllers/Business/FinanceLedger.php
+++ b/app/Http/Controllers/Business/FinanceLedger.php
@@ -9,6 +9,7 @@ use App\Utilities\FinanceLedgerAccess;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
+use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\DB;
use Illuminate\Validation\Rule;
use Modules\EcommerceFinance\Services\FinanceLedgerAccountTemplate;
@@ -31,84 +32,158 @@ class FinanceLedger extends Controller
array_keys(FinanceLedgerAccountStatus::OPTIONS),
[FinanceLedgerAccountStatus::NORMAL],
));
- $query = $connection->table('fin_accounts')->whereIn(
- 'account_short',
- $connection->table('fin_account_statuses')
- ->select('account_short')
- ->whereIn('status', $abnormalStatuses),
- );
- $keyword = trim((string) $request->query('pending_q', ''));
+ $handlingMethods = [
+ 'online' => '线上',
+ 'offline' => '线下',
+ ];
+ $completionStatuses = [
+ 'completed' => '已完成',
+ 'in_progress' => '进行中',
+ 'not_started' => '待开始',
+ 'incomplete' => '未完成',
+ ];
+ $filters = [
+ 'pending_short' => trim((string) $request->query('pending_short', '')),
+ 'pending_type' => trim((string) $request->query('pending_type', '')),
+ 'pending_status' => trim((string) $request->query('pending_status', '')),
+ 'pending_reason' => trim((string) $request->query('pending_reason', '')),
+ 'pending_method' => trim((string) $request->query('pending_method', '')),
+ 'pending_owner' => trim((string) $request->query('pending_owner', '')),
+ ];
- if ($keyword !== '') {
- $query->where(function ($builder) use ($keyword): void {
- foreach (['company_name', 'account_short', 'account_no', 'owner'] as $column) {
- $builder->orWhere($column, 'like', '%' . $keyword . '%');
- }
+ $query = $connection->table('fin_accounts as accounts')
+ ->join('fin_account_statuses as account_status', 'account_status.account_short', '=', 'accounts.account_short')
+ ->leftJoin('fin_account_type_overrides as type_override', 'type_override.account_short', '=', 'accounts.account_short')
+ ->leftJoin('fin_account_todos as account_todo', 'account_todo.account_short', '=', 'accounts.account_short')
+ ->whereIn('account_status.status', $abnormalStatuses);
+
+ if ($filters['pending_short'] !== '') {
+ $query->where('accounts.account_short', 'like', '%' . $filters['pending_short'] . '%');
+ }
+ if ($filters['pending_type'] !== '') {
+ $query->where(function ($effectiveType) use ($filters): void {
+ $effectiveType->where(function ($baseType) use ($filters): void {
+ $baseType->whereNull('type_override.id');
+ if ($filters['pending_type'] === '__empty__') {
+ $baseType->where(function ($blank): void {
+ $blank->whereNull('accounts.account_type')->orWhere('accounts.account_type', '');
+ });
+ } else {
+ $baseType->where('accounts.account_type', $filters['pending_type']);
+ }
+ })->orWhere(function ($overrideType) use ($filters): void {
+ $overrideType->whereNotNull('type_override.id');
+ if ($filters['pending_type'] === '__empty__') {
+ $overrideType->where(function ($blank): void {
+ $blank->whereNull('type_override.account_type')->orWhere('type_override.account_type', '');
+ });
+ } else {
+ $overrideType->where('type_override.account_type', $filters['pending_type']);
+ }
+ });
});
}
+ if ($filters['pending_status'] !== '' && array_key_exists($filters['pending_status'], FinanceLedgerAccountStatus::OPTIONS)) {
+ $query->where('account_status.status', $filters['pending_status']);
+ }
+ if ($filters['pending_reason'] !== '') {
+ $query->where('account_todo.abnormal_reason', 'like', '%' . $filters['pending_reason'] . '%');
+ }
+ if ($filters['pending_method'] !== '' && array_key_exists($filters['pending_method'], $handlingMethods)) {
+ $query->where('account_todo.handling_method', $filters['pending_method']);
+ }
+ if ($filters['pending_owner'] !== '') {
+ $query->where('account_todo.owner_name', 'like', '%' . $filters['pending_owner'] . '%');
+ }
$summaryQuery = clone $query;
$summary = [
'count' => (clone $query)->count(),
- 'balance' => round((float) (clone $summaryQuery)->sum('current_balance'), 2),
- 'blocked' => round((float) (clone $summaryQuery)->sum('blocked_amount'), 2),
- 'daily_available' => round((float) (clone $summaryQuery)->sum('daily_available'), 2),
+ 'balance' => round((float) (clone $summaryQuery)->sum('accounts.current_balance'), 2),
+ 'blocked' => round((float) (clone $summaryQuery)->sum('accounts.blocked_amount'), 2),
+ 'daily_available' => round((float) (clone $summaryQuery)->sum('accounts.daily_available'), 2),
];
+ $sortField = (string) $request->query('pending_sort', '');
+ $sortField = in_array($sortField, ['started_at', 'completed_at'], true) ? $sortField : '';
+ $sortDirection = strtolower((string) $request->query('pending_direction', 'asc'));
+ $sortDirection = $sortDirection === 'desc' ? 'desc' : 'asc';
+
+ $query->select([
+ 'accounts.*',
+ 'account_status.status as effective_account_status',
+ 'type_override.id as type_override_id',
+ 'type_override.account_type as effective_account_type',
+ 'account_todo.abnormal_reason as todo_abnormal_reason',
+ 'account_todo.handling_method as todo_handling_method',
+ 'account_todo.owner_name as todo_owner_name',
+ 'account_todo.started_at as todo_started_at',
+ 'account_todo.completed_at as todo_completed_at',
+ 'account_todo.current_progress as todo_current_progress',
+ 'account_todo.completion_status as todo_completion_status',
+ ]);
+
+ if ($sortField !== '') {
+ $query->orderBy('account_todo.' . $sortField, $sortDirection);
+ } else {
+ $query->orderByDesc('accounts.blocked_amount');
+ }
$rows = $query
- ->orderByDesc('blocked_amount')
- ->orderBy('company_name')
- ->orderBy('account_short')
- ->paginate(50, ['*'], 'pending_page')
+ ->orderBy('accounts.company_name')
+ ->orderBy('accounts.account_short')
+ ->paginate(50)
+ ->appends($filters)
+ ->appends(array_filter([
+ 'pending_sort' => $sortField,
+ 'pending_direction' => $sortField !== '' ? $sortDirection : '',
+ ]))
->withQueryString();
- $accountShorts = $rows->pluck('account_short')->filter()->unique()->values()->all();
- $statuses = $accountShorts === []
- ? collect()
- : $connection->table('fin_account_statuses')
- ->whereIn('account_short', $accountShorts)
- ->pluck('status', 'account_short');
- $typeOverrides = $accountShorts === []
- ? collect()
- : $connection->table('fin_account_type_overrides')
- ->whereIn('account_short', $accountShorts)
- ->pluck('account_type', 'account_short');
- $todos = $accountShorts === []
- ? collect()
- : $connection->table('fin_account_todos')
- ->whereIn('account_short', $accountShorts)
- ->get()
- ->keyBy('account_short');
-
- $rows->getCollection()->transform(static function ($row) use ($statuses, $typeOverrides, $todos) {
- $row->account_status = FinanceLedgerAccountStatus::normalize($statuses[$row->account_short] ?? null);
- if ($typeOverrides->has($row->account_short)) {
- $row->account_type = (string) ($typeOverrides->get($row->account_short) ?? '');
+ $rows->getCollection()->transform(static function ($row) {
+ $row->account_status = FinanceLedgerAccountStatus::normalize($row->effective_account_status ?? null);
+ if ($row->type_override_id !== null) {
+ $row->account_type = (string) ($row->effective_account_type ?? '');
}
- $todo = $todos->get($row->account_short);
- $row->todo_abnormal_reason = $todo->abnormal_reason ?? null;
- $row->todo_handling_method = $todo->handling_method ?? null;
- $row->todo_owner_name = $todo->owner_name ?? null;
- $row->todo_started_at = $todo->started_at ?? null;
- $row->todo_completed_at = $todo->completed_at ?? null;
- $row->todo_current_progress = $todo->current_progress ?? null;
+ $row->todo_completion_status = $row->todo_completion_status ?: 'not_started';
return $row;
});
+ $typeOptions = FinanceLedgerAccountType::options(
+ $connection->table('fin_accounts')
+ ->whereNotNull('account_type')->where('account_type', '<>', '')
+ ->distinct()->orderBy('account_type')->pluck('account_type')->all(),
+ $connection->table('fin_account_type_overrides')
+ ->whereNotNull('account_type')->where('account_type', '<>', '')
+ ->distinct()->orderBy('account_type')->pluck('account_type')->all(),
+ );
+ $reasonOptions = $connection->table('fin_account_todos')
+ ->whereNotNull('abnormal_reason')->where('abnormal_reason', '<>', '')
+ ->distinct()->orderBy('abnormal_reason')->pluck('abnormal_reason')->all();
+ $ownerOptions = $connection->table('fin_account_todos')
+ ->whereNotNull('owner_name')->where('owner_name', '<>', '')
+ ->distinct()->orderBy('owner_name')->pluck('owner_name')->all();
+
return view('business.pages.financial-ledger.unavailable-balance-tasks', [
'rows' => $rows,
'summary' => $summary,
- 'keyword' => $keyword,
+ 'filters' => $filters,
+ 'sortField' => $sortField,
+ 'sortDirection' => $sortDirection,
'backUrl' => route('business.pages.show', [
'section' => 'financial-operations',
'page' => 'ledger',
]) . '?tab=accounts',
'canEdit' => FinanceLedgerAccess::canEditAccountStatus(user()),
- 'handlingMethods' => [
- 'online' => '线上',
- 'offline' => '线下',
- ],
+ 'handlingMethods' => $handlingMethods,
+ 'completionStatuses' => $completionStatuses,
+ 'accountStatusOptions' => array_intersect_key(
+ FinanceLedgerAccountStatus::OPTIONS,
+ array_flip($abnormalStatuses),
+ ),
+ 'accountTypeOptions' => $typeOptions,
+ 'reasonOptions' => $reasonOptions,
+ 'ownerOptions' => $ownerOptions,
]);
}
@@ -124,6 +199,7 @@ class FinanceLedger extends Controller
'started_at' => ['nullable', 'date'],
'completed_at' => ['nullable', 'date'],
'current_progress' => ['nullable', 'string', 'max:100'],
+ 'completion_status' => ['nullable', 'string', Rule::in(['completed', 'in_progress', 'not_started', 'incomplete'])],
]);
$accountShort = trim((string) $data['account_short']);
@@ -148,28 +224,51 @@ class FinanceLedger extends Controller
);
$now = now();
+ $completionStatus = (string) ($data['completion_status'] ?? 'not_started');
$values = [
'abnormal_reason' => $data['abnormal_reason'] ?? null,
'handling_method' => $data['handling_method'] ?? null,
'owner_name' => $data['owner_name'] ?? null,
- 'started_at' => $data['started_at'] ?? null,
- 'completed_at' => $data['completed_at'] ?? null,
+ 'started_at' => filled($data['started_at'] ?? null)
+ ? Carbon::parse($data['started_at'])->format('Y-m-d H:i:s')
+ : null,
+ 'completed_at' => filled($data['completed_at'] ?? null)
+ ? Carbon::parse($data['completed_at'])->format('Y-m-d H:i:s')
+ : null,
'current_progress' => $data['current_progress'] ?? null,
+ 'completion_status' => $completionStatus,
'updated_by' => (string) user()->name,
'updated_at' => $now,
];
- $todos = $connection->table('fin_account_todos');
- $existing = $todos->where('account_short', $accountShort)->exists();
- if ($existing) {
- $todos->where('account_short', $accountShort)->update($values);
- } else {
- $todos->insert($values + [
- 'account_short' => $accountShort,
- 'created_at' => $now,
- ]);
- }
+ $connection->transaction(function () use ($connection, $accountShort, $values, $now, $completionStatus): void {
+ $todos = $connection->table('fin_account_todos');
+ $existing = $todos->where('account_short', $accountShort)->exists();
+ if ($existing) {
+ $todos->where('account_short', $accountShort)->update($values);
+ } else {
+ $todos->insert($values + [
+ 'account_short' => $accountShort,
+ 'created_at' => $now,
+ ]);
+ }
- return redirect()->back()->with('success', '待办事项已保存');
+ if ($completionStatus === 'completed') {
+ $connection->table('fin_account_statuses')
+ ->where('account_short', $accountShort)
+ ->update([
+ 'status' => FinanceLedgerAccountStatus::NORMAL,
+ 'updated_by' => (string) user()->name,
+ 'updated_at' => $now,
+ ]);
+ }
+ });
+
+ return redirect()->back()->with(
+ 'success',
+ $completionStatus === 'completed'
+ ? '待办已完成,账户状态已自动设为正常'
+ : '待办事项已保存',
+ );
}
public function updateMapping(Request $request, int $id, FinanceLedgerCalculator $calculator): JsonResponse
diff --git a/database/migrations/2026_10_21_160000_add_completion_status_to_finance_ledger_account_todos.php b/database/migrations/2026_10_21_160000_add_completion_status_to_finance_ledger_account_todos.php
new file mode 100644
index 00000000..18ce9b16
--- /dev/null
+++ b/database/migrations/2026_10_21_160000_add_completion_status_to_finance_ledger_account_todos.php
@@ -0,0 +1,39 @@
+connection());
+ if (! $schema->hasTable('fin_account_todos') || $schema->hasColumn('fin_account_todos', 'completion_status')) {
+ return;
+ }
+
+ $schema->table('fin_account_todos', function (Blueprint $table): void {
+ $table->string('completion_status', 20)->default('not_started')->after('current_progress');
+ $table->index(['completion_status'], 'fin_account_todos_completion_status_index');
+ });
+ }
+
+ public function down(): void
+ {
+ $schema = Schema::connection($this->connection());
+ if (! $schema->hasTable('fin_account_todos') || ! $schema->hasColumn('fin_account_todos', 'completion_status')) {
+ return;
+ }
+
+ $schema->table('fin_account_todos', function (Blueprint $table): void {
+ $table->dropIndex('fin_account_todos_completion_status_index');
+ $table->dropColumn('completion_status');
+ });
+ }
+};
diff --git a/resources/views/business/pages/financial-ledger/unavailable-balance-tasks.blade.php b/resources/views/business/pages/financial-ledger/unavailable-balance-tasks.blade.php
index b8327042..ade70920 100644
--- a/resources/views/business/pages/financial-ledger/unavailable-balance-tasks.blade.php
+++ b/resources/views/business/pages/financial-ledger/unavailable-balance-tasks.blade.php
@@ -21,10 +21,14 @@
.pending-balance-card b { display:block; margin-top:.2rem; overflow:hidden; font-size:.92rem; font-variant-numeric:tabular-nums; line-height:1.35; text-overflow:ellipsis; white-space:nowrap; }
.pending-balance-alert { display:flex; align-items:center; gap:.35rem; padding:.65rem .8rem; margin-bottom:.75rem; border:1px solid #bbf7d0; border-radius:.5rem; background:#f0fdf4; color:var(--vip-success, #53793E); font-size:.72rem; }
.pending-balance-alert.is-error { border-color:rgba(184,0,0,.24); background:#fff5f5; color:var(--vip-danger, #B80000); }
- .pending-balance-toolbar { display:flex; align-items:flex-end; flex-wrap:wrap; gap:.55rem; padding:.75rem 1rem; margin-bottom:.75rem; border:1px solid var(--vip-border, #D8D2C7); border-radius:.65rem; background:#fff; }
- .pending-balance-field { display:flex; flex:1 1 18rem; flex-direction:column; gap:.3rem; min-width:12rem; color:var(--vip-content-muted, #595959); font-size:.68rem; }
- .pending-balance-field input { height:2.15rem; padding:0 .65rem; border:1px solid var(--vip-border, #D8D2C7); border-radius:.4rem; background:#fff; color:var(--vip-content-ink, #171717); font:inherit; }
- .pending-balance-field input:focus { border-color:var(--vip-content-accent-dark, #856632); outline:none; box-shadow:0 0 0 3px rgba(133,102,50,.16); }
+ .pending-balance-toolbar { padding:.75rem 1rem; margin-bottom:.75rem; border:1px solid var(--vip-border, #D8D2C7); border-radius:.65rem; background:#fff; }
+ .pending-balance-filter-grid { display:grid; grid-template-columns:repeat(6, minmax(0, 1fr)); gap:.55rem; }
+ .pending-balance-field { display:flex; flex-direction:column; gap:.3rem; min-width:0; color:var(--vip-content-muted, #595959); font-size:.68rem; }
+ .pending-balance-field input,
+ .pending-balance-field select { width:100%; min-width:0; height:2.15rem; padding:0 .55rem; border:1px solid var(--vip-border, #D8D2C7); border-radius:.4rem; background:#fff; color:var(--vip-content-ink, #171717); font:inherit; }
+ .pending-balance-field input:focus,
+ .pending-balance-field select:focus { border-color:var(--vip-content-accent-dark, #856632); outline:none; box-shadow:0 0 0 3px rgba(133,102,50,.16); }
+ .pending-balance-toolbar-actions { display:flex; justify-content:flex-end; gap:.55rem; margin-top:.65rem; }
.pending-balance-button { display:inline-flex; align-items:center; justify-content:center; gap:.35rem; height:2.15rem; padding:0 .8rem; border:1px solid var(--vip-border, #D8D2C7); border-radius:.4rem; background:#fff; color:var(--vip-content-accent-dark, #856632); font:inherit; font-weight:600; text-decoration:none; cursor:pointer; }
.pending-balance-button.is-primary { border-color:var(--vip-content-accent-dark, #856632); background:var(--vip-content-accent-dark, #856632); color:#fff; }
.pending-balance-table-wrap { overflow:auto; border:1px solid var(--vip-border, #D8D2C7); border-radius:.65rem; background:#fff; }
@@ -37,6 +41,9 @@
.pending-balance-table tbody tr:last-child td { border-bottom:0; }
.pending-balance-table tbody tr:hover td { background:#fcfaf4; }
.pending-balance-table .num { font-variant-numeric:tabular-nums; text-align:right; }
+ .pending-balance-sort { display:inline-flex; align-items:center; justify-content:center; gap:.2rem; color:inherit; text-decoration:none; }
+ .pending-balance-sort:hover { color:var(--vip-content-accent-dark, #856632); }
+ .pending-balance-sort .material-icons-outlined { font-size:.9rem; }
.pending-balance-task-input { width:100%; min-width:8rem; height:2rem; padding:0 .45rem; border:1px solid var(--vip-border, #D8D2C7); border-radius:.35rem; background:#fff; color:var(--vip-content-ink, #171717); font:inherit; }
.pending-balance-task-input.is-compact { min-width:6.5rem; }
.pending-balance-task-input:focus { border-color:var(--vip-content-accent-dark, #856632); outline:none; box-shadow:0 0 0 3px rgba(133,102,50,.14); }
@@ -54,8 +61,10 @@
.pending-balance-pagination__links { display:flex; align-items:center; gap:.75rem; }
.pending-balance-pagination a { color:var(--vip-content-accent-dark, #856632); font-weight:600; text-decoration:none; }
.pending-balance-pagination a:hover { color:#6A5129; }
+ @media (max-width:1100px) { .pending-balance-filter-grid { grid-template-columns:repeat(3, minmax(0, 1fr)); } }
@media (max-width:900px) { .pending-balance-summary { grid-template-columns:repeat(2, minmax(0, 1fr)); } }
- @media (max-width:560px) { .pending-balance-page { margin:1rem .75rem 1.5rem; } .pending-balance-summary { grid-template-columns:1fr; } }
+ @media (max-width:700px) { .pending-balance-filter-grid { grid-template-columns:repeat(2, minmax(0, 1fr)); } }
+ @media (max-width:560px) { .pending-balance-page { margin:1rem .75rem 1.5rem; } .pending-balance-summary, .pending-balance-filter-grid { grid-template-columns:1fr; } }
@@ -84,21 +93,87 @@
error{{ $errors->first() }}
@endif
-
+ @php
+ $sortQuery = request()->query();
+ unset($sortQuery['pending_page']);
+ $sortUrl = static fn (string $field): string => route(
+ 'business.financial-ledger.unavailable-balances',
+ array_merge($sortQuery, [
+ 'pending_sort' => $field,
+ 'pending_direction' => $sortField === $field && $sortDirection === 'asc' ? 'desc' : 'asc',
+ ]),
+ );
+ @endphp
+
diff --git a/tests/Feature/Business/FinanceLedgerAccountStatusTest.php b/tests/Feature/Business/FinanceLedgerAccountStatusTest.php
index bff4536c..beec21a6 100644
--- a/tests/Feature/Business/FinanceLedgerAccountStatusTest.php
+++ b/tests/Feature/Business/FinanceLedgerAccountStatusTest.php
@@ -161,6 +161,17 @@ class FinanceLedgerAccountStatusTest extends FeatureTestCase
'blocked_amount' => 50,
'daily_available' => 50,
],
+ [
+ 'company_name' => '待办测试公司二',
+ 'account_short' => '待办测试账户二',
+ 'account_no' => '6222000000000100',
+ 'account_type' => '个人卡',
+ 'bank_name' => '待办测试银行二',
+ 'bank_city_branch' => '杭州 / 待办测试支行',
+ 'current_balance' => 100,
+ 'blocked_amount' => 10,
+ 'daily_available' => 90,
+ ],
];
foreach ($accounts as $account) {
$connection->table('fin_accounts')->insert($account);
@@ -176,6 +187,11 @@ class FinanceLedgerAccountStatusTest extends FeatureTestCase
'status' => 'normal',
'updated_by' => 'Test User',
],
+ [
+ 'account_short' => '待办测试账户二',
+ 'status' => 'online_banking_abnormal',
+ 'updated_by' => 'Test User',
+ ],
]);
$connection->table('fin_account_type_overrides')->insert([
'account_short' => '待办测试账户',
@@ -190,7 +206,7 @@ class FinanceLedgerAccountStatusTest extends FeatureTestCase
->assertSee('待办测试公司')
->assertSee('data-pending-account="待办测试账户"', false)
->assertSee('不可用金额合计')
- ->assertSee('25.50')
+ ->assertSee('35.50')
->assertSee('限额')
->assertSee('网商银行')
->assertSee('6222000000000099')
@@ -199,6 +215,17 @@ class FinanceLedgerAccountStatusTest extends FeatureTestCase
->assertSee('异常原因')
->assertSee('办理方式')
->assertSee('当前进度')
+ ->assertSee('是否完成')
+ ->assertSee('name="pending_short"', false)
+ ->assertSee('name="pending_type"', false)
+ ->assertSee('name="pending_status"', false)
+ ->assertSee('name="pending_reason"', false)
+ ->assertSee('name="pending_method"', false)
+ ->assertSee('name="pending_owner"', false)
+ ->assertSee('按开始时间排序')
+ ->assertSee('按完成时间排序')
+ ->assertDontSee('name="pending_q"', false)
+ ->assertSee('data-pending-account="待办测试账户二"', false)
->assertDontSee('data-pending-account="正常状态测试账户"', false)
->assertDontSee('data-pending-account="零金额测试账户"', false);
@@ -210,6 +237,7 @@ class FinanceLedgerAccountStatusTest extends FeatureTestCase
'started_at' => '2026-10-21T09:00',
'completed_at' => '2026-10-21T11:30',
'current_progress' => '处理中',
+ 'completion_status' => 'in_progress',
])->assertRedirect();
$this->assertDatabaseHas('fin_account_todos', [
@@ -218,22 +246,68 @@ class FinanceLedgerAccountStatusTest extends FeatureTestCase
'handling_method' => 'online',
'owner_name' => '待办负责人',
'current_progress' => '处理中',
+ 'completion_status' => 'in_progress',
'updated_by' => 'Test User',
], 'business_mirror');
+ $connection->table('fin_account_todos')->insert([
+ 'account_short' => '待办测试账户二',
+ 'abnormal_reason' => '网银证书过期',
+ 'handling_method' => 'offline',
+ 'owner_name' => '其他负责人',
+ 'started_at' => '2026-10-21 10:00:00',
+ 'current_progress' => '待联系银行',
+ 'completion_status' => 'not_started',
+ 'updated_by' => 'Test User',
+ ]);
+
$this->loginAs()->get($this->unavailableBalancesUrl())
->assertOk()
->assertSee('账户冻结')
->assertSee('待办负责人')
- ->assertSee('处理中');
+ ->assertSee('处理中')
+ ->assertSee('进行中');
- $this->loginAs()->get($this->unavailableBalancesUrl() . '?pending_q=待办负责人')
+ $filteredUrl = $this->unavailableBalancesUrl() . '?' . http_build_query([
+ 'pending_short' => '待办测试账户',
+ 'pending_type' => '网商银行',
+ 'pending_status' => 'limited',
+ 'pending_reason' => '账户冻结',
+ 'pending_method' => 'online',
+ 'pending_owner' => '待办负责人',
+ ]);
+ $this->loginAs()->get($filteredUrl)
->assertOk()
- ->assertSee('data-pending-account="待办测试账户"', false);
+ ->assertSee('data-pending-account="待办测试账户"', false)
+ ->assertDontSee('data-pending-account="待办测试账户二"', false);
- $this->loginAs()->get($this->unavailableBalancesUrl() . '?pending_q=不存在')
+ $sortResponse = $this->loginAs()->get(
+ $this->unavailableBalancesUrl() . '?pending_sort=started_at&pending_direction=desc'
+ )->assertOk()->assertSee('aria-sort="descending"', false);
+ preg_match_all('/data-pending-account="([^"]+)"/u', $sortResponse->getContent(), $sortedAccounts);
+ $this->assertSame(['待办测试账户二', '待办测试账户'], array_values(array_intersect(
+ $sortedAccounts[1],
+ ['待办测试账户', '待办测试账户二'],
+ )));
+
+ $this->loginAs()->post($this->accountTodoUrl(), [
+ 'account_short' => '待办测试账户',
+ 'abnormal_reason' => '账户冻结',
+ 'handling_method' => 'online',
+ 'owner_name' => '待办负责人',
+ 'started_at' => '2026-10-21T09:00',
+ 'completed_at' => '2026-10-21T12:00',
+ 'current_progress' => '已处理',
+ 'completion_status' => 'completed',
+ ])->assertRedirect();
+
+ $this->assertDatabaseHas('fin_account_statuses', [
+ 'account_short' => '待办测试账户',
+ 'status' => 'normal',
+ ], 'business_mirror');
+
+ $this->loginAs()->get($this->unavailableBalancesUrl())
->assertOk()
- ->assertSee('当前没有账户异常待办')
->assertDontSee('data-pending-account="待办测试账户"', false);
}
--
2.47.2
From c0acda261d3170d6d6ff1e0362d9c0354c26d2dc Mon Sep 17 00:00:00 2001
From: shanshi <3316401569@qq.com>
Date: Wed, 21 Oct 2026 15:58:17 +0800
Subject: [PATCH 5/7] =?UTF-8?q?=E8=B4=A2=E5=8A=A1=E5=8F=B0=E8=B4=A6?=
=?UTF-8?q?=EF=BC=9A=E5=9B=BA=E5=AE=9A=E5=BC=82=E5=B8=B8=E5=BE=85=E5=8A=9E?=
=?UTF-8?q?=E8=A1=A8=E5=A4=B4?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../financial-ledger/unavailable-balance-tasks.blade.php | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/resources/views/business/pages/financial-ledger/unavailable-balance-tasks.blade.php b/resources/views/business/pages/financial-ledger/unavailable-balance-tasks.blade.php
index ade70920..fb95b291 100644
--- a/resources/views/business/pages/financial-ledger/unavailable-balance-tasks.blade.php
+++ b/resources/views/business/pages/financial-ledger/unavailable-balance-tasks.blade.php
@@ -31,11 +31,11 @@
.pending-balance-toolbar-actions { display:flex; justify-content:flex-end; gap:.55rem; margin-top:.65rem; }
.pending-balance-button { display:inline-flex; align-items:center; justify-content:center; gap:.35rem; height:2.15rem; padding:0 .8rem; border:1px solid var(--vip-border, #D8D2C7); border-radius:.4rem; background:#fff; color:var(--vip-content-accent-dark, #856632); font:inherit; font-weight:600; text-decoration:none; cursor:pointer; }
.pending-balance-button.is-primary { border-color:var(--vip-content-accent-dark, #856632); background:var(--vip-content-accent-dark, #856632); color:#fff; }
- .pending-balance-table-wrap { overflow:auto; border:1px solid var(--vip-border, #D8D2C7); border-radius:.65rem; background:#fff; }
+ .pending-balance-table-wrap { max-height:calc(100vh - 360px); min-height:20rem; overflow:auto; border:1px solid var(--vip-border, #D8D2C7); border-radius:.65rem; background:#fff; }
.pending-balance-table { width:100%; min-width:72rem; border-collapse:separate; border-spacing:0; font-size:.72rem; }
.pending-balance-table th,
.pending-balance-table td { padding:.55rem .65rem; border-right:1px solid #e8e3da; border-bottom:1px solid #e8e3da; text-align:left; vertical-align:middle; white-space:nowrap; }
- .pending-balance-table th { background:var(--vip-content-accent-soft, #F7F0DE); font-weight:700; text-align:center; }
+ .pending-balance-table th { position:sticky; top:0; z-index:4; background:var(--vip-content-accent-soft, #F7F0DE); box-shadow:0 1px 0 var(--vip-border, #D8D2C7); font-weight:700; text-align:center; }
.pending-balance-table th:last-child,
.pending-balance-table td:last-child { border-right:0; }
.pending-balance-table tbody tr:last-child td { border-bottom:0; }
--
2.47.2
From e873f35241e780549ecc9ddbee713606965c46ae Mon Sep 17 00:00:00 2001
From: shanshi <3316401569@qq.com>
Date: Wed, 21 Oct 2026 16:03:36 +0800
Subject: [PATCH 6/7] =?UTF-8?q?=E8=B4=A2=E5=8A=A1=E5=8F=B0=E8=B4=A6?=
=?UTF-8?q?=EF=BC=9A=E5=BC=82=E5=B8=B8=E5=BE=85=E5=8A=9E=E5=88=86=E9=A1=B5?=
=?UTF-8?q?=E6=94=B9=E4=B8=BA=E6=97=A0=E5=88=B7=E6=96=B0=E5=88=87=E6=8D=A2?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../unavailable-balance-tasks.blade.php | 145 ++++++++++++++++--
.../FinanceLedgerAccountStatusTest.php | 4 +
2 files changed, 135 insertions(+), 14 deletions(-)
diff --git a/resources/views/business/pages/financial-ledger/unavailable-balance-tasks.blade.php b/resources/views/business/pages/financial-ledger/unavailable-balance-tasks.blade.php
index fb95b291..14c2da7b 100644
--- a/resources/views/business/pages/financial-ledger/unavailable-balance-tasks.blade.php
+++ b/resources/views/business/pages/financial-ledger/unavailable-balance-tasks.blade.php
@@ -57,10 +57,21 @@
.pending-balance-status.is-limited { border-color:rgba(147,95,7,.32); background:#fff8eb; color:var(--vip-warning, #935F07); }
.pending-balance-status.is-business_deregistered { border-color:rgba(89,89,89,.28); background:#f3f1ec; color:var(--vip-content-muted, #595959); }
.pending-balance-empty { padding:3rem 1rem; color:var(--vip-content-muted, #595959); text-align:center; }
- .pending-balance-pagination { display:flex; align-items:center; justify-content:space-between; gap:.75rem; padding:.7rem .9rem; color:var(--vip-content-muted, #595959); }
- .pending-balance-pagination__links { display:flex; align-items:center; gap:.75rem; }
- .pending-balance-pagination a { color:var(--vip-content-accent-dark, #856632); font-weight:600; text-decoration:none; }
- .pending-balance-pagination a:hover { color:#6A5129; }
+ .pending-balance-page.is-paginating .pending-balance-table-wrap { opacity:.58; pointer-events:none; }
+ .ledger-pagination { display:flex; align-items:center; justify-content:space-between; gap:.75rem; padding:.65rem .9rem; color:var(--vip-content-muted, #595959); font-size:.72rem; }
+ .ledger-pagination a { margin-left:.75rem; color:var(--vip-content-accent-dark, #856632); font-weight:600; text-decoration:none; }
+ .ledger-pagination__controls { display:flex; align-items:center; justify-content:flex-end; gap:.45rem; margin-left:auto; }
+ .ledger-page-box { display:inline-flex; align-items:center; gap:.1rem; height:1.7rem; }
+ .ledger-page-box__arrow { display:inline-grid; place-items:center; width:1.2rem; height:1.5rem; padding:0; border:0; background:transparent; color:var(--vip-content-accent-dark, #856632); font:inherit; text-decoration:none; }
+ .ledger-page-box__arrow:hover { color:#6A5129; }
+ .ledger-page-box__arrow:focus:not(:focus-visible) { outline:none; }
+ .ledger-page-box__arrow:focus-visible { outline:1px solid var(--vip-content-accent-dark, #856632); outline-offset:0; border-radius:2px; }
+ .ledger-page-box__triangle { width:0; height:0; border-top:5px solid transparent; border-bottom:5px solid transparent; }
+ .ledger-page-box__triangle.is-previous { border-right:8px solid currentColor; }
+ .ledger-page-box__triangle.is-next { border-left:8px solid currentColor; }
+ .ledger-page-box__arrow.is-disabled { color:#b9b3a9; cursor:not-allowed; }
+ .ledger-page-box__current { min-width:2.2rem; padding:0 .15rem; color:var(--vip-content-ink, #171717); font-size:.7rem; font-variant-numeric:tabular-nums; font-weight:700; text-align:center; white-space:nowrap; }
+ .pending-balance-page__status { color:var(--vip-content-muted, #595959); font-size:.68rem; }
@media (max-width:1100px) { .pending-balance-filter-grid { grid-template-columns:repeat(3, minmax(0, 1fr)); } }
@media (max-width:900px) { .pending-balance-summary { grid-template-columns:repeat(2, minmax(0, 1fr)); } }
@media (max-width:700px) { .pending-balance-filter-grid { grid-template-columns:repeat(2, minmax(0, 1fr)); } }
@@ -204,7 +215,7 @@
是否完成 |
-
+
@forelse ($rows as $row)
|
@@ -258,19 +269,125 @@
@if ($rows->total() > 0)
- |