退单统计

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 $relationSearch = true,$items,$itemsformattedTree;
protected $relationSearch = true,$items,$itemsformattedTree,$sources;
public function _initialize()
{
@ -42,6 +42,29 @@ class Aftersale extends Backend
$this->itemsformattedTree = $formattedTree;
$this->view->assign("items", $formattedTree);
$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();
}
@ -128,6 +151,7 @@ class Aftersale extends Backend
$start = now()->modify('-30 days')->format('Y-m-d');
$end_at = now()->format('Y-m-d 23:29:59');
$area_code = request()->get('area_id');
$source = request()->get('source');
$filter = request()->get('range', '');
if (!empty($filter)) {
$arr = explode(' - ', $filter);
@ -140,15 +164,19 @@ class Aftersale extends Backend
}
$build->where('audit_time', '>=', $start);
$build->where('audit_time', '<=', $end_at);
$build->group('a.area_id');
$build->group('a.source,a.area_id');
if ($area_code) {
$build->where('area_id', 'like', $this->getSelectAreaCode($area_code) . '%');
}
if ($source) {
$build->where('source', $source);
}
$build->field(
[
'a.source',
'a.area_id',
'count(a.id) order_total',
'count(c.id) after_total',
@ -157,7 +185,10 @@ class Aftersale extends Backend
)->with([
'area' => function ($q) {
$q->field('id,area_code,merge_name');
}
},
'source' => function ($q) {
$q->field('id,title');
},
]);
$res = $build->paginate();
@ -189,6 +220,7 @@ class Aftersale extends Backend
$start = now()->modify('-30 days')->format('Y-m-d');
$end_at = now()->format('Y-m-d 23:29:59');
$item_id = request()->get('item_id');
$source = request()->get('source');
$filter = request()->get('range', '');
if (!empty($filter)) {
$arr = explode(' - ', $filter);
@ -201,7 +233,7 @@ class Aftersale extends Backend
}
$build->where('audit_time', '>=', $start);
$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;
$build->whereIn('item_id', $item_ids);
}
if ($source) {
$build->where('source', $source);
}
$build->field(
[
'a.source',
'a.item_id',
'a.item_title',
'count(a.id) order_total',
'count(c.id) after_total',
'sum(c.refund_amount) refund_amount',
]
);
)->with([
'source' => function ($q) {
$q->field('id,title');
},
]);
$res = $build->paginate();
$total = $res->total();

View File

@ -199,6 +199,9 @@ class Order extends Model
public function item(){
return $this->belongsTo(Item::class,'item_id',);
}
public function source(){
return $this->belongsTo(Source::class,'source');
}
public function area(){
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 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="area_id" style="display: none" class="form-control" name="area_id" hidden type="text" />
</div>
<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;">
<button class="btn btn-default ml-2" id="first-search2" style="position: absolute;left: 500px;">查询</button>
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: 650px;">查询</button>
<button class="btn btn-default ml-2" id="reset2" style="position: absolute;left: 705px;">重置</button>
</div>
<!-- 查询按钮 -->
@ -216,6 +225,13 @@
<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">
<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">
<input type="text" id="item_id" class="zd-input__inner mr-2" style="width: 275px">
</div>

View File

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

View File

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