退单统计

This commit is contained in:
hant 2025-06-22 18:41:46 +08:00
parent 2b209eef1e
commit 02dbc1233a
5 changed files with 194 additions and 90 deletions

View File

@ -19,7 +19,7 @@ class Aftersale extends Backend
{ {
protected $noNeedRight = ['*']; protected $noNeedRight = ['*'];
protected $relationSearch = true,$items,$itemsformattedTree; protected $relationSearch = true,$items,$itemsformattedTree,$sources;
public function _initialize() public function _initialize()
{ {
@ -42,6 +42,29 @@ class Aftersale extends Backend
$this->itemsformattedTree = $formattedTree; $this->itemsformattedTree = $formattedTree;
$this->view->assign("items", $formattedTree); $this->view->assign("items", $formattedTree);
$this->view->assign("default_daterange", $default_daterange); $this->view->assign("default_daterange", $default_daterange);
$sources = Db::name('source')
->where('status', 1)
->field(['id', 'title', 'key_word', 'pid'])
->order('pid', 'asc')
->order('sort', 'desc')
->select();
$this->sources = $sources;
$filtered = array_filter($sources, function ($item) {
return $item['pid'] == 0;
});
$pid_map = array_column($filtered, null, 'id');
$res = [];
foreach ($sources as $item) {
if ($item['pid'] != 0 && isset($pid_map[$item['pid']])) {
$res [] = [
...$item, 'ptitle' => $pid_map[$item['pid']]['title'],'name'=>$item['title'].' - '.$pid_map[$item['pid']]['title'],
];
}
}
$this->view->assign("sources", $res);
parent::_initialize(); parent::_initialize();
} }
@ -128,6 +151,7 @@ class Aftersale extends Backend
$start = now()->modify('-30 days')->format('Y-m-d'); $start = now()->modify('-30 days')->format('Y-m-d');
$end_at = now()->format('Y-m-d 23:29:59'); $end_at = now()->format('Y-m-d 23:29:59');
$area_code = request()->get('area_id'); $area_code = request()->get('area_id');
$source = request()->get('source');
$filter = request()->get('range', ''); $filter = request()->get('range', '');
if (!empty($filter)) { if (!empty($filter)) {
$arr = explode(' - ', $filter); $arr = explode(' - ', $filter);
@ -140,15 +164,19 @@ class Aftersale extends Backend
} }
$build->where('audit_time', '>=', $start); $build->where('audit_time', '>=', $start);
$build->where('audit_time', '<=', $end_at); $build->where('audit_time', '<=', $end_at);
$build->group('a.area_id'); $build->group('a.source,a.area_id');
if ($area_code) { if ($area_code) {
$build->where('area_id', 'like', $this->getSelectAreaCode($area_code) . '%'); $build->where('area_id', 'like', $this->getSelectAreaCode($area_code) . '%');
} }
if ($source) {
$build->where('source', $source);
}
$build->field( $build->field(
[ [
'a.source',
'a.area_id', 'a.area_id',
'count(a.id) order_total', 'count(a.id) order_total',
'count(c.id) after_total', 'count(c.id) after_total',
@ -157,7 +185,10 @@ class Aftersale extends Backend
)->with([ )->with([
'area' => function ($q) { 'area' => function ($q) {
$q->field('id,area_code,merge_name'); $q->field('id,area_code,merge_name');
} },
'source' => function ($q) {
$q->field('id,title');
},
]); ]);
$res = $build->paginate(); $res = $build->paginate();
@ -189,6 +220,7 @@ class Aftersale extends Backend
$start = now()->modify('-30 days')->format('Y-m-d'); $start = now()->modify('-30 days')->format('Y-m-d');
$end_at = now()->format('Y-m-d 23:29:59'); $end_at = now()->format('Y-m-d 23:29:59');
$item_id = request()->get('item_id'); $item_id = request()->get('item_id');
$source = request()->get('source');
$filter = request()->get('range', ''); $filter = request()->get('range', '');
if (!empty($filter)) { if (!empty($filter)) {
$arr = explode(' - ', $filter); $arr = explode(' - ', $filter);
@ -201,7 +233,7 @@ class Aftersale extends Backend
} }
$build->where('audit_time', '>=', $start); $build->where('audit_time', '>=', $start);
$build->where('audit_time', '<=', $end_at); $build->where('audit_time', '<=', $end_at);
$build->group('a.item_id,a.item_title'); $build->group('a.source,a.item_id,a.item_title');
@ -210,16 +242,24 @@ class Aftersale extends Backend
$item_ids [] = $item_id; $item_ids [] = $item_id;
$build->whereIn('item_id', $item_ids); $build->whereIn('item_id', $item_ids);
} }
if ($source) {
$build->where('source', $source);
}
$build->field( $build->field(
[ [
'a.source',
'a.item_id', 'a.item_id',
'a.item_title', 'a.item_title',
'count(a.id) order_total', 'count(a.id) order_total',
'count(c.id) after_total', 'count(c.id) after_total',
'sum(c.refund_amount) refund_amount', 'sum(c.refund_amount) refund_amount',
] ]
); )->with([
'source' => function ($q) {
$q->field('id,title');
},
]);
$res = $build->paginate(); $res = $build->paginate();
$total = $res->total(); $total = $res->total();

View File

@ -199,6 +199,9 @@ class Order extends Model
public function item(){ public function item(){
return $this->belongsTo(Item::class,'item_id',); return $this->belongsTo(Item::class,'item_id',);
} }
public function source(){
return $this->belongsTo(Source::class,'source');
}
public function area(){ public function area(){
return $this->belongsTo(Area::class,'area_id','area_code'); return $this->belongsTo(Area::class,'area_id','area_code');

View File

@ -195,14 +195,23 @@
<div id="chart-filter-table2" style="margin-top:20px;margin-bottom: 30px;"> <div id="chart-filter-table2" style="margin-top:20px;margin-bottom: 30px;">
<div style="display: flex;position: relative" class="col-sm-6 row"> <div style="display: flex;position: relative" class="col-sm-6 row">
<div style="display: inline-block;width: 300px;position: absolute"> <select id="table2-source" title="请选择订单渠道" name="row[source]" class="form-control"
style="display: inline-block;width: 150px;position: absolute">
<option value="">全部</option>
{foreach $sources as $item}
<option value="{$item['id']}">{$item['name']}</option>
{/foreach}
</select>
<div style="display: inline-block;width: 300px;position: absolute;left: 170px">
<input id="city-search" data-rule="required" class="form-control" data-toggle="city-picker" type="text" /> <input id="city-search" data-rule="required" class="form-control" data-toggle="city-picker" type="text" />
<input id="area_id" style="display: none" class="form-control" name="area_id" hidden type="text" /> <input id="area_id" style="display: none" class="form-control" name="area_id" hidden type="text" />
</div> </div>
<input type="text" class="form-control datetimerange" value="{$default_daterange}" data-locale='{"format":"YYYY-MM-DD"}' <input type="text" class="form-control datetimerange" value="{$default_daterange}" data-locale='{"format":"YYYY-MM-DD"}'
placeholder="指定日期" name="filter[daterange]" id="daterange-table2" autocomplete="off" style="position: absolute;left: 320px;width: 180px;"> placeholder="指定日期" name="filter[daterange]" id="daterange-table2" autocomplete="off" style="position: absolute;left: 475px;width: 180px;">
<button class="btn btn-default ml-2" id="first-search2" style="position: absolute;left: 500px;">查询</button> <button class="btn btn-default ml-2" id="first-search2" style="position: absolute;left: 650px;">查询</button>
<button class="btn btn-default ml-2" id="reset2" style="position: absolute;left: 705px;">重置</button>
</div> </div>
<!-- 查询按钮 --> <!-- 查询按钮 -->
@ -216,6 +225,13 @@
<div id="chart-filter-table3" style="margin-top:20px;margin-bottom: 30px;"> <div id="chart-filter-table3" style="margin-top:20px;margin-bottom: 30px;">
<div style="display: flex;position: relative" class="col-lg-6 col-sm-8 row"> <div style="display: flex;position: relative" class="col-lg-6 col-sm-8 row">
<select id="table3-source" title="请选择订单渠道" name="row[source]" class="form-control"
style="display: inline-block;width: 150px; margin-right: 10px">
<option value="">全部</option>
{foreach $sources as $item}
<option value="{$item['id']}">{$item['name']}</option>
{/foreach}
</select>
<div style="max-width: 275px"> <div style="max-width: 275px">
<input type="text" id="item_id" class="zd-input__inner mr-2" style="width: 275px"> <input type="text" id="item_id" class="zd-input__inner mr-2" style="width: 275px">
</div> </div>

View File

@ -28,21 +28,39 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'cascader'], function
// 拼装文本 // 拼装文本
function assembleOrderMessage(data) { function assembleOrderMessage(data) {
const message = `订单编号: ${data.order_no} let message = '';
if (!data.coupon) {
message = `订单编号: ${data.order_no}
服务名称: ${data.item_title} 服务名称: ${data.item_title}
客户昵称: ${data.customer} 客户昵称: ${data.customer}
客户电话: ${data.tel} 客户电话: ${data.tel}
客户地址: ${data.address} 客户地址: ${data.address}
收款金额: ${data.receive_type === 1 ? '已收定金 ' + data.online_amount : '已收全款 ' + data.online_amount} 下单金额: ${data.receive_type === 1 ? '已收定金 ' + formatNumber(data.online_amount) : '已收全款 ' + formatNumber(data.online_amount)}
优惠活动: ${data.coupon?.description || '无'} 订单备注: ${data.remark || '无'}
订单详情: ${data.detail || '无'}
订单备注: ${data.remark}
预约时间: ${data.plan_time || '无'} 预约时间: ${data.plan_time || '无'}
`; `;
} else {
message = `订单编号: ${data.order_no}
服务名称: ${data.item_title}
客户昵称: ${data.customer}
客户电话: ${data.tel}
客户地址: ${data.address}
下单金额: ${data.receive_type === 1 ? '已收定金 ' + formatNumber(data.online_amount) : '已收全款 ' + formatNumber(data.online_amount)}
优惠活动: ${data.coupon?.description || '无'}
订单备注: ${data.remark ||'无'}
预约时间: ${data.plan_time || '无'}
`;
}
return message; return message;
} }
function formatNumber(value, decimals = 1) {
let num = parseFloat(value);
if (isNaN(num)) return '0.0'.slice(0, decimals + 2); // e.g. '0.0' or '0.00'
return num.toFixed(decimals);
}
var Controller = { var Controller = {
index: function () { index: function () {
@ -83,7 +101,7 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'cascader'], function
fixed: true, fixed: true,
searchList: { searchList: {
"0": __('Status 0'), "0": __('Status 0'),
"10": __('Status 10'), "10": __('Status 10'),
"20": __('Status 20'), "20": __('Status 20'),
"30": __('Status 30'), "30": __('Status 30'),
"40": __('Status 40'), "40": __('Status 40'),
@ -115,7 +133,8 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'cascader'], function
var now = new Date(); var now = new Date();
var diffMinutes = (now - createTime) / (1000 * 60); // 计算分钟差 var diffMinutes = (now - createTime) / (1000 * 60); // 计算分钟差
return diffMinutes > 20 ? '<span style="color: red;">超时</span>' : '<span class="label label-success">未超时</span>';; return diffMinutes > 20 ? '<span style="color: red;">超时</span>' : '<span class="label label-success">未超时</span>';
;
}, },
searchList: { searchList: {
"1": '超时', "1": '超时',
@ -211,13 +230,15 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'cascader'], function
autocomplete: false autocomplete: false
}, },
{field: 'area_id', title: __('地区'), searchList: function (column) { {
field: 'area_id', title: __('地区'), searchList: function (column) {
return Template('categorytpl', {}); return Template('categorytpl', {});
}, formatter: function (value, row, index) { }, formatter: function (value, row, index) {
return '无'; return '无';
}, visible: false }, visible: false
}, },
{field: 'item_id', title: '服务项目', searchList: function (column) { {
field: 'item_id', title: '服务项目', searchList: function (column) {
return Template('items_picker', {}); return Template('items_picker', {});
}, formatter: function (value, row, index) { }, formatter: function (value, row, index) {
return '无'; return '无';
@ -470,7 +491,6 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'cascader'], function
resetIdleTimer(); resetIdleTimer();
var _data = items; var _data = items;
$('#select_item').zdCascader({ $('#select_item').zdCascader({
data: _data, data: _data,
@ -478,7 +498,7 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'cascader'], function
// console.log(data,allPathData); // console.log(data,allPathData);
$('#select_item_id').val(data.value); $('#select_item_id').val(data.value);
}, },
clear:true, clear: true,
clickParent: true clickParent: true
}); });
@ -486,14 +506,14 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'cascader'], function
add: function () { add: function () {
$("#mybuttom").on("click", function () { $("#mybuttom").on("click", function () {
const res = $("form[role=form]").isValid(); const res = $("form[role=form]").isValid();
if ($('#lng').val() && $('#lng').val() !== ''){ if ($('#lng').val() && $('#lng').val() !== '') {
if (res) { if (res) {
Form.api.submit($("form[role=form]"),function (data){ Form.api.submit($("form[role=form]"), function (data) {
copyToClipboard(assembleOrderMessage(data)); copyToClipboard(assembleOrderMessage(data));
}); });
} }
}else{ } else {
Toastr.success('请选择有效地址'); Toastr.success('请选择有效地址');
} }
@ -503,7 +523,7 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'cascader'], function
$("#mysubmit").on("click", function () { $("#mysubmit").on("click", function () {
const res = $("form[role=form]").isValid(); const res = $("form[role=form]").isValid();
if ($('#lng').val() && $('#lng').val() !== ''){ if ($('#lng').val() && $('#lng').val() !== '') {
if (res) { if (res) {
Form.api.submit($("form[role=form]"), function (data, ret) { Form.api.submit($("form[role=form]"), function (data, ret) {
clearInfo(); clearInfo();
@ -511,7 +531,7 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'cascader'], function
return false; return false;
}); });
} }
}else{ } else {
Toastr.success('请选择有效地址'); Toastr.success('请选择有效地址');
} }
return false; return false;
@ -519,54 +539,54 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'cascader'], function
$("#smart").on("click", function () { $("#smart").on("click", function () {
Fast.api.ajax({ Fast.api.ajax({
url: "order/smart", // 你的 API 地址 url: "order/smart", // 你的 API 地址
type: "post", type: "post",
contentType: 'application/json', contentType: 'application/json',
dataType: "json", dataType: "json",
data: JSON.stringify({ data: JSON.stringify({
str: $('#smart_text').val() str: $('#smart_text').val()
}) })
}, function (data) { }, function (data) {
if (data.mobile !== '') { if (data.mobile !== '') {
$('#c-tel').val(data.mobile); $('#c-tel').val(data.mobile);
} }
if (data.name !== '') { if (data.name !== '') {
$('#c-customer').val(data.name); $('#c-customer').val(data.name);
} }
if (data.item.id !== 0) { if (data.item.id !== 0) {
$('#item_id_value').val(data.item.id); $('#item_id_value').val(data.item.id);
$('#item_id').val(data.item.item); $('#item_id').val(data.item.item);
} }
if (data.idn) { if (data.idn) {
$('#c-source-id').val(data.idn); $('#c-source-id').val(data.idn);
} }
// $city.citypicker({ // $city.citypicker({
// province: data.province, // province: data.province,
// city: data.city, // city: data.city,
// district: data.region // district: data.region
// }); // });
if (data.area_id) { if (data.area_id) {
$("#area_id").val(data.area_id); $("#area_id").val(data.area_id);
} }
if (data.lat) { if (data.lat) {
$("#lat").val(data.lat); $("#lat").val(data.lat);
} }
if (data.lng) { if (data.lng) {
$("#lng").val(data.lng); $("#lng").val(data.lng);
} }
if (data.area_id) { if (data.area_id) {
$("#area_id").val(data.area_id); $("#area_id").val(data.area_id);
} }
if (data.plan_time) { if (data.plan_time) {
$("#time").val(data.plan_time); $("#time").val(data.plan_time);
} }
if (data.addr && data.addr !== '') { if (data.addr && data.addr !== '') {
$("#c-address").val(data.addr); $("#c-address").val(data.addr);
$("#area_name").val(data.addr); $("#area_name").val(data.addr);
} }
Toastr.info('识别成功'); Toastr.info('识别成功');
return false; return false;
}); });
}); });
Controller.api.bindevent(); Controller.api.bindevent();
Controller.api.map(); Controller.api.map();
@ -739,26 +759,27 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'cascader'], function
$('#suggestionList').show(); $('#suggestionList').show();
}) })
.on('keydown', function (e) { .on('keydown', function (e) {
const $items = $('#suggestionList li'); const $items = $('#suggestionList li');
const len = $items.length; const len = $items.length;
if (!len) return; if (!len) return;
if (e.key === 'ArrowDown') { if (e.key === 'ArrowDown') {
e.preventDefault();
selectedIndex = (selectedIndex + 1) % len;
updateActiveItem($items);
} else if (e.key === 'ArrowUp') {
e.preventDefault();
selectedIndex = (selectedIndex - 1 + len) % len;
updateActiveItem($items);
} else if (e.key === 'Enter') {
if (selectedIndex >= 0 && selectedIndex < len) {
e.preventDefault(); e.preventDefault();
$items.eq(selectedIndex).trigger('mousedown'); selectedIndex = (selectedIndex + 1) % len;
updateActiveItem($items);
} else if (e.key === 'ArrowUp') {
e.preventDefault();
selectedIndex = (selectedIndex - 1 + len) % len;
updateActiveItem($items);
} else if (e.key === 'Enter') {
if (selectedIndex >= 0 && selectedIndex < len) {
e.preventDefault();
$items.eq(selectedIndex).trigger('mousedown');
}
} }
} });
});
function updateActiveItem($items) { function updateActiveItem($items) {
$items.removeClass('active'); $items.removeClass('active');
if (selectedIndex >= 0) { if (selectedIndex >= 0) {
@ -836,7 +857,7 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'cascader'], function
addressSelected = true; addressSelected = true;
}); });
function choseFirst(){ function choseFirst() {
const first = $('#suggestionList li').first(); const first = $('#suggestionList li').first();
const name = first.data('name'); const name = first.data('name');
const location = first.data('location'); // "经度,纬度" const location = first.data('location'); // "经度,纬度"

View File

@ -48,12 +48,30 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'template', 'addtabs'
e.preventDefault(); e.preventDefault();
const range = $('#daterange-table2').val(); const range = $('#daterange-table2').val();
const area_id = $('#area_id').val(); const area_id = $('#area_id').val();
const source = $('#table2-source').val();
let data = ''; let data = '';
if (range !== ''){ if (range !== ''){
data += 'range=' + range; data += 'range=' + range;
} }
if (area_id !== ''){ if (area_id !== ''){
data += 'area_id=' + area_id; data += '&area_id=' + area_id;
}
if (source !== ''){
data += '&source=' + source;
}
// data = encodeURIComponent(data);
$("#table2").bootstrapTable('refresh',{
url:'statistics/aftersale/city?' + data,
});
});
$('#reset2').on('click', function (e) {
$("#city-search").citypicker('reset');
$("#area_id").val('');
$('#table2-source option:first').prop('selected', true);
const range = $('#daterange-table2').val();
let data = '';
if (range !== ''){
data += 'range=' + range;
} }
// data = encodeURIComponent(data); // data = encodeURIComponent(data);
$("#table2").bootstrapTable('refresh',{ $("#table2").bootstrapTable('refresh',{
@ -65,6 +83,7 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'template', 'addtabs'
const range = $('#daterange-table3').val(); const range = $('#daterange-table3').val();
const item_id = $('#item_id_value').val(); const item_id = $('#item_id_value').val();
const source = $('#table3-source').val();
let data = ''; let data = '';
if (range !== ''){ if (range !== ''){
data += 'range=' + range; data += 'range=' + range;
@ -72,6 +91,9 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'template', 'addtabs'
if (item_id !== ''){ if (item_id !== ''){
data += '&item_id=' + item_id; data += '&item_id=' + item_id;
} }
if (source !== ''){
data += '&source=' + source;
}
// data = encodeURIComponent(data); // data = encodeURIComponent(data);
$("#table3").bootstrapTable('refresh',{ $("#table3").bootstrapTable('refresh',{
url:'statistics/aftersale/item?' + data, url:'statistics/aftersale/item?' + data,
@ -156,6 +178,7 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'template', 'addtabs'
searchFormVisible: true, searchFormVisible: true,
columns: [ columns: [
[ [
{field: 'source.title', title: '渠道', operate: false},
{field: 'area.merge_name', title: '城市', operate: false}, {field: 'area.merge_name', title: '城市', operate: false},
{field: 'order_total', title: '订单总数', operate: false}, {field: 'order_total', title: '订单总数', operate: false},
{field: 'after_total', title: '退款订单数', operate: false}, {field: 'after_total', title: '退款订单数', operate: false},
@ -205,6 +228,7 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'template', 'addtabs'
searchFormVisible: true, searchFormVisible: true,
columns: [ columns: [
[ [
{field: 'source.title', title: '渠道', operate: false},
{field: 'item_title', title: '服务项目', operate: false}, {field: 'item_title', title: '服务项目', operate: false},
{field: 'order_total', title: '订单总数', operate: false}, {field: 'order_total', title: '订单总数', operate: false},
{field: 'refund_amount', title: '退款金额', operate: false}, {field: 'refund_amount', title: '退款金额', operate: false},