Commit 98d1cbb6 by 姜雷

Merge branch 'test' into 'master'

Test See merge request !28
parents 525b3cd9 1cd3cd59
......@@ -3,9 +3,11 @@ VUE_APP_LIB_BASE_URL=/systemManageShell/
VUE_APP_CUSTOMER_MENU_CODE=0001
VUE_APP_BASE_MENU_CODE=0002
VUE_APP_SYSTEM_MENU_CODE=0003
VUE_APP_DASHBOARD_CODE=0079
VUE_APP_WHITE_LIST=/login,/404,/401
VUE_APP_LIB_MANAGER=http://ex-dev-dcxy-static.168cad.top
VUE_APP_SYSTEM_SERVER_URL=http://ex-dev-dcxy-system-manage.168cad.top
VUE_APP_CUSTOMER_SERVER_URL=http://ex-dev-customer-manage.168cad.top
VUE_APP_BASE_SERVER_URL=http://ex-dev-dcxy-base-manage.168cad.top
VUE_APP_BASE_SERVER_URL_APP=http://ex-dev-dcxy-base-app.168cad.top
VUE_APP_STATISTICS_SERVER_URL=http://ex-dev-dcxy-statistics-manage.168cad.top
......@@ -4,3 +4,4 @@ VUE_APP_SYSTEM_SERVER_URL=http://ex-dev-dcxy-system-manage.168cad.top
VUE_APP_CUSTOMER_SERVER_URL=http://ex-dev-customer-manage.168cad.top
VUE_APP_BASE_SERVER_URL=http://ex-dev-dcxy-base-manage.168cad.top
VUE_APP_BASE_SERVER_URL_APP=http://ex-dev-dcxy-base-app.168cad.top
VUE_APP_STATISTICS_SERVER_URL=http://ex-dev-dcxy-statistics-manage.168cad.top
......@@ -3,3 +3,4 @@ VUE_APP_SYSTEM_SERVER_URL=http://ex-test-dcxy-system-manage.168cad.top
VUE_APP_CUSTOMER_SERVER_URL=http://ex-test-customer-manage.168cad.top
VUE_APP_BASE_SERVER_URL=http://ex-test-dcxy-base-manage.168cad.top
VUE_APP_BASE_SERVER_URL_APP=http://ex-test-dcxy-base-app.168cad.top
VUE_APP_STATISTICS_SERVER_URL=http://ex-test-dcxy-statistics-manage.168cad.top
const removeConsolePlugin = [];
if (process.env.NODE_ENV === 'production') {
removeConsolePlugin.push('transform-remove-console');
}
module.exports = {
presets: ['@vue/app'],
plugins: removeConsolePlugin,
};
......@@ -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",
......@@ -44,6 +46,7 @@
"@vue/eslint-config-prettier": "^4.0.1",
"babel-eslint": "^10.0.1",
"babel-plugin-component": "^1.1.1",
"babel-plugin-transform-remove-console": "^6.9.4",
"cross-env": "^5.2.0",
"eslint": "^5.8.0",
"eslint-plugin-vue": "^5.0.0",
......
......@@ -5,13 +5,13 @@
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
<link rel="icon" href="<%= BASE_URL %>favicon.ico" />
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuex@3.1.0/dist/vuex.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vue-router@3.0.1/dist/vue-router.js"></script>
<script src="<%= VUE_APP_LIB_MANAGER %><%= VUE_APP_LIB_BASE_URL %>lib/manageShell.umd.js"></script>
<script src="<%= VUE_APP_LIB_MANAGER %>/customerManage/lib/customerManage.umd.js"></script>
<script src="<%= VUE_APP_LIB_MANAGER %>/baseManage/lib/baseManage.umd.js"></script>
<script src="<%= VUE_APP_LIB_MANAGER %>/systemManage/lib/systemManage.umd.js"></script>
<script src="https://unpkg.com/vue@2.6.10/dist/vue.js"></script>
<script src="https://unpkg.com/vuex@3.1.0/dist/vuex.js"></script>
<script src="https://unpkg.com/vue-router@3.0.1/dist/vue-router.js"></script>
<script src="<%= VUE_APP_LIB_MANAGER %><%= VUE_APP_LIB_BASE_URL %>lib/manageShell.umd.min.js"></script>
<script src="<%= VUE_APP_LIB_MANAGER %>/customerManage/lib/customerManage.umd.min.js"></script>
<script src="<%= VUE_APP_LIB_MANAGER %>/baseManage/lib/baseManage.umd.min.js"></script>
<script src="<%= VUE_APP_LIB_MANAGER %>/systemManage/lib/systemManage.umd.min.js"></script>
<link
rel="stylesheet"
href="<%= VUE_APP_LIB_MANAGER %><%= VUE_APP_LIB_BASE_URL %>lib/manageShell.css"
......
<template>
<div id="app">
<app-layout v-if="isInnerPage">
<app-layout
v-if="isInnerPage"
:title="title"
>
<div
class="AppTopBar"
slot="title"
......@@ -10,6 +13,7 @@
:routers="allRoutes"
:dashboardVisiable="dashboardVisiable"
:toggleFastLink="toggleFastLink"
:selectRouteMenu="selectRouteMenu"
/>
</div>
<template v-if="dashboardVisiable">
......@@ -25,6 +29,10 @@
:routers="route"
/>
<sidebar-nav
v-else-if="showSelected"
:routers="selectedRoute"
/>
<sidebar-nav
v-else
:routers="route"
/>
......@@ -45,8 +53,7 @@
import UserBox from './containers/layout/components/UserBox';
import IconMenus from './containers/layout/IconMenu/IconMenu';
import CollectMenu from './containers/layout/Collect/Collect';
const whiteList = process.env.VUE_APP_WHITE_LIST.split(','); // 不重定向白名单
import { mapGetters } from 'vuex';
export default {
name: 'App',
......@@ -63,12 +70,16 @@ export default {
},
data() {
return {
title: '',
selectedRoute: [],
routerDone: false,
isInnerPage: true,
showFastLink: false,
showSelected: false,
};
},
computed: {
...mapGetters(['whiteList']),
dashboardVisiable() {
return this.$route.name === 'dashboard';
},
......@@ -79,6 +90,7 @@ export default {
} else {
this.showComponents();
}
this.title = document.title;
},
watch: {
$route(newVal) {
......@@ -93,7 +105,7 @@ export default {
methods: {
showComponents(store) {
this.isInnerPage =
whiteList.indexOf(this.$route.path) !== -1 ? false : true;
this.whiteList.indexOf(this.$route.path) !== -1 ? false : true;
if (store) {
store.install(this.$store);
this.$nextTick(vm => {
......@@ -104,7 +116,20 @@ export default {
}
},
toggleFastLink(entity) {
if (entity) {
this.$router.push({
path: '/',
});
this.showFastLink = entity;
} else {
this.showFastLink = entity;
this.showSelected = false;
}
},
selectRouteMenu(routes) {
this.selectedRoute = routes;
this.showSelected = true;
this.showFastLink = false;
},
},
};
......
......@@ -8,7 +8,12 @@ export const fetchAreaList = req =>
method: 'get',
...req,
});
export const fetchAccessAreaList = req =>
fetch({
url: path + '/dcxy/baseArea/queryAll',
method: 'get',
...req,
});
//获取下拉列表
export const fetchSelectList = req =>
fetch({
......@@ -31,6 +36,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({
......
......@@ -23,7 +23,7 @@ const createBaseFetch = config => {
},
error => {
// Do something with request error
console.log(error); // for debug
console.error(error); // for debug
Promise.reject(error);
}
);
......@@ -70,7 +70,7 @@ const createBaseFetch = config => {
});
})
.catch(err => {
console.log(err);
console.error(err);
logout = false;
});
}
......
import fetch from '../fetch';
const path = process.env.VUE_APP_CUSTOMER_SERVER_URL;
const path = process.env.VUE_APP_STATISTICS_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,
});
......@@ -39,7 +39,7 @@ service.interceptors.request.use(
},
error => {
// Do something with request error
console.log(error); // for debug
console.error(error); // for debug
Promise.reject(error);
}
);
......@@ -85,7 +85,7 @@ service.interceptors.response.use(response => {
});
})
.catch(err => {
console.log(err);
console.error(err);
logout = false;
});
}
......
......@@ -27,7 +27,7 @@ export const validateCode = res => {
};
export const errorHandle = error => {
console.log('err' + error); // for debug
console.error('err' + error); // for debug
store.dispatch('fetchDone');
if (error.message && /timeout/.test(error.message)) {
Message({
......
......@@ -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 {
......@@ -65,6 +65,7 @@ $--font-path: '~element-ui/lib/theme-chalk/fonts';
height: 40px;
line-height: 40px;
}
.el-input__icon,
.el-cascader,
.el-input__inner,
.el-button {
......@@ -89,6 +90,7 @@ $--font-path: '~element-ui/lib/theme-chalk/fonts';
height: 56px;
line-height: 56px;
}
.el-input__icon,
.el-cascader,
.el-input__inner,
.el-button {
......
......@@ -24,6 +24,11 @@ export default {
value: Number,
},
mixins: [areaMixin],
computed: {
areaList() {
return this[`areaList${this.accessType}`];
},
},
methods: {
changeHandle(val) {
if (val) {
......
import { fetchAreaList } from '@/api/base/index';
import { fetchAreaList, fetchAccessAreaList } from '@/api/base/index';
import typeMapping from './typeMapping';
const GET_AREA_LIST = 'GET_AREA_LIST';
const FETCH_STATE = 'FETCH_STATE';
const FETCH_END = 'FETCH_END';
const state = () => ({
list: [],
fetching: false,
});
const state = () => {
let areaState = {};
typeMapping.map(type => {
areaState[`list${type.value}`] = [];
areaState[`fetching${type.value}`] = false;
});
return areaState;
};
const getters = {
areaList: state => state.list,
const initGetters = () => {
let getters = {};
typeMapping.map(type => {
getters[`areaList${type.value}`] = state => state[`list${type.value}`];
});
return getters;
};
const getters = initGetters();
const actions = {
fetchAreaList({ state, commit }) {
if (state.fetching) return;
commit(FETCH_STATE, true);
return fetchAreaList().then(res => {
const initAction = () => {
let actions = {};
typeMapping.map(type => {
actions[`fetchAreaList${type.value}`] = ({ state, commit }) => {
if (state[`fetching${type.value}`]) return;
commit(FETCH_STATE, type.value);
let fetchHandle = null;
if (type.value == 0) {
fetchHandle = fetchAreaList;
} else {
fetchHandle = fetchAccessAreaList;
}
return fetchHandle().then(res => {
const list = res.data;
commit(GET_AREA_LIST, list);
commit(FETCH_STATE, false);
commit(GET_AREA_LIST, { type: type.value, list });
commit(FETCH_END, type.value);
});
},
};
});
return actions;
};
const actions = initAction();
const mutations = {
[GET_AREA_LIST](state, list) {
state.list = list;
[GET_AREA_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 => `areaList${type.value}`);
const actionsList = typeMapping.map(type => `fetchAreaList${type.value}`);
export default {
props: {
accessType: {
type: Number,
default: 1, // 0 全部 1 权限
},
},
created() {
store.install(this.$store);
},
mounted() {
if (!this.areaList.length) {
this.fetchAreaList();
if (
this[`areaList${this.accessType}`] &&
!this[`areaList${this.accessType}`].length
) {
this[`fetchAreaList${this.accessType}`]();
}
// if (!this.areaList.length) {
// this.fetchAreaList();
// }
},
computed: {
...mapGetters('area', ['areaList']),
...mapGetters('area', getterList),
},
methods: {
...mapActions('area', ['fetchAreaList']),
...mapActions('area', actionsList),
getAreaName(id) {
let item = this.areaList.find(area => area.id === id);
let item = this[`areaList${this.accessType}`].find(
area => area.id === id
);
if (item) {
return item.areaName;
} else {
return '';
}
},
getAreaOperatorInfo(id) {
let item = this[`areaList${this.accessType}`].find(
area => area.id === id
);
if (item) {
return {
rechargeOperateId: item.rechargeOperateId,
rechargeOperateName: item.rechargeOperateName,
};
} else {
return null;
}
},
},
};
const typeMapping = [{ value: 0, label: '全部' }, { value: 1, label: '权限' }];
export default typeMapping;
......@@ -64,11 +64,11 @@ export default {
insert(this.IMG_URL + response.key);
};
let _error = err => {
console.log(err);
console.error(err);
if (err.isRequestError && err.code == 401) {
this.getImgToken(true);
} else {
console.log(err);
console.error(err);
this.$refs.input.value = '';
}
};
......
<template>
<search-item
label="赠送类型"
:size="size"
>
<el-select
clearable
filterable
:value="value"
@change="changeHandle"
>
<el-option
v-for="(item, index) in giveTypeList"
:key="index"
:value="item.value"
:label="item.name"
></el-option>
</el-select>
</search-item>
</template>
<script>
import { mapGetters, mapActions } from 'vuex';
export default {
name: 'give-select',
props: {
size: {
type: String,
default: '',
},
value: {
type: String,
default: '',
},
},
mounted() {
if (!this.giveTypeList.length) {
this.fetchGiveTypeList();
}
},
computed: {
...mapGetters(['giveTypeList']),
},
methods: {
...mapActions(['fetchGiveTypeList']),
changeHandle(val) {
this.$emit('input', val);
},
getTypeName(val){
let item = this.giveTypeList.find(({value}) => val === value);
return item?item.name: '';
}
},
};
</script>
......@@ -172,14 +172,14 @@ export default {
});
})
.catch(err => {
console.log('删除错误:' + err.msg);
console.error('删除错误:' + err.msg);
});
},
_error(err) {
if (err.isRequestError && err.code == '401') {
this.getImgToken(true);
} else {
console.log(err);
console.error(err);
this.$refs.input.value = '';
}
},
......
......@@ -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 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, list);
commit(FETCH_STATE, false);
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;
......@@ -13,6 +13,10 @@ export default {
type: Number,
default: null,
},
isAll: {
type: Number,
default: null,
},
},
created() {
store.install(this.$store);
......@@ -20,6 +24,7 @@ export default {
this.fetchServiceTypeList({
serviceType: this.ServiceType,
serviceState: this.ServiceState,
isAll: this.isAll,
});
}
},
......
......@@ -21,11 +21,14 @@ const getters = {
};
const actions = {
fetchServiceTypeList({ state, commit }, { serviceType, serviceState }) {
fetchServiceTypeList(
{ state, commit },
{ serviceType, serviceState, isAll }
) {
if (state.fetching[serviceType]) return;
commit(FETCH_STATE, { value: true, index: serviceType });
return fetchServiceList({
params: { serviceType, state: serviceState },
params: { serviceType, state: serviceState, isAll: isAll },
}).then(res => {
const { selfService } = res;
commit(SERVICE_TYPE_LIST, { list: selfService, index: serviceType });
......
<template>
<div
class="Dashboard-DataCard"
v-loading="loading.area"
>
<div class="Dashboard-SearchBar">
<search-item label="运营商">
<operator-select
:accessType="1"
:value="filters.operateId"
@input="val => changeFilterHandle({ operateId: val })"
/>
</search-item>
<el-radio-group
class="Dashboard-Radio"
:value="filters.timeType"
@input="val => changeFilterHandle({ 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
:data="campusRankList"
:changeCampusHandle="changeCampusHandle"
:updateCampusName="updateCampusName"
:showOtherNameHandle="showOtherNameHandle"
/>
<div class="SeviceRatio-title">{{showOther?'其他校区':areaInfo.areaName}}</div>
</div>
</template>
<script>
import CampusRankChart from './CampusRankChart';
import { mapGetters, mapActions } from 'vuex';
export default {
name: 'CampusRank',
components: { CampusRankChart },
computed: {
...mapGetters('Dashboard', [
'campusRankList',
'filters',
'areaInfo',
'loading',
]),
},
data() {
return { showOther: false };
},
methods: {
...mapActions('Dashboard', [
'fetchConsumeList',
'updateCampusName',
'updateFilters',
]),
changeFilterHandle(data) {
this.updateFilters(data);
this.fetchConsumeList();
},
changeCampusHandle(data) {
this.fetchConsumeList({
areaId: data.areaId,
updatePercentList: true,
}).then(() => {
this.updateCampusName(data);
this.showOther = false;
});
},
showOtherNameHandle() {
this.showOther = true;
},
},
};
</script>
<template>
<div class="CampusRank">
<div id="CampusRank"></div>
<div class="CampusRank-title">截至到{{ today }},前十校区消费汇总数据</div>
<div id="CampusRank">
<div class="CampusRank-legend">
<template v-if="this.data && this.data.length === 10">
其他校区:<span>{{other.toFixed(2)}}</span><br>
前十校区:<span>{{topTen.toFixed(2)}}</span>
</template>
<template v-else-if="this.data.length">
校区汇总:<span>{{topTen.toFixed(2)}}</span>
</template>
</div>
</div>
</div>
</template>
<script>
import chartMixin from '../chartMixin';
import { mapActions } from 'vuex';
export default {
name: 'CampusRank',
name: 'CampusRankChart',
mixins: [chartMixin],
props: {
changeCampusHandle: {
type: Function,
},
changeCampusHandle: { type: Function },
updateCampusName: { type: Function },
showOtherNameHandle: { type: Function },
},
data() {
let totle = this.data.reduce((count, item) => count + item.count, 0);
let total = this.data.reduce((count, item) => count + item.count, 0);
return {
chart: null,
today: this.$formatDate(new Date(), 'yyyy-MM-dd'),
totle: totle,
total: total,
topTen: 0,
other: 0,
percentDom: null,
areaNameDom: null,
SelectedDataIndex: null,
......@@ -36,7 +48,7 @@ export default {
container: 'CampusRank',
forceFit: true,
height: this.height,
padding: [0, 130, 0, 0],
padding: [20, 160, 20, 0],
});
this.chart.source(this.data);
this.chart.coord('theta', {
......@@ -45,54 +57,7 @@ export default {
});
this.chart.tooltip(false);
// 图例
this.chart.legend({
position: 'right-center',
marker: 'square',
useHtml: true,
itemTpl: (value, color, checked, index) => {
const obj = this.data[index];
return obj.type === '8'
? `
<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="background-color:${color};"
></i>
<span>其他校区<br/>汇总数据</span>
</li>
`
: `<li
class="g2-legend-list-item item-noshow item-${index} ${checked}"
data-value="${value}"
data-color=${color}
>
<i
class="g2-legend-marker"
style="background-color:${color};"
></i>
${value}
</li>`;
},
'g2-legend-list-item': {
display: 'flex',
// color: '#333',
},
'g2-legend-marker': {
width: '10px',
height: '5px',
borderRadius: 'none',
marginTop: '8px',
marginRight: '10px',
},
'item-noshow': {
display: 'none',
},
});
this.chart.legend(false);
// 坐标系及着色
this.chartGeom = this.chart
.intervalStack()
......@@ -116,7 +81,6 @@ export default {
position: ['50%', '50%'],
html: `<div style="color:#333;font-size: 20px;text-align: center;width: 4em;">
<span class="CampusRank-percent">0.00</span>%<br>
<span class="CampusRank-areaName" style="font-size:16px;"></span>
</div>`,
alignX: 'middle',
alignY: 'middle',
......@@ -126,16 +90,23 @@ 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);
this.total = this.data.reduce((count, item) => count + item.count, 0);
if (this.data.length === 10) {
this.other = this.data[this.data.length - 1].count;
this.topTen = this.total - this.other;
} else {
this.other = 0;
this.topTen = this.total;
}
let data = this.SelectedDataIndex;
this.chartGeom.setSelected(this.data[0]);
this.changeSelected(this.data[0]);
this.updateCampusName(this.data[0]);
this.SelectedDataIndex = this.data[0];
}
} else {
this.updateCampusName();
this.SelectedDataIndex = null;
}
},
clickHandle(ev) {
......@@ -143,14 +114,18 @@ export default {
if (this.SelectedDataIndex.areaId !== data.areaId) {
this.SelectedDataIndex = data;
this.changeSelected(data);
if (data.areaId) {
this.changeCampusHandle(data);
} else {
this.showOtherNameHandle();
}
}
},
changeSelected(data) {
this.percentDom = document.querySelector('.CampusRank-percent');
this.areaNameDom = document.querySelector('.CampusRank-areaName');
if (this.percentDom) {
this.percentDom.innerHTML = ((data.count / this.totle) * 100).toFixed(
this.percentDom.innerHTML = ((data.count / this.total) * 100).toFixed(
2
);
}
......@@ -164,9 +139,19 @@ export default {
<style lang="scss">
.CampusRank {
position: relative;
.CampusRank-title {
font-size: 16px;
text-align: center;
padding-bottom: 12px;
}
.CampusRank-legend {
position: absolute;
width: 160px;
transform: translateY(-50%);
line-height: 30px;
top: 50%;
right: 0;
}
}
</style>
<template>
<div
class="Dashboard-DataCard CustomerConsumption"
v-loading="loading"
>
<div class="Dashboard-SearchBar">
<search-item label="运营商">
<operator-select
:accessType="1"
:value="filters.operateId"
@input="val => getConsumeOrderList({operateId: val})"
/>
</search-item>
<search-item label="区域">
<area-select
:value="filters.areaId"
@input="val => getConsumeOrderList({areaId: val})"
type="user"
/>
</search-item>
<el-radio-group
class="Dashboard-Radio"
:value="filters.timeType"
@input="val => getConsumeOrderList({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
class="CustomerConsumptionChart-tip"
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>
</div>
<Chart
:data="consumeList"
:consumeCount="consumeCount"
/>
</div>
</template>
<script>
import Chart from './chart';
import { mapActions, mapGetters } from 'vuex';
export default {
name: 'CustomerConsumption',
components: {
Chart,
},
computed: {
...mapGetters('Dashboard/customerConsumption', [
'consumeList',
'consumeCount',
'filters',
'loading',
]),
},
created() {
this.initData();
},
methods: {
...mapActions('Dashboard/customerConsumption', [
'updateFilters',
'getConsumeOrderList',
]),
initData() {
this.getConsumeOrderList();
},
},
};
</script>
<style lang="scss">
.CustomerConsumption {
.Dashboard-title {
display: flex;
align-items: center;
}
.CustomerConsumptionChart-tip {
position: absolute;
right: 0;
margin-left: 10px;
color: #333;
font-size: 14px;
font-weight: normal;
text-align: center;
}
}
</style>
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 FETCH_DATA = 'FETCH_DATA';
const initFilters = () => ({
operateId: undefined,
areaId: undefined,
timeType: 0, // 0 本日,1 本月, 2 本年
});
const filtersStore = initFiltersStore(initFilters);
const nowTime = new Date();
const state = () => ({
loading: false,
list: [],
consumeCountVo: {
payMen: 0,
payableMoney: 0,
},
});
const getters = {
consumeList: state => state.list,
consumeCount: state => state.consumeCountVo,
loading: state => state.loading,
};
const actions = {
getConsumeOrderList({ commit, getters, dispatch }, data) {
commit(FETCH_DATA, true);
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 });
commit(FETCH_DATA, false);
})
.catch(err => {
console.error(err);
commit(FETCH_DATA, false);
});
},
};
const mutations = {
[FETCH_CONSUME_LIST](state, data) {
state.list = data.list;
state.consumeCountVo = data.consumeCountVo;
},
[FETCH_DATA](state, fetching) {
state.loading = fetching;
},
};
export default {
namespaced: true,
modules: { filtersStore },
state,
getters,
actions,
mutations,
};
<template>
<div id='CustomerConsumptionChart'></div>
</template>
<script>
import G2 from '@antv/g2';
import chartMixin from '../chartMixin';
export default {
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: [50, 80, 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.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>
<template>
<div
class="CustomerFeedback"
@mousewheel.stop=""
>
<div class="Dashboard-SearchBar">
<div class="CustomerFeedbackTitle">
<span class="CustomerFeedback-icon"><img src="@/assets/images/dashboard/feedback.jpg"></span>
<span class="CustomerFeedback-text">反馈未处理-</span>
<span class="red">{{count}}</span>
</div>
</div>
<div
class="CustomerFeedback-List"
v-if="show"
:style="`width:${width}px`"
>
<div
class="CustomerFeedback-Item"
v-for="(item, index) in feedbackList"
:key="index"
>
<span class="CustomerFeedback-ItemNum">{{item.count}}</span>
<span
class="CustomerFeedback-ItemName"
:title="item.areaName"
>{{item.areaName}}</span>
</div>
</div>
</div>
</template>
<script>
import { mapGetters, mapActions } from 'vuex';
export default {
name: 'CustomerFeedback',
computed: {
...mapGetters('Dashboard/customerFeedback', ['feedbackList', 'count']),
},
data() {
return {
show: false,
};
},
created() {
this.initData();
},
mounted() {
let boxDom = document.querySelector('.CustomerFeedback');
let listDom = document.querySelector('.CustomerFeedback-List');
this.width = boxDom.clientWidth;
this.show = true;
},
methods: {
...mapActions('Dashboard/customerFeedback', ['getFeedbackList']),
initData() {
this.getFeedbackList();
},
},
};
</script>
<style lang="scss">
@import '@/assets/styles/variables.scss';
.CustomerFeedback {
display: flex;
flex-direction: column;
.CustomerFeedbackTitle {
width: 100%;
display: flex;
align-items: center;
padding: 0 20px;
line-height: 34px;
font-size: 18px;
.CustomerFeedback-icon {
width: 27px;
height: 27px;
}
.CustomerFeedback-text {
margin-left: 10px;
margin-right: 5px;
}
.red {
color: #ff0000;
}
}
.CustomerFeedback-List {
flex: 1;
overflow-y: auto;
}
.CustomerFeedback-Item {
display: flex;
align-items: center;
height: 60px;
font-size: 18px;
.CustomerFeedback-ItemNum {
width: 70px;
margin-right: 22px;
text-align: right;
color: #ff0000;
}
.CustomerFeedback-ItemName {
flex: 1;
padding-right: 20px;
line-height: 28px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
}
@media screen and (max-width: $bigScreenWidth) {
.CustomerFeedback {
.CustomerFeedbackTitle {
padding: 0 10px;
font-size: 14px;
}
.CustomerFeedback-Item {
height: 40px;
font-size: 14px;
.CustomerFeedback-ItemNum {
width: 70px;
margin-right: 22px;
}
.CustomerFeedback-ItemName {
padding-right: 20px;
line-height: 28px;
}
}
}
}
</style>
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
class="Dashboard-DataCard CustomerRecharge"
v-loading="loading"
>
<div class="Dashboard-SearchBar">
<search-item label="运营商">
<operator-select
:accessType="1"
:value="filters.operateId"
@input="val => getRechargeOrderList({operateId: 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 class="CustomerRechargeChart-tip">
<span
class="CustomerRecharge-tooltip"
style="background-color:#4e82fb;"
></span> 总充值:
<span style="color:#f00;">{{ rechargeCount && rechargeCount.rechargeMoney ? (rechargeCount.rechargeMoney).toFixed(2) : '0.00' }}</span>
<span
class="CustomerRecharge-tooltip"
style="background-color:#26c9a8;"
></span> 充值人数:
<span style="color:#f00;">{{ rechargeCount && rechargeCount.rechargeMen ? rechargeCount.rechargeMen : 0 }}</span>
</div>
</div>
<Chart :data="rechargeList" />
</div>
</template>
<script>
import Chart from './chart';
import { mapGetters, mapActions } from 'vuex';
export default {
name: 'CustomerRecharge',
components: {
Chart,
},
computed: {
...mapGetters('Dashboard/customerRecharge', [
'rechargeList',
'rechargeCount',
'filters',
'loading',
]),
},
created() {
this.initData();
},
methods: {
...mapActions('Dashboard/customerRecharge', [
'updateFilters',
'getRechargeOrderList',
]),
initData() {
this.getRechargeOrderList();
},
},
};
</script>
<style lang="scss">
.CustomerRecharge {
.Dashboard-title {
display: flex;
align-items: center;
}
.CustomerRechargeChart-tip {
position: absolute;
right: 0;
color: #333;
font-size: 14px;
font-weight: normal;
text-align: center;
.CustomerRecharge-tooltip {
display: inline-block;
width: 10px;
height: 5px;
margin-bottom: 2px;
margin-left: 10px;
}
}
}
</style>
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 FETCH_DATA = 'FETCH_DATA';
const initFilters = () => ({
operateId: undefined,
timeType: 0, // 0 本日,1 本月, 2 本年
});
const filtersStore = initFiltersStore(initFilters);
const nowTime = new Date();
const state = () => ({
loading: false,
list: [],
rechargeOrderCountVo: {
rechargeMen: 0,
rechargeMoney: 0,
},
});
const getters = {
rechargeList: state => state.list,
rechargeCount: state => state.rechargeOrderCountVo,
loading: state => state.loading,
};
const actions = {
getRechargeOrderList({ commit, getters, dispatch }, data) {
commit(FETCH_DATA, true);
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_DATA, false);
commit(FETCH_RECHARGE_LIST, { list: data, rechargeOrderCountVo });
})
.catch(err => {
console.error(err);
commit(FETCH_DATA, false);
});
},
};
const mutations = {
[FETCH_RECHARGE_LIST](state, data) {
state.list = data.list;
state.rechargeOrderCountVo = data.rechargeOrderCountVo;
},
[FETCH_DATA](state, fetching) {
state.loading = fetching;
},
};
export default {
namespaced: true,
modules: { filtersStore },
state,
getters,
actions,
mutations,
};
<template>
<div class="CustomerRechargeChart">
<div
id='CustomerRechargeChart'
:style="`height: ${height}px;width: ${width?width+'px':'auto'}`"
></div>
<!-- <div id='CustomerRechangeSlider'></div> -->
<CustomerRechangeSlider
class="CustomerRechangeSlider"
:data="data"
:span="4"
:onChange="wheelHandle"
/>
</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';
import CustomerRechangeSlider from './slider';
const Shape = G2.Shape;
const Util = G2.Util;
const Global = G2.Global;
function getRectRange(points, step) {
var xValues = [];
var yValues = [];
for (var i = 0, len = points.length; i < len; i++) {
var point = points[i];
xValues.push(point.x);
yValues.push(point.y - step);
}
var xMin = Math.min.apply(null, xValues);
var yMin = Math.min.apply(null, yValues);
var xMax = Math.max.apply(null, xValues);
var yMax = Math.max.apply(null, yValues);
return {
x: xMin,
y: yMin,
width: xMax - xMin,
height: yMax - yMin,
};
}
// 左侧柱子
Shape.registerShape('interval', 'left', {
draw: function draw(cfg, container) {
var points = this.parsePoints(cfg.points);
var style = Util.mix(Global.shape.interval, cfg.style, { fill: cfg.color });
var rectCfg = getRectRange(points, cfg.size / 2); // cfg.size 对应 .size(25)
return container.addShape('rect', {
className: 'interval',
attrs: Util.mix(rectCfg, style),
});
},
});
// 右侧柱子
Shape.registerShape('interval', 'right', {
draw: function draw(cfg, container) {
var points = this.parsePoints(cfg.points);
var style = Util.mix(Global.shape.interval, cfg.style, { fill: cfg.color });
var rectCfg = getRectRange(points, -cfg.size / 2); // cfg.size 对应 .size(25)
return container.addShape('rect', {
className: 'interval',
attrs: Util.mix(rectCfg, style),
});
},
});
export default {
name: 'CustomerRechargeChart',
components: { CustomerRechangeSlider },
mixins: [chartMixin],
props: {
rechargeCount: {
type: Object,
},
},
data() {
return {
demoList: [
{ areaName: 'bvs', rechargeMoney: 13.45, rechargeMen: 12 },
{ areaName: 'gsn', rechargeMoney: 23.45, rechargeMen: 13 },
{ areaName: 'dsf', rechargeMoney: 12.45, rechargeMen: 16 },
{ areaName: 'bzx', rechargeMoney: 45.45, rechargeMen: 63 },
{ areaName: 'bvmf', rechargeMoney: 3213.454, rechargeMen: 32 },
{ areaName: 'swer', rechargeMoney: 256, rechargeMen: 125 },
{ areaName: 'cvsdf', rechargeMoney: 541, rechargeMen: 14 },
{ areaName: 'cvsdf', rechargeMoney: 10.15, rechargeMen: 574 },
],
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: '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: 'CustomerRechargeChart',
forceFit: true,
height: this.height,
padding: [30, 90, 20, 120],
});
this.chart.source(dv, {
rechargeMoney: {
alias: '充值金额',
},
rechargeMen: {
alias: '充值人数',
},
});
// this.chart.tooltip({
// offset: 250,
// });
this.chart.axis('areaName', {
label: {
offsetX: -80,
htmlTemplate(text, item, index) {
return `<div class="CustomerRechargeChart-areaTitle">${text}</div>`;
},
},
});
this.chart.axis('rechargeMoney', {
grid: null,
});
this.chart.axis('rechargeMen');
this.chart.coord().transpose();
this.chart
.interval()
.position('areaName*rechargeMoney')
.color('#4e82fb')
.shape('left')
.size(18);
this.chart
.interval()
.position('areaName*rechargeMen')
.shape('right')
.color('#26c9a8')
.size(18);
this.chart.legend(false);
this.chart.guide().text({
top: true,
position: ['100%', '0%'],
content: '(人)',
offsetX: 30,
offsetY: -8,
});
this.chart.guide().text({
top: true,
position: ['100%', '100%'],
content: '(元)',
offsetX: 30,
offsetY: 8,
});
this.chart.render();
// this.initSlider();
},
initSlider() {
const wrapDom = document.getElementById('CustomerRechargeChart');
let height = wrapDom.clientHeight;
// const arrLength = this.data.length;
// let spanNum = 4 / arrLength;
this.sliderDom = document.getElementById('CustomerRechangeSlider');
this.sliderDom.style = `height:${height}px`;
this.slider = new Slider({
container: this.sliderDom, // dom 容器 id 或者 dom 容器对象
width: 26, // slider 的宽度,默认为 'auto',即自适应宽度
height: height, // slider 的高度,默认为 '26px'
padding: [50, 0, 50, 0],
xAxis: 'rechargeMen', // 背景图的横轴对应字段,同时为数据筛选的字段
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() {
const wrapDom = document.getElementById('CustomerRechargeChart');
let height = wrapDom.clientHeight;
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: height,
padding: [50, 0, 50, 0],
xAxis: 'value',
yAxis: 'areaName',
startRadio: this.ds.state.startRadio,
endRadio: this.ds.state.endRadio,
data: this.data,
// minSpan: this.ds.state.endRadio,
// maxSpan: this.ds.state.endRadio,
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) {
const arrLength = this.data.length;
let spanNum = 4 / arrLength;
this.ds.setState('startRadio', Math.abs(1 - spanNum));
this.ds.setState('endRadio', 1);
this.dv.source(this.data);
// let height = this.data.length * 50;
// this.chart.changeHeight(height);
// this.updateSlider();
this.updateData();
} else {
this.initData();
// this.initSlider();
}
},
wheelHandle({ startRadio, endRadio }) {
const tStartRadio = Math.abs(1 - endRadio);
const sEndRadio = Math.abs(1 - startRadio);
console.log(tStartRadio, sEndRadio);
this.ds.setState('startRadio', tStartRadio);
this.ds.setState('endRadio', sEndRadio);
},
},
};
</script>
<style lang="scss">
.CustomerRechargeChart {
position: relative;
#CustomerRechargeChart {
width: 100%;
// overflow-x: hidden;
// overflow-y: auto;
}
.CustomerRechangeSlider {
position: absolute;
top: 0;
right: 0;
width: 28px;
height: 100%;
background-color: #eee;
}
#CustomerRechangeSlider {
margin-left: -30px;
width: 100%;
height: 300px;
transform: translate(100%, -100%);
}
.CustomerRechargeChart-areaTitle {
color: #666;
width: 85px;
height: 2.4em;
line-height: 1.2;
overflow: hidden;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
}
}
</style>
<template>
<div class="ks-Slider">
<div
class="ks-Slider-prev"
@click="clickPrev"
>
<img
v-if="pageNum === 1 || total === 0"
src="@/assets/images/dashboard/up_down_grey.png"
/>
<img
v-else
src="@/assets/images/dashboard/up_down_light.png"
>
</div>
<div class="ks-Slider-inner"></div>
<div
class="ks-Slider-next"
@click="clickNext"
>
<img
v-if="pageNum === pages || total === 0"
src="@/assets/images/dashboard/up_down_grey.png"
/>
<img
v-else
src="@/assets/images/dashboard/up_down_light.png"
>
</div>
</div>
</template>
<script>
export default {
name: 'CustomerRechangeSlider',
props: {
data: {
type: Array,
},
span: {
type: Number,
default: 4,
},
onChange: {
type: Function,
},
},
data() {
return {
pageNum: 1,
pageSize: this.span,
pages: 0,
total: 0,
startRadio: 0,
endRadio: 1,
};
},
watch: {
data() {
this.refreshSlider();
},
},
mounted() {},
methods: {
refreshSlider() {
let total = this.data.length;
this.total = total;
if (total) {
this.pages = Math.ceil(total / this.pageSize);
this.pageNum = 1;
this.startRadio = 0;
this.endRadio = this.pageSize / total;
} else {
this.pageNum = 1;
this.pages = 0;
this.startRadio = 0;
this.endRadio = 1;
}
},
clickPrev() {
if (this.pageNum === 1) return;
let nextNum = this.pageNum - 1;
const pStartRadio = this.startRadio;
let endRadio = pStartRadio;
console.log(nextNum);
let startRadio =
nextNum === 1 ? 0 : ((nextNum - 1) * this.pageSize) / this.total;
this.onChange &&
this.onChange({
startRadio,
endRadio,
});
this.pageNum = nextNum;
this.startRadio = startRadio;
this.endRadio = endRadio;
},
clickNext() {
if (this.pageNum === this.pages) return;
let nextNum = this.pageNum + 1;
const pEndRadio = this.endRadio;
let startRadio = pEndRadio;
let endRadio =
nextNum === this.pages ? 1 : (nextNum * this.pageSize) / this.total;
this.onChange &&
this.onChange({
startRadio,
endRadio,
});
this.pageNum = nextNum;
this.startRadio = startRadio;
this.endRadio = endRadio;
},
},
};
</script>
<style lang="scss">
.ks-Slider {
position: relative;
.ks-Slider-prev,
.ks-Slider-next {
position: absolute;
width: 28px;
height: 18px;
background-color: #666;
&.disabled {
background-color: #999;
}
}
.ks-Slider-prev {
top: 0;
}
.ks-Slider-next {
transform: rotate(180deg);
bottom: 0;
}
.ks-Slider-inner {
width: 100%;
background-color: #666;
}
}
</style>
<template>
<div
class="Dashboard-Row Dashboard-DataCard"
v-loading="loading.regist"
>
<div class="Dashboard-SearchBar">
<search-item label="年份">
<el-date-picker
v-model.trim="campusFilters.year"
@change="changeReportYearHandle"
type="year"
value-format="yyyy"
placeholder="请选择"
>
</el-date-picker>
</search-item>
<search-item label="校区">
<area-select
v-model.trim="campusFilters.areaId"
@input="changeReportAreaHandle"
type="user"
/>
</search-item>
</div>
<div class="Dashboard-CampusData">
<div class="Dashboard-CampusItem">
<div class="Dashboard-title">{{ reportYear }}年每月注册数据</div>
<RegisterByMonth
:data="registePerMonth"
:changeReportMonthHandle="changeReportMonthHandle"
/>
</div>
<div class="Dashboard-CampusItem">
<div class="Dashboard-title">
{{ reportYear }}{{ reportMonth }}月注册数据
</div>
<RegisterByDay :data="registePerDay" />
</div>
<div class="Dashboard-CampusItem">
<div class="Dashboard-title">{{ reportYear }}年每月活跃数据</div>
<ActiveUserByMonth :data="activeUser" />
</div>
</div>
</div>
</template>
<script>
import { mapGetters, mapActions } from 'vuex';
import RegisterByMonth from './components/RegisterByMonth';
import RegisterByDay from './components/RegisterByDay';
import ActiveUserByMonth from './components/ActiveUserByMonth';
export default {
name: 'CustomerRegister',
components: {
RegisterByMonth,
RegisterByDay,
ActiveUserByMonth,
},
data() {
return {
campusFilters: {
year: '',
areaId: undefined,
},
};
},
computed: {
...mapGetters('Dashboard', [
'reportYear',
'reportMonth',
'registePerDay',
'registePerMonth',
'activeUser',
'loading',
]),
},
methods: {
...mapActions('Dashboard', ['fetchReportList']),
changeReportYearHandle(val) {
if (val) {
let areaId = this.campusFilters.areaId;
this.fetchReportList({
year: val,
areaId: areaId ? areaId : null,
});
}
},
changeReportAreaHandle(val) {
if (val) {
let year = this.campusFilters.year;
this.fetchReportList({
year: year ? year : null,
areaId: val,
});
}
},
changeReportMonthHandle(month) {
console.log(month);
let year = this.campusFilters.year;
let areaId = this.campusFilters.areaId;
this.fetchReportList({
year: year ? year : null,
areaId: areaId ? areaId : null,
month: month,
});
},
},
};
</script>
......@@ -4,7 +4,7 @@
<script>
import G2 from '@antv/g2';
import chartMixin from '../chartMixin';
import chartMixin from '../../chartMixin';
export default {
name: 'ActiveUserByMonth',
......@@ -35,6 +35,7 @@ export default {
tickCount: 12,
},
mark: {
type: 'cat',
formatter: value => {
let service = this.serviceList.find(item => item.value === value);
return service ? service.name : '';
......
......@@ -4,7 +4,7 @@
<script>
import G2 from '@antv/g2';
import chartMixin from '../chartMixin';
import chartMixin from '../../chartMixin';
export default {
name: 'RegisterByDay',
......
......@@ -3,10 +3,15 @@
</template>
<script>
import G2 from '@antv/g2';
import chartMixin from '../chartMixin';
import chartMixin from '../../chartMixin';
export default {
name: 'RegisterByMonth',
props: {
changeReportMonthHandle: {
type: Function,
},
},
mixins: [chartMixin],
data() {
return {
......@@ -47,6 +52,11 @@ export default {
.position('month*count')
.color('#4e82fb');
this.chart.render();
this.chart.on('interval:click', this.clickHandle);
},
clickHandle(ev) {
let data = ev.data._origin;
this.changeReportMonthHandle(data.month);
},
},
};
......
......@@ -3,15 +3,43 @@ 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 LOADING_AREA = 'LOADING_AREA';
const LOADING_SERVICE = 'LOADING_SERVICE';
const LOADING_EQUIPMENT = 'LOADING_EQUIPMENT';
const LOADING_REGIST = 'LOADING_REGIST';
const initFilters = () => ({
operateId: undefined,
timeType: 0, // 0 本日,1 本月, 2 本年
});
const filtersStore = initFiltersStore(initFilters);
const nowTime = new Date();
const state = () => ({
loading: {
area: false,
service: false,
equipment: false,
regist: false,
},
consume: {
areaInfo: {
areaName: '',
areaId: undefined,
},
consumeAfterVos: [],
consumeHardVos: [],
list: [],
percentList: [],
......@@ -37,38 +65,79 @@ const getters = {
registePerMonth: state => state.report.registePerMonth,
activeUser: state => state.report.activeUser,
titleData: state => state.title,
areaInfo: state => state.consume.areaInfo,
consumeAfterVos: state => state.consume.consumeAfterVos,
consumeHardVos: state => state.consume.consumeHardVos,
campusRankList: state => state.consume.list,
seviceRatioList: state => state.consume.percentList,
loading: state => state.loading,
};
const actions = {
fetchConsumeList({ commit }, entity) {
fetchConsumeList({ commit, getters }, 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;
}
if (data && data.updatePercentList) {
entity.areaId = data.areaId;
commit(LOADING_SERVICE, true);
} else if (data && data.updateEuipmentList) {
entity.serviceId = data.serviceId;
entity.areaId = getters.areaInfo.areaId;
commit(LOADING_EQUIPMENT, true);
} else {
commit(LOADING_AREA, true);
}
return fetchConsumeList({
params: {
data: {
...entity,
},
}).then(res => {
})
.then(res => {
let list = res.data;
if (entity && entity.updatePercentList) {
if (data && data.updatePercentList) {
commit(GET_PERCENT_DATA, list);
return;
}
if (entity && entity.updateEuipmentList) {
commit(GET_EUIPMENT_DATA, list);
return;
}
commit(LOADING_SERVICE, false);
} else if (data && data.updateEuipmentList) {
commit(GET_EUIPMENT_DATA, list);
commit(LOADING_EQUIPMENT, false);
} else {
commit(GET_CONSUME_DATA, list);
commit(LOADING_AREA, false);
}
})
.catch(err => {
console.error(err);
commit(LOADING_AREA, false);
});
},
fetchReportList({ commit }, entity) {
commit(LOADING_REGIST, true);
return fetchReportList({
params: {
...entity,
},
}).then(res => {
})
.then(res => {
let list = res.data;
commit(GET_REPORT_DATA, list);
commit(LOADING_REGIST, false);
})
.catch(err => {
console.error(err);
commit(LOADING_REGIST, false);
});
},
fetchTitleList({ commit }) {
......@@ -77,19 +146,54 @@ const actions = {
commit(GET_TITLE_DATA, data);
});
},
updateCampusName({ commit }, data) {
if (data) {
commit(GET_CAMPUS_DATA, data);
} else {
commit(GET_CAMPUS_DATA, {
areaId: undefined,
areaName: '',
});
}
},
};
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
: [];
let campusList = data.list ? data.list : [];
if (campusList.length > 10) {
let arrStart = campusList.slice(0, 9);
let arrEnd = campusList.slice(9).reduce(
(item, curentVal) => ({
...item,
count: item.count + curentVal.count,
}),
{
areaName: '其他校区',
count: 0,
}
);
campusList = [...arrStart, arrEnd];
}
state.consume.list = campusList;
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.consumeHardVos = data.consumeHardVos
? data.consumeHardVos
: [];
state.consume.consumeAfterVos = data.consumeAfterVos
? data.consumeAfterVos
: [];
},
[GET_REPORT_DATA]: (state, data) => {
state.report.year = data.year;
......@@ -107,10 +211,34 @@ const mutations = {
[GET_TITLE_DATA](state, data) {
state.title = data;
},
[GET_CAMPUS_DATA](state, name) {
state.consume.areaInfo = name;
},
[LOADING_AREA](state, fetching) {
state.loading.area = fetching;
state.loading.service = fetching;
state.loading.equipment = fetching;
},
[LOADING_SERVICE](state, fetching) {
state.loading.service = fetching;
state.loading.equipment = fetching;
},
[LOADING_EQUIPMENT](state, fetching) {
state.loading.equipment = fetching;
},
[LOADING_REGIST](state, fetching) {
state.loading.regist = fetching;
},
};
export default {
namespaced: true,
modules: {
filtersStore,
customerRecharge,
customerConsumption,
customerFeedback,
},
state,
getters,
actions,
......
<template>
<div
class="Dashboard-DataCard EquipmentList"
v-loading="loading.equipment"
>
<div class="Dashboard-title">
<div class="Dashboard-title-icon equipment">
<img src="@/assets/images/dashboard/icon_paiming@2x.png" />
</div>
<div class="Dashboard-title-text">各设备消费排名</div>
<el-radio-group v-model="showTopFive">
<el-radio-button :label="true">前五名</el-radio-button>
<el-radio-button :label="false">后五名</el-radio-button>
</el-radio-group>
</div>
<EquipmentListChart :data="showTopFive?consumeHardVos:consumeAfterVos" />
</div>
</template>
<script>
import EquipmentListChart from './EquipmentListChart';
import { mapGetters, mapActions } from 'vuex';
export default {
name: 'EquipmentList',
components: {
EquipmentListChart,
},
data() {
return {
showTopFive: true,
};
},
computed: {
...mapGetters('Dashboard', [
'consumeHardVos',
'consumeAfterVos',
'loading',
]),
},
methods: {
...mapActions('Dashboard', ['fetchConsumeList']),
},
};
</script>
<style lang="scss">
.EquipmentList {
.Dashboard-title {
.el-radio-button__inner {
line-height: 0;
}
}
}
</style>
<template>
<div class="Dashboard-Equipment">
<div
class="Dashboard-Equipment"
:style="`width:${width}px`"
>
<template v-if="show">
<div
v-for="(item, index) in data.slice(0, 5)"
:key="index"
class="Dashboard-Equipment-item"
>
<div class="Dashboard-Equipment-icon" v-if="index === 0">
<div
class="Dashboard-Equipment-icon"
v-if="index === 0"
>
<img src="@/assets/images/dashboard/icon_no.1@2x.png" />
</div>
<div class="Dashboard-Equipment-icon" v-else-if="index === 1">
<div
class="Dashboard-Equipment-icon"
v-else-if="index === 1"
>
<img src="@/assets/images/dashboard/icon_no.2@2x.png" />
</div>
<div class="Dashboard-Equipment-icon" v-else-if="index === 2">
<div
class="Dashboard-Equipment-icon"
v-else-if="index === 2"
>
<img src="@/assets/images/dashboard/icon_no.3@2x.png" />
</div>
<div class="Dashboard-Equipment-icon" v-else>NO.{{ index + 1 }}</div>
<div
class="Dashboard-Equipment-icon"
v-else-if="index === 3"
>
<img src="@/assets/images/dashboard/icon_4@2x.png" />
</div>
<div
class="Dashboard-Equipment-icon"
v-else
>
<img src="@/assets/images/dashboard/icon_5@2x.png" />
</div>
<div class="Dashboard-Equipment-name">{{ item.equipmentNumber }}</div>
<div class="Dashboard-Equipment-location">
<div
class="Dashboard-Equipment-location"
:title="item.equipmentPosition"
>
{{ item.equipmentPosition }}
</div>
<div class="Dashboard-Equipment-money">{{ item.money.toFixed(2) }}</div>
</div>
</template>
</div>
</template>
......@@ -33,6 +61,19 @@ export default {
default: () => [],
},
},
data() {
return {
show: false,
width: 0,
};
},
mounted() {
let boxDom = document.querySelector('.EquipmentList');
let listDom = document.querySelector('.Dashboard-Equipment');
this.width = boxDom.clientWidth;
this.show = true;
},
};
</script>
......@@ -65,12 +106,16 @@ export default {
width: 64px;
}
.Dashboard-Equipment-location {
width: 200px;
width: 160px;
height: 1em;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.Dashboard-Equipment-money {
font-size: 16px;
text-align: right;
width: 116px;
width: 100px;
}
}
}
......
<template>
<div
class="Dashboard-DataCard"
v-loading="loading.service"
>
<div class="Dashboard-title">
<div class="Dashboard-title-icon service">
<img src="@/assets/images/dashboard/icon_zhanbi@2x.png" />
</div>
<div class="Dashboard-title-text">各服务消费占比</div>
<!-- <div class="Dashboard-title-top">TOP1</div> -->
</div>
<SeviceRatioChart
:data="seviceRatioList"
:changeServiceHandle="changeServiceHandle"
/>
<div class="SeviceRatio-title">{{areaInfo.areaName}}</div>
</div>
</template>
<script>
import SeviceRatioChart from './SeviceRatioChart';
import { mapActions, mapGetters } from 'vuex';
export default {
name: 'SeviceRatio',
components: { SeviceRatioChart },
computed: {
...mapGetters('Dashboard', ['seviceRatioList', 'areaInfo', 'loading']),
},
methods: {
...mapActions('Dashboard', ['fetchConsumeList']),
changeServiceHandle(data) {
this.fetchConsumeList({
serviceId: data.serviceId,
updateEuipmentList: true,
});
},
},
};
</script>
<style lang="scss">
.SeviceRatio-title {
font-size: 16px;
text-align: center;
padding-bottom: 12px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
</style>
<template>
<div class="SeviceRatio">
<div id="SeviceRatio"></div>
<div class="SeviceRatio-title">成都师范学院</div>
</div>
</template>
<script>
......@@ -48,6 +45,7 @@ export default {
this.chart.legend({
position: 'right-center',
marker: 'square',
clickable: false,
useHtml: true,
containerTpl: `
<div class="g2-legend">
......@@ -61,7 +59,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"
......@@ -73,11 +71,10 @@ export default {
`;
},
'g2-legend': {
width: '130px',
width: '125px',
// left: '-20px',
},
'g2-legend-list-item': {
height: '22px',
color: '#333',
},
'g2-legend-marker': {
......@@ -95,34 +92,28 @@ export default {
// 辅助元素
this.chart.guide().html({
position: ['50%', '50%'],
html: `<div style="color:#333;font-size:20px;text-align: center;">
html: `<div class="SeviceRatio-centerText">
<span class="SeviceRatio-percent">0.00</span>%<br>
<span class="SeviceRatio-serviceName" style="font-size:16px;"></span>
<span class="SeviceRatio-serviceName" ></span>
</div>`,
alignX: 'middle',
alignY: 'middle',
});
this.chart.render();
this.chart.on('plotclick', this.clickHandle);
this.chart.on('interval:click', this.clickHandle);
},
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];
}
}
},
clickHandle(ev) {
let data = ev.data._origin;
if (this.SelectedDataIndex.serviceId !== data.serviceId) {
let data = ev.data._origin;
this.SelectedDataIndex = data;
this.changeSelected(data);
this.changeServiceHandle(data);
}
......@@ -145,8 +136,25 @@ export default {
</script>
<style lang="scss">
.SeviceRatio-title {
font-size: 16px;
@import '@/assets/styles/variables.scss';
#SeviceRatio {
.SeviceRatio-centerText {
color: #333;
font-size: 20px;
text-align: center;
}
.SeviceRatio-serviceName {
font-size: 16px;
}
}
@media screen and (max-width: $bigScreenWidth) {
#SeviceRatio {
.SeviceRatio-centerText {
font-size: 16px;
}
.SeviceRatio-serviceName {
font-size: 14px;
}
}
}
</style>
......@@ -9,6 +9,7 @@ export default {
return {
chart: null,
height: 270,
width: 0,
chartGeom: null,
};
},
......@@ -21,8 +22,9 @@ export default {
const innerWidth = window.innerWidth;
const innerHeight = window.innerHeight;
if (innerWidth < 1600) {
this.height = (innerHeight - 250) / 2;
this.height = (innerHeight - 270) / 2;
}
this.width = this.$el.clientWidth;
this.initData();
},
methods: {
......
......@@ -16,6 +16,7 @@
v-if="customerManage && customerManage.length"
menuName="会员管理"
:menuList="customerManage[0].children"
:selectRouteMenu="selectRouteMenu"
>
<img
slot="icon"
......@@ -32,6 +33,7 @@
v-if="baseManage && baseManage.length"
menuName="基础数据"
:menuList="baseManage[0].children"
:selectRouteMenu="selectRouteMenu"
>
<img
slot="icon"
......@@ -48,6 +50,7 @@
v-if="systemManage && systemManage.length"
menuName="系统管理"
:menuList="systemManage[0].children"
:selectRouteMenu="selectRouteMenu"
>
<img
slot="icon"
......@@ -76,6 +79,7 @@
</MenuItem>
</div>
<div
v-if="dashboardButtonVisiable"
class="DashboardButton"
@click="clickHandle"
>
......@@ -110,6 +114,9 @@ export default {
toggleFastLink: {
type: Function,
},
selectRouteMenu: {
type: Function,
},
},
data() {
return {
......@@ -159,6 +166,12 @@ export default {
);
return item ? formatRouteLink([item]) : null;
},
dashboardButtonVisiable() {
let item = this.routers.find(
menu => menu.menuCode === process.env.VUE_APP_DASHBOARD_CODE
);
return item ? true : false;
},
},
methods: {
clickHandle() {
......@@ -223,7 +236,7 @@ export default {
}
}
.fastLink.marleft {
margin-left: 133px;
margin-left: 70px;
}
.DashboardButton {
height: 80px;
......
......@@ -5,13 +5,16 @@
@mouseenter="toggleSubmenus"
@mouseleave="toggleSubmenus"
>
<div class="IconMenus">
<div class="IconMenus-icon">
<div
class="IconMenu"
@click="selectHandle"
>
<div class="IconMenu-icon">
<slot name="icon">
<img src="@/assets/images/layout/icon_huiyuanguanli@2x.png" />
</slot>
</div>
<div class="IconMenus-name">{{ menuName }}</div>
<div class="IconMenu-name">{{ menuName }}</div>
</div>
<div
:class="`SubmenuBox ${showSubMenus?'show':''}`"
......@@ -67,6 +70,9 @@ export default {
type: Array,
default: () => [],
},
selectRouteMenu: {
type: Function,
},
},
data() {
return {
......@@ -89,6 +95,11 @@ export default {
toggleSubmenus() {
this.showSubMenus = !this.showSubMenus;
},
selectHandle() {
if (typeof this.selectRouteMenu === 'function') {
this.selectRouteMenu(this.menuList);
}
},
},
};
</script>
......@@ -104,7 +115,7 @@ export default {
&:hover {
background-color: #68aaff;
}
.IconMenus {
.IconMenu {
display: flex;
width: 100%;
height: 100%;
......@@ -112,12 +123,12 @@ export default {
justify-content: center;
align-items: center;
cursor: pointer;
.IconMenus-icon {
.IconMenu-icon {
width: 17px;
height: 17px;
margin-bottom: 5px;
}
.IconMenus-name {
.IconMenu-name {
font-size: 14px;
}
}
......@@ -137,9 +148,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 +163,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 +174,8 @@ export default {
}
.like-icon {
display: inline-block;
width: 16px;
height: 16px;
width: 12px;
height: 12px;
}
.like-icon,
.Submenu-link-text {
......@@ -176,18 +187,34 @@ export default {
.MenuItem {
width: 112px;
height: 80px;
.IconMenus {
.IconMenus-icon {
.IconMenu {
.IconMenu-icon {
width: 34px;
height: 34px;
margin-bottom: 10px;
}
.IconMenus-name {
.IconMenu-name {
font-size: 18px;
}
}
.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;
}
}
}
}
......
......@@ -448,7 +448,7 @@ export default {
this.cancelUserinfoUpdate();
})
.catch(err => {
console.log(err);
console.error(err);
});
}
},
......@@ -489,7 +489,7 @@ export default {
this.cancelCellphoneUpdate();
})
.catch(err => {
console.log(err);
console.error(err);
});
}
},
......@@ -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 {
......
......@@ -149,7 +149,7 @@ export default {
this.$router.push({ path: '/' });
})
.catch(err => {
console.log(err);
console.error(err);
this.loading = false;
});
}
......@@ -168,7 +168,7 @@ export default {
})
.catch(err => {
this.$message.error('获取验证码失败!');
console.log(err);
console.error(err);
});
},
},
......
......@@ -5,8 +5,8 @@
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
<link rel="icon" href="<%= BASE_URL %>favicon.ico" />
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuex@3.1.0/dist/vuex.js"></script>
<script src="https://unpkg.com/vue@2.6.10/dist/vue.js"></script>
<script src="https://unpkg.com/vuex@3.1.0/dist/vuex.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vue-router@3.0.1/dist/vue-router.js"></script>
<title>系统管理</title>
</head>
......@@ -20,9 +20,9 @@
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
<script src="<%= VUE_APP_LIB_MANAGER %>/customerManage/lib/customerManage.umd.js"></script>
<script src="<%= VUE_APP_LIB_MANAGER %>/baseManage/lib/baseManage.umd.js"></script>
<script src="<%= VUE_APP_LIB_MANAGER %>/systemManage/lib/systemManage.umd.js"></script>
<script src="<%= VUE_APP_LIB_MANAGER %>/customerManage/lib/customerManage.umd.min.js"></script>
<script src="<%= VUE_APP_LIB_MANAGER %>/baseManage/lib/baseManage.umd.min.js"></script>
<script src="<%= VUE_APP_LIB_MANAGER %>/systemManage/lib/systemManage.umd.min.js"></script>
<script src="https://webapi.amap.com/maps?v=1.4.12&key=dd6103c90f2f17310a8711f2d330a0a6"></script>
<script src="https://webapi.amap.com/ui/1.0/main.js?v=1.0.11"></script>
<script src="https://unpkg.com/@antv/data-set"></script>
......
......@@ -12,6 +12,18 @@ setTimeout(() => {
System.createSystem({
basePath: process.env.VUE_APP_BASE_URL,
routers: allAasyncRouterMap,
constantRouter: [
{
path: '/asd',
name: 'asd',
component: {
render(h) {
return <div>asdasdasd</div>;
},
},
},
],
whiteList: '/asd',
routeFilter: (routes, allRoute) => {
if (allRoute && allRoute.length) {
let addRoute = System.utils.route.formatRouteLink(allRoute);
......
......@@ -40,7 +40,7 @@ const createBaseFetch = config => {
},
error => {
// Do something with request error
console.log(error); // for debug
console.error(error); // for debug
Promise.reject(error);
}
);
......@@ -86,7 +86,7 @@ const createBaseFetch = config => {
});
})
.catch(err => {
console.log(err);
console.error(err);
logout = false;
});
}
......
......@@ -24,16 +24,35 @@ class SystemShell {
this.mixinModule = mixinModule;
this.utils = utilsModule;
}
createSystem = ({ basePath = '/', routers, routeFilter }) => {
createSystem = ({
basePath = '/',
routers,
routeFilter,
constantRouter,
whiteList,
}) => {
const router = new VueRouter({
mode: 'history',
base: basePath,
scrollBehavior: () => ({ y: 0 }),
routes: constantRouterMap,
routes:
constantRouter && constantRouter instanceof Array
? [...constantRouterMap, ...constantRouter]
: constantRouterMap,
});
routeStore.install(store);
extendCom(Vue);
configRoutePermission(router, store, routers);
const allWhiteList =
whiteList && typeof whiteList === 'string'
? [
...process.env.VUE_APP_WHITE_LIST.split(','),
...whiteList.split(','),
]
: process.env.VUE_APP_WHITE_LIST.split(','); // 不重定向白名单
console.log(allWhiteList);
store.dispatch('getWhiteList', allWhiteList);
configRoutePermission(router, store, routers, allWhiteList);
Vue.config.productionTip = false;
let app = new Vue({
......
......@@ -4,11 +4,14 @@ import 'nprogress/nprogress.css'; // Progress 进度条样式
import { getToken } from '@/utils/auth'; // 验权
import { getAuthRoute } from '../utils/route';
const configRoutePermission = (router, store, allAasyncRouterMap) => {
const configRoutePermission = (
router,
store,
allAasyncRouterMap,
whiteList
) => {
NProgress.configure({ showSpinner: false }); // NProgress Configuration
const whiteList = process.env.VUE_APP_WHITE_LIST.split(','); // 不重定向白名单
const getRouteAdd = () => {
let allRoute = store.getters.asyncRoutes;
let asyncRouters = getAuthRoute(allAasyncRouterMap, allRoute);
......@@ -42,7 +45,7 @@ const configRoutePermission = (router, store, allAasyncRouterMap) => {
next({ ...to, replace: true });
})
.catch(err => {
console.log(err);
console.error(err);
store.dispatch('FedLogOut').then(() => {
next({ path: '/login' });
});
......
......@@ -26,7 +26,7 @@ const actions = {
return res;
})
.catch(err => {
console.log(err);
console.error(err);
fetch--;
});
......
import sidebar from './sidebar';
import collect from './collect';
import { FETCH_START, FETCH_DONE } from './mutation-types';
const GET_WHITE_LIST = 'GET_WHITE_LIST';
let fetchCount = 0;
const state = () => ({
loading: false,
whiteList: [],
});
const getters = {
loading: state => state.loading,
whiteList: state => state.whiteList,
};
const actions = {
......@@ -18,6 +21,9 @@ const actions = {
fetchDone({ commit }, notLoading) {
commit(FETCH_DONE, notLoading);
},
getWhiteList({ commit }, list) {
commit(GET_WHITE_LIST, list);
},
};
const mutations = {
[FETCH_START](state, notLoading) {
......@@ -34,6 +40,9 @@ const mutations = {
state.loading = false;
}
},
[GET_WHITE_LIST](state, list) {
state.whiteList = [...state.whiteList, ...list];
},
};
export default {
modules: {
......
......@@ -33,7 +33,7 @@ const actions = {
}
})
.catch(err => {
console.log(err);
console.error(err);
commit(GET_CUSTOMER_BASEINFO, state());
});
},
......@@ -48,7 +48,7 @@ const actions = {
}
})
.catch(err => {
console.log(err);
console.error(err);
commit(GET_CUSTOMER_BASEINFO, state());
});
},
......
const UPDATE_FILTERS = 'UPDATE_FILTERS';
const RESET_FILTERS = 'RESET_FILTERS';
const initFiltersStore = (
initState = {},
initState = () => ({}),
otherGetters = {},
otherActions = {},
otherMutations = {}
) => {
const state = () => ({
filters: initState,
filters: initState(),
});
const getters = {
filters: state => state.filters,
......@@ -17,6 +18,9 @@ const initFiltersStore = (
updateFilters({ commit }, filters) {
commit(UPDATE_FILTERS, filters);
},
resetFilters({ commit }) {
commit(RESET_FILTERS);
},
...otherActions,
};
const mutations = {
......@@ -26,6 +30,9 @@ const initFiltersStore = (
...filters,
};
},
[RESET_FILTERS](state) {
state.filters = initState();
},
...otherMutations,
};
......
import { UPDATE_PAGINATION } from '../mutation-types';
const UPDATE_PAGINATION = 'UPDATE_PAGINATION';
const RESET_PAGINATION = 'RESET_PAGINATION';
const state = () => ({
pageNum: 1, // 当前分页
......@@ -17,6 +18,9 @@ const actions = {
updatePagination({ commit }, pagination) {
commit(UPDATE_PAGINATION, pagination);
},
resetPagination({ commit }) {
commit(RESET_PAGINATION);
},
};
const mutations = {
......@@ -25,6 +29,14 @@ const mutations = {
...pagination,
});
},
[RESET_PAGINATION](state) {
state = Object.assign(state, {
pageNum: 1, // 当前分页
pageSize: 10, // 分页大小
total: 0,
pages: 0,
});
},
};
export default {
......
export const USER_LOGIN = 'USER_LOGIN';
export const FETCH_PRODUCT_LIST = 'FETCH_PRODUCT_LIST';
// pagination
export const UPDATE_PAGINATION = 'UPDATE_PAGINATION';
\ No newline at end of file
......@@ -17,7 +17,6 @@ import UserDialog from '../components/Dialog/UserDialogWrap';
import AreaSelect from '../components/input/AreaSelect/index';
import ServiceTypeSelect from '../components/input/ServiceTypeSelect/index';
import GiveTypeSelect from '../components/input/GiveTypeSelect.vue';
import BaseDataSelect from '../components/input/BaseDataSelect/index';
import OperatorSelect from '../components/input/OperatorSelect/index';
import BeansSelect from '../components/input/BeanTypeSelect/index';
......@@ -42,7 +41,6 @@ const extendVue = Vue => {
Vue.component(AreaSelect.name, AreaSelect);
Vue.component(BaseDataSelect.name, BaseDataSelect);
Vue.component(ServiceTypeSelect.name, ServiceTypeSelect);
Vue.component(GiveTypeSelect.name, GiveTypeSelect);
Vue.component(OperatorSelect.name, OperatorSelect);
Vue.component(BeansSelect.name, BeansSelect);
Vue.component(ImageUploader.name, ImageUploader);
......
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