效果图:

数据库表:记账表
CREATE TABLE `fa_pigs_journal_account` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',
`admin_id` int(10) NOT NULL DEFAULT '0' COMMENT '管理员ID',
`typedata` enum('pay','income') NOT NULL DEFAULT 'pay' COMMENT '收入/支出(单选):pay=支出,income=收入',
`type_id` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '费用类别ID(单选)',
`donetime` int(10) DEFAULT NULL COMMENT '收入/支出时间',
`money` float(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '金额',
`description` varchar(255) DEFAULT '' COMMENT '描述',
`createtime` int(10) DEFAULT NULL COMMENT '创建时间',
`updatetime` int(10) DEFAULT NULL COMMENT '更新时间',
`deletetime` int(10) DEFAULT NULL COMMENT '删除时间',
`weigh` int(10) NOT NULL DEFAULT '0' COMMENT '权重', PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE utf8mb4_unicode_ci COMMENT='记账表';HTML源码
文件application/admin/view/journalmanager/income_trend/index.html
<div class="row" style="margin-top:15px;">
<div class="col-xs-12">
<div class="panel panel-default panel-intro panel-statistics">
<div class="panel-body">
<h4>收支趋势图</h4><br>
<div id="datefilter">
<form id="form1" action="" role="form" novalidate class="form-inline">
<a href="javascript:;" class="btn btn-primary btn-refresh"><i class="fa fa-refresh"></i></a>
<a href="javascript:;" class="btn btn-success btn-filter">{:__('Last 30 Days')}</a>
<a href="javascript:;" class="btn btn-success btn-filter">{:__('Last month')}</a>
<a href="javascript:;" class="btn btn-success btn-filter">{:__('This month')}</a>
<a href="javascript:;" class="btn btn-success btn-filter">{:__('Last year')}</a>
<a href="javascript:;" class="btn btn-success btn-filter">{:__('This year')}</a>
<div class="input-group">
<span class="input-group-addon"><i class="fa fa-calendar"></i></span>
<input type="text" class="form-control input-inline datetimerange" data-type="order" placeholder="指定日期" style="width:270px;"/>
</div>
<a href="javascript:;" class="btn btn-default" style="font-size:14px;color:dodgerblue;">
<i class="fa fa-rmb"></i>
<span class="extend">
总收入:<span id="all_income">{$extend.allIncome}</span>
总支出:<span id="all_pay">{$extend.allPay}</span>
结余:<span id="all_balance">{$extend.allBalance}</span>
</span>
</a>
</form>
</div>
<div id="echarts1" style="height:400px;width:100%;margin-top:15px;"></div>
</div>
</div>
</div></div>控制器源码
文件application/admin/controller/journalmanager/IncomeTrend.php
<?phpnamespace app\admin\controller\journalmanager;use app\admin\model\User;use app\common\controller\Backend;use think\Db;/**
* 收支趋势图
*
* @icon fa fa-bar-chart
*/class IncomeTrend extends Backend{ /**
*
*/
protected $model = null; protected $noNeedRight = []; protected $isSuperAdmin = false; /**
* 查看
*/
public function index() { if ($this->request->isPost()) { $date = $this->request->post('date', ''); list($category, $incomeData, $payData, $balanceData, $extend) = $this->getIncomeStatisticsData($date); $statistics = ['category' => $category, 'incomeData' => $incomeData, 'payData' => $payData, 'balanceData' => $balanceData, 'extend' => $extend];
$this->success('', '', $statistics);
} list($category, $incomeData, $payData, $balanceData, $extend) = $this->getIncomeStatisticsData(); $this->assignconfig('category', $category); $this->assignconfig('incomeData', $incomeData); $this->assignconfig('payData', $payData); $this->assignconfig('balanceData', $balanceData); $this->view->assign('extend', $extend); return $this->view->fetch();
} /**
* 获取收支统计数据
* @param string $date
* @return array
*/
protected function getIncomeStatisticsData($date = '') { if ($date) { list($start, $end) = explode(' - ', $date); $starttime = strtotime($start); $endtime = strtotime($end);
} else { // 默认是当月
$starttime = \fast\Date::unixtime('month', 0, 'begin'); $endtime = \fast\Date::unixtime('month', 0, 'end');
} $totalseconds = $endtime - $starttime; $format = '%Y-%m-%d'; if ($totalseconds > 86400 * 30 * 2) { // 大于两个月,则横坐标为以月为粒度 形式'Y-m'
$format = '%Y-%m';
} else { if ($totalseconds > 86400) { // 小于两个月 且大于一天,则横坐标以天为粒度 形式'Y-m-d'
$format = '%Y-%m-%d';
} else { // 小于一天,则横坐标为以小时为粒度 形式'H:00'
$format = '%H:00';
}
}
// 收入
$orderList = \app\admin\model\journalmanager\JournalAccount::where('donetime', 'between time', [$starttime, $endtime])->where('typedata','income')
->field('donetime, typedata, SUM(money) AS amount, DATE_FORMAT(FROM_UNIXTIME(donetime), "' . $format . '") AS pay_date')
->group('pay_date')
->select(); // 支出
$payOrderList = \app\admin\model\journalmanager\JournalAccount::where('donetime', 'between time', [$starttime, $endtime])->where('typedata','pay')
->field('donetime, typedata, SUM(money) AS amount, DATE_FORMAT(FROM_UNIXTIME(donetime), "' . $format . '") AS pay_date')
->group('pay_date')
->select(); if ($totalseconds > 84600 * 30 * 2) { // 大于两个月,则横坐标为以月为粒度 形式'Y-m'
$starttime = strtotime('last month', $starttime); while (($starttime = strtotime('next month', $starttime)) <= $endtime) { $column[] = date('Y-m', $starttime);
}
} else { if ($totalseconds > 86400) { // 小于两个月 且大于一天,则横坐标以天为粒度 形式'Y-m-d'
for ($time = $starttime; $time <= $endtime;) { $column[] = date("Y-m-d", $time); $time += 86400;
}
} else { // 小于一天,则横坐标为以小时为粒度 形式'H:00'
for ($time = $starttime; $time <= $endtime;) { $column[] = date("H:00", $time); $time += 3600;
}
}
} // 收入
$list = array_fill_keys($column, 0); $allIncome = 0; foreach ($orderList as $k => $v) { $list[$v['pay_date']] = round($v['amount'], 2); $allIncome += round($v['amount'], 2);
} $payList = array_fill_keys($column, 0); // 支出
$allPay = 0; foreach ($payOrderList as $k => $v) { $payList[$v['pay_date']] = round($v['amount'], 2); $allPay += round($v['amount'], 2);
} $balanceList = array_fill_keys($column, 0); // 结余
$allBalance = $allIncome - $allPay; foreach ($balanceList as $k => $v) { $balanceList[$k] = round($list[$k] - $payList[$k], 2);
} $category = array_keys($list); $incomeData = array_values($list); $payData = array_values($payList); $balanceData = array_values($balanceList); $extend = array('allIncome'=>$allIncome,'allPay'=>$allPay,'allBalance'=>$allBalance); return [$category, $incomeData, $payData, $balanceData, $extend];
}
}js源码
文件public/assets/js/backend/journalmanager/income_trend.js
define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'echarts', 'echarts-theme'], function ($, undefined, Backend, Table, Form, Echarts) { var Controller = { index: function () { // 基于准备好的dom,初始化echarts实例
var myChart1 = Echarts.init($('#echarts1')[0], 'walden'); // 指定图表的配置项和数据
var option1 = { title: { text: '', subtext: ''
}, tooltip: { trigger: 'axis'
}, legend: { data: ['收入', '支出', '结余']
}, toolbox: { show: true, feature: { dataView: {show: true, readOnly: false}, magicType: {show: true, type: ['line', 'bar']}, restore: {show: true}, saveAsImage: {show: true}
}
}, calculable: true, xAxis: { type: 'category', boundaryGap: false, data: Config.category
}, yAxis: {}, grid: [{ left: 'left', top: 'top', right: '10', bottom: 30
}], series: [
{ name: "收入", type: 'line', smooth: true, areaStyle: { normal: {}
}, lineStyle: { normal: { width: 1.5
}
}, data: Config.incomeData
},
{ name: "支出", type: 'line', smooth: true, areaStyle: { normal: {}
}, lineStyle: { normal: { width: 1.5
}
}, data: Config.payData
},
{ name: "结余", type: 'line', smooth: true, areaStyle: { normal: {}
}, lineStyle: { normal: { width: 1.5
}
}, data: Config.balanceData
}
]
}; // 使用刚指定的配置项和数据显示图表。
myChart1.setOption(option1);
$(window).resize(function () {
myChart1.resize();
});
$(".datetimerange").data("callback", function (start, end) { var date = start.format(this.locale.format) + " - " + end.format(this.locale.format);
$(this.element).val(date); refresh_echart($(this.element).data("type"), date);
}); Form.api.bindevent($("#form1")); var si = {}; var refresh_echart = function (type, date) {
si[type] && clearTimeout(si[type]);
si[type] = setTimeout(function () { Fast.api.ajax({ url: 'journalmanager/income_trend/index', data: {date: date, type: type}, loading: false
}, function (data) { if (type == 'order') {
option1.xAxis.data = data.category;
option1.series[0].data = data.data;
option1.series[1].data = data.payData;
option1.series[2].data = data.balanceData;
myChart1.clear();
myChart1.setOption(option1, true);
$("#all_income").text(data.extend.allIncome);
$("#all_pay").text(data.extend.allPay);
$("#all_balance").text(data.extend.allBalance);
} return false;
});
}, 50);
}; //点击按钮
$(document).on("click", ".btn-filter", function () { var label = $(this).text(); var obj = $(this).closest("form").find(".datetimerange").data("daterangepicker"); var dates = obj.ranges[label];
obj.startDate = dates[0];
obj.endDate = dates[1];
obj.clickApply();
}); //点击刷新
$(document).on("click", ".btn-refresh", function () { if ($(this).data("type")) { refresh_echart($(this).data("type"), "");
} else { var input = $(this).closest("form").find(".datetimerange"); var type = $(input).data("type"); var date = $(input).data("date"); refresh_echart(type, date);
}
}); //每隔一分钟定时刷新图表
setInterval(function () {
$(".btn-refresh").trigger("click");
}, 60000);
}, add: function () { Controller.api.bindevent();
}, edit: function () { Controller.api.bindevent();
}, api: { bindevent: function () { Form.api.bindevent($("form[role=form]"));
}
}
}; return Controller;
});多语言设置
文件application/admin/lang/zh-cn.php
'Last 30 days' => '最近30天', 'Last month' => '上月', 'This month' => '本月', 'Last year' => '去年', 'This year' => '今年',
设置了日期控件的参数
在public/assets/js/require-form.js文件的daterangepicker函数里追加了两行代码
ranges[__('Last Year')] = [Moment().subtract(1, 'year').startOf('year'), Moment().subtract(1, 'year').endOf('year')];
ranges[__('This Year')] = [Moment().startOf('year'), Moment().endOf('year')];最后一步,添加上菜单规则
规则:journalmanager/income_trend
访问URL:http://yoursite/YFmcqx.php/journalmanager/income_trend?ref=addtabs
