This commit is contained in:
yaosen 2024-06-25 14:40:30 +08:00
parent 38a6f53380
commit bc0e6ba142
5 changed files with 324 additions and 65 deletions

View File

@ -161,7 +161,16 @@ export const asyncRoutes = [
title: '流转订单',
roles: ['order_back', 'editor']
}
}
},
{
path: 'product',
component: () => import('@/views/order/product'),
name: 'productNameList',
meta: {
title: '产品统计列表',
roles: ['order_pub', 'editor']
}
},
]
},
{

View File

@ -52,6 +52,58 @@
</div>
</div>
</el-col>
<el-col :xs="12" :sm="12" :lg="6" class="card-panel-col">
<div class="card-panel">
<div class="card-panel-icon-wrapper icon-shopping">
<svg-icon icon-class="shopping" class-name="card-panel-icon" />
</div>
<div class="card-panel-description">
<div class="card-panel-text">
核销订单数
</div>
<count-to :start-val="0" :end-val="asset" :duration="1" class="card-panel-num" />
</div>
</div>
</el-col>
<el-col :xs="12" :sm="12" :lg="6" class="card-panel-col">
<div class="card-panel">
<div class="card-panel-icon-wrapper icon-shopping">
<svg-icon icon-class="shopping" class-name="card-panel-icon" />
</div>
<div class="card-panel-description">
<div class="card-panel-text">
核销订单金额
</div>
<count-to :start-val="0" :end-val="asset_price" :duration="10" class="card-panel-num" />
</div>
</div>
</el-col>
<el-col :xs="12" :sm="12" :lg="6" class="card-panel-col">
<div class="card-panel">
<div class="card-panel-icon-wrapper icon-shopping">
<svg-icon icon-class="shopping" class-name="card-panel-icon" />
</div>
<div class="card-panel-description">
<div class="card-panel-text">
退款订单数
</div>
<count-to :start-val="0" :end-val="refund" :duration="10" class="card-panel-num" />
</div>
</div>
</el-col>
<el-col :xs="12" :sm="12" :lg="6" class="card-panel-col">
<div class="card-panel">
<div class="card-panel-icon-wrapper icon-shopping">
<svg-icon icon-class="shopping" class-name="card-panel-icon" />
</div>
<div class="card-panel-description">
<div class="card-panel-text">
退款订单金额
</div>
<count-to :start-val="0" :end-val="refund_price" :duration="10" class="card-panel-num" />
</div>
</div>
</el-col>
</el-row>
</template>
@ -67,7 +119,11 @@ export default {
wait:0,
doing:0,
all:0,
total:0
total:0,
asset:0,
asset_price:0,
refund:0,
refund_price:0
}
},
mounted() {
@ -77,8 +133,12 @@ export default {
this.doing = parseFloat(res.data.doing)
this.all = parseFloat(res.data.all)
this.total = parseFloat(res.data.total)/100
this.asset = parseFloat(res.data.asset)
this.asset_price = parseFloat(res.data.asset_price)/100
this.refund = parseFloat(res.data.refund)
this.refund_price = parseFloat(res.data.refund_price)/100
}).catch(err=>{
console.log(err)
console.log(err)
})
})
},

View File

@ -0,0 +1,125 @@
<template>
<div class="app-container">
<div class="filter-container">
<el-input v-model="listQuery.product_name" placeholder="产品名称" style="width: 200px;" class="filter-item" />
<el-cascader
v-model="listQuery.os_status"
placeholder="平台状态"
:options="os_arr"
class="filter-item"
@change="handleChange"
/>
<el-button class="filter-item" type="primary" icon="el-icon-search" @click="getList">
搜索
</el-button>
</div>
<el-table v-loading="listLoading" :data="list" border fit highlight-current-row style="width: 100%">
<el-table-column align="center" fixed label="产品名称" width="220" prop="product_name" />
<el-table-column align="center" fixed label="平台" width="80" prop="os" />
<el-table-column align="center" fixed label="订单数" width="80" prop="all" />
<el-table-column align="center" label="订单金额" width="180" prop="total" />
<el-table-column align="center" width="500px" label="待跟进" prop="wait" />
<el-table-column width="138px" align="center" label="跟进中" prop="doing" />
<el-table-column align="center" width="500px" label="核销数" prop="asset" />
<el-table-column width="138px" align="center" label="核销金额" prop="asset_price" />
<el-table-column align="center" width="500px" label="退款数" prop="asset" />
<el-table-column width="138px" align="center" label="退款金额" prop="asset_price" />
</el-table>
<pagination
v-show="total>0"
:total="total"
:page.sync="listQuery.page"
:limit.sync="listQuery.limit"
@pagination="getList"
/>
</div>
</template>
<script>
// import Pagination from '@/components/Pagination'
import Pagination from '@/components/PaginationFixed'
export default {
name: 'productNameList',
components: { Pagination },
data() {
return {
active: 'follow',
types: { 0: '', 1: '', 2: '', 3: 'primary', 4: 'success', 5: 'warning' },
types2: { 1: 'primary', 2: 'success', 3: 'warning' },
status_arr: ['待跟进', '跟进中', '已核销', '核销失败', '放弃跟单'],
type_arr: ['-', '收益', '支出'],
timetype_arr: {},
order_status: ['#9e9f9c', '#04bcd9', '#fc9904', '#1193f4', '#48b14b', '#eb1662', '#9d1cb5'],
follow_status: ['#9e9f9c', '#04bcd9', '#fc9904', '#1193f4', '#48b14b', '#eb1662'],
options: [],
list: [],
total: 0,
listLoading: true,
listQuery: {
page: 1,
limit: 10,
product_name: ''
},
os_arr: { 1: '美团', 2: '快手', 3: '抖音' },
form: {},
rules:{
flowObj: [
{ required: true, message: '请选择活动区域', trigger: 'change' }
],
}
}
},
created() {
this.listQuery.status = this.$route.query.status || null
this.listQuery.zhubo = this.$route.query.zhubo || null
if (this.$route.query.start && this.$route.query.end) {
this.listQuery.times = [this.$route.query.start, this.$route.query.end]
}
this.getList()
},
methods: {
getList() {
this.$axios.get('/admin/index/productNameList', { params: this.listQuery }).then(response => {
this.list = response.data.data
this.total = response.data.total
this.timetype_arr = response.ext.timetype
this.oss = response.ext.oss
this.listLoading = false
}).catch(() => {
})
}
}
}
</script>
<style scoped>
.app-container {
position: relative;
padding-bottom: 60px; /* 分页条的高度 */
}
.filter-container,
.el-table {
padding-bottom: 52px; /* 分页条的高度,以避免内容重叠 */
}
</style>

View File

@ -1,4 +1,5 @@
<?php
namespace app\admin\controller;
use app\model\Admins;
@ -7,22 +8,24 @@ use app\model\Finances;
use app\model\Onlines;
use app\model\Orders;
use Qiniu\Auth;
use support\Log;
use support\Redis;
use support\Request;
class IndexController extends base
{
public function isWork(Request $request) {
public function isWork(Request $request)
{
$lastTime = Redis::get('CRM:USER:ONLINE:'.$request->admin->id);
$lastTime = Redis::get('CRM:USER:ONLINE:' . $request->admin->id);
$end = Redis::hGet('CRM:USER:ONLINE:END', $request->admin->id);
if(!empty($lastTime) && time() - $lastTime <= 5 * 60) {
} elseif(empty($lastTime) && $end) {
$has = Onlines::where("admin_id", $request->admin->id)->order('id desc')->find();
if($has && empty($has->end)) {
Onlines::where("id", $has->id)->update(['end' => $end]);
}else{
if (!empty($lastTime) && time() - $lastTime <= 5 * 60) {
} elseif (empty($lastTime) && $end) {
$has = Onlines::where("admin_id", $request->admin->id)->order('id desc')->find();
if ($has && empty($has->end)) {
Onlines::where("id", $has->id)->update(['end' => $end]);
} else {
$start = Redis::hGet('CRM:USER:ONLINE', $request->admin->id);
//创建一个新的
Onlines::create([
@ -31,77 +34,105 @@ class IndexController extends base
'end' => $end
]);
}
//创建一个新的
Onlines::create([
'admin_id' => $request->admin->id,
'start' => time(),
'end' => 0
]);
} else {
//第一次,给建个新的
Onlines::create([
'admin_id' => $request->admin->id,
'start' => time(),
'end' => 0
]);
}
//创建一个新的
Onlines::create([
'admin_id' => $request->admin->id,
'start' => time(),
'end' => 0
]);
} else {
//第一次,给建个新的
Onlines::create([
'admin_id' => $request->admin->id,
'start' => time(),
'end' => 0
]);
}
Redis::Set('CRM:USER:ONLINE:'.$request->admin->id, time(), 'ex', 10 *60);
Redis::Set('CRM:USER:ONLINE:' . $request->admin->id, time(), 'ex', 10 * 60);
Redis::hSet('CRM:USER:ONLINE:END', $request->admin->id, time() + 60);
$new = Redis::get('CRM:USER:ONLINE:NEW:'.$request->admin->id);
Redis::del('CRM:USER:ONLINE:NEW:'.$request->admin->id);
$back = Redis::set('CRM:USER:ONLINE:FOLLOW:LOCK:'. $request->admin->id, time(), 'EX', 60, 'nx');
$new = Redis::get('CRM:USER:ONLINE:NEW:' . $request->admin->id);
Redis::del('CRM:USER:ONLINE:NEW:' . $request->admin->id);
$back = Redis::set('CRM:USER:ONLINE:FOLLOW:LOCK:' . $request->admin->id, time(), 'EX', 60, 'nx');
$follow = $backs = 0;
if($back) {
$follow = Orders::where('admin_id', $request->admin->id)->whereBetween('next_follow',[1,(time()-60)*1000])->count();
if ($back) {
$follow = Orders::where('admin_id', $request->admin->id)->whereBetween('next_follow', [1, (time() - 60) * 1000])->count();
$backs = Backs::where('admin', $request->admin->id)->where('status', 0)->count();
}
return $this->success(['new'=> $new ??0, 'follow'=> $follow,'back' => $backs]);
}
return $this->success(['new' => $new ?? 0, 'follow' => $follow, 'back' => $backs]);
}
public function line() {
public function line()
{
$time = strtotime(date('Y-m-d'));
$data = [];
for($i=10; $i>0; $i--) {
$_time = $time - 3600*24*$i;
$d = Finances::whereBetween('create_time', [$_time, $_time + 24*3600-1])
->where('status',1)
for ($i = 10; $i > 0; $i--) {
$_time = $time - 3600 * 24 * $i;
$d = Finances::whereBetween('create_time', [$_time, $_time + 24 * 3600 - 1])
->where('status', 1)
->fieldRaw('SUM(IF(type=1,`total`,0))/100 as total, SUM(IF(type=2,`total`,0))/100 as refund')
->find();
$d->date = date("m-d", $_time);
$data[] = $d;
}
$_data = [];
foreach($data as $d) {
foreach ($data as $d) {
$_data['title'][] = trim($d->date);
$_data['total'][] = $d->total ??0;
$_data['refund'][] = abs($d->refund ??0);
$_data['total'][] = $d->total ?? 0;
$_data['refund'][] = abs($d->refund ?? 0);
}
return $this->success($_data);
}
public function orders(Request $request) {
$order = Orders::where('admin_id', $request->admin->id)->fieldRaw('SUM(IF(status=0,1,0)) as wait, SUM(IF(status=1,1,0)) as doing, SUM(total_price) as total,count(id) as `all`')->find();
public function orders(Request $request)
{
return $this->success(['wait' => $order->wait ?? 0,'doing' => $order->doing ?? 0,'total' => $order->total ?? 0,'all' => $order->all ?? 0 ]);
$order = Orders::where('admin_id', $request->admin->id)->fieldRaw('
SUM(IF(status=0,1,0)) as wait,
SUM(IF(status=1,1,0)) as doing,
SUM(total_price) as total,
count(id) as `all`,
SUM(IF(asset_price>0 AND status=2,1,0)) as asset,
SUM(IF(asset_price>0 AND status=2,asset_price,0)) as asset_price,
SUM(CASE
WHEN (os = 1 AND STATUS = 5) OR (os = 3 AND STATUS = 4) OR (os = 2 AND STATUS = 1) THEN 1
ELSE 0
END) AS refund,
SUM(CASE
WHEN (os = 1 AND STATUS = 5) OR (os = 3 AND STATUS = 4) OR (os = 2 AND STATUS = 1) THEN actual_price
ELSE 0
END) AS refund_price
')->find();
// Log::warning("=====",['sql'=>$order]);
return $this->success([
'wait' => $order->wait ?? 0,
'doing' => $order->doing ?? 0,
'total' => $order->total ?? 0,
'all' => $order->all ?? 0,
'asset' => $order->asset ?? 0,
'asset_price' => $order->asset_price ?? 0,
'refund' => $order->refund ?? 0,
'refund_price' => $order->refund_price ?? 0
]);
}
public function pie() {
public function pie()
{
$time = strtotime(date('Y-m-d'));
$_time = $time - 3600*24*10;
$_time = $time - 3600 * 24 * 10;
$data = Orders::whereBetween('create_time', [$_time, time()])->fieldRaw('sum(total_price) as value, status')->group('status')->select();
$ddd = [];
foreach(Orders::StatusName as $k=>$d) {
foreach (Orders::StatusName as $k => $d) {
$item['name'] = $d;
$item['value'] = 0;
foreach($data as $dd) {
if($dd->status == $k) {
$item['value'] = $dd->value/100;
foreach ($data as $dd) {
if ($dd->status == $k) {
$item['value'] = $dd->value / 100;
break;
}
}
@ -111,21 +142,23 @@ class IndexController extends base
return $this->success($ddd);
}
public function Info(Request $request) {
public function Info(Request $request)
{
$info = Admins::where('id', $request->admin->id)->find();
return $this->success([
'roles'=> [$info->is_super ? 'admin': 'editor'],
'avatar'=> $info->avatar ?? '/avatar.webp',
'name'=> $info->name,
'is_anchor'=> $info->is_anchor ?1:0,
'oss'=> Orders::OSS
'roles' => [$info->is_super ? 'admin' : 'editor'],
'avatar' => $info->avatar ?? '/avatar.webp',
'name' => $info->name,
'is_anchor' => $info->is_anchor ? 1 : 0,
'oss' => Orders::OSS
]);
}
public function qiniu() {
public function qiniu()
{
// 用于签名的公钥和私钥
$accessKey = config('qiniu.accessKey');
$secretKey = config('qiniu.secretKey');
@ -134,20 +167,52 @@ class IndexController extends base
$auth = new Auth($accessKey, $secretKey);
$bucket = 'tt-api';
$key = 'imgs/'.Date('Ym/d-His').'.jpg';
$key = 'imgs/' . Date('Ym/d-His') . '.jpg';
// 生成上传Token
$token = $auth->uploadToken($bucket);
return $this->success(['qiniu_key'=> $key, 'qiniu_token' => $token,'qiniu_url' => 'http://up-z2.qiniup.com']);
return $this->success(['qiniu_key' => $key, 'qiniu_token' => $token, 'qiniu_url' => 'http://up-z2.qiniup.com']);
}
public function avatar(Request $request) {
public function avatar(Request $request)
{
$file = $request->file("file");
$filename = '/uploads/avatar/'.$request->admin->id.'.'.$file->getUploadExtension();
$file->move(public_path().$filename);
$filename = '/uploads/avatar/' . $request->admin->id . '.' . $file->getUploadExtension();
$file->move(public_path() . $filename);
$item = Admins::where('id', $request->admin->id)->find();
$item->avatar = $filename;
$item->save();
return $this->success($filename);
}
public function productNameList(Request $request)
{
$limit = $request->get('limit', 10);
$product_name = $request->get('product_name');
$list = Orders::where('admin_id',$request->admin->id)
->fieldRaw('
os,product_name,
SUM(IF(status=0,1,0)) as wait,
SUM(IF(status=1,1,0)) as doing,
SUM(total_price) as total,
count(id) as `all`,
SUM(IF(asset_price>0 AND status=2,1,0)) as asset,
SUM(IF(asset_price>0 AND status=2,asset_price,0)) as asset_price,
SUM(CASE
WHEN (os = 1 AND STATUS = 5) OR (os = 3 AND STATUS = 4) OR (os = 2 AND STATUS = 1) THEN 1
ELSE 0
END) AS refund,
SUM(CASE
WHEN (os = 1 AND STATUS = 5) OR (os = 3 AND STATUS = 4) OR (os = 2 AND STATUS = 1) THEN actual_price
ELSE 0
END) AS refund_price
')
->group('os,product_name')
->order('total desc');
if (!empty($product_name)){
$list = $list->where('product_name','like','%'.$product_name.'%');
}
$list = $list->paginate($limit);
return $this->success($list);
}
}

View File

@ -104,7 +104,7 @@ class Orders extends base{
}
public function backs() {
return $this->hasOne(Backs::class, 'order_id')->visible(['status']);
return $this->hasOne(Backs::class, 'order_id')->where('status',0)->visible(['status','admin_id']);
}
public function follow() {