Commit 6b6448a2 by 姜雷

修改数据首页

parent b62212f0
......@@ -7,7 +7,7 @@
"scripts": {
"dev": "vue-cli-service serve --port 8891",
"dev:local": "vue-cli-service serve ./src/dev-local/develop.js --port 8891",
"start": "npm run dev",
"start": "npm run dev:local",
"lint": "eslint --ext .js,.vue src",
"build": "vue-cli-service build --modern",
"build:dev": "cross-env npm run buildapp:dev && npm run buildlib:dev",
......@@ -23,7 +23,9 @@
"analyz": "cross-env NODE_ENV=production npm_config_report=true npm run build"
},
"dependencies": {
"@antv/data-set": "^0.10.2",
"@antv/g2": "^3.5.1",
"@antv/g2-plugin-slider": "^2.1.1",
"axios": "^0.18.0",
"blueimp-md5": "^2.10.0",
"js-cookie": "^2.2.0",
......
......@@ -31,6 +31,13 @@ export const fetchOperatorList = req =>
method: 'get',
...req,
});
// 获取权限运营商
export const fetchAccessOperatorList = req =>
fetch({
url: path + '/dcxy/baseOperator/queryAccessOper',
method: 'get',
...req,
});
// 获取豆类型
export const fetchBeansList = req =>
fetch({
......
......@@ -5,6 +5,7 @@ const path = process.env.VUE_APP_CUSTOMER_SERVER_URL;
export const fetchConsumeList = req =>
fetch({
url: path + '/dcxy/reportInfo/queryConsumeList',
method: 'POST',
timeout: 30000,
...req,
});
......@@ -18,3 +19,20 @@ export const fetchTitleList = req =>
url: path + '/dcxy/reportInfo/queryTitleList',
...req,
});
export const getRechargeOrderList = req =>
fetch({
url: path + '/dcxy/reportInfo/queryRechargeOrderList',
method: 'POST',
...req,
});
export const getConsumeOrderList = req =>
fetch({
url: path + '/dcxy/reportInfo/queryConsumeOrderList',
method: 'POST',
...req,
});
export const getFeedbackList = req =>
fetch({
url: path + '/dcxy/reportInfo/queryFeedbackList',
...req,
});
......@@ -5,8 +5,8 @@ $--color-primary: #1459fc;
$--color-danger: #ff3333;
/* 改变 icon 字体路径变量,必需 */
$--font-path: '~element-ui/lib/theme-chalk/fonts';
@import '~element-ui/packages/theme-chalk/src/index';
// $--font-path: '~element-ui/lib/theme-chalk/fonts';
// @import '~element-ui/packages/theme-chalk/src/index';
#app {
.el-checkbox__input.is-checked + .el-checkbox__label {
......
......@@ -6,7 +6,7 @@
@change="changeHandle"
>
<el-option
v-for="(item, index) in operatorOptionList"
v-for="(item, index) in optionsList"
:key="index"
:value="item.id"
:label="item.operateName"
......@@ -29,6 +29,12 @@ export default {
default: null,
},
},
computed: {
optionsList() {
return this[`operatorOptionList${this.accessType}`];
},
},
mixins: [operatorMixin],
methods: {
changeHandle(val) {
......
import { fetchOperatorList } from '@/api/base/index';
import { fetchOperatorList, fetchAccessOperatorList } from '@/api/base/index';
import typeMapping from './typeMapping';
const FETCH_OPERATOR_LIST = 'FETCH_OPERATOR_LIST';
const FETCH_STATE = 'FETCH_STATE';
const FETCH_END = 'FETCH_END';
const state = () => ({
list: [],
fetching: false,
});
const getters = {
operatorOptionList: state => state.list,
const state = () => {
let operatorState = {};
typeMapping.map(type => {
operatorState[`list${type.value}`] = [];
operatorState[`fetching${type.value}`] = false;
});
return operatorState;
};
const initGetters = () => {
let getters = {};
typeMapping.map(type => {
getters[`operatorOptionList${type.value}`] = state =>
state[`list${type.value}`];
});
return getters;
};
const getters = initGetters();
const actions = {
fetchOperatorList({ state, commit }) {
if (state.fetching) return;
commit(FETCH_STATE, true);
return fetchOperatorList().then(res => {
const list = res.data;
commit(FETCH_OPERATOR_LIST, list);
commit(FETCH_STATE, false);
});
},
const initAction = () => {
let actions = {};
typeMapping.map(type => {
actions[`fetchOperatorList${type.value}`] = ({ state, commit }) => {
if (state[`fetching${type.value}`]) return;
commit(FETCH_STATE, type.value);
let fetchHandle = null;
if (type.value == 0) {
fetchHandle = fetchOperatorList;
} else {
fetchHandle = fetchAccessOperatorList;
}
return fetchHandle().then(res => {
const list = res.data;
commit(FETCH_OPERATOR_LIST, { type: type.value, list });
commit(FETCH_END, type.value);
});
};
});
return actions;
};
const actions = initAction();
const mutations = {
[FETCH_OPERATOR_LIST](state, list) {
state.list = list;
[FETCH_OPERATOR_LIST](state, { type, list }) {
state[`list${type}`] = list;
},
[FETCH_STATE](state, type) {
state[`fetching${type}`] = true;
},
[FETCH_STATE](state, fetching) {
state.fetching = fetching;
[FETCH_END](state, type) {
state[`fetching${type}`] = false;
},
};
......
import { mapGetters, mapActions } from 'vuex';
import store from './store';
import typeMapping from './typeMapping';
const getterList = typeMapping.map(type => `operatorOptionList${type.value}`);
const actionsList = typeMapping.map(type => `fetchOperatorList${type.value}`);
export default {
props: {
accessType: {
type: Number,
default: 0, // 0 全部 1 权限
},
},
created() {
store.install(this.$store);
},
mounted() {
if (!this.operatorOptionList.length) {
this.fetchOperatorList();
if (
this[`operatorOptionList${this.accessType}`] &&
!this[`operatorOptionList${this.accessType}`].length
) {
this[`fetchOperatorList${this.accessType}`]();
}
},
computed: {
...mapGetters('OperatoerOption', ['operatorOptionList']),
...mapGetters('OperatoerOption', getterList),
},
methods: {
...mapActions('OperatoerOption', ['fetchOperatorList']),
...mapActions('OperatoerOption', actionsList),
getOperatorName(id) {
let item = this.operatorOptionList.find(
let item = this[`operatorOptionList${this.accessType}`].find(
ServiceType => ServiceType.id === id
);
if (item) {
......
const typeMapping = [{ value: 0, label: '全部' }, { value: 1, label: '权限' }];
export default typeMapping;
......@@ -3,14 +3,28 @@
<div class="Dashboard-SearchBar">
<search-item label="运营商">
<operator-select
v-model.trim="operatorFilters.operatorId"
@input="changeConsumeOperatorHandle"
:accessType="1"
:value="filters.operatorId"
@input="val => fetchConsumeList({ operatorId: val })"
/>
</search-item>
<el-radio-group class="Dashboard-Radio">
<el-radio class="Dashboard-Radio-item">本日</el-radio>
<el-radio class="Dashboard-Radio-item">本月</el-radio>
<el-radio class="Dashboard-Radio-item">本年</el-radio>
<el-radio-group
class="Dashboard-Radio"
:value="filters.timeType"
@input="val => fetchConsumeList({ timeType: val })"
>
<el-radio
class="Dashboard-Radio-item"
:label="0"
>本日</el-radio>
<el-radio
class="Dashboard-Radio-item"
:label="1"
>本月</el-radio>
<el-radio
class="Dashboard-Radio-item"
:label="2"
>本年</el-radio>
</el-radio-group>
</div>
<CampusRankChart
......@@ -27,40 +41,17 @@ import { mapGetters, mapActions } from 'vuex';
export default {
name: 'CampusRank',
components: { CampusRankChart },
data() {
return {
operatorFilters: {
operatorId: undefined,
},
};
},
computed: {
...mapGetters('Dashboard', ['campusRankList']),
...mapGetters('Dashboard', ['campusRankList', 'filters']),
},
methods: {
...mapActions('Dashboard', ['fetchConsumeList']),
changeCampusHandle(data) {
let operateId = this.operatorFilters.operateId;
let year = this.operatorFilters.year;
let month = this.operatorFilters.month;
this.fetchConsumeList({
year: year ? year : null,
month: month ? month : null,
operateId: operateId ? operateId : null,
areaId: data.areaId,
updatePercentList: true,
});
},
changeConsumeOperatorHandle(val) {
if (val && this.operatorFilters.year) {
let month = this.operatorFilters.month;
this.fetchConsumeList({
year: this.operatorFilters.year,
month: month ? month : null,
operateId: val,
});
}
},
},
};
</script>
......@@ -6,6 +6,7 @@
<script>
import chartMixin from '../chartMixin';
import { mapActions } from 'vuex';
export default {
name: 'CampusRank',
......@@ -30,6 +31,7 @@ export default {
this.initChart();
},
methods: {
...mapActions('Dashboard', ['updateCampusName']),
initChart() {
this.chart = new G2.Chart({
container: 'CampusRank',
......@@ -64,7 +66,7 @@ export default {
></i>
<span>其他校区<br/>汇总数据</span>
</li>
`
`
: `<li
class="g2-legend-list-item item-noshow item-${index} ${checked}"
data-value="${value}"
......@@ -146,6 +148,7 @@ export default {
}
},
changeSelected(data) {
this.updateCampusName(data.areaName);
this.percentDom = document.querySelector('.CampusRank-percent');
this.areaNameDom = document.querySelector('.CampusRank-areaName');
if (this.percentDom) {
......
......@@ -2,31 +2,73 @@
<div class="Dashboard-DataCard">
<div class="Dashboard-SearchBar">
<search-item label="运营商">
<operator-select v-model.trim="customerFilter.operatorId" />
<operator-select
:accessType="1"
:value="filters.operatorId"
@input="val => getRechargeOrderList({operatorId: val})"
/>
</search-item>
<search-item label="区域">
<area-select
v-model.trim="customerFilter.areaId"
:value="filters.areaId"
@input="val => getRechargeOrderList({areaId: val})"
type="user"
/>
</search-item>
<el-radio-group class="Dashboard-Radio">
<el-radio class="Dashboard-Radio-item">本日</el-radio>
<el-radio class="Dashboard-Radio-item">本月</el-radio>
<el-radio class="Dashboard-Radio-item">本年</el-radio>
<el-radio-group
class="Dashboard-Radio"
:value="filters.timeType"
@input="val => getRechargeOrderList({timeType: val})"
>
<el-radio
class="Dashboard-Radio-item"
:label="0"
>本日</el-radio>
<el-radio
class="Dashboard-Radio-item"
:label="1"
>本月</el-radio>
<el-radio
class="Dashboard-Radio-item"
:label="2"
>本年</el-radio>
</el-radio-group>
</div>
<div class="Dashboard-title">会员消费数据</div>
<Chart
:data="consumeList"
:consumeCount="consumeCount"
/>
</div>
</template>
<script>
import Chart from './chart';
import { mapActions, mapGetters } from 'vuex';
export default {
name: 'CustomerConsumption',
data() {
return {
customerFilter: {},
};
components: {
Chart,
},
computed: {
...mapGetters('Dashboard/customerConsumption', [
'consumeList',
'consumeCount',
'filters',
]),
},
created() {
this.initData();
},
methods: {
...mapActions('Dashboard/customerConsumption', [
'updateFilters',
'getConsumeOrderList',
]),
initData() {
this.getConsumeOrderList();
},
},
};
</script>
import { getConsumeOrderList } from '@/api/dashboard/dashboard';
import initFiltersStore from '@/store/modules/filters';
import { getFilters, formatDate } from '@/utils/index';
const FETCH_CONSUME_LIST = 'FETCH_CONSUME_LIST';
const initFilters = () => ({
operatorId: undefined,
areaId: undefined,
timeType: 0, // 0 本日,1 本月, 2 本年
});
const filtersStore = initFiltersStore(initFilters);
const nowTime = new Date();
const state = () => ({
list: [],
consumeCountVo: {
payMen: 0,
payableMoney: 0,
},
});
const getters = {
consumeList: state => state.list,
consumeCount: state => state.consumeCountVo,
};
const actions = {
getConsumeOrderList({ commit, getters, dispatch }, data) {
if (data) {
dispatch('updateFilters', data);
}
let entity = getFilters(getters.filters);
switch (entity.timeType) {
case 1:
entity.month = formatDate(nowTime, 'yyyy-MM');
break;
case 2:
entity.year = formatDate(nowTime, 'yyyy');
break;
case 0:
default:
entity.date = formatDate(nowTime, 'yyyy-MM-dd');
break;
}
return getConsumeOrderList({
data: entity,
}).then(res => {
const { data, consumeCountVo } = res;
commit(FETCH_CONSUME_LIST, { list: data, consumeCountVo });
});
},
};
const mutations = {
[FETCH_CONSUME_LIST](state, data) {
state.list = data.list;
state.consumeCountVo = data.consumeCountVo;
},
};
export default {
namespaced: true,
modules: { filtersStore },
state,
getters,
actions,
mutations,
};
<template>
<div id='ActiveUserByMonth'></div>
<div id='CustomerConsumptionChart'></div>
</template>
<script>
......@@ -7,6 +7,94 @@ import G2 from '@antv/g2';
import chartMixin from '../chartMixin';
export default {
name: '',
name: 'CustomerConsumptionChart',
mixins: [chartMixin],
props: {
consumeCount: {
type: Object,
},
},
data() {
return {
totalDom: null,
countDom: null,
};
},
mounted() {
this.initChart();
},
methods: {
initChart() {
this.chart = new G2.Chart({
container: 'CustomerConsumptionChart',
forceFit: true,
height: this.height,
padding: [100, 50, 50, 100],
});
this.chart.source(this.data);
this.chart
.axis('payableMoney')
.coord()
.transpose();
// 图例
this.chart.tooltip(false).legend(false);
// 坐标系及着色
this.chartGeom = this.chart
.interval()
.position('serviceName*payableMoney')
.color('serviceName', ['#4e82fb', '#fb774e', '#ffc934', '#41bf52'])
.label('payableMoney', {
offset: 10,
});
let money =
this.consumeCount && this.consumeCount.payableMoney
? this.consumeCount.payableMoney
: '0.00';
let menCount =
this.consumeCount && this.consumeCount.payMen
? this.consumeCount.payMen
: '0';
// 辅助元素
this.chart.guide().html({
position: ['0%', '0%'],
htmlContent: `<div style="color:#333;font-size:14px;text-align: center;">
合计消费总金额:<span class="CustomerConsumption-total" style="color:#f00;">0.00元</span>
合计消费人数:<span class="CustomerConsumption-count" style="color:#f00;">0人</span>
</div>`,
alignX: 'left',
alignY: 'top',
offsetX: -40,
offsetY: -40,
});
this.chart.render();
this.chart.guide().text({
top: true,
position: ['100%', '100%'],
content: '(元)',
offsetX: 20,
offsetY: 8,
});
},
updateData() {
this.totalDom = document.querySelector('.CustomerConsumption-total');
this.countDom = document.querySelector('.CustomerConsumption-count');
if (this.totalDom && this.countDom) {
let money =
this.consumeCount && this.consumeCount.payableMoney
? this.consumeCount.payableMoney
: '0.00';
let menCount =
this.consumeCount && this.consumeCount.payMen
? this.consumeCount.payMen
: '0';
this.totalDom.innerHTML = money + '元';
this.countDom.innerHTML = menCount + '人';
}
},
},
};
</script>
......@@ -7,25 +7,39 @@
<div class="CustomerFeedbackTitle">
<span class="CustomerFeedback-icon"><img src="@/assets/images/dashboard/feedback.jpg"></span>
<span class="CustomerFeedback-text">反馈未处理-</span>
<span class="red">49</span>
<span class="red">{{count}}</span>
</div>
</div>
<div class="CustomerFeedback-List">
<div
class="CustomerFeedback-Item"
v-for="(item, index) in 10"
v-for="(item, index) in feedbackList"
:key="index"
>
<span class="CustomerFeedback-ItemNum">12</span>
<span class="CustomerFeedback-ItemName">成都师范学院</span>
<span class="CustomerFeedback-ItemNum">{{item.count}}</span>
<span class="CustomerFeedback-ItemName">{{item.areaName}}</span>
</div>
</div>
</div>
</template>
<script>
import { mapGetters, mapActions } from 'vuex';
export default {
name: 'CustomerFeedback',
computed: {
...mapGetters('Dashboard/customerFeedback', ['feedbackList', 'count']),
},
created() {
this.initData();
},
methods: {
...mapActions('Dashboard/customerFeedback', ['getFeedbackList']),
initData() {
this.getFeedbackList();
},
},
};
</script>
......
import { getFeedbackList } from '@/api/dashboard/dashboard';
const FETCH_FFEDBACK_LIST = 'FETCH_FFEDBACK_LIST';
const state = () => ({
list: [],
count: 0,
});
const getters = {
feedbackList: state => state.list,
count: state => state.count,
};
const actions = {
getFeedbackList({ commit }) {
return getFeedbackList().then(res => {
const { data, count } = res;
commit(FETCH_FFEDBACK_LIST, { list: data, count });
});
},
};
const mutations = {
[FETCH_FFEDBACK_LIST](state, data) {
state.list = data.list;
state.count = data.count;
},
};
export default {
namespaced: true,
state,
getters,
actions,
mutations,
};
<template>
<div id='ActiveUserByMonth'></div>
</template>
<script>
import G2 from '@antv/g2';
import chartMixin from '../chartMixin';
export default {
name: '',
};
</script>
<template>
<div class="Dashboard-DataCard">
<div class="Dashboard-SearchBar">
<search-item label="运营商">
<operator-select v-model.trim="customerFilter.operatorId" />
</search-item>
<el-radio-group class="Dashboard-Radio">
<el-radio class="Dashboard-Radio-item">本日</el-radio>
<el-radio class="Dashboard-Radio-item">本月</el-radio>
<el-radio class="Dashboard-Radio-item">本年</el-radio>
</el-radio-group>
</div>
<div class="Dashboard-title">会员充值数据</div>
<Chart />
</div>
</template>
<script>
import Chart from './chart';
export default {
name: 'CustomerRechange',
components: {
Chart,
},
data() {
return {
customerFilter: {},
};
},
};
</script>
<template>
<div id='CustomerRechangeChart'></div>
</template>
<script>
import G2 from '@antv/g2';
import chartMixin from '../chartMixin';
export default {
name: 'CustomerRechangeChart',
mixins: [chartMixin],
data() {
let totle = this.data.reduce((count, item) => count + item.count, 0);
return {
totle: totle,
percentDom: null,
serviceNameDom: null,
SelectedDataIndex: null,
};
},
mounted() {
this.initChart();
},
methods: {
initChart() {
this.totle = this.data.reduce((count, item) => count + item.count, 0);
this.chart = new G2.Chart({
container: 'CustomerRechangeChart',
forceFit: true,
height: this.height,
padding: [0, 130, 0, 0],
});
this.chart.source(this.data);
this.chart.coord('theta', {
radius: 0.75,
innerRadius: 0.6,
});
this.chart.tooltip(false);
// 图例
this.chart.legend({
position: 'right-center',
marker: 'square',
useHtml: true,
containerTpl: `
<div class="g2-legend">
<ul class="g2-legend-list" style="list-style-type:none;margin:0;padding:0;"></ul>
</div>`,
itemTpl: (value, color, checked, index) => {
const obj = this.data[index];
checked = checked ? 'checked' : 'unChecked';
return `
<li
class="g2-legend-list-item item-${index} ${checked}"
data-value="${value}"
data-color=${color}
style="cursor: pointer;font-size:14px;"
>
<i
class="g2-legend-marker"
style="display:inline-block;margin-right:10px;background-color:${color};"
></i>
<span class="g2-legend-text">${value}: </span>
<span>${obj.count}</span>
</li>
`;
},
'g2-legend': {
width: '130px',
// left: '-20px',
},
'g2-legend-list-item': {
height: '22px',
color: '#333',
},
'g2-legend-marker': {
width: '10px',
height: '5px',
borderRadius: 'none',
},
});
// 坐标系及着色
this.chartGeom = this.chart
.intervalStack()
.position('count')
.color('serviceName', ['#4e82fb', '#fb774e', '#ffc934', '#41bf52']);
// 辅助元素
this.chart.guide().html({
position: ['50%', '50%'],
html: `<div style="color:#333;font-size:20px;text-align: center;">
<span class="SeviceRatio-percent">0.00</span>%<br>
<span class="SeviceRatio-serviceName" style="font-size:16px;"></span>
</div>`,
alignX: 'middle',
alignY: 'middle',
});
this.chart.render();
// this.chart.on('interval:click', this.clickHandle);
},
},
};
</script>
<template>
<div class="Dashboard-DataCard">
<div class="Dashboard-SearchBar">
<search-item label="运营商">
<operator-select
:accessType="1"
:value="filters.operatorId"
@input="val => getRechargeOrderList({operatorId: val})"
/>
</search-item>
<el-radio-group
class="Dashboard-Radio"
:value="filters.timeType"
@input="val => getRechargeOrderList({timeType: val})"
>
<el-radio
class="Dashboard-Radio-item"
:label="0"
>本日</el-radio>
<el-radio
class="Dashboard-Radio-item"
:label="1"
>本月</el-radio>
<el-radio
class="Dashboard-Radio-item"
:label="2"
>本年</el-radio>
</el-radio-group>
</div>
<div class="Dashboard-title">会员充值数据</div>
<Chart
:data="rechargeList"
:rechargeCount="rechargeCount"
/>
</div>
</template>
<script>
import Chart from './chart';
import { mapGetters, mapActions } from 'vuex';
export default {
name: 'CustomerRechange',
components: {
Chart,
},
computed: {
...mapGetters('Dashboard/customerRecharge', [
'rechargeList',
'rechargeCount',
'filters',
]),
},
created() {
this.initData();
},
methods: {
...mapActions('Dashboard/customerRecharge', [
'updateFilters',
'getRechargeOrderList',
]),
initData() {
this.getRechargeOrderList();
},
},
};
</script>
import { getRechargeOrderList } from '@/api/dashboard/dashboard';
import initFiltersStore from '@/store/modules/filters';
import { getFilters, formatDate } from '@/utils';
const FETCH_RECHARGE_LIST = 'FETCH_RECHARGE_LIST';
const initFilters = () => ({
operatorId: undefined,
timeType: 0, // 0 本日,1 本月, 2 本年
});
const filtersStore = initFiltersStore(initFilters);
const nowTime = new Date();
const state = () => ({
list: [],
rechargeOrderCountVo: {
rechargeMen: 0,
rechargeMoney: 0,
},
});
const getters = {
rechargeList: state => state.list,
rechargeCount: state => state.rechargeOrderCountVo,
};
const actions = {
getRechargeOrderList({ commit, getters, dispatch }, data) {
if (data) {
dispatch('updateFilters', data);
}
let entity = getFilters(getters.filters);
switch (entity.timeType) {
case 1:
entity.month = formatDate(nowTime, 'yyyy-MM');
break;
case 2:
entity.year = formatDate(nowTime, 'yyyy');
break;
case 0:
default:
entity.date = formatDate(nowTime, 'yyyy-MM-dd');
break;
}
return getRechargeOrderList({
data: entity,
}).then(res => {
const { data, rechargeOrderCountVo } = res;
commit(FETCH_RECHARGE_LIST, { list: data, rechargeOrderCountVo });
});
},
};
const mutations = {
[FETCH_RECHARGE_LIST](state, data) {
state.list = data.list;
state.rechargeOrderCountVo = data.rechargeOrderCountVo;
},
};
export default {
namespaced: true,
modules: { filtersStore },
state,
getters,
actions,
mutations,
};
<template>
<div class="CustomerRechangeChart">
<div class="CustomerRechangeChart-tip">
<span
class="CustomerRechange-tooltip"
style="background-color:#4e82fb;"
></span> 累计充值:
<span style="color:#f00;">{{ rechargeCount && rechargeCount.rechargeMoney ? (rechargeCount.rechargeMoney).toFixed(2) : '0.00' }}</span>
<span
class="CustomerRechange-tooltip"
style="background-color:#26c9a8;"
></span> 充值人数:
<span style="color:#f00;">{{ rechargeCount && rechargeCount.rechargeMen ? rechargeCount.rechargeMen : 0 }}</span>
</div>
<div id='CustomerRechangeChart'></div>
<div id='CustomerRechangeSlider'></div>
</div>
</template>
<script>
import G2 from '@antv/g2';
import Slider from '@antv/g2-plugin-slider';
import chartMixin from '../chartMixin';
import DataSet from '@antv/data-set';
import _ from 'lodash';
export default {
name: 'CustomerRechangeChart',
mixins: [chartMixin],
props: {
rechargeCount: {
type: Object,
},
},
data() {
return {
demoList: [
{ areaName: 'bvs', rechargeMoney: 13.45 },
{ areaName: 'gsn', rechargeMoney: 23.45 },
{ areaName: 'dsf', rechargeMoney: 12.45 },
{ areaName: 'bzx', rechargeMoney: 45.45 },
{ areaName: 'bvmf', rechargeMoney: 3213.454 },
{ areaName: 'swer', rechargeMoney: 256 },
{ areaName: 'cvsdf', rechargeMoney: 541 },
{ areaName: 'cvsdf', rechargeMoney: 10.15 },
],
slider: null,
sliderDom: null,
ds: null,
dv: null,
SelectedDataIndex: null,
};
},
methods: {
initData() {
this.ds = new DataSet({
state: {
startRadio: 0,
endRadio: 0.2,
},
});
const dv = this.ds.createView().source(this.data);
this.dv = dv;
dv.transform({
type: 'fold',
fields: ['rechargeMoney', 'rechargeMen'], // 展开字段集
key: 'key', // key字段
value: 'value', // value字段
retains: ['areaName'],
}).transform({
type: 'map',
callback(row) {
if (row.key === 'rechargeMoney') {
row.key = '充值金额';
}
if (row.key === 'rechargeMen') {
row.key = '充值人数';
}
return row;
},
});
dv.transform({
type: 'filter',
callback: obj => {
if (
_.isNil(this.ds.state.startRadio) ||
_.isNil(this.ds.state.endRadio)
) {
return true;
}
const dataIndex = _.findIndex(this.data, {
areaName: obj.areaName,
});
const currentRadio = dataIndex / this.data.length;
return (
currentRadio >= this.ds.state.startRadio &&
currentRadio <= this.ds.state.endRadio
);
},
});
this.chart = new G2.Chart({
container: 'CustomerRechangeChart',
forceFit: true,
height: this.height,
padding: [100, 50, 50, 120],
});
this.chart.source(dv);
this.chart.axis('areaName', {
label: {
offset: 5,
},
});
this.chart.axis('value');
this.chart.coord().transpose();
this.chart.legend(false);
// 坐标系及着色
this.chartGeom = this.chart
.interval()
.position('areaName*value')
.color('key', ['#4e82fb', '#26c9a8'])
.adjust([
{
type: 'dodge',
marginRatio: 0.2,
},
]);
this.chart.render();
this.initSlider();
},
initSlider() {
// const arrLength = this.data.length;
// let spanNum = 4 / arrLength;
this.sliderDom = document.getElementById('CustomerRechangeSlider');
this.slider = new Slider({
container: this.sliderDom, // dom 容器 id 或者 dom 容器对象
width: 26, // slider 的宽度,默认为 'auto',即自适应宽度
height: 300, // slider 的高度,默认为 '26px'
xAxis: 'value', // 背景图的横轴对应字段,同时为数据筛选的字段
yAxis: 'areaName', // 背景图的纵轴对应字段
startRadio: this.ds.state.startRadio, // 新增
endRadio: this.ds.state.endRadio, // 新增
data: this.data, // slider 的数据源
textStyle: {
display: 'none',
},
handleStyle: {
img: require('@/assets/images/dashboard/QXtfhORGlDuRvLXFzpsQ.png'),
width: 26,
height: 5,
},
onChange: ({ startRadio, endRadio }) => {
this.ds.setState('startRadio', startRadio);
this.ds.setState('endRadio', endRadio);
}, // 更新数据状态量的回调函数
});
this.slider.layout = 'vertical';
this.slider.render(); // 渲染
},
updateSlider() {
let dom = this.sliderDom;
dom.innerHTML = '';
const arrLength = this.data.length;
if (arrLength) {
// let spanNum = 4 / arrLength;
this.slider = new Slider({
container: this.sliderDom,
width: 26,
height: 300,
xAxis: 'value',
yAxis: 'areaName',
startRadio: this.ds.state.startRadio,
endRadio: this.ds.state.endRadio,
data: this.data,
// minSpan: spanNum,
// maxSpan: spanNum,
handleStyle: {
img: require('@/assets/images/dashboard/QXtfhORGlDuRvLXFzpsQ.png'),
width: 26,
height: 5,
},
onChange: ({ startRadio, endRadio }) => {
this.ds.setState('startRadio', startRadio);
this.ds.setState('endRadio', endRadio);
},
});
this.slider.layout = 'vertical';
this.slider.render();
}
},
refreshData() {
if (this.chart) {
this.dv.source(this.data);
console.log(this.slider);
this.updateSlider();
this.updateData();
} else {
this.initData();
this.initSlider();
}
},
},
};
</script>
<style lang="scss">
.CustomerRechangeChart {
position: relative;
#CustomerRechangeSlider {
margin-left: -30px;
width: 100%;
height: 300px;
transform: translate(100%, -100%);
}
.CustomerRechangeChart-tip {
position: absolute;
left: 80px;
top: 60px;
color: #333;
font-size: 14px;
text-align: center;
}
.CustomerRechange-tooltip {
display: inline-block;
width: 10px;
height: 5px;
margin-bottom: 2px;
margin-left: 20px;
}
}
</style>
......@@ -3,15 +3,30 @@ import {
fetchReportList,
fetchTitleList,
} from '@/api/dashboard/dashboard';
import customerRecharge from './CustomerRecharge/CustomerRechargeStore';
import customerConsumption from './CustomerConsumption/CustomerConsumptionStore';
import customerFeedback from './CustomerFeedback/CustomerFeedbackStore';
import initFiltersStore from '@/store/modules/filters';
import { getFilters, formatDate } from '@/utils/index';
const GET_CONSUME_DATA = 'GET_CONSUME_DATA';
const GET_REPORT_DATA = 'GET_REPORT_DATA';
const GET_TITLE_DATA = 'GET_TITLE_DATA';
const GET_PERCENT_DATA = 'GET_PERCENT_DATA';
const GET_EUIPMENT_DATA = 'GET_EUIPMENT_DATA';
const GET_CAMPUS_DATA = 'GET_CAMPUS_DATA';
const initFilters = () => ({
operatorId: undefined,
timeType: 0, // 0 本日,1 本月, 2 本年
});
const filtersStore = initFiltersStore(initFilters);
const nowTime = new Date();
const state = () => ({
consume: {
areaName: '',
consumeAfterVos: [],
consumeHardVos: [],
list: [],
percentList: [],
......@@ -37,24 +52,50 @@ const getters = {
registePerMonth: state => state.report.registePerMonth,
activeUser: state => state.report.activeUser,
titleData: state => state.title,
areaName: state => state.consume.areaName,
consumeAfterVos: state => state.consume.consumeAfterVos,
consumeHardVos: state => state.consume.consumeHardVos,
campusRankList: state => state.consume.list,
seviceRatioList: state => state.consume.percentList,
};
const actions = {
fetchConsumeList({ commit }, entity) {
fetchConsumeList({ commit, dispatch, getters }, data) {
if (data) {
const { timeType, operatorId } = data;
(timeType || timeType == 0) &&
dispatch('updateFilters', {
timeType: timeType,
});
operatorId &&
dispatch('updateFilters', {
operatorId,
});
}
let entity = getFilters(getters.filters);
switch (entity.timeType) {
case 1:
entity.month = formatDate(nowTime, 'yyyy-MM');
break;
case 2:
entity.year = formatDate(nowTime, 'yyyy');
break;
case 0:
default:
entity.date = formatDate(nowTime, 'yyyy-MM-dd');
break;
}
return fetchConsumeList({
params: {
data: {
...entity,
},
}).then(res => {
let list = res.data;
if (entity && entity.updatePercentList) {
if (data && data.updatePercentList) {
commit(GET_PERCENT_DATA, list);
return;
}
if (entity && entity.updateEuipmentList) {
if (data && data.updateEuipmentList) {
commit(GET_EUIPMENT_DATA, list);
return;
}
......@@ -77,19 +118,28 @@ const actions = {
commit(GET_TITLE_DATA, data);
});
},
updateCampusName({ commit }, name) {
commit(GET_CAMPUS_DATA, name);
},
};
const mutations = {
[GET_CONSUME_DATA]: (state, data) => {
state.consume.consumeHardVos = data.consumeHardVos;
state.consume.list = data.list;
state.consume.percentList = data.percentList;
state.consume.consumeHardVos = data.consumeHardVos
? data.consumeHardVos
: [];
state.consume.consumeAfterVos = data.consumeAfterVos
? data.consumeAfterVos
: [];
state.consume.list = data.list ? data.list : [];
state.consume.percentList = data.percentList ? data.percentList : [];
},
[GET_PERCENT_DATA]: (state, data) => {
state.consume.percentList = data.percentList;
},
[GET_EUIPMENT_DATA]: (state, data) => {
state.consume.consumeHardVos = data.consumeHardVos;
state.consume.consumeAfterVos = data.consumeAfterVos;
},
[GET_REPORT_DATA]: (state, data) => {
state.report.year = data.year;
......@@ -107,10 +157,19 @@ const mutations = {
[GET_TITLE_DATA](state, data) {
state.title = data;
},
[GET_CAMPUS_DATA](state, name) {
state.consume.areaName = name;
},
};
export default {
namespaced: true,
modules: {
filtersStore,
customerRecharge,
customerConsumption,
customerFeedback,
},
state,
getters,
actions,
......
......@@ -2,7 +2,7 @@
<div>
<div class="Dashboard">
<div class="Dashboard-Row Dashboard-CustomerData">
<CustomerRechange class="Dashboard-DataCard" />
<CustomerRecharge class="Dashboard-DataCard" />
<CustomerConsumption class="Dashboard-DataCard" />
<CustomerFeedback class="Dashboard-DataCard" />
</div>
......@@ -22,7 +22,7 @@ import CustomerRegister from './CustomerRegister/CustomerRegister';
import SeviceRatio from './SeviceRatio/SeviceRatio';
import CampusRank from './CampusRank/CampusRank';
import EquipmentList from './EquipmentList/EquipmentList';
import CustomerRechange from './CustomerRechange/CustomerRechange';
import CustomerRecharge from './CustomerRecharge/CustomerRecharge';
import CustomerConsumption from './CustomerConsumption/CustomerConsumption';
import CustomerFeedback from './CustomerFeedback/CustomerFeedback';
......@@ -30,7 +30,7 @@ export default {
name: 'Dashboard',
components: {
CustomerRegister,
CustomerRechange,
CustomerRecharge,
CustomerConsumption,
CustomerFeedback,
SeviceRatio,
......
......@@ -10,7 +10,7 @@
<el-radio-button :label="false">后五名</el-radio-button>
</el-radio-group>
</div>
<EquipmentListChart :data="consumeHardVos" />
<EquipmentListChart :data="showTopFive?consumeHardVos:consumeAfterVos" />
</div>
</template>
......@@ -29,7 +29,7 @@ export default {
};
},
computed: {
...mapGetters('Dashboard', ['consumeHardVos']),
...mapGetters('Dashboard', ['consumeHardVos', 'consumeAfterVos']),
},
methods: {
...mapActions('Dashboard', ['fetchConsumeList']),
......
......@@ -11,6 +11,7 @@
:data="seviceRatioList"
:changeServiceHandle="changeServiceHandle"
/>
<div class="SeviceRatio-title">{{areaName}}</div>
</div>
</template>
......@@ -20,22 +21,13 @@ import { mapActions, mapGetters } from 'vuex';
export default {
name: 'SeviceRatio',
components: { SeviceRatioChart },
data() {
return {};
},
computed: {
...mapGetters('Dashboard', ['seviceRatioList']),
...mapGetters('Dashboard', ['seviceRatioList', 'areaName']),
},
methods: {
...mapActions('Dashboard', ['fetchConsumeList']),
changeServiceHandle(data) {
let operateId = this.operatorFilters.operateId;
let year = this.operatorFilters.year;
let month = this.operatorFilters.month;
this.fetchConsumeList({
year: year ? year : null,
month: month ? month : null,
operateId: operateId ? operateId : null,
serviceId: data.serviceId,
updateEuipmentList: true,
});
......@@ -43,3 +35,11 @@ export default {
},
};
</script>
<style lang="scss">
.SeviceRatio-title {
font-size: 16px;
text-align: center;
padding-bottom: 12px;
}
</style>
<template>
<div class="SeviceRatio">
<div id="SeviceRatio"></div>
<div class="SeviceRatio-title">成都师范学院</div>
</div>
<div id="SeviceRatio"></div>
</template>
<script>
......@@ -61,7 +58,7 @@ export default {
class="g2-legend-list-item item-${index} ${checked}"
data-value="${value}"
data-color=${color}
style="cursor: pointer;font-size:14px;"
style="cursor: pointer;font-size:14px;width:100%;"
>
<i
class="g2-legend-marker"
......@@ -108,15 +105,9 @@ export default {
updateData() {
if (this.data.length) {
this.totle = this.data.reduce((count, item) => count + item.count, 0);
let data = this.SelectedDataIndex;
if (data) {
this.changeSelected(data);
this.chartGeom.setSelected(data);
} else {
this.chartGeom.setSelected(this.data[0]);
this.changeSelected(this.data[0]);
this.SelectedDataIndex = this.data[0];
}
this.chartGeom.setSelected(this.data[0]);
this.changeSelected(this.data[0]);
this.SelectedDataIndex = this.data[0];
}
},
clickHandle(ev) {
......@@ -143,11 +134,3 @@ export default {
},
};
</script>
<style lang="scss">
.SeviceRatio-title {
font-size: 16px;
text-align: center;
padding-bottom: 12px;
}
</style>
......@@ -137,9 +137,9 @@ export default {
height: auto;
}
.Submenu-title {
height: 46px;
line-height: 46px;
font-size: 18px;
height: 30px;
line-height: 30px;
font-size: 14px;
color: #78fcf3;
.like-icon {
margin-left: 10px;
......@@ -152,9 +152,9 @@ export default {
.Submenu-link {
display: inline-block;
min-width: 140px;
height: 40px;
line-height: 40px;
font-size: 18px;
height: 28px;
line-height: 28px;
font-size: 14px;
color: #fff;
}
.like-icon {
......@@ -163,8 +163,8 @@ export default {
}
.like-icon {
display: inline-block;
width: 16px;
height: 16px;
width: 12px;
height: 12px;
}
.like-icon,
.Submenu-link-text {
......@@ -188,6 +188,22 @@ export default {
}
.SubmenuBox {
top: 80px;
.Submenu-title {
height: 46px;
line-height: 46px;
font-size: 18px;
}
.Submenu-list {
.Submenu-link {
height: 40px;
line-height: 40px;
font-size: 18px;
}
}
.like-icon {
width: 16px;
height: 16px;
}
}
}
}
......
......@@ -532,17 +532,17 @@ export default {
width: 148px;
background-color: #68aaff;
border-radius: 8px;
font-size: 18px;
font-size: 14px;
overflow: hidden;
.UserMenus-Link {
cursor: pointer;
height: 46px;
line-height: 46px;
height: 30px;
line-height: 30px;
display: flex;
align-items: center;
.UserMenus-Link-icon {
width: 16px;
height: 16px;
width: 12px;
height: 12px;
margin: 0 10px 0;
}
}
......@@ -627,7 +627,16 @@ export default {
line-height: 30px;
font-size: 24px;
.UserMenus {
font-size: 18px;
top: 35px;
.UserMenus-Link {
height: 46px;
line-height: 46px;
.UserMenus-Link-icon {
width: 16px;
height: 16px;
}
}
}
}
.avatar-quit {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment