@@ -96,32 +96,29 @@ function renderPlan() {
} ) ( ) ;
const sm = pf . sign _month || "" ;
const signMonthCell = ` <td class="p-2 text-center align-middle text-sm"><span class="pf-sm-text cursor-pointer hover:text-blue-600" id="pf-sm- ${ pf . id } " onclick="event.stopPropagation(); planEditSignMonth(event, ${ pf . id } )"> ${ sm || '—' } </span></td> ` ;
return ` <tr class="border-b border-slate-100 hover:bg-slate-50 cursor-pointer" onclick="planOpenPfEditModal( ${ pf . id } )"><td class="p-2 text-sm font-medium text-center align-middle"> ${ esc ( pf . customer _name ) } </td> ${ signMonthCell } <td class="p-2 text-center align-middle text-sm"> ${ money ( pf . sign _amount ) } </td> ${ mCols } ${ totalCol } </tr> ` ;
return ` <tr class="border-b border-slate-100 hover:bg-slate-50 cursor-pointer" onclick="planOpenPfEditModal( ${ pf . id } )"><td class="p-2 text-center align-middle text-sm"> ${ esc ( pf . client _name || "" ) } </td><td class="p-2 text-sm font-medium text-center align-middle"> ${ esc ( pf . customer _name ) } </td><td class="p-2 text-center align-middle text-sm"> ${ esc ( pf . weight || "20%" ) } </td><td class="p-2 text-center align-middle text-sm"> ${ esc ( pf . project _type || "待签约" ) } </td> ${ signMonthCell } <td class="p-2 text-center align-middle text-sm"> ${ money ( pf . sign _amount ) } </td> ${ mCols } ${ totalCol } </tr> ` ;
} ;
const now2 = new Date ( ) ;
const defaultMonth2 = now2 . getFullYear ( ) + "-" + String ( now2 . getMonth ( ) + 1 ) . padStart ( 2 , "0" ) ;
if ( ! state . planMonth ) state . planMonth = defaultMonth2 ;
if ( state . planQuarter === undefined ) state . planQuarter = Math . floor ( now2 . getMonth ( ) / 3 ) ;
const monthSet2 = new Set ( [ defaultMonth2 ] ) ;
pfs . forEach ( pf => { let bd = [ ] ; try { bd = JSON . parse ( pf . budget _data || "[]" ) ; } catch ( e ) { } bd . forEach ( b => { if ( b . month ) monthSet2 . add ( b . month ) ; } ) ; } ) ;
const allMonths = [ ... monthSet2 ] . sort ( ) . revers e( ) ;
const qLabels2 = [ "Q1 (1-3月)" , "Q2 (4-6月)" , "Q3 (7-9月)" , "Q4 (10-12月)" ] ;
const toolMonthSelect = all Months. map ( m => '<option value="' + m + '" ' + ( m === state . planMonth ? 'selected' : '' ) + '>' + m + '</option>' ) . join ( "" ) ;
const toolQuarterSelect = qLabels2 . map ( ( l , i ) => '<option value="' + i + '" ' + ( i === state . planQuarter ? 'selec ted ': '' ) + '>' + l + '</option>' ) . join ( "" ) ;
const toolDateSelect = state . planView === 'monthly' ? '<span class="text-sm text-slate-500 ml-2">月份:</span><select onchange="state.planMonth=this.value;renderPlan()" class="text-sm font-medium py-1 px-2.5 cursor-pointer" style="appearance:none;-webkit-appearance:none;background-color:transparent;border:0;outline:none;background:url('data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 width=%2212%22 height=%2212%22 viewBox=%220 0 24 24%22 fill=%22none%22 stroke=%22%236b7280%22 stroke-width=%222%22 stroke-linecap=%22round%22 stroke-linejoin=%22round%22%3E%3Cpolyline points=%226 9 12 15 18 9%22%3E%3C/polyline%3E%3C/svg%3E') no-repeat right 4px center;padding-right:22px;min-height:30px">' + toolMonthSelect + '</select>' : state . planView === 'quarterly' ? '<span class="text-sm text-slate-500 ml-2">季度:</span><select onchange="state.planQuarter=parseInt(this.value);localStorage.setItem(\'opc-fin-quarter\',this.value);renderPlan()" class="text-sm font-medium py-1 px-2.5 cursor-pointer" style="appearance:none;-webkit-appearance:none;background-color:transparent;border:0;outline:none;background:url('data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 width=%2212%22 height=%2212%22 viewBox=%220 0 24 24%22 fill=%22none%22 stroke=%22%236b7280%22 stroke-width=%222%22 stroke-linecap=%22round%22 stroke-linejoin=%22round%22%3E%3Cpolyline points=%226 9 12 15 18 9%22%3E%3C/polyline%3E%3C/svg%3E') no-repeat right 4px center;padding-right:22px;min-height:30px">' + toolQuarterSelect + '</select>' : '' ;
const finHeaderBase = ` <span class="text-sm text-slate-500">视图:</span><select onchange="setPlanView(this.value)" class="text-sm font-medium py-1 px-2.5 cursor-pointer" style="appearance:none;-webkit-appearance:none;background-color:transparent;border:0;outline:none;background:url('data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 width=%2212%22 height=%2212%22 viewBox=%220 0 24 24%22 fill=%22none%22 stroke=%22%236b7280%22 stroke-width=%222%22 stroke-linecap=%22round%22 stroke-linejoin=%22round%22%3E%3Cpolyline points=%226 9 12 15 18 9%22%3E%3C/polyline%3E%3C/svg%3E') no-repeat right 4px center;padding-right:22px;min-height:30px"><option value="overview" ${ state . planView === 'overview' ? 'selected' : '' } >总视图</option><option value="quarterly" ${ state . planView === 'quarterly' ? 'selected' : '' } >季度视图</option><option value="monthly" ${ state . planView === 'monthly' ? 'selected' : '' } >月度视图</option></select> ${ toolDateSelect } ` ;
const qLabels = [ 'Q1' , 'Q2' , 'Q3' , 'Q4' ] ;
const qMonths = [ [ 1 , 2 , 3 ] , [ 4 , 5 , 6 ] , [ 7 , 8 , 9 ] , [ 10 , 11 , 12 ] ] ;
const nowD = new Dat e( ) ;
const activeQ = state . planQuarter !== undefined ? state . planQuarter : Math . floor ( nowD . getMonth ( ) / 3 ) ;
const activeMonths = q Months[ activeQ ] ;
const finHeaderBase = ` <div class="flex items-center gap-2"><button onclick="setPlanView('overview')" class="px-3 py-1 rounded text-sm font-medium ${ state . planView === 'overview' ? 'bg-blue-600 text-white' : 'bg-slate-100 text-slate-600 hover:bg-slate-200' } ">总视图</button><span class="text-slate-300 mx-2">|</span><span class="text-sm text-slate-500">按季度:</span> ${ qLabels . map ( ( q , i ) => ` <button onclick="setPlanQuarter( ${ i } )" class="px-3 py-1 rounded text-sm font-medium ${ state . planView === 'quarterly' && activeQ === i ? 'bg-blue-600 text-white' : 'bg-slate-100 text-slate-600 hover:bg-slate-200' } "> ${ q } </button> ` ) . join ( '' ) } <span class="text-slate-300 mx-2">|</span><span class="text-sm text-slate-500">按月度:</span> ${ activeMonths . map ( m => { const ms = ` 2026- ${ String ( m ) . padStart ( 2 , '0' ) } ` ; return ` <button onclick="setPlanMonth(' ${ ms } ')" class="px-3 py-1 rounded text-sm font-medium ${ state . planView === 'monthly' && state . planMonth === ms ? 'bg-blue-600 text-whi te': 'bg-slate-100 text-slate-600 hover:bg-slate-200' } "> ${ m } 月</button> ` ; } ).join('')}</div> ` ;
const finAddBtn = ` <button class="btn btn-primary btn-sm" onclick="openPlanModal()">新增财务项目</button> ` ;
const planFilterTabs = ` <div class="finance-tabs" style="padding:0 0 0 0;border-bottom:1px solid #e2e8f0;margin-bottom:0;display:flex;gap:4px"><button onclick="switchPlanFilter('overview')" class="finance-tab ${ state . planFilter === 'overview' ? ' active' : '' } " style="font-size:14px;font-weight:600;padding:8px 16px;margin-right:0">总览</button><button onclick="switchPlanFilter('projects')" class="finance-tab ${ state . planFilter === 'projects' ? ' active' : '' } " style="font-size:14px;font-weight:600">项目计划 ( ${ pfs . length } )</button><button onclick="switchPlanFilter('expense')" class="finance-tab ${ state . planFilter === 'expense' ? ' active' : '' } " style="font-size:14px;font-weight:600">平台费用计划</button></div> ` ;
document . querySelector ( "#plan" ) . innerHTML = ` <div class="grid gap-4">
${ planFilterTabs }
<div class="flex items-center gap-3 px-4 py-3 rounded-lg border" style="background:linear-gradient(135deg,#dbeafe,#bfdbfe);border-color:#3b82f6">
<i data-lucide="lightbulb" class="text-blue-600 flex-shrink-0" style="width:18px;height:18px"></i>
<span class="text-sm text-blue-900"><strong>计划提醒:</strong>计划也是预算,只有真正能够列出计划的预算,才有靠谱的落地可能性</span>
</div>
${ planFilterTabs }
${ state . planFilter !== 'expense' && state . planFilter !== 'overview' ? card ( ` <div class="flex justify-between items-center"><div class="flex items-center gap-2"> ${ finHeaderBase } </div> ${ finAddBtn } </div> ` , "p-4" ) : '' }
<div id="planModal" class="hidden fixed inset-0 z-50 flex items-center justify-center bg-black/40" onclick="closePlanModal()"><div class="bg-white rounded-2xl shadow-2xl w-full max-w-6xl mx-4 max-h-[92vh] overflow-y-auto" onclick="event.stopPropagation()"><div class="sticky top-0 z-10 bg-white/95 backdrop-blur border-b border-slate-100 px-8 py-5 flex items-center justify-between"><div><h3 class="text-xl font-bold text-slate-800" id="planModalTitle">新增项目财务</h3><p class="text-xs text-slate-400 mt-0.5">填写项目财务信息与月度预算</p></div><div class="flex items-center gap-2"><button class="btn btn-ghost btn-sm text-red-600 hidden" id="planDeleteBtn" onclick="deletePlanItem()"><i data-lucide="trash-2"></i>删除</button><button class="btn btn-ghost btn-sm rounded-full w-8 h-8 p-0" onclick="closePlanModal()"><i data-lucide="x"></i></button></div></div>
<div class="finance-tabs">
@@ -153,7 +150,7 @@ function renderPlan() {
</div>
<div class="grid grid-cols-3 gap-3">
<label class="block"><span class="fin-label">联系电话</span><input name="contact_phone" class="form-ctrl" placeholder="请输入联系电话"></label>
<label class="block"><span class="fin-label">其他</span><input name="other_info" class="form-ctrl" placeholder="备注信息"></label>
<label class="block"><span class="fin-label">其他</span><input name="other_info" class="form-ctrl" placeholder="备注信息"></label></div><div class="grid grid-cols-3 gap-3"><label class="block"><span class="fin-label">客户名称</span><input name="client_name" class="form-ctrl" placeholder="客户名称"></label><label class="block"><span class="fin-label">项目类型</span><select name="project_type" class="form-ctrl bg-white"><option>待签约</option><option>已签约</option></select></label><label class="block"><span class="fin-label">加权</span><select name="weight" class="form-ctrl bg-white"><option>20%</option><option>40%</option><option>60%</option><option>80%</option><option>100%</option></select></label>
</div>
<div class="block"><span class="fin-label">业务类型</span><div class="flex flex-wrap gap-2 mt-1" id="plan_businessTypeChecks"> ${ fmTypes . map ( t => ` <label class="inline-flex items-center gap-1 px-2.5 py-1 rounded-full border border-slate-200 cursor-pointer hover:border-blue-300 text-sm bt-chip" onclick="planToggleBtChip(this)"><input type="checkbox" name="business_type[]" value=" ${ t } " class="hidden"><span> ${ t } </span></label> ` ) . join ( "" ) } </div></div>
</div>
@@ -288,8 +285,10 @@ function renderPlan() {
return h ;
} ;
const SIGNED _COLS = [
{ key : 'client_name' , label : '客户' } ,
{ key : 'customer_name' , label : '项目名称' } ,
,
{ key : 'weight' , label : '加权' } ,
{ key : 'project_type' , label : '项目类型' } ,
{ key : 'sign_amount' , label : '签约金额' } ,
{ key : 'rev' , label : '已确收' } ,
{ key : 'gross' , label : '毛利' } ,
@@ -299,13 +298,13 @@ function renderPlan() {
{ key : 'paid' , label : '已付' } ,
{ key : 'cashflow' , label : '现金流' } ,
{ key : 'exe_rate' , label : '执行率' } ,
{ key : 'gross_rate' , label : '毛利率' } ,
{ key : 'pay_rate' , label : '回款率' } ,
{ key : 'paid_rate' , label : '付款率' } ,
] ;
const UNSIGNED _COLS = [
{ key : 'client_name' , label : '客户' } ,
{ key : 'customer_name' , label : '项目名称' } ,
,
{ key : 'weight' , label : '加权' } ,
{ key : 'project_type' , label : '项目类型' } ,
{ key : 'sign_amount' , label : '签约金额' } ,
{ key : 'sign_month' , label : '签约月份' } ,
{ key : 'gross' , label : '毛利' } ,
@@ -320,7 +319,7 @@ function renderPlan() {
return typeof v === 'object' ? [ c . label , v . val , v . cls ] : [ c . label , v , c . color ] ;
} ) ;
const thead = isUnsigned ? buildThead ( UNSIGNED _COLS , 'uns' ) : buildThead ( SIGNED _COLS , 'sig' ) ;
const theadCols = isUnsigned ? 6 : 13 ;
const theadCols = isUnsigned ? 9 : 14 ;
const tbody = rows . length ? ` <tbody> ${ rows . join ( "" ) } </tbody> ` : ` <tr><td colspan=" ${ theadCols } " class="p-6 text-center text-slate-400"> ${ emptyText } </td></tr> ` ;
const cardGrid = isUnsigned ? 'grid-cols-4' : 'grid-cols-6' ;
@@ -345,14 +344,14 @@ function renderPlan() {
const profit = gross ;
const pfCls = profit >= 0 ? 'text-green-600' : 'text-red-600' ;
const pfVal = profit ? money ( profit ) : '<span class="text-slate-300">—</span>' ;
return ` <tr class="border-b border-slate-100 hover:bg-slate-50 cursor-pointer" onclick="planOpenPfEditModal( ${ pf . id } )"><td class="p-2 text-sm font-medium text-center align-middle truncate" style="min-width:300px;max-width:300px;width:300px" title=" ${ esc ( pf . customer _name ) } "> ${ esc ( pf . customer _name ) } </td><td class="p-2 text-center align-middle text-sm font-medium"> ${ money ( pf . sign _amount ) } </td><td class="p-2 text-center text-blue-700 align-middle"> ${ fmtNoUnit ( rev ) } </td><td class="p-2 text-center align-middle text-sm font-medium"> ${ fmtNoUnit ( gross ) } </td><td class="p-2 text-center text-rose-700 align-middle"> ${ fmtNoUnit ( cost ) } </td><td class="p-2 text-center font-semibold align-middle ${ pfCls } "> ${ pfVal } </td><td class="p-2 text-center text-amber-700 align-middle"> ${ fmtNoUnit ( payment ) } </td><td class="p-2 text-center text-purple-700 align-middle"> ${ fmtNoUnit ( paid ) } </td><td class="p-2 text-center font-semibold align-middle ${ cfCls } "> ${ cf } </td><td class="p-2 text-center align-middle text-sm"> ${ exe } </td><td class="p-2 text-center align-middle text-sm"> ${ grossR } </td><td class="p-2 text-center align-middle text-sm"> ${ payR } </td><td class="p-2 text-center align-middle text-sm"> ${ paid R} </td></tr> ` ;
return ` <tr class="border-b border-slate-100 hover:bg-slate-50 cursor-pointer" onclick="planOpenPfEditModal( ${ pf . id } )"><td class="p-2 text-center align-middle text-sm"> ${ esc ( pf . client _name || "" ) } </td><td class="p-2 text-sm font-medium text-center align-middle truncate" style="min-width:300px;max-width:300px;width:300px" title=" ${ esc ( pf . customer _name ) } "> ${ esc ( pf . customer _name ) } </td><td class="p-2 text-center align-middle text-sm" onclick="event.stopPropagation()"> ${ planInlineSelect ( 'weight' , pf . id , 'weight' , pf . weight || '20%' , [ '20%' , '40%' , '60%' , '80%' , '100%' ] ) } </td><td class="p-2 text-center align-middle text-sm" onclick="event.stopPropagation()"> ${ planInlineSelect ( 'ptype' , pf . id , 'project_type' , pf . project _type || '待签约' , [ '待签约' , '已签约' ] ) } </td><td class="p-2 text-center align-middle text-sm font-medium"> ${ money ( pf . sign _amount ) } </td><td class="p-2 text-center text-blue-700 align-middle"> ${ fmtNoUnit ( rev ) } </td><td class="p-2 text-center align-middle text-sm font-medium"> ${ fmtNoUnit ( gross ) } </td><td class="p-2 text-center text-rose-700 align-middle"> ${ fmtNoUnit ( cost ) } </td><td class="p-2 text-center font-semibold align-middle ${ pfCls } "> ${ pfVal } </td><td class="p-2 text-center text-amber-700 align-middle"> ${ fmtNoUnit ( payment ) } </td><td class="p-2 text-center text-purple-700 align-middle"> ${ fmtNoUnit ( paid ) } </td><td class="p-2 text-center font-semibold align-middle ${ cfCls } "> ${ cf } </td><td class="p-2 text-center align-middle text-sm"> ${ exe } </td><td class="p-2 text-center align-middle text-sm"> ${ pay R} </td></tr> ` ;
} ;
// 待签约简版行
const tdRowUnsigned = ( pf , cost , gross ) => {
const profit = gross ;
const pfCls = profit >= 0 ? 'text-green-600' : 'text-red-600' ;
const pfVal = profit ? money ( profit ) : '<span class="text-slate-300">—</span>' ;
return ` <tr class="border-b border-slate-100 hover:bg-slate-50 cursor-pointer" onclick="planOpenPfEditModal( ${ pf . id } )"><td class="p-2 text-sm font-medium text-center align-middle truncate" style="min-width:300px;max-width:300px;width:300px" title=" ${ esc ( pf . customer _name ) } "> ${ esc ( pf . customer _name ) } </td><td class="p-2 text-center align-middle text-sm font-medium"> ${ money ( pf . sign _amount ) } </td><td class="p-2 text-center align-middle text-sm"> ${ pf . sign _month || '—' } </td><td class="p-2 text-center align-middle text-sm font-medium"> ${ fmtNoUnit ( gross ) } </td><td class="p-2 text-center text-rose-700 align-middle"> ${ fmtNoUnit ( cost ) } </td><td class="p-2 text-center font-semibold align-middle ${ pfCls } "> ${ pfVal } </td></tr> ` ;
return ` <tr class="border-b border-slate-100 hover:bg-slate-50 cursor-pointer" onclick="planOpenPfEditModal( ${ pf . id } )"><td class="p-2 text-center align-middle text-sm"> ${ esc ( pf . client _name || "" ) } </td><td class="p-2 text-sm font-medium text-center align-middle truncate" style="min-width:300px;max-width:300px;width:300px" title=" ${ esc ( pf . customer _name ) } "> ${ esc ( pf . customer _name ) } </td><td class="p-2 text-center align-middle text-sm" onclick="event.stopPropagation()"> ${ planInlineSelect ( 'weight' , pf . id , 'weight' , pf . weight || '20%' , [ '20%' , '40%' , '60%' , '80%' , '100%' ] ) } </td><td class="p-2 text-center align-middle text-sm" onclick="event.stopPropagation()"> ${ planInlineSelect ( 'ptype' , pf . id , 'project_type' , pf . project _type || '待签约' , [ '待签约' , '已签约' ] ) } </td><td class="p-2 text-center align-middle text-sm font-medium"> ${ money ( pf . sign _amount ) } </td><td class="p-2 text-center align-middle text-sm"> ${ pf . sign _month || '—' } </td><td class="p-2 text-center align-middle text-sm font-medium"> ${ fmtNoUnit ( gross ) } </td><td class="p-2 text-center text-rose-700 align-middle"> ${ fmtNoUnit ( cost ) } </td><td class="p-2 text-center font-semibold align-middle ${ pfCls } "> ${ pfVal } </td></tr> ` ;
} ;
if ( state . planView === 'monthly' ) {
@@ -787,7 +786,7 @@ window.planOpenPfEditModal = (pfId) => {
document . querySelector ( "#plan_pf-id-input" ) . value = pf . id ;
document . querySelector ( "#planModalTitle" ) . textContent = "编辑项目财务" ;
document . querySelector ( "#planDeleteBtn" ) . classList . remove ( "hidden" ) ;
const form = document . querySelector ( "#finance Modal form" ) ;
const form = document . querySelector ( "#plan Modal form" ) ;
form . querySelector ( '[name="project_id"]' ) . value = pf . project _id || "" ;
const deptDisplay = form . querySelector ( '.bg-slate-50 [disabled]' ) ;
if ( deptDisplay ) deptDisplay . value = pf . project _id || "" ;
@@ -823,6 +822,9 @@ window.planOpenPfEditModal = (pfId) => {
setVal ( "contact_name" , pf . contact _name ) ;
setVal ( "contact_phone" , pf . contact _phone ) ;
setVal ( "other_info" , pf . other _info ) ;
setVal ( "client_name" , pf . client _name ) ;
form . querySelector ( '[name="project_type"]' ) . value = pf . project _type || "待签约" ;
form . querySelector ( '[name="weight"]' ) . value = pf . weight || "20%" ;
let budgetData = [ ] ;
try { budgetData = JSON . parse ( pf . budget _data || "[]" ) ; } catch ( e ) { budgetData = [ ] ; }
planInitRevpayTable ( budgetData . length ? budgetData : null ) ;
@@ -1018,7 +1020,7 @@ function renderPlanOverview() {
if ( window . lucide ) window . lucide . createIcons ( ) ;
// 渲染图表(复用 home.js 的 moneyTick / chartOptions / monthLabels)
if ( financeMonthly && window . Chart ) {
renderOverview Charts ( financeMonthly ) ;
renderPlan Charts ( financeMonthly ) ;
}
}
@@ -1028,24 +1030,42 @@ function renderPlanCharts(data) {
const baseOpts = typeof chartOptions === 'function' ? chartOptions ( moneyTick ) : { responsive : true , maintainAspectRatio : false } ;
var iconf = { type : "line" , options : baseOpts , data : { labels : labels } } ;
var c1 = document . querySelector ( "#fi nChartSign" ) ;
var c1 = document . querySelector ( "#pla nChartSign" ) ;
if ( c1 && window . Chart ) {
if ( state . planChart1 ) state . planChart1 . destroy ( ) ;
state . planChart1 = new Chart ( c1 , Object . assign ( { } , iconf , { data : { labels : labels , datasets : [ { label : "签约金额" , data : data . map ( function ( x ) { return x . sign || 0 ; } ) , borderColor : "#6366f1" , backgroundColor : "rgba(99,102,241,0.06)" , fill : true , tension : 0.3 } ] } } ) ) ;
}
var c2 = document . querySelector ( "#fi nChartRev" ) ;
var c2 = document . querySelector ( "#pla nChartRev" ) ;
if ( c2 && window . Chart ) {
if ( state . planChart2 ) state . planChart2 . destroy ( ) ;
state . planChart2 = new Chart ( c2 , Object . assign ( { } , iconf , { data : { labels : labels , datasets : [ { label : "确收" , data : data . map ( function ( x ) { return x . revenue || 0 ; } ) , borderColor : "#2563eb" , backgroundColor : "rgba(37,99,235,0.06)" , fill : true , tension : 0.3 } , { label : "毛利" , data : data . map ( function ( x ) { return x . gross || 0 ; } ) , borderColor : "#059669" , backgroundColor : "rgba(5,150,105,0.06)" , fill : true , tension : 0.3 } ] } } ) ) ;
}
var c3 = document . querySelector ( "#fi nChartCash" ) ;
var c3 = document . querySelector ( "#pla nChartCash" ) ;
if ( c3 && window . Chart ) {
if ( state . planChart3 ) state . planChart3 . destroy ( ) ;
state . planChart3 = new Chart ( c3 , Object . assign ( { } , iconf , { data : { labels : labels , datasets : [ { label : "回款" , data : data . map ( function ( x ) { return x . payment || 0 ; } ) , borderColor : "#d97706" , backgroundColor : "rgba(217,119,6,0.06)" , fill : true , tension : 0.3 } , { label : "已付" , data : data . map ( function ( x ) { return x . cost || 0 ; } ) , borderColor : "#7c3aed" , backgroundColor : "rgba(124,58,237,0.06)" , fill : true , tension : 0.3 } ] } } ) ) ;
}
var c4 = document . querySelector ( "#fi nChartProfit" ) ;
var c4 = document . querySelector ( "#pla nChartProfit" ) ;
if ( c4 && window . Chart ) {
if ( state . planChart4 ) state . planChart4 . destroy ( ) ;
state . planChart4 = new Chart ( c4 , Object . assign ( { } , iconf , { data : { labels : labels , datasets : [ { label : "利润" , data : data . map ( function ( x ) { return x . gross || 0 ; } ) , borderColor : "#6366f1" , backgroundColor : "rgba(99,102,241,0.06)" , fill : true , tension : 0.3 } ] } } ) ) ;
}
} ;
function planInlineSelect ( name , pfId , field , currentVal , options ) {
var opts = '' ;
for ( var i = 0 ; i < options . length ; i ++ ) {
opts += '<option value="' + options [ i ] + '"' + ( options [ i ] === currentVal ? ' selected' : '' ) + '>' + options [ i ] + '</option>' ;
}
return '<select onchange="planInlineUpdate(' + pfId + ', \'' + field + '\'' + ', this.value)" class="text-sm bg-transparent border-0 cursor-pointer text-center" style="appearance:auto;-webkit-appearance:auto;vertical-align:middle">' + opts + '</select>' ;
}
window . planInlineUpdate = async ( pfId , field , value ) => {
try {
await api ( "/api/planFinances/" + pfId , { method : "PUT" , body : JSON . stringify ( { data : { [ field ] : value } } ) } ) ;
await load ( ) ;
toast ( "已更新" , "success" ) ;
} catch ( error ) {
toast ( "更新失败:" + error . message , "error" ) ;
}
} ;