feat(client): 财务筛选增加往来单位并统一自定义时间组件(两端,原型同步)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@@ -22,6 +22,7 @@ class FinanceListNotifier extends AsyncNotifier<PageResult<FinanceRecord>> {
|
||||
String _month = '';
|
||||
String _startDate = '';
|
||||
String _endDate = '';
|
||||
int? _partnerId;
|
||||
|
||||
@override
|
||||
Future<PageResult<FinanceRecord>> build() {
|
||||
@@ -35,6 +36,7 @@ class FinanceListNotifier extends AsyncNotifier<PageResult<FinanceRecord>> {
|
||||
month: _month.isEmpty ? null : _month,
|
||||
startDate: _startDate.isEmpty ? null : _startDate,
|
||||
endDate: _endDate.isEmpty ? null : _endDate,
|
||||
partnerId: _partnerId,
|
||||
page: _page,
|
||||
pageSize: _pageSize,
|
||||
);
|
||||
@@ -46,6 +48,13 @@ class FinanceListNotifier extends AsyncNotifier<PageResult<FinanceRecord>> {
|
||||
reload();
|
||||
}
|
||||
|
||||
/// 往来单位筛选(工具栏 ComboSearchField / 移动端 chip→sheet 共用)。
|
||||
void setPartner(int? partnerId) {
|
||||
_partnerId = partnerId;
|
||||
_page = 1;
|
||||
reload();
|
||||
}
|
||||
|
||||
void setMonth(String month) {
|
||||
_month = month;
|
||||
_startDate = '';
|
||||
|
||||
@@ -15,18 +15,24 @@ import '../../core/theme/context_tokens.dart';
|
||||
import '../../core/utils/clock.dart';
|
||||
import '../../core/utils/export_util.dart';
|
||||
import '../../models/finance.dart';
|
||||
import '../../models/partner.dart';
|
||||
import '../../providers/finance_provider.dart';
|
||||
import '../../providers/partner_provider.dart' show allPartnersProvider;
|
||||
import '../../providers/stock_in_provider.dart';
|
||||
import '../../providers/stock_out_provider.dart';
|
||||
import '../../widgets/combo_search_field.dart';
|
||||
import '../../widgets/searchable_option_field.dart' show OptionItem;
|
||||
import '../../widgets/ds/ds_atoms.dart';
|
||||
import '../../widgets/ds/ds_bar_chart.dart';
|
||||
import '../../widgets/ds/ds_kpi.dart';
|
||||
import '../../widgets/ds/ds_table.dart';
|
||||
import '../../widgets/ds/m_kpi_grid.dart';
|
||||
import '../../widgets/ds/m_sheet.dart';
|
||||
import '../../widgets/ds/status_icon_map.dart';
|
||||
import '../../widgets/finance_entry_dialog.dart';
|
||||
import '../../widgets/finance_partner_drawer.dart';
|
||||
import '../../widgets/mobile_list_card.dart';
|
||||
import '../../widgets/wheel_date_picker.dart' show showDateRangeDropdown;
|
||||
import '../../widgets/write_guard.dart';
|
||||
import '../../core/theme/app_fonts.dart';
|
||||
import '../../widgets/ds/ds_toast.dart';
|
||||
@@ -44,6 +50,10 @@ class _FinanceScreenState extends ConsumerState<FinanceScreen> {
|
||||
String _rangeLabel = '本月';
|
||||
// 流水类型筛选(原型 3 chips 扩到 5)
|
||||
String _flowChip = '全部';
|
||||
// 往来单位筛选(桌面 toolbar ComboSearchField / 移动端时间行尾 chip→sheet 共用)
|
||||
int? _partnerId;
|
||||
// 自定义时间区间(记住上次选择,重开 showDateRangeDropdown 时回显)
|
||||
DateTimeRange? _customRange;
|
||||
// 窄屏三分段(原型 m-finance .seg:应收 / 应付 / 流水)
|
||||
int _mSeg = 0;
|
||||
|
||||
@@ -63,11 +73,14 @@ class _FinanceScreenState extends ConsumerState<FinanceScreen> {
|
||||
super.initState();
|
||||
// 默认本月(与 KPI 口径一致)
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
if (mounted) _applyRange('本月');
|
||||
if (mounted) _applyRange('本月', context);
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _applyRange(String range) async {
|
||||
/// [anchorContext] 用于「自定义」分支锚定 showDateRangeDropdown(照 stock_in
|
||||
/// _dateMenu 写法:传入触发该 chip 的 Builder context,桌面下拉/窄屏 sheet 由
|
||||
/// 组件内部自适应);本月/本季分支不弹层,传外层 context 即可。
|
||||
Future<void> _applyRange(String range, BuildContext anchorContext) async {
|
||||
final now = appNow();
|
||||
final notifier = ref.read(financeListProvider.notifier);
|
||||
switch (range) {
|
||||
@@ -87,23 +100,27 @@ class _FinanceScreenState extends ConsumerState<FinanceScreen> {
|
||||
_rangeLabel = '本季';
|
||||
});
|
||||
case '自定义':
|
||||
final picked = await showDateRangePicker(
|
||||
context: context,
|
||||
firstDate: DateTime(now.year - 3),
|
||||
lastDate: now,
|
||||
initialDateRange:
|
||||
final picked = await showDateRangeDropdown(
|
||||
anchorContext,
|
||||
initial: _customRange ??
|
||||
DateTimeRange(start: DateTime(now.year, now.month, 1), end: now),
|
||||
);
|
||||
if (picked == null) return;
|
||||
notifier.setRange(_ymd.format(picked.start), _ymd.format(picked.end));
|
||||
setState(() {
|
||||
_range = range;
|
||||
_customRange = picked;
|
||||
_rangeLabel =
|
||||
'${_ymd.format(picked.start)} ~ ${_ymd.format(picked.end)}';
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void _setPartner(int? partnerId) {
|
||||
setState(() => _partnerId = partnerId);
|
||||
ref.read(financeListProvider.notifier).setPartner(partnerId);
|
||||
}
|
||||
|
||||
void _reload() {
|
||||
ref.read(financeListProvider.notifier).reload();
|
||||
ref.invalidate(financePartnerRowsProvider);
|
||||
@@ -267,33 +284,93 @@ class _FinanceScreenState extends ConsumerState<FinanceScreen> {
|
||||
|
||||
// ── 时间范围 chips(原型 .timerow)──
|
||||
Widget _timeRow(dynamic t, bool mobile) {
|
||||
// 移动端时间行尾追加「往来单位」chip(D6:showMSheet 可搜单选,× 清除);
|
||||
// 桌面往来单位筛选走 _flowTable toolbar 的 ComboSearchField(不在此行)。
|
||||
final partnerName = mobile
|
||||
? _partnerNameById(
|
||||
ref.watch(allPartnersProvider).valueOrNull?.data ?? const [],
|
||||
_partnerId)
|
||||
: null;
|
||||
|
||||
final children = <Widget>[
|
||||
Text('时间范围', style: TextStyle(fontSize: AppDims.fsSm, color: t.muted)),
|
||||
const SizedBox(width: 12),
|
||||
for (final r in const ['本月', '本季']) ...[
|
||||
DsChip(
|
||||
label: r,
|
||||
selected: _range == r,
|
||||
caret: false,
|
||||
onTap: () => _applyRange(r, context)),
|
||||
const SizedBox(width: 10),
|
||||
],
|
||||
Builder(
|
||||
// anchorCtx = chip 自身位置:自定义范围下拉/sheet 锚定在 chip 下方
|
||||
// (照 stock_in_list_screen.dart _dateMenu 写法)
|
||||
builder: (anchorCtx) => DsChip(
|
||||
label: '自定义',
|
||||
selected: _range == '自定义',
|
||||
onTap: () => _applyRange('自定义', anchorCtx)),
|
||||
),
|
||||
if (mobile) ...[
|
||||
const SizedBox(width: 10),
|
||||
DsChip(
|
||||
label: '往来单位',
|
||||
value: partnerName,
|
||||
onTap: _openPartnerSheet,
|
||||
onClear: () => _setPartner(null)),
|
||||
],
|
||||
];
|
||||
|
||||
if (mobile) {
|
||||
children.addAll([
|
||||
const SizedBox(width: 10),
|
||||
WriteGuard(
|
||||
child: DsButton('登记',
|
||||
small: true,
|
||||
icon: LucideIcons.plus,
|
||||
variant: DsBtnVariant.primary,
|
||||
onPressed: () => showFinanceEntryDialog(context)),
|
||||
),
|
||||
]);
|
||||
// 窄屏内容(label+4 chip+登记)常超宽:整行横向可滚(照 _flowTable 移动
|
||||
// 工具栏 chips 横向可滚同款写法),而非把登记钉死右侧裁掉往来单位 chip。
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(bottom: 18),
|
||||
child: SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal, child: Row(children: children)),
|
||||
);
|
||||
}
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(bottom: 18),
|
||||
child: Row(children: [
|
||||
Text('时间范围', style: TextStyle(fontSize: AppDims.fsSm, color: t.muted)),
|
||||
const SizedBox(width: 12),
|
||||
for (final r in const ['本月', '本季', '自定义']) ...[
|
||||
DsChip(
|
||||
label: r,
|
||||
selected: _range == r,
|
||||
caret: r == '自定义',
|
||||
onTap: () => _applyRange(r)),
|
||||
const SizedBox(width: 10),
|
||||
],
|
||||
if (mobile) ...[
|
||||
const Spacer(),
|
||||
WriteGuard(
|
||||
child: DsButton('登记',
|
||||
small: true,
|
||||
icon: LucideIcons.plus,
|
||||
variant: DsBtnVariant.primary,
|
||||
onPressed: () => showFinanceEntryDialog(context)),
|
||||
),
|
||||
],
|
||||
]),
|
||||
child: Row(children: children),
|
||||
);
|
||||
}
|
||||
|
||||
String? _partnerNameById(List<Partner> partners, int? id) {
|
||||
if (id == null) return null;
|
||||
for (final p in partners) {
|
||||
if (p.id == id) return p.name;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// 移动端「往来单位」chip → 底部 sheet 可搜索单选(D6,对齐桌面 ComboSearchField)。
|
||||
Future<void> _openPartnerSheet() async {
|
||||
final partners = ref.read(allPartnersProvider).valueOrNull?.data ?? const [];
|
||||
final options = [
|
||||
for (final p in partners) OptionItem(id: p.id, name: p.name, code: p.code),
|
||||
];
|
||||
final sel = await showMSheet<int?>(
|
||||
context,
|
||||
title: '往来单位',
|
||||
builder: (_) => _PartnerSheetBody(options: options, selectedId: _partnerId),
|
||||
);
|
||||
if (sel == null || !mounted) return;
|
||||
// -1 = 清除选择(照 SearchableOptionField 的既有约定)
|
||||
_setPartner(sel == -1 ? null : sel);
|
||||
}
|
||||
|
||||
// ── KPI 4 卡 ──
|
||||
Widget _kpis(dynamic t, bool mobile) {
|
||||
final outSum = ref.watch(stockOutSummaryProvider).valueOrNull;
|
||||
@@ -547,6 +624,20 @@ class _FinanceScreenState extends ConsumerState<FinanceScreen> {
|
||||
style: TextStyle(fontSize: AppDims.fsSm, color: t.muted)),
|
||||
const SizedBox(width: 12),
|
||||
...chips,
|
||||
const SizedBox(width: 10),
|
||||
// 往来单位:可搜索单选下拉(→ financeListProvider.setPartner;照
|
||||
// stock_out_list_screen.dart 客户 ComboSearchField 写法)。仅桌面
|
||||
// 工具栏放置,移动端对应筛选在 _timeRow 尾部的「往来单位」chip → sheet。
|
||||
ComboSearchField(
|
||||
options: (ref.watch(allPartnersProvider).valueOrNull?.data ??
|
||||
const [])
|
||||
.map((p) => OptionItem(id: p.id, name: p.name, code: p.code))
|
||||
.toList(),
|
||||
selectedId: _partnerId,
|
||||
hint: '往来单位',
|
||||
width: 180,
|
||||
onChanged: _setPartner,
|
||||
),
|
||||
const Spacer(),
|
||||
Text('共 $total 条',
|
||||
style: TextStyle(
|
||||
@@ -869,3 +960,108 @@ class _FinanceScreenState extends ConsumerState<FinanceScreen> {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// 移动端「往来单位」sheet 内容:搜索框 + 全部/逐项列表 + 选中勾(照
|
||||
/// combo_search_field.dart _ComboPop 的搜索+勾选样式,装进 showMSheet 底部
|
||||
/// 容器而非锚定弹层)。选中返回 id;点「全部往来单位」返回 -1(清除,约定
|
||||
/// 同 SearchableOptionField)。
|
||||
class _PartnerSheetBody extends StatefulWidget {
|
||||
final List<OptionItem> options;
|
||||
final int? selectedId;
|
||||
const _PartnerSheetBody({required this.options, required this.selectedId});
|
||||
|
||||
@override
|
||||
State<_PartnerSheetBody> createState() => _PartnerSheetBodyState();
|
||||
}
|
||||
|
||||
class _PartnerSheetBodyState extends State<_PartnerSheetBody> {
|
||||
String _kw = '';
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final t = context.tokens;
|
||||
final hits = _kw.trim().isEmpty
|
||||
? widget.options
|
||||
: widget.options.where((o) => o.matches(_kw)).toList();
|
||||
|
||||
Widget optRow({
|
||||
required String name,
|
||||
required bool selected,
|
||||
required VoidCallback onTap,
|
||||
}) =>
|
||||
InkWell(
|
||||
onTap: onTap,
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 13, horizontal: 4),
|
||||
decoration: BoxDecoration(
|
||||
border: Border(bottom: BorderSide(color: t.borderSubtle)),
|
||||
),
|
||||
child: Row(children: [
|
||||
Expanded(
|
||||
child: Text(name,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
fontSize: AppDims.fsBody,
|
||||
fontWeight:
|
||||
selected ? FontWeight.w600 : FontWeight.w400,
|
||||
color: selected ? t.primary : t.text)),
|
||||
),
|
||||
if (selected) ...[
|
||||
const SizedBox(width: 8),
|
||||
Icon(LucideIcons.check, size: 18, color: t.primary),
|
||||
],
|
||||
]),
|
||||
),
|
||||
);
|
||||
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 11, vertical: 9),
|
||||
decoration: BoxDecoration(
|
||||
color: t.bg,
|
||||
borderRadius: BorderRadius.circular(AppDims.rMd),
|
||||
),
|
||||
child: Row(children: [
|
||||
Icon(LucideIcons.search, size: 15, color: t.faint),
|
||||
const SizedBox(width: 8),
|
||||
Expanded(
|
||||
child: TextField(
|
||||
style: TextStyle(fontSize: AppDims.fsBody, color: t.text),
|
||||
decoration: InputDecoration(
|
||||
isCollapsed: true,
|
||||
contentPadding: EdgeInsets.zero,
|
||||
border: InputBorder.none,
|
||||
hintText: '搜索往来单位…',
|
||||
hintStyle:
|
||||
TextStyle(fontSize: AppDims.fsBody, color: t.faint),
|
||||
),
|
||||
onChanged: (v) => setState(() => _kw = v),
|
||||
),
|
||||
),
|
||||
]),
|
||||
),
|
||||
const SizedBox(height: 6),
|
||||
optRow(
|
||||
name: '全部往来单位',
|
||||
selected: widget.selectedId == null,
|
||||
onTap: () => Navigator.of(context).pop(-1),
|
||||
),
|
||||
if (hits.isEmpty)
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(20),
|
||||
child: Text('无匹配结果',
|
||||
style: TextStyle(color: t.muted, fontSize: AppDims.fsSm)),
|
||||
)
|
||||
else
|
||||
for (final o in hits)
|
||||
optRow(
|
||||
name: o.name,
|
||||
selected: o.id == widget.selectedId,
|
||||
onTap: () => Navigator.of(context).pop(o.id),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 108 KiB After Width: | Height: | Size: 108 KiB |
|
Before Width: | Height: | Size: 108 KiB After Width: | Height: | Size: 108 KiB |
|
Before Width: | Height: | Size: 109 KiB After Width: | Height: | Size: 109 KiB |
|
Before Width: | Height: | Size: 100 KiB After Width: | Height: | Size: 101 KiB |
|
Before Width: | Height: | Size: 100 KiB After Width: | Height: | Size: 101 KiB |
|
Before Width: | Height: | Size: 101 KiB After Width: | Height: | Size: 102 KiB |
@@ -83,7 +83,7 @@
|
||||
<span class="lbl">时间范围</span>
|
||||
<div class="chip on" data-range="本月" onclick="setRange('本月')">本月</div>
|
||||
<div class="chip" data-range="本季" onclick="setRange('本季')">本季</div>
|
||||
<div class="chip" data-range="自定义" onclick="setRange('自定义')">自定义 <svg viewBox="0 0 24 24"><use href="#i-ic16"/></svg></div>
|
||||
<div class="chip" data-range="自定义" onclick="openCustomRange(this)">自定义 <svg viewBox="0 0 24 24"><use href="#i-ic16"/></svg></div>
|
||||
</div>
|
||||
|
||||
<!-- 2. KPI 条 -->
|
||||
@@ -123,6 +123,7 @@
|
||||
<div class="chip on" data-flt="全部" onclick="setFlowFilter('全部')">全部</div>
|
||||
<div class="chip" data-flt="收款" onclick="setFlowFilter('收款')">收款</div>
|
||||
<div class="chip" data-flt="付款" onclick="setFlowFilter('付款')">付款</div>
|
||||
<div class="chip" id="chipParty" onclick="openPartyFilter(this)">往来单位 <span class="cv" id="partyVal"></span><svg viewBox="0 0 24 24"><use href="#i-chevron-down"/></svg></div>
|
||||
<div class="sp"></div>
|
||||
<span class="ref" id="flowCount"></span>
|
||||
</div>
|
||||
@@ -141,7 +142,16 @@
|
||||
<script src="shell.js"></script>
|
||||
<script>
|
||||
/* ---- 1. 时间范围 ---- */
|
||||
function setRange(r){ document.querySelectorAll('.chip[data-range]').forEach(c=>c.classList.toggle('on',c.dataset.range===r)); document.getElementById('finSub').textContent='应收/应付概览 · '+r; if(r==='自定义')toast('自定义区间选择器(原型略)'); }
|
||||
function setRange(r){ document.querySelectorAll('.chip[data-range]').forEach(c=>c.classList.toggle('on',c.dataset.range===r)); document.getElementById('finSub').textContent='应收/应付概览 · '+r; }
|
||||
// 自定义区间:起止并排双滚轮锚定 chip 下方(datewheel.js openRange,对齐真实端 showDateRangeDropdown)
|
||||
let customFrom='', customTo='';
|
||||
function openCustomRange(t){
|
||||
DateWheel.openRange(t,{from:customFrom,to:customTo,onCommit:(f,to)=>{
|
||||
customFrom=f; customTo=to;
|
||||
document.querySelectorAll('.chip[data-range]').forEach(c=>c.classList.toggle('on',c.dataset.range==='自定义'));
|
||||
document.getElementById('finSub').textContent='应收/应付概览 · '+f+' ~ '+to;
|
||||
}});
|
||||
}
|
||||
|
||||
/* ---- 3. 趋势图数据(万元) ---- */
|
||||
const TREND=[
|
||||
@@ -200,9 +210,13 @@ const FLOWS=[
|
||||
{date:'2026-06-11',type:'收款',party:'华东酒类批发',amt:55200,ref:'SK-20260611-06',note:'梦之蓝M6'}
|
||||
];
|
||||
let flowFilter='全部';
|
||||
let flowParty='全部';
|
||||
const PARTIES=['全部',...Array.from(new Set(FLOWS.map(f=>f.party)))];
|
||||
function setFlowFilter(f){ flowFilter=f; document.querySelectorAll('.chip[data-flt]').forEach(c=>c.classList.toggle('on',c.dataset.flt===f)); renderFlows(); }
|
||||
// 往来单位:可搜索单选下拉(chip + openSearchMenu,照 stock-out-list.html 客户筛选写法)
|
||||
function openPartyFilter(t){ openSearchMenu(t,PARTIES.map(v=>({v,label:v,sel:v===flowParty})),v=>{ flowParty=v; closeMenus(); renderFlows(); },{placeholder:'搜索往来单位…'}); }
|
||||
function renderFlows(){
|
||||
const rows=FLOWS.filter(f=>flowFilter==='全部'||f.type===flowFilter);
|
||||
const rows=FLOWS.filter(f=>(flowFilter==='全部'||f.type===flowFilter)&&(flowParty==='全部'||f.party===flowParty));
|
||||
const tb=document.getElementById('flowBody');
|
||||
if(rows.length===0){ tb.innerHTML=`<tr><td colspan="6"><div class="empty">没有匹配的流水记录</div></td></tr>`; }
|
||||
else { tb.innerHTML=rows.map(f=>`<tr>
|
||||
@@ -215,6 +229,8 @@ function renderFlows(){
|
||||
</tr>`).join(''); }
|
||||
document.getElementById('flowCount').textContent=`共 ${rows.length} 条`;
|
||||
document.getElementById('flowSub').textContent=`本月 ${FLOWS.length} 笔 · 收 ${FLOWS.filter(f=>f.type==='收款').length} / 付 ${FLOWS.filter(f=>f.type==='付款').length}`;
|
||||
document.getElementById('partyVal').textContent=flowParty==='全部'?'':flowParty;
|
||||
document.getElementById('chipParty').classList.toggle('on',flowParty!=='全部');
|
||||
}
|
||||
|
||||
/* ---- 抽屉:某往来单位流水 ---- */
|
||||
@@ -246,6 +262,7 @@ document.addEventListener('keydown',e=>{ if(e.key==='Escape'){closeMenus();close
|
||||
buildChart(); buildSummary(); renderFlows();
|
||||
</script>
|
||||
<a class="ds-fab" href="../index.html" title="返回设计系统" aria-label="返回设计系统"><svg viewBox="0 0 24 24"><use href="#i-arrow-left"/></svg></a>
|
||||
<script src="datewheel.js"></script>
|
||||
<script src="fab.js"></script>
|
||||
<script src="notify.js"></script>
|
||||
<script src="statusbar.js"></script>
|
||||
|
||||
@@ -7,12 +7,30 @@
|
||||
<link rel="stylesheet" href="../tokens.css">
|
||||
<link rel="stylesheet" href="../atoms.css">
|
||||
<link rel="stylesheet" href="../mobile-atoms.css">
|
||||
<style>body{margin:0; font-family:var(--font); color:var(--text); background:var(--page); -webkit-font-smoothing:antialiased;}</style>
|
||||
<style>
|
||||
body{margin:0; font-family:var(--font); color:var(--text); background:var(--page); -webkit-font-smoothing:antialiased;}
|
||||
/* 时间范围筛选行(回填真实端已有形态,照桌面 finance.html .timerow):
|
||||
label+4 chip+登记 常超宽 → 整行横向可滚(照 m-stock-out-list.html 工具栏
|
||||
chips 横向可滚同款写法),不把登记钉死右侧裁掉往来单位 chip */
|
||||
.timerow{display:flex; align-items:center; gap:10px; margin:2px 0 14px; overflow-x:auto;}
|
||||
.timerow::-webkit-scrollbar{display:none;}
|
||||
.timerow .lbl{font-size:var(--fs-sm); color:var(--muted); margin-right:2px; flex:none; white-space:nowrap;}
|
||||
.timerow > *{flex:none;}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="m-top" data-m-top data-title="财务管理"></div>
|
||||
<div class="m-page has-tabs">
|
||||
<div class="m-scroll">
|
||||
<!-- 时间范围(真实端 FinanceScreen._timeRow 移动分支:存量漂移回填) -->
|
||||
<div class="timerow">
|
||||
<span class="lbl">时间范围</span>
|
||||
<div class="chip on" data-range="本月" onclick="setRange('本月')">本月</div>
|
||||
<div class="chip" data-range="本季" onclick="setRange('本季')">本季</div>
|
||||
<div class="chip" data-range="自定义" onclick="openCustomRange()">自定义 <svg viewBox="0 0 24 24"><use href="#i-ic16"/></svg></div>
|
||||
<div class="chip" id="chipParty" onclick="openPartnerSheet()">往来单位 <span class="cv" id="partyVal"></span><svg viewBox="0 0 24 24"><use href="#i-chevron-down"/></svg></div>
|
||||
<div class="btn primary sm" onclick="mToast('登记收支(原型)')"><svg viewBox="0 0 24 24"><use href="#i-plus"/></svg>登记</div>
|
||||
</div>
|
||||
<div class="m-kpi">
|
||||
<div class="k"><div class="kl">本月收入</div><div class="kv">¥86.4万</div><div class="kd up">▲ 8.2% 较上月</div></div>
|
||||
<div class="k"><div class="kl">本月支出</div><div class="kv">¥52.1万</div><div class="kd down">▲ 3.1% 较上月</div></div>
|
||||
@@ -29,6 +47,7 @@
|
||||
|
||||
<script src="icons.js"></script>
|
||||
<script src="mobile-shell.js"></script>
|
||||
<script src="datewheel.js"></script>
|
||||
<script>
|
||||
const AR=[
|
||||
{party:'城东烟酒行',amt:'¥86,200',status:'未结清',date:'账期 06-30'},
|
||||
@@ -47,13 +66,51 @@ const FLOW=[
|
||||
let tab='ar';
|
||||
const FTABS=['ar','ap','flow'];
|
||||
function setTab(t){ tab=t; const idx=FTABS.indexOf(t); document.querySelectorAll('.seg button').forEach((b,i)=>b.classList.toggle('on',i===idx)); render(); }
|
||||
|
||||
/* ---- 时间范围(照桌面 finance.html:仅改文案,不真实过滤 AR/AP/FLOW 展示数据) ---- */
|
||||
function setRange(r){ document.querySelectorAll('.chip[data-range]').forEach(c=>c.classList.toggle('on',c.dataset.range===r)); }
|
||||
// 自定义范围:窄屏走底部 sheet 纵排双滚轮(datewheel.js mountRange,对齐真实端
|
||||
// showDateRangeDropdown 在 isMobile 下走 showMSheet,照 m-stock-in-list.html openAdvDateSheet 写法)
|
||||
let customFrom='', customTo='';
|
||||
function openCustomRange(){
|
||||
openSheet('日期区间','<div id="rangeMount"></div>');
|
||||
DateWheel.mountRange(document.getElementById('rangeMount'),{from:customFrom,to:customTo,onCommit:(f,to)=>{
|
||||
customFrom=f; customTo=to;
|
||||
document.querySelectorAll('.chip[data-range]').forEach(c=>c.classList.toggle('on',c.dataset.range==='自定义'));
|
||||
closeSheet();
|
||||
}});
|
||||
}
|
||||
|
||||
/* ---- 往来单位(D6:chip → 底部 sheet 可搜索单选,× 清除;仅过滤「流水」分段) ---- */
|
||||
let flowParty='全部';
|
||||
const PARTNERS=['全部',...Array.from(new Set([...AR,...AP,...FLOW].map(r=>r.party.split(' · ')[0])))];
|
||||
function openPartnerSheet(){
|
||||
openSheet('往来单位',
|
||||
'<div class="m-search" style="margin-bottom:8px"><svg viewBox="0 0 24 24"><use href="#i-search"/></svg>'
|
||||
+'<input id="partnerKw" placeholder="搜索往来单位…" oninput="drawPartnerOpts(this.value)"></div>'
|
||||
+'<div id="partnerOptList"></div>');
|
||||
drawPartnerOpts('');
|
||||
}
|
||||
function drawPartnerOpts(kw){
|
||||
const q=(kw||'').trim().toLowerCase();
|
||||
const hits=PARTNERS.filter(v=>v==='全部'||!q||v.toLowerCase().includes(q));
|
||||
document.getElementById('partnerOptList').innerHTML=hits.length
|
||||
? hits.map(v=>`<div class="m-opt ${v===flowParty?'sel':''}" onclick="setFlowParty('${v}')">${v}<svg class="ck" viewBox="0 0 24 24"><use href="#i-check"/></svg></div>`).join('')
|
||||
: '<div style="padding:20px 4px;text-align:center;color:var(--muted);font-size:var(--fs-sm)">无匹配结果</div>';
|
||||
}
|
||||
function setFlowParty(v){ flowParty=v; closeSheet(); render(); }
|
||||
|
||||
function render(){
|
||||
const cfg={ar:['应收账款',AR],ap:['应付账款',AP],flow:['资金流水',FLOW]}[tab];
|
||||
document.getElementById('cnt').textContent=`${cfg[0]} · 共 ${cfg[1].length}`;
|
||||
document.getElementById('list').innerHTML=cfg[1].map(r=>`<div class="m-card"><div class="m-row">
|
||||
// 往来单位筛选仅作用于「流水」分段(对应真实端 financeListProvider.setPartner 只过滤流水表)
|
||||
const rows=tab==='flow'?cfg[1].filter(r=>flowParty==='全部'||r.party.split(' · ')[0]===flowParty):cfg[1];
|
||||
document.getElementById('cnt').textContent=`${cfg[0]} · 共 ${rows.length}`;
|
||||
document.getElementById('list').innerHTML=rows.map(r=>`<div class="m-card"><div class="m-row">
|
||||
<div class="mc-main"><div class="mc-nm">${r.party}</div><div class="mc-sub">${r.date}</div></div>
|
||||
<div class="mc-right"><span class="badge b-${r.status}">${r.status}</span><span class="mc-amt">${r.amt}</span></div>
|
||||
</div></div>`).join('');
|
||||
document.getElementById('partyVal').textContent=flowParty==='全部'?'':flowParty;
|
||||
document.getElementById('chipParty').classList.toggle('on',flowParty!=='全部');
|
||||
}
|
||||
render();
|
||||
</script>
|
||||
|
||||