数据统计
This commit is contained in:
parent
06f22f5674
commit
41d5eb5297
|
@ -42,3 +42,12 @@ export function delArea(id) {
|
|||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 查询运营区简单数据列表
|
||||
export function listSimpleArea(query) {
|
||||
return request({
|
||||
url: '/bst/area/simpleList',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
|
|
@ -12,3 +12,16 @@ export function getOrderDailyAmount(query) {
|
|||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取订单排行榜数据
|
||||
* @param {Object} query 查询参数
|
||||
* @returns {Promise} 返回排行榜数据
|
||||
*/
|
||||
export function getOrderRank(query) {
|
||||
return request({
|
||||
url: '/dashboard/order/rank',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
|
|
@ -9,6 +9,8 @@
|
|||
@visible-change="handleVisibleChange"
|
||||
remote
|
||||
:remote-method="remoteMethod"
|
||||
:clearable="clearable"
|
||||
:size="size"
|
||||
>
|
||||
<div class="select-header">
|
||||
<div>
|
||||
|
|
|
@ -48,12 +48,22 @@ export const $remoteSelect = {
|
|||
// 空数据文本
|
||||
emptyText: {
|
||||
type: String,
|
||||
default: '暂无数据'
|
||||
default: '请选择'
|
||||
},
|
||||
// 获取选项前回调
|
||||
beforeGetOptions: {
|
||||
type: Function,
|
||||
default: () => {return true;}
|
||||
},
|
||||
// 是否可清空
|
||||
clearable: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 尺寸
|
||||
size: {
|
||||
type: String,
|
||||
default: null
|
||||
}
|
||||
},
|
||||
}
|
|
@ -8,6 +8,8 @@
|
|||
:multiple="multiple"
|
||||
:init-options="initOptions"
|
||||
v-on="$listeners"
|
||||
:clearable="clearable"
|
||||
:size="size"
|
||||
/>
|
||||
</template>
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ import store from './store';
|
|||
|
||||
import { getConfigKey } from '@/api/system/config';
|
||||
import { getDicts } from '@/api/system/dict/data';
|
||||
import { checkPermi, checkRole, isSysAdmin } from '@/utils/permission';
|
||||
import { checkPermi, checkRole, isAgent, isSysAdmin } from '@/utils/permission';
|
||||
import { addDateRange, handleTree, parseTime, resetForm, selectDictLabel, selectDictLabels } from '@/utils/ruoyi';
|
||||
import './assets/icons'; // icon
|
||||
import './permission'; // permission control
|
||||
|
@ -59,6 +59,7 @@ Vue.prototype.handleTree = handleTree
|
|||
Vue.prototype.checkPermi = checkPermi
|
||||
Vue.prototype.checkRole = checkRole
|
||||
Vue.prototype.isSysAdmin = isSysAdmin
|
||||
Vue.prototype.isAgent = isAgent
|
||||
|
||||
// 全局组件挂载
|
||||
Vue.component('DictTag', DictTag)
|
||||
|
|
|
@ -197,16 +197,29 @@ export const StatKeys = {
|
|||
ORDER_COUNT: "order_count", // 订单数量
|
||||
ORDER_PAY_AMOUNT: "order_pay_amount", // 订单支付金额
|
||||
ORDER_REFUND_AMOUNT: "order_refund_amount", // 订单退款金额
|
||||
|
||||
BONUS_COUNT: "bonus_count", // 分成数量
|
||||
BONUS_AMOUNT: "bonus_amount", // 分成总金额
|
||||
BONUS_REFUND_AMOUNT: "bonus_refund_amount", // 分成总退款
|
||||
BONUS_WAIT_DIVIDE_AMOUNT: "bonus_wait_divide_amount", // 分成未入账金额
|
||||
|
||||
USER_COUNT: "user_count", // 用户数量
|
||||
USER_TODAY_COUNT: "user_today_count", // 今日新增用户数量
|
||||
USER_BALANCE: "user_balance", // 用户余额
|
||||
USER_MCH_COUNT: "user_mch_count", // 运营商数量
|
||||
USER_AGENT_COUNT: "user_agent_count", // 代理商数量
|
||||
|
||||
DEVICE_COUNT: "device_count", // 设备数量
|
||||
DEVICE_STATUS_COUNT: "device_status_count", // 设备状态数量
|
||||
DEVICE_ONLINE_STATUS_COUNT: "device_online_status_count", // 设备在线状态数量
|
||||
|
||||
AREA_COUNT: "area_count", // 运营区数量
|
||||
MODEL_COUNT: "model_count", // 型号数量
|
||||
|
||||
AREA_JOIN_COUNT: "area_join_count", // 加盟商数量
|
||||
AREA_JOIN_PARTNER_COUNT: "area_join_partner_count", // 合伙人数量
|
||||
|
||||
WITHDRAW_SUCCESS_AMOUNT: "withdraw_success_amount", // 成功提现金额
|
||||
}
|
||||
|
||||
// 收款账户类型
|
||||
|
|
|
@ -52,3 +52,10 @@ export function checkRole(value) {
|
|||
export function isSysAdmin() {
|
||||
return checkRole(['sysAdmin'])
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否代理商
|
||||
*/
|
||||
export function isAgent() {
|
||||
return checkRole(['agent'])
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
<el-button size="mini" icon="el-icon-full-screen" @click="setFitView">全局查看</el-button>
|
||||
<el-button size="mini" icon="el-icon-picture" @click="toggleMapStyle">切换样式</el-button>
|
||||
<el-button size="mini" icon="el-icon-document" @click="toggleLabels">{{ showLabels ? '隐藏' : '显示' }}文字</el-button>
|
||||
<el-button size="mini" icon="el-icon-video-play" @click="togglePlaybackPanel">{{ isPlaybackVisible ? '隐藏' : '显示' }}轨迹控制台</el-button>
|
||||
<el-button size="mini" icon="el-icon-video-play" v-if="locationLogList && locationLogList.length > 0" @click="togglePlaybackPanel">{{ isPlaybackVisible ? '隐藏' : '显示' }}轨迹控制台</el-button>
|
||||
</el-button-group>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -94,6 +94,11 @@ export default {
|
|||
methods: {
|
||||
// 获取运营区域
|
||||
getArea() {
|
||||
if (!this.areaId) {
|
||||
this.area = {};
|
||||
this.areaSubList = [];
|
||||
return;
|
||||
}
|
||||
getArea(this.areaId).then(res => {
|
||||
this.area = res.data;
|
||||
this.getAreaSubList();
|
||||
|
|
115
src/views/bst/index/AdminIndex.vue
Normal file
115
src/views/bst/index/AdminIndex.vue
Normal file
|
@ -0,0 +1,115 @@
|
|||
<template>
|
||||
<div class="app-container" style="min-height: 600px;" v-loading="loading">
|
||||
<el-row :gutter="gutter" v-if="stat">
|
||||
<el-col :span="18">
|
||||
<!-- 统计信息 -->
|
||||
<admin-stat :stat="stat" :today-stat="todayStat" />
|
||||
|
||||
<!-- 设备统计信息 -->
|
||||
<device-stat :stat="stat"/>
|
||||
|
||||
<el-row :gutter="gutter" style="margin-top:12px">
|
||||
<el-col :span="24">
|
||||
<order-daily-stat/>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<!-- 待办事项 -->
|
||||
<todo-list :stat="stat"/>
|
||||
|
||||
<order-rank style="margin-top: 12px;"/>
|
||||
|
||||
</el-col>
|
||||
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getStat } from '@/api/dashboard/dashboard'
|
||||
import { StatKeys } from '@/utils/enums'
|
||||
import AdminStat from '@/views/bst/index/components/AdminStat'
|
||||
import DeviceStat from '@/views/bst/index/components/DeviceStat'
|
||||
import TodoList from '@/views/bst/index/components/TodoList'
|
||||
import { getLastDateStr } from '@/utils'
|
||||
import OrderDailyStat from '@/views/bst/index/components/OrderDailyStat'
|
||||
import OrderRank from '@/views/bst/index/components/OrderRank'
|
||||
|
||||
export default {
|
||||
name: 'AdminIndex',
|
||||
components: {
|
||||
AdminStat,
|
||||
DeviceStat,
|
||||
TodoList,
|
||||
OrderDailyStat,
|
||||
OrderRank,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
span: 6,
|
||||
gutter: 12,
|
||||
loading: false,
|
||||
stat: null,
|
||||
todayStat: null,
|
||||
userInfo: null,
|
||||
userName: null,
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getStat();
|
||||
this.getTodayStat();
|
||||
},
|
||||
methods: {
|
||||
getStat() {
|
||||
this.loading = true;
|
||||
getStat({
|
||||
keys: [
|
||||
StatKeys.ORDER_COUNT,
|
||||
StatKeys.ORDER_PAY_AMOUNT,
|
||||
StatKeys.ORDER_REFUND_AMOUNT,
|
||||
StatKeys.DEVICE_COUNT,
|
||||
StatKeys.DEVICE_STATUS_COUNT,
|
||||
StatKeys.DEVICE_ONLINE_STATUS_COUNT,
|
||||
StatKeys.AREA_COUNT,
|
||||
StatKeys.AREA_JOIN_COUNT,
|
||||
StatKeys.AREA_JOIN_PARTNER_COUNT,
|
||||
StatKeys.USER_COUNT,
|
||||
StatKeys.USER_BALANCE,
|
||||
StatKeys.USER_MCH_COUNT,
|
||||
StatKeys.USER_AGENT_COUNT,
|
||||
StatKeys.MODEL_COUNT,
|
||||
StatKeys.WITHDRAW_SUCCESS_AMOUNT,
|
||||
]
|
||||
}).then(res => {
|
||||
this.stat = res.data
|
||||
}).finally(() => {
|
||||
this.loading = false;
|
||||
})
|
||||
},
|
||||
getTodayStat() {
|
||||
getStat({
|
||||
keys:[
|
||||
StatKeys.USER_COUNT,
|
||||
StatKeys.ORDER_PAY_AMOUNT,
|
||||
StatKeys.ORDER_REFUND_AMOUNT,
|
||||
],
|
||||
dateRange: [
|
||||
getLastDateStr(0),
|
||||
getLastDateStr(0)
|
||||
]
|
||||
}).then(res => {
|
||||
this.todayStat = res.data
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.title-1 {
|
||||
font-size: 18px;
|
||||
margin-bottom: 8px;
|
||||
color: #409EFF;
|
||||
}
|
||||
</style>
|
152
src/views/bst/index/MchIndex.vue
Normal file
152
src/views/bst/index/MchIndex.vue
Normal file
|
@ -0,0 +1,152 @@
|
|||
<template>
|
||||
<div class="app-container" style="min-height: 600px;" v-loading="loading">
|
||||
<el-row :gutter="gutter" v-if="stat">
|
||||
<el-col :span="18">
|
||||
<div class="title-1">
|
||||
<i class="el-icon-s-data"></i>
|
||||
运营统计
|
||||
<el-select v-model="areaId" @change="onChangeArea" placeholder="请选择运营区">
|
||||
<el-option v-for="item in areaList" :key="item.id" :label="item.name" :value="item.id"/>
|
||||
</el-select>
|
||||
</div>
|
||||
<!-- 统计信息 -->
|
||||
<mch-stat :stat="stat" :today-stat="todayStat" />
|
||||
|
||||
<!-- 设备统计信息 -->
|
||||
<device-stat :stat="stat"/>
|
||||
|
||||
<el-row :gutter="gutter" style="margin-top:12px">
|
||||
<el-col :span="24">
|
||||
<order-daily-stat/>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<div class="title-1">
|
||||
<i class="el-icon-user"></i>
|
||||
我的
|
||||
</div>
|
||||
<balance-panel v-if="userInfo" :balance="userInfo.balance" :waitBonus="userStat.bonus.waitAmount" :totalWithdraw="userStat.withdraw.successAmount"/>
|
||||
|
||||
<!-- 待办事项 -->
|
||||
<todo-list :stat="stat"/>
|
||||
|
||||
</el-col>
|
||||
|
||||
</el-row>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getStat } from '@/api/dashboard/dashboard'
|
||||
import { StatKeys } from '@/utils/enums'
|
||||
import MchStat from '@/views/bst/index/components/MchStat'
|
||||
import DeviceStat from '@/views/bst/index/components/DeviceStat'
|
||||
import TodoList from '@/views/bst/index/components/TodoList'
|
||||
import { getLastDateStr } from '@/utils'
|
||||
import OrderDailyStat from '@/views/bst/index/components/OrderDailyStat'
|
||||
import { getInfo } from '@/api/login'
|
||||
import { listSimpleArea } from '@/api/bst/area'
|
||||
import BalancePanel from '@/views/bst/index/components/BalancePanel'
|
||||
|
||||
export default {
|
||||
name: 'Index',
|
||||
components: {
|
||||
MchStat,
|
||||
DeviceStat,
|
||||
TodoList,
|
||||
OrderDailyStat,
|
||||
BalancePanel,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
span: 6,
|
||||
gutter: 12,
|
||||
loading: false,
|
||||
stat: null,
|
||||
todayStat: null,
|
||||
userInfo: null,
|
||||
areaId: null,
|
||||
areaList: [],
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getUserInfo();
|
||||
this.getAreaList();
|
||||
},
|
||||
methods: {
|
||||
getAreaList() {
|
||||
listSimpleArea().then(res => {
|
||||
this.areaList = res.data;
|
||||
if (this.areaList.length > 0) {
|
||||
this.areaId = this.areaList[0].id;
|
||||
this.onChangeArea();
|
||||
}
|
||||
})
|
||||
},
|
||||
onChangeArea() {
|
||||
this.getStat();
|
||||
this.getTodayStat();
|
||||
},
|
||||
getUserInfo() {
|
||||
getInfo().then(res => {
|
||||
this.userInfo = res.user;
|
||||
this.userStat = res.stat;
|
||||
})
|
||||
},
|
||||
// 获取运营统计信息
|
||||
getStat() {
|
||||
this.loading = true;
|
||||
getStat({
|
||||
areaId: this.areaId,
|
||||
keys: [
|
||||
StatKeys.ORDER_COUNT,
|
||||
StatKeys.ORDER_PAY_AMOUNT,
|
||||
StatKeys.ORDER_REFUND_AMOUNT,
|
||||
StatKeys.BONUS_AMOUNT,
|
||||
StatKeys.BONUS_REFUND_AMOUNT,
|
||||
StatKeys.DEVICE_COUNT,
|
||||
StatKeys.DEVICE_STATUS_COUNT,
|
||||
StatKeys.DEVICE_ONLINE_STATUS_COUNT,
|
||||
StatKeys.AREA_JOIN_COUNT,
|
||||
StatKeys.AREA_JOIN_PARTNER_COUNT,
|
||||
]
|
||||
}).then(res => {
|
||||
this.stat = res.data
|
||||
}).finally(() => {
|
||||
this.loading = false;
|
||||
})
|
||||
},
|
||||
// 获取今日运营统计信息
|
||||
getTodayStat() {
|
||||
getStat({
|
||||
areaId: this.areaId,
|
||||
keys:[
|
||||
StatKeys.ORDER_COUNT,
|
||||
StatKeys.ORDER_PAY_AMOUNT,
|
||||
StatKeys.ORDER_REFUND_AMOUNT,
|
||||
StatKeys.BONUS_AMOUNT,
|
||||
StatKeys.BONUS_REFUND_AMOUNT,
|
||||
StatKeys.DEVICE_COUNT,
|
||||
],
|
||||
dateRange: [
|
||||
getLastDateStr(0),
|
||||
getLastDateStr(0)
|
||||
]
|
||||
}).then(res => {
|
||||
this.todayStat = res.data
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.title-1 {
|
||||
font-size: 18px;
|
||||
margin-bottom: 8px;
|
||||
color: #409EFF;
|
||||
line-height: 36px;
|
||||
}
|
||||
</style>
|
128
src/views/bst/index/components/AdminStat.vue
Normal file
128
src/views/bst/index/components/AdminStat.vue
Normal file
|
@ -0,0 +1,128 @@
|
|||
<template>
|
||||
<div class="stat-container">
|
||||
<statistics-card
|
||||
class="stat-card"
|
||||
:value="stat.order.payAmount - stat.orderRefund.amount"
|
||||
label="订单实收"
|
||||
icon="el-icon-money"
|
||||
start-color="#1890FF"
|
||||
end-color="#69C0FF"
|
||||
sub-label="今日订单实收"
|
||||
:sub-value="todayStat.order.payAmount - todayStat.orderRefund.amount"
|
||||
:precision="2"
|
||||
/>
|
||||
<statistics-card
|
||||
class="stat-card"
|
||||
:value="stat.order.count"
|
||||
label="订单数量"
|
||||
icon="el-icon-tickets"
|
||||
start-color="#52C41A"
|
||||
end-color="#95DE64"
|
||||
sub-label="今日订单数量"
|
||||
:sub-value="todayStat.order.count"
|
||||
/>
|
||||
<statistics-card
|
||||
class="stat-card"
|
||||
:value="stat.device.count"
|
||||
label="车辆总数"
|
||||
icon="el-icon-bicycle"
|
||||
start-color="#722ED1"
|
||||
end-color="#B37FEB"
|
||||
sub-label="车型总数"
|
||||
:sub-value="stat.model.count"
|
||||
:show-value-change="false"
|
||||
/>
|
||||
<statistics-card
|
||||
class="stat-card"
|
||||
:value="stat.area.count"
|
||||
label="运营区"
|
||||
icon="el-icon-map-location"
|
||||
start-color="#13C2C2"
|
||||
end-color="#5CDBD3"
|
||||
sub-label="店铺数量"
|
||||
:sub-value="0"
|
||||
:show-value-change="false"
|
||||
/>
|
||||
<statistics-card
|
||||
class="stat-card"
|
||||
:value="stat.areaJoin.joinCount"
|
||||
label="加盟商"
|
||||
icon="el-icon-office-building"
|
||||
start-color="#FA8C16"
|
||||
end-color="#FFC069"
|
||||
sub-label="合伙人"
|
||||
:sub-value="stat.areaJoin.partnerCount"
|
||||
:show-value-change="false"
|
||||
/>
|
||||
<statistics-card
|
||||
class="stat-card"
|
||||
:value="stat.user.count"
|
||||
label="用户总数"
|
||||
icon="el-icon-user-solid"
|
||||
start-color="#EB2F96"
|
||||
end-color="#FF85C0"
|
||||
sub-label="今日新增"
|
||||
:sub-value="todayStat.user.count"
|
||||
/>
|
||||
<statistics-card
|
||||
class="stat-card"
|
||||
:value="stat.user.mchCount"
|
||||
label="运营商"
|
||||
icon="el-icon-s-custom"
|
||||
start-color="#2F54EB"
|
||||
end-color="#85A5FF"
|
||||
sub-label="代理商"
|
||||
:sub-value="stat.user.agentCount"
|
||||
:show-value-change="false"
|
||||
/>
|
||||
<statistics-card
|
||||
class="stat-card"
|
||||
:value="stat.user.balance"
|
||||
label="用户余额"
|
||||
icon="el-icon-wallet"
|
||||
start-color="#F5222D"
|
||||
end-color="#FF7875"
|
||||
sub-label="累计提现"
|
||||
:sub-value="stat.withdraw.successAmount"
|
||||
:show-value-change="false"
|
||||
:precision="2"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import StatisticsCard from '@/components/StatisticsCard'
|
||||
export default {
|
||||
name: 'Stat',
|
||||
components: {
|
||||
StatisticsCard
|
||||
},
|
||||
props: {
|
||||
stat: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
todayStat: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
span: 4
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.stat-container {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
margin-bottom: 10px;
|
||||
.stat-card {
|
||||
flex: 0 1 calc(25% - 10px);
|
||||
}
|
||||
}
|
||||
</style>
|
219
src/views/bst/index/components/BalancePanel.vue
Normal file
219
src/views/bst/index/components/BalancePanel.vue
Normal file
|
@ -0,0 +1,219 @@
|
|||
<template>
|
||||
<el-card class="balance-panel" shadow="never">
|
||||
<div class="panel-content">
|
||||
<!-- 主要数据区域 -->
|
||||
<div class="main-data">
|
||||
<div class="balance-item">
|
||||
<div class="item-icon" style="background: linear-gradient(135deg, #36a3f7, #5b8def);">
|
||||
<i class="el-icon-money"></i>
|
||||
</div>
|
||||
<div class="item-info">
|
||||
<div class="item-label">当前余额</div>
|
||||
<div class="item-value">{{balance | fix2 | dv}}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="balance-item">
|
||||
<div class="item-icon" style="background: linear-gradient(135deg, #f6ab4f, #ffbc58);">
|
||||
<i class="el-icon-time"></i>
|
||||
</div>
|
||||
<div class="item-info">
|
||||
<div class="item-label">未入账</div>
|
||||
<div class="item-value">{{waitBonus | fix2 | dv}}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 次要数据区域 -->
|
||||
<div class="secondary-data">
|
||||
<div class="withdraw-item">
|
||||
<div class="item-label">累计提现</div>
|
||||
<div class="item-value">{{totalWithdraw | fix2 | dv}}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 提现按钮区域 -->
|
||||
<div class="withdraw-btn-area">
|
||||
<el-button type="primary" icon="el-icon-wallet" @click="handleWithdraw">申请提现</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 提现弹窗 -->
|
||||
<withdraw-add-dialog
|
||||
:visible.sync="withdrawDialogVisible"
|
||||
@success="$emit('refresh')"
|
||||
/>
|
||||
</el-card>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import WithdrawAddDialog from "@/views/bst/withdraw/components/WithdrawAddDialog.vue";
|
||||
|
||||
export default {
|
||||
name: 'BalancePanel',
|
||||
components: {
|
||||
WithdrawAddDialog
|
||||
},
|
||||
props: {
|
||||
// 余额
|
||||
balance: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
// 未入账
|
||||
waitBonus: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
// 总提现
|
||||
totalWithdraw: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
withdrawDialogVisible: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 处理提现按钮点击
|
||||
handleWithdraw() {
|
||||
this.withdrawDialogVisible = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.balance-panel {
|
||||
margin-bottom: 12px;
|
||||
|
||||
.panel-content {
|
||||
padding-top: 10px;
|
||||
|
||||
.main-data {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
margin-bottom: 10px;
|
||||
|
||||
.balance-item {
|
||||
width: 50%;
|
||||
padding-right: 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 10px;
|
||||
transition: all 0.3s;
|
||||
|
||||
&:last-child {
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
.item-icon {
|
||||
width: 52px;
|
||||
height: 52px;
|
||||
border-radius: 14px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
margin-right: 12px;
|
||||
transition: all 0.3s;
|
||||
box-shadow: 0 3px 6px rgba(0, 0, 0, 0.1);
|
||||
|
||||
i {
|
||||
font-size: 26px;
|
||||
color: white;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
}
|
||||
|
||||
.item-info {
|
||||
flex: 1;
|
||||
|
||||
.item-label {
|
||||
font-size: 15px;
|
||||
color: #606266;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.item-value {
|
||||
font-size: 22px;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
background: linear-gradient(135deg, #333, #666);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.secondary-data {
|
||||
border-top: 1px dashed #eaeaea;
|
||||
padding-top: 8px;
|
||||
margin-bottom: 8px;
|
||||
|
||||
.withdraw-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
.item-label {
|
||||
font-size: 14px;
|
||||
color: #606266;
|
||||
}
|
||||
|
||||
.item-value {
|
||||
font-size: 15px;
|
||||
color: #606266;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.withdraw-btn-area {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
||||
.el-button {
|
||||
font-size: 16px;
|
||||
padding: 12px 25px;
|
||||
width: 100%;
|
||||
border-radius: 8px;
|
||||
font-weight: 500;
|
||||
transition: all 0.3s;
|
||||
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
|
||||
|
||||
&:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
&:active {
|
||||
transform: translateY(1px);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 768px) {
|
||||
.balance-panel {
|
||||
.panel-content {
|
||||
.main-data {
|
||||
.balance-item {
|
||||
width: 100%;
|
||||
padding: 10px 0;
|
||||
|
||||
&:first-child {
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
93
src/views/bst/index/components/MchStat.vue
Normal file
93
src/views/bst/index/components/MchStat.vue
Normal file
|
@ -0,0 +1,93 @@
|
|||
<template>
|
||||
<div class="stat-container">
|
||||
<statistics-card
|
||||
class="stat-card"
|
||||
:value="stat.order.payAmount - stat.orderRefund.amount"
|
||||
label="订单实收"
|
||||
icon="el-icon-bank-card"
|
||||
start-color="#FF6F61"
|
||||
end-color="#FF9F80"
|
||||
sub-label="今日订单实收"
|
||||
:sub-value="todayStat.order.payAmount - todayStat.orderRefund.amount"
|
||||
:precision="2"
|
||||
/>
|
||||
<statistics-card
|
||||
class="stat-card"
|
||||
:value="stat.order.count"
|
||||
label="订单数量"
|
||||
icon="el-icon-document"
|
||||
start-color="#FF6F61"
|
||||
end-color="#FF9F80"
|
||||
sub-label="今日订单数量"
|
||||
:sub-value="todayStat.order.count"
|
||||
/>
|
||||
<statistics-card
|
||||
class="stat-card"
|
||||
:value="stat.bonus.amount - stat.bonusRefund.amount"
|
||||
label="收入金额"
|
||||
icon="el-icon-document"
|
||||
start-color="#FF6F61"
|
||||
end-color="#FF9F80"
|
||||
sub-label="今日收入金额"
|
||||
:sub-value="todayStat.bonus.amount - todayStat.bonusRefund.amount"
|
||||
/>
|
||||
<statistics-card
|
||||
class="stat-card"
|
||||
:value="stat.device.count"
|
||||
label="车辆总数"
|
||||
icon="el-icon-bicycle"
|
||||
start-color="#73D13D"
|
||||
end-color="#A0D911"
|
||||
sub-label="今日新增"
|
||||
:sub-value="todayStat.device.count"
|
||||
/>
|
||||
<statistics-card
|
||||
class="stat-card"
|
||||
:value="stat.areaJoin.joinCount"
|
||||
label="加盟商"
|
||||
icon="el-icon-money"
|
||||
start-color="#FFC069"
|
||||
end-color="#FF9C6E"
|
||||
sub-label="合伙人"
|
||||
:sub-value="stat.areaJoin.partnerCount"
|
||||
:show-value-change="false"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import StatisticsCard from '@/components/StatisticsCard'
|
||||
export default {
|
||||
name: 'Stat',
|
||||
components: {
|
||||
StatisticsCard
|
||||
},
|
||||
props: {
|
||||
stat: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
todayStat: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
span: 4
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.stat-container {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
.stat-card {
|
||||
flex: 1;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
}
|
||||
</style>
|
68
src/views/bst/index/components/OrderRank.vue
Normal file
68
src/views/bst/index/components/OrderRank.vue
Normal file
|
@ -0,0 +1,68 @@
|
|||
<template>
|
||||
<el-card shadow="never" header="订单排行">
|
||||
<el-date-picker
|
||||
v-model="queryParams.createDateRange"
|
||||
type="daterange"
|
||||
range-separator="至"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
value-format="yyyy-MM-dd"
|
||||
@change="getRankList"
|
||||
style="width: 100%;"
|
||||
size="mini"
|
||||
:picker-options="DatePickerOptions.DEFAULT"
|
||||
/>
|
||||
|
||||
<el-table
|
||||
:data="rankList"
|
||||
style="width: 100%;"
|
||||
size="mini"
|
||||
v-loading="loading"
|
||||
>
|
||||
<el-table-column label="排行" type="index" width="50">
|
||||
<template slot-scope="d">{{d.$index + 1}}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="运营区" prop="areaName" />
|
||||
<el-table-column label="金额" prop="orderAmount" />
|
||||
<el-table-column label="数量" prop="orderCount" />
|
||||
<el-table-column label="进行中" prop="processingCount" />
|
||||
</el-table>
|
||||
|
||||
</el-card>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getOrderRank } from '@/api/dashboard/dashboardOrder'
|
||||
import { getLastDateStr } from '@/utils'
|
||||
import { DatePickerOptions } from '@/utils/constants'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
DatePickerOptions,
|
||||
loading: false,
|
||||
rankList: [],
|
||||
queryParams: {
|
||||
createDateRange: [getLastDateStr(0), getLastDateStr(0)],
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getRankList();
|
||||
},
|
||||
methods: {
|
||||
getRankList() {
|
||||
this.loading = true;
|
||||
getOrderRank(this.queryParams).then(res => {
|
||||
this.rankList = res.data;
|
||||
}).finally(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
</style>
|
|
@ -1,155 +0,0 @@
|
|||
<template>
|
||||
<el-row :gutter="10">
|
||||
<el-col :span="span" class="stat-col">
|
||||
<statistics-card
|
||||
:value="stat.order.payAmount - stat.orderRefund.amount"
|
||||
label="订单实收"
|
||||
icon="el-icon-bank-card"
|
||||
start-color="#FF6F61"
|
||||
end-color="#FF9F80"
|
||||
sub-label="今日订单实收"
|
||||
:sub-value="todayStat.order.payAmount - todayStat.orderRefund.amount"
|
||||
:precision="2"
|
||||
/>
|
||||
</el-col>
|
||||
<el-col :span="span" class="stat-col">
|
||||
<statistics-card
|
||||
:value="stat.order.count"
|
||||
label="订单数量"
|
||||
icon="el-icon-document"
|
||||
start-color="#FF6F61"
|
||||
end-color="#FF9F80"
|
||||
sub-label="今日订单数量"
|
||||
:sub-value="todayStat.order.count"
|
||||
/>
|
||||
</el-col>
|
||||
<el-col :span="span" class="stat-col">
|
||||
<statistics-card
|
||||
:value="stat.area.count"
|
||||
label="总收入"
|
||||
icon="el-icon-wallet"
|
||||
start-color="#69C0FF"
|
||||
end-color="#40A9FF"
|
||||
sub-label="今日收入"
|
||||
:sub-value="stat.user.balance"
|
||||
:precision="2"
|
||||
/>
|
||||
</el-col>
|
||||
<el-col :span="span" class="stat-col" v-if="isSysAdmin()">
|
||||
<statistics-card
|
||||
:value="stat.user.balance"
|
||||
label="商户余额"
|
||||
icon="el-icon-wallet"
|
||||
start-color="#69C0FF"
|
||||
end-color="#40A9FF"
|
||||
sub-label="待结算金额"
|
||||
:sub-value="stat.user.balance"
|
||||
:show-value-change="false"
|
||||
:precision="2"
|
||||
/>
|
||||
</el-col>
|
||||
<el-col :span="span" class="stat-col">
|
||||
<statistics-card
|
||||
:value="stat.device.count"
|
||||
label="车辆总数"
|
||||
icon="el-icon-bicycle"
|
||||
start-color="#73D13D"
|
||||
end-color="#A0D911"
|
||||
sub-label="车型总数"
|
||||
:sub-value="stat.model.count"
|
||||
:show-value-change="false"
|
||||
/>
|
||||
</el-col>
|
||||
<el-col :span="span" class="stat-col" v-if="isSysAdmin()">
|
||||
<statistics-card
|
||||
:value="stat.area.count"
|
||||
label="运营区数量"
|
||||
icon="el-icon-map-location"
|
||||
start-color="#FFC069"
|
||||
end-color="#FF9C6E"
|
||||
sub-label="店铺数量"
|
||||
:sub-value="stat.user.balance"
|
||||
:show-value-change="false"
|
||||
/>
|
||||
</el-col>
|
||||
<el-col :span="span" class="stat-col">
|
||||
<statistics-card
|
||||
:value="stat.user.count"
|
||||
label="用户总数"
|
||||
icon="el-icon-user"
|
||||
start-color="#9254DE"
|
||||
end-color="#F759AB"
|
||||
sub-label="今日新增"
|
||||
:sub-value="todayStat.user.count"
|
||||
/>
|
||||
</el-col>
|
||||
<el-col :span="span" class="stat-col">
|
||||
<statistics-card
|
||||
:value="stat.area.count"
|
||||
label="累计提现"
|
||||
icon="el-icon-money"
|
||||
start-color="#FFC069"
|
||||
end-color="#FF9C6E"
|
||||
sub-label="审核中"
|
||||
:sub-value="stat.user.balance"
|
||||
:show-value-change="false"
|
||||
:precision="2"
|
||||
/>
|
||||
</el-col>
|
||||
<el-col :span="span" class="stat-col" v-if="!isSysAdmin()">
|
||||
<statistics-card
|
||||
:value="stat.area.count"
|
||||
label="加盟商"
|
||||
icon="el-icon-money"
|
||||
start-color="#FFC069"
|
||||
end-color="#FF9C6E"
|
||||
sub-label="合伙人"
|
||||
:sub-value="stat.user.balance"
|
||||
:show-value-change="false"
|
||||
/>
|
||||
</el-col>
|
||||
<el-col :span="span" class="stat-col" v-if="!isSysAdmin()">
|
||||
<statistics-card
|
||||
:value="stat.area.count"
|
||||
label="用户投诉"
|
||||
icon="el-icon-warning"
|
||||
start-color="#FFC069"
|
||||
end-color="#FF9C6E"
|
||||
sub-label="故障反馈"
|
||||
:sub-value="stat.user.balance"
|
||||
:show-value-change="false"
|
||||
/>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import StatisticsCard from '@/components/StatisticsCard'
|
||||
export default {
|
||||
name: 'Stat',
|
||||
components: {
|
||||
StatisticsCard
|
||||
},
|
||||
props: {
|
||||
stat: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
todayStat: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
span: 6
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.stat-col {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
</style>
|
|
@ -1,105 +1,27 @@
|
|||
<template>
|
||||
<div class="app-container" style="min-height: 600px;" v-loading="loading">
|
||||
<el-row :gutter="gutter" v-if="stat">
|
||||
<el-col :span="18">
|
||||
<!-- 统计信息 -->
|
||||
<stat :stat="stat" :today-stat="todayStat" />
|
||||
|
||||
<!-- 设备统计信息 -->
|
||||
<device-stat :stat="stat"/>
|
||||
|
||||
<el-row :gutter="gutter" style="margin-top:12px">
|
||||
<el-col :span="24">
|
||||
<order-daily-stat/>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<!-- 待办事项 -->
|
||||
<todo-list :stat="stat"/>
|
||||
|
||||
|
||||
<el-card style="margin-top: 12px;" header="订单排行" shadow="never">
|
||||
待实现
|
||||
</el-card>
|
||||
|
||||
</el-col>
|
||||
|
||||
</el-row>
|
||||
|
||||
<div>
|
||||
<mch-index v-if="active === 'mch'"/>
|
||||
<admin-index v-if="active === 'admin'"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getStat } from '@/api/dashboard/dashboard'
|
||||
import { StatKeys } from '@/utils/enums'
|
||||
import Stat from '@/views/bst/index/components/Stat'
|
||||
import DeviceStat from '@/views/bst/index/components/DeviceStat'
|
||||
import TodoList from '@/views/bst/index/components/TodoList'
|
||||
import { getLastDateStr } from '@/utils'
|
||||
import OrderDailyStat from './components/OrderDailyStat.vue'
|
||||
|
||||
import MchIndex from '@/views/bst/index/MchIndex.vue'
|
||||
import AdminIndex from '@/views/bst/index/AdminIndex.vue'
|
||||
export default {
|
||||
name: 'Index',
|
||||
components: {
|
||||
Stat,
|
||||
DeviceStat,
|
||||
TodoList,
|
||||
OrderDailyStat,
|
||||
MchIndex,
|
||||
AdminIndex
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
span: 6,
|
||||
gutter: 12,
|
||||
loading: false,
|
||||
stat: null,
|
||||
todayStat: null,
|
||||
queryParams: {
|
||||
keys: [
|
||||
StatKeys.ORDER_COUNT,
|
||||
StatKeys.ORDER_PAY_AMOUNT,
|
||||
StatKeys.ORDER_REFUND_AMOUNT,
|
||||
StatKeys.BONUS_COUNT,
|
||||
StatKeys.BONUS_AMOUNT,
|
||||
StatKeys.BONUS_REFUND_AMOUNT,
|
||||
StatKeys.USER_COUNT,
|
||||
StatKeys.USER_BALANCE,
|
||||
StatKeys.DEVICE_COUNT,
|
||||
StatKeys.DEVICE_STATUS_COUNT,
|
||||
StatKeys.DEVICE_ONLINE_STATUS_COUNT,
|
||||
StatKeys.AREA_COUNT,
|
||||
StatKeys.MODEL_COUNT
|
||||
]
|
||||
}
|
||||
active: 'mch',
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getStat();
|
||||
this.getTodayStat();
|
||||
},
|
||||
methods: {
|
||||
getStat() {
|
||||
this.loading = true;
|
||||
getStat(this.queryParams).then(res => {
|
||||
this.stat = res.data
|
||||
}).finally(() => {
|
||||
this.loading = false;
|
||||
})
|
||||
},
|
||||
getTodayStat() {
|
||||
getStat({
|
||||
keys:[
|
||||
StatKeys.USER_COUNT,
|
||||
StatKeys.ORDER_PAY_AMOUNT,
|
||||
StatKeys.ORDER_REFUND_AMOUNT,
|
||||
],
|
||||
dateRange: [
|
||||
getLastDateStr(0),
|
||||
getLastDateStr(0)
|
||||
]
|
||||
}).then(res => {
|
||||
this.todayStat = res.data
|
||||
})
|
||||
if (this.isSysAdmin()) {
|
||||
this.active = 'admin'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,11 +12,10 @@
|
|||
<form-col :span="span" label="车型名称" prop="name">
|
||||
<el-input v-model="form.name" placeholder="请输入车型名称" />
|
||||
</form-col>
|
||||
<form-col :span="span" label="所属用户" prop="userId">
|
||||
<form-col :span="span" label="所属用户" prop="userId" v-if="checkPermi(['system:user:list'])">
|
||||
<user-input
|
||||
v-model="form.userId"
|
||||
:text.sync="form.userName"
|
||||
:disabled="!checkPermi(['system:user:list'])"
|
||||
@change="handleChangeUser"
|
||||
/>
|
||||
</form-col>
|
||||
|
|
Loading…
Reference in New Issue
Block a user