Commit 83b0ab7b by liuchenxi

update: 短信分权

parent 51074830
......@@ -7,6 +7,7 @@
<title>GIC后台</title>
<link rel="stylesheet" type="text/css" href="<%= htmlWebpackPlugin.options.BASE_URL %>static/fonts/iconfont.css">
<link rel="stylesheet" type="text/css" href="<%= htmlWebpackPlugin.options.BASE_URL %>static/css/common.css">
<link rel="stylesheet" href="//at.alicdn.com/t/font_2996579_93aeeozj35q.css">
<!-- <link rel="stylesheet" href="//web-1251519181.file.myqcloud.com/components/element.2.12.0.css"> -->
<!-- element 皮肤 -->
<!-- <link rel="stylesheet" type="text/css" href="http://web-1251519181.file.myqcloud.com/lib/elementUI/theme.1.0.1/index.css"> -->
......
......@@ -138,12 +138,18 @@ a:hover {
.mt15{
margin-top: 15px!important;
}
.mt17{
margin-top: 17px!important;
}
.mt20{
margin-top: 20px!important;
}
.mt30{
margin-top: 30px!important;
}
.mt40{
margin-top: 40px!important;
}
.mt100{
margin-top: 100px!important;
}
......@@ -177,6 +183,9 @@ a:hover {
.mb15{
margin-bottom: 15px!important;
}
.mb17{
margin-bottom: 17px!important;
}
.mb20{
margin-bottom: 20px!important;
}
......@@ -253,6 +262,9 @@ a:hover {
.w600{
width: 600px!important;
}
.h32 {
height: 32px;
}
.ml120{
margin-left: 120px;
}
......@@ -334,6 +346,9 @@ a:hover {
.fz28{
font-size: 28px;
}
.fz36 {
font-size: 36px;
}
.fz30{
font-size: 30px;
}
......@@ -521,3 +536,18 @@ img::after {
// ::-webkit-scrollbar {
// display: none;
// }
// 幽灵按钮放在全局方便全局改样式
.ghost-btn {
background: #fff !important;
color: #1890ff !important;
&:hover {
border-color: #40a9ff;
color: #40a9ff !important;
}
&:active {
border-color: #096dd9;
color: #096dd9 !important;
}
}
......@@ -61,7 +61,6 @@ export default {
return this.$store.state.marketing.breadcrumb;
},
layoutTips() {
console.log(this.$store.state.marketing.layoutTips);
return this.$store.state.marketing.layoutTips;
},
showDescription() {
......
......@@ -3,7 +3,8 @@
<template slot="content">
<p class="tip-popover">{{ text }}</p>
</template>
<i class="iconfont icon-xinxixianshi"></i>
<!-- icon-xinxixianshi -->
<i class="iconfont icon-QuestionCircleOutlined"></i>
</el-tooltip>
</template>
......
......@@ -29,6 +29,11 @@ export default {
path: 'do',
name: '充值',
component: () => import(/* webpackChunkName: "recharge" */ '../../views/recharge/recharge.vue')
},
{
path: 'accountDetail',
name: '账户明细',
component: () => import(/* webpackChunkName: "recharge" */ '../../views/recharge/account-detail.vue')
}
]
};
......@@ -53,3 +53,21 @@ export const videoChartData = params => requests('api-mall/' + 'get-traffic-cost
//计费中心--消费详情-视频列表-导出
export const downloadTrafficCostListExcel = config.api + 'api-mall/' + 'download-traffic-cost-list-execl';
// 计费部门分页查询
export const getDepartList = params => requests(PREFIX + 'list-account-department-by-page', params);
// 保存计费规则
export const saveAccountRule = params => requests(PREFIX + 'save-account-rule', params);
// 获取计费规则
export const getAccountRule = params => requests(PREFIX + 'get-enterprise-account-rule', params);
// 启用停用计费部门
export const updateDepartStatus = params => requests(PREFIX + 'update-account-department-status', params);
// 增加计费部门
export const addAccountDepart = params => requests(PREFIX + 'add-account-department', params);
// 获取部门列表
export const getAllDepart = params => requests('api-admin/' + 'list-clerk-department', params);
export default {
computed: {
getCodeAuth() {
return code => this.$getButtonLimit(this.getCode(code));
},
getCode() {
return code => this.$buttonCode[code];
}
}
};
<template>
<div>
<div class="tip"></div>
<div class="dm-wrap">
<div class="top">
<div class="left">
<el-input v-model="search.departName" placeholder="请输入部门名称" prefix-icon="el-icon-search" style="width: 260px" clearable @change="getTableData" @clear="getTableData" />
<el-select v-model="search.status" placeholder="全部状态" class="w160" @change="getTableData">
<el-option v-for="item in statusList" :key="item.value" :value="item.value" :label="item.label" />
</el-select>
</div>
<div class="right">
<el-button type="primary" @click="addDepartDialog.visible = true">增加独立计费部门</el-button>
</div>
</div>
<div class="content mt20">
<el-table :data="tableData.data" element-loading-text="拼命加载中">
<el-table-column v-for="(v, i) in tableData.header" :key="i" :prop="v.prop" :min-width="v.minWidth" :label="v.label" :formatter="v.formatter" :fixed="v.fixed" show-overflow-tooltip :render-header="onRender">
<template slot-scope="scope">
<span v-if="v.formatter" v-html="v.formatter(scope.row)"></span>
<span v-else>{{ scope.row[v.prop] || '--' }}</span>
</template>
</el-table-column>
<el-table-column label="操作" min-width="130">
<template slot-scope="{ row }">
<el-button type="text">查看门店</el-button>
<el-button type="text">记录</el-button>
<el-button type="text">启用</el-button>
</template>
</el-table-column>
</el-table>
<dm-pagination background class="dm-pagination" @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page="tableData.currentPage" :page-sizes="tableData.pageSizeList" :page-size="tableData.pageSize" layout="total, sizes, prev, pager, next" :total="tableData.total" hide-on-single-page />
</div>
</div>
<add-depart :visible.sync="addDepartDialog.visible" />
</div>
</template>
<script>
import { formatDateTimeByType } from '@/utils/index.js';
import { getDepartList } from '@/service/api/rechargeApi.js';
import tip from '@/components/tip';
import AddDepart from './components/add-depart.vue';
export default {
name: 'detail-table',
components: { tip, AddDepart },
prop: {},
data() {
return {
search: {
departName: '',
status: null
},
tableData: {
data: [],
currentPage: 1,
pageSizeList: [20, 40, 60, 80],
pageSize: 20,
header: [],
total: 0
},
addDepartDialog: {
visible: false
},
statusList: [
{ label: '启用', value: 1 },
{ label: '停用', value: 0 }
]
};
},
created() {
this.getTableHeader();
this.getTableData();
this.$store.commit('mutations_breadcrumb', [
{ name: '企业管理', path: '' },
{ name: '计费中心', path: '/recharge/board' },
{ name: '账户明细', path: '' }
]);
},
mounted() {
this.$store.commit(
'mutations_layoutTips',
`<div class="layout--tips" style="display: flex">
<i class="iconfont icon-warning-circle-fill" style="margin-top: -3px"></i>
<div class="ml5">
“多账户模式”下,系统将根据所配置的计费规则将通讯费用按照下方的“独立计费部门”进行汇总核算及扣除费用:<br />
1、计费规则中:费用由“会员所属服务门店”、“导购所在门店”承担时,最终都会根据门店所属的“独立计费部门”进行汇总核算和扣除,若产生费用时会员无服务门店,则由总部承担费用;<br />
2、计费规则中:费用由“活动创建人”承担时,会根据该管理员所属的“独立计费部门”进行汇总核算和扣除;<br />
注意:若管理员或门店所归属的部门未被设置为“独立计费部门”,则系统将依次向其上级部门查询是否被设置为独立计费部门,查询不到则记录到“企业总部”账上;
</div>
</div>`
);
},
beforeDestroy() {
this.$store.commit('mutations_layoutTips', '');
},
methods: {
// table-methods
async getTableData() {
const para = {
searchParams: this.search.departName,
status: this.search.status,
pageSize: this.tableData.pageSize,
currentPage: this.tableData.currentPage
};
const { result } = await getDepartList(para);
this.tableData.data = result.result || [];
this.tableData.total = result.totalCount;
},
getTableHeader() {
this.tableData.header = [
{
label: { name: '独立计费部门' },
prop: 'accountDepartName',
minWidth: 120
},
{
label: { name: '上级部门' },
prop: 'parentDepartName',
minWidth: 120
},
{
label: { name: '计费门店数', tipText: '由此“独立计费部门”统一核算费用的门店数,若当前部门的某下级部门被设置为了独立计费部门并且为启用状态,则此处将不包含下级部门的门店数' },
prop: '',
minWidth: 120,
formatter: row => row.storeCount || 0
},
{
label: { name: '账户余额(元)' },
prop: 'accountFunds',
minWidth: 120,
formatter: row => (row.storeCount ? parseFloat(row.storeCount).toFixed(2) : '0.00')
},
{
label: { labnameel: '添加时间' },
prop: 'createTime',
minWidth: 120,
formatter: row => (row.createTime ? formatDateTimeByType(row.createTime, 'yyyy-MM-dd hh-mm-ss') : '--')
}
];
},
handleSizeChange(val) {
this.tableData.pageSize = val;
this.tableData.currentPage = 1;
this.getTableData();
},
handleCurrentChange(val) {
this.tableData.currentPage = val;
this.getTableData();
},
// 渲染表头icon
onRender(h, { column }) {
let isShowTip = column.label.name == '计费门店数';
return h('span', [
h('span', column.label.name),
h(isShowTip && tip, {
props: { text: column.label.tipText }
})
]);
}
},
computed: {
// do something
},
watch: {
// do something
}
};
</script>
<style scoped lang="scss">
.top {
display: flex;
justify-content: space-between;
align-items: center;
}
</style>
<template>
<!--
所有账户余额: 仅开启多账户模式时,显示此字段 部门权限为可见所有的用户或者超管登录时可显示【所有账户余额】字段;
账户明细按钮: 仅开启多账户模式时,显示此字段 开启多账户模式下的超管可见 || 开启多账户模式下的子账号部门权限为所有并且赋予了【
】操作按钮权限时可见
首次开启多账户计费规则时,点击保存按钮则弹框二次提示,保存成功后显示保存成功弹框,点击保存成功
计费部门筛选框规则:开启多账户计费模式时显示计费部门筛选框; && 超管和部门权限为可见所有的子账号显示计费部门筛选框;
-->
<section class="recharge">
<div class="dm-wrap">
<h3 class="dm-title__label--outer">账户余额</h3>
<span class="fz22 danger-color vertical-middle pr20">{{ (recharge.balance / 100) | amount }}</span>
<el-button class="ml10 vertical-middle" size="mini" type="primary" @click="$router.push('/recharge/do')">充值</el-button>
<el-button style="padding: 3px 0" type="text" @click="$router.push('/recharge/record')">记录</el-button>
<div class="flex_between">
<h3 class="title">账户余额</h3>
<el-button class="h32 ghost-btn" type="primary" @click="setRuleVisible = true">计费规则配置</el-button>
</div>
<div class="money_wrap">
<div class="current">
<div class="sub_title">账户余额(元)</div>
<p class="fz22 vertical-middle money" @click="$router.push('/recharge/record')">{{ (recharge.balance / 100) | amount }}<i class="el-icon-arrow-right arrow" /></p>
<el-button class="vertical-middle pl20 pr20" type="primary" @click="$router.push('/recharge/do')">充值</el-button>
<el-button class="vertical-middle pl20 pr20" @click="$router.push('/recharge/record')">记录</el-button>
</div>
<div class="all" v-if="isMoreAccount">
<div class="sub_title">
所有账户余额(元)<span class="tip"><i class="iconfont icon-anquanbaozhang" />已开启多账户计费模式</span>
</div>
<p class="fz22 vertical-middle money" @click="$router.push('/recharge/accountDetail')">{{ (recharge.balance / 100) | amount }}<i class="el-icon-arrow-right arrow" /></p>
<!--该按钮待埋点-->
<!-- <el-button v-if="getCodeAuth('marketingAccountDetail')" :limit-code="getCode('marketingAccountDetail')" class="vertical-middle" @click="$router.push('/recharge/accountDetail')">账户明细</el-button> -->
<el-button class="vertical-middle" @click="$router.push('/recharge/accountDetail')">账户明细</el-button>
</div>
</div>
</div>
<div class="dm-wrap">
<h3 class="dm-title__label--outer">今日消费</h3>
<h3 class="title">今日消费</h3>
<select-depart v-if="isMoreAccount" :data="deparment1" class="mb20" @load="() => load('one')" @getDepartId="id => getDepartId(id, 'one')" @remote-search="val => remoteSearch(val, 'one')" :loading="deparment1.loading" />
<el-row class="recharge-today" :gutter="20">
<el-col :span="5">
<div class="recharge-today-item border2 clearfix">
<div class="text-center fl">
<el-col :span="6">
<div class="recharge-today-item border2 flex_between">
<div class="text-center">
<div class="icon-box">
<i class="iconfont icon-tijianyi fz28"></i>
<i class="iconfont icon-wenbenmsg fz36"></i>
</div>
<p>文本短信</p>
</div>
<div class="text-center fr">
<p class="text-right">
<span class="fz30">{{ recharge.messageCount || 0 }}</span>
</p>
<div class="flex_between flex1 pl10">
<div class="flex_column">
<p>文本短信</p>
<p>
<span class="fz22">{{ recharge.messageCount || 0 }}</span>
</p>
</div>
<p class="regular-font-color">合计费用:¥{{ (recharge.messageFee / 1000) | amount }}</p>
</div>
</div>
</el-col>
<el-col :span="5">
<div class="recharge-today-item border2 clearfix">
<div class="text-center fl">
<el-col :span="6">
<div class="recharge-today-item border2 flex_between">
<div class="text-center">
<div class="icon-box color2">
<i class="iconfont icon-shoujiyanzhengma fz30"></i>
<i class="iconfont icon-duanxinyzm fz28"></i>
</div>
<p>短信验证码</p>
</div>
<div class="text-center fr">
<p class="text-right">
<span class="fz30">{{ recharge.smsCount || 0 }}</span>
</p>
<div class="flex_between flex1 pl10">
<div class="flex_column">
<p>短信验证码</p>
<p>
<span class="fz22">{{ recharge.smsCount || 0 }}</span>
</p>
</div>
<p class="regular-font-color">合计费用:¥{{ (recharge.smsFee / 1000) | amount }}</p>
</div>
</div>
</el-col>
<el-col :span="5">
<div class="recharge-today-item border2 clearfix">
<div class="text-center fl">
<el-col :span="6">
<div class="recharge-today-item border2 flex_between">
<div class="text-center">
<div class="icon-box color3">
<i class="iconfont icon-yuyinxiaoxi fz30"></i>
<i class="iconfont icon-shuangxianghujiao fz36"></i>
</div>
<p>语音验证码</p>
</div>
<div class="text-center fr">
<p class="text-right">
<span class="fz30">{{ recharge.voiceCount || 0 }}</span>
</p>
<p class="regular-font-color">合计费用:¥{{ (recharge.voiceFee / 1000) | amount }}</p>
</div>
</div>
</el-col>
<el-col :span="5">
<div class="recharge-today-item border2 clearfix">
<div class="text-center fl">
<div class="icon-box color4">
<i class="iconfont icon-shuangxianghujiao fz26"></i>
<div class="flex_between flex1 pl10">
<div class="flex_column">
<p>双向呼叫</p>
<p>
<span class="fz22">{{ recharge.callTime || 0 }}</span>
分钟
</p>
</div>
<p>双向呼叫</p>
</div>
<div class="text-center fr">
<p class="text-right">
<span class="fz30">{{ recharge.callTime || 0 }}</span> 分钟
</p>
<p class="regular-font-color">合计费用:¥{{ (recharge.callFee / 1000) | amount }}</p>
</div>
</div>
</el-col>
<el-col :span="5">
<div class="recharge-today-item border2 clearfix">
<div class="text-center fl">
<div class="icon-box color5">
<i class="iconfont icon-luyinzhong fz26"></i>
<el-col :span="6">
<div class="recharge-today-item border2 flex_between">
<div class="text-center">
<div class="icon-box color4">
<i class="iconfont icon-tonghualuyin fz36"></i>
</div>
<p>通话录音</p>
</div>
<div class="text-center fr">
<p class="text-right">
<span class="fz30">{{ recharge.recordCallTime || 0 }}</span> 分钟
</p>
<div class="flex_between flex1 pl10">
<div class="flex_column">
<p>通话录音</p>
<p>
<span class="fz22">{{ recharge.recordCallTime || 0 }}</span>
分钟
</p>
</div>
<p class="regular-font-color">合计费用:¥{{ (recharge.recordCallfee / 100) | amount }}</p>
</div>
</div>
</el-col>
<!-- <el-col :span="5">
<div class="recharge-today-item border2 clearfix">
<div class="text-center fl">
<div class="icon-box color6">
<i class="iconfont icon-shipinliuliang fz26"></i>
</div>
<p>视频流量</p>
</div>
<div class="text-center fr">
<p class="text-right">
<span class="fz30">{{ videoTraffic.traffic || 0 }}</span> GB
</p>
<p class="regular-font-color">合计费用:¥{{ videoTraffic.trafficCost | amount }}</p>
</div>
</div>
</el-col> -->
</el-row>
</div>
<div class="dm-wrap" v-loading="loading">
<div class="pb22">
<div slot="header" class="clearfix">
<el-date-picker :pickerOptions="pickerOptions" v-model="dateTime" type="daterange" range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期" @change="consumeRecord"></el-date-picker>
<select-depart v-if="isMoreAccount" :data="deparment2" @load="() => load('two')" @getDepartId="id => getDepartId(id, 'two')" @remote-search="val => remoteSearch(val, 'two')" :loading="deparment2.loading" />
<span class="fz12 gray">* 此处仅支持筛选近半年的统计数据</span>
</div>
</div>
......@@ -125,17 +131,16 @@
</template>
</template>
</el-table-column>
<el-table-column align="left" prop="fee" label="费用">
<el-table-column align="left" prop="fee" label="费用(元)">
<template slot="header" slot-scope="scope">
费用
费用(元)
<el-tooltip :open-delay="200" class="item" effect="dark" placement="top">
<div slot="content" style="width:300px;line-height:1.5;">由于运营商的计费单价会有调整以及海外短信是按不同地区进行费用扣除。但此处是用统一用当下的单价乘以计费条数进行合计,所以可能导致此处的费用和详情不一致,最终以详情的合计金额为准。</div>
<i class="el-icon-info minor-font-color cursor-pointer ml5"></i>
<i class="iconfont icon-QuestionCircleOutlined minor-font-color cursor-pointer ml5"></i>
</el-tooltip>
</template>
<template slot-scope="scope">
<span>{{ (scope.row.fee / (scope.row.type === 'record' ? 100 : 1000)).toFixed(2) }}</span
>
<span->{{ (scope.row.fee / (scope.row.type === 'record' ? 100 : 1000)).toFixed(2) }}</span->
</template>
</el-table-column>
<el-table-column label="操作" align="left" width="220px" fixed="right">
......@@ -145,31 +150,77 @@
</el-table-column>
</el-table>
</div>
<set-rule-drawer :visible.sync="setRuleVisible" :data="accountRule" @getNewData="getRule" />
</section>
</template>
<script>
import { rechargeCenter, consumeRecord, todayVideoTraffic } from '@/service/api/rechargeApi.js';
import { rechargeCenter, consumeRecord, todayVideoTraffic, getDepartList, getAccountRule } from '@/service/api/rechargeApi.js';
import { formatDateTimeByType } from '@/utils/index.js';
import SelectDepart from './components/select-depart.vue';
import setRuleDrawer from './components/set-rule-dialog.vue';
import authCodeMethods from '@/utils/authCodeMethods';
export default {
name: 'recharge',
components: {
SelectDepart,
setRuleDrawer
},
mixins: [authCodeMethods],
data() {
return {
dateTime: [Date.now(), Date.now()],
minDate: new Date(),
loading: false,
tableList: [],
recharge: {},
listParams: {
beginTime: '',
endTime: ''
endTime: '',
accountDepartId: ''
},
videoTraffic: {},
pickerOptions: {
disabledDate(val) {
return Date.now() >= val.getTime() + 6 * 30 * 24 * 60 * 60 * 1000;
disabledDate: val => {
// 只能筛选半年之内得数据,并且最大跨度为一个月
const halfYearBefore = Date.now() - 6 * 30 * 24 * 60 * 60 * 1000;
const oneMothods = 30 * 24 * 60 * 60 * 1000;
if (this.minDate) {
const curTime = this.minDate.getTime();
const min = curTime - oneMothods > halfYearBefore ? curTime - oneMothods : halfYearBefore;
return val.getTime() > curTime + oneMothods || val.getTime() < min;
}
return val.getTime() <= halfYearBefore;
},
onPick: ({ maxDate, minDate }) => {
this.minDate = minDate;
}
}
},
deparment1: {
list: [],
searchParams: '',
status: -1,
pageSize: 20,
currentPage: 1,
loading: false,
isLimit: false, // 是否最后一页
departId: '', // 选中时得id
zbFlag: 1
},
deparment2: {
list: [],
searchParams: '',
status: -1,
pageSize: 20,
currentPage: 1,
loading: false,
isLimit: false,
departId: '', // 选中时得id
zbFlag: 1
},
accountRule: {},
isMoreAccount: false,
setRuleVisible: false
};
},
created() {
......@@ -177,6 +228,8 @@ export default {
this.rechargeCenter();
this.consumeRecord();
// this.getTodayVideoTraffic();
this.getDepartmentList();
this.getRule();
this.$store.commit('mutations_breadcrumb', [{ name: '企业管理', path: '' }, { name: '计费中心', path: '' }]); // eslint-disable-line
},
filters: {
......@@ -199,7 +252,7 @@ export default {
},
async rechargeCenter() {
try {
let res = await rechargeCenter();
let res = await rechargeCenter({ accountDepartId: this.deparment1.departId });
this.recharge = res.result.account;
} catch (err) {
console.log(err);
......@@ -214,21 +267,84 @@ export default {
this.listParams.beginTime = '';
this.listParams.endTime = '';
}
this.listParams.accountDepartId = this.deparment2.departId;
try {
let res = await consumeRecord(this.listParams);
const result = res.result;
// , { name: '视频流量', type: 'video', fee: result.allTraffic, count: result.allTrafficCost }
this.tableList = [{ name: '短信营销', type: 'marketing', fee: result.messageFee, count: result.messageCount }, { name: '短信验证码', type: 'sms', fee: result.smsFee, count: result.smsCount }, { name: '语音验证码', type: 'voice', fee: result.voiceFee, count: result.voiceCount }, { name: '双向呼叫', type: 'call', fee: result.callFee, count: result.callTime }, { name: '通话录音', type: 'record', fee: result.recordCallfee, count: result.recordCallTime }]; // eslint-disable-line
this.tableList = [{ name: '短信营销', type: 'marketing', fee: result.messageFee, count: result.messageCount }, { name: '短信验证码', type: 'sms', fee: result.smsFee, count: result.smsCount }, { name: '双向呼叫', type: 'call', fee: result.callFee, count: result.callTime }, { name: '通话录音', type: 'record', fee: result.recordCallfee, count: result.recordCallTime }]; // eslint-disable-line
} catch (err) {
console.log(err);
}
this.loading = false;
},
// 获取计费部门
async getDepartmentList(type) {
// 该方法对不同得计费部门下拉做处理, 默认初始加载取deparment1 || deparment2均可,因为参数相同
const depart = type == 'one' ? this.deparment1 : this.deparment2;
depart['loading'] = true;
const para = Object.assign({}, depart);
const typeArr = ['list', 'loading', 'isLimit', 'departId'];
this.deleteAttribute(para, typeArr);
let { result } = await getDepartList(para);
if (type) {
this.setDepartData(depart, result);
} else {
this.setDepartData(this.deparment1, result);
this.setDepartData(this.deparment2, result);
}
},
// 清除接口无用属性
deleteAttribute(obj, typeArr) {
for (const key in obj) {
if (typeArr.includes(key)) delete obj[key];
}
},
// 设置departmentData
setDepartData(obj, result) {
const data = result.result || [];
const total = result.totalCount;
obj['list'].push(...data);
obj['isLimit'] = obj['list'].length >= total; // list得数据量>=数据总数时做出限制
obj['loading'] = false;
},
// 下拉滚动加载
load(type) {
const depart = type == 'one' ? this.deparment1 : this.deparment2;
if (depart['isLimit'] || depart['loading']) return;
depart['currentPage']++;
this.getDepartmentList(type);
},
getDepartId(id, type) {
// 选中不同下拉得id时,调用对应得表格方法
const depart = type == 'one' ? this.deparment1 : this.deparment2;
depart['departId'] = id;
if (type == 'one') this.rechargeCenter();
else this.consumeRecord();
},
// 下拉远程搜索
remoteSearch(val, type) {
const depart = type == 'one' ? this.deparment1 : this.deparment2;
depart['searchParams'] = val;
depart['currentPage'] = 1;
depart['isLimit'] = false;
depart['list'] = [];
this.getDepartmentList(type);
},
// 获取计费规则
async getRule() {
const result = await getAccountRule();
this.accountRule = result.result || {};
this.isMoreAccount = !!this.accountRule.status;
}
}
};
</script>
<style lang="scss" scoped>
.recharge {
font-family: PingFangSC-Regular, PingFang SC;
}
.recharge .box-card {
margin: 24px;
}
......@@ -242,26 +358,29 @@ export default {
.icon-box {
width: 60px;
height: 60px;
background: rgba(240, 80, 80, 1);
border-radius: 3px;
background: rgba(253, 107, 56, 0.15);
border-radius: 4px;
i {
line-height: 60px;
color: #fd6b38;
}
&.color2 {
background: #7366ba;
background: rgba(38, 218, 208, 0.15);
i {
color: #26dad0;
}
}
&.color3 {
background: #23b7e5;
background: rgba(92, 137, 255, 0.15);
i {
color: #5c89ff;
}
}
&.color4 {
background: #4fa6f1;
}
&.color5 {
background: #f39925;
}
&.color6 {
background: #52c41a;
}
i {
line-height: 60px;
color: #fff;
background: rgba(250, 140, 22, 0.15);
i {
color: #fa8c16;
}
}
}
.fl {
......@@ -281,4 +400,71 @@ export default {
}
}
}
.title {
font-size: 16px;
color: #303133;
line-height: 22px;
font-weight: 600;
margin-bottom: 24px;
}
.money_wrap {
display: flex;
.current,
.all {
flex: 1;
}
.sub_title {
color: #606266;
line-height: 20px;
}
.money {
margin: 6px 0 40px;
font-size: 26px;
font-family: PingFangSC-Semibold, PingFang SC;
font-weight: 600;
color: #303133;
line-height: 37px;
cursor: pointer;
display: flex;
align-items: center;
&:hover {
color: #1890ff;
.arrow {
color: #1890ff;
}
}
.arrow {
margin-left: 4px;
color: #909399;
}
}
.tip {
color: #606266;
line-height: 20px;
i {
color: #1890ff;
margin-right: 5px;
}
}
}
.flex_between {
display: flex;
justify-content: space-between;
}
.flex_column {
display: flex;
flex-direction: column;
justify-content: space-between;
}
.flex1 {
flex: 1;
}
.fz22 {
font-size: 22px;
font-weight: 600;
}
.regular-font-color {
align-self: flex-end;
text-align: right;
}
</style>
<template>
<el-dialog title="增加独立计费部门" :visible.sync="visible" width="420px" custom-class="dialog" @close="onClose">
<div class="content mt27">
<span class="label">选择部门:</span>
<div class="border">
<el-input class="mb10" v-model="search" prefix-icon="el-icon-search" placeholder="请输入部门名称" @change="onSearchTree"></el-input>
<el-tree ref="tree" class="tree" default-expand-all :data="treeData" check-strictly node-key="departId" :props="props" show-checkbox @check-change="handleCheckChange" :filter-node-method="filterNode">
<span slot-scope="{ node, data }">
<span>{{ node.label }}</span>
</span>
</el-tree>
</div>
</div>
<div class="text-right mt20">
<el-button plain>取消</el-button>
<el-button type="primary">确认</el-button>
</div>
</el-dialog>
</template>
<script>
// addAccountDepart
import { getAllDepart } from '@/service/api/rechargeApi.js';
export default {
name: 'addDepartment',
props: {
visible: Boolean
},
data() {
return {
departId: [],
search: '',
treeData: [],
props: {
children: 'childList',
label: 'departName'
}
};
},
methods: {
onClose() {
this.$emit('update:visible', false);
},
async getDepart() {
const { result } = await getAllDepart();
this.treeData = [result] || [];
},
onSearchTree() {
this.$refs.tree.filter(this.search);
},
filterNode(value, data) {
if (!value) return true;
return data.departName.indexOf(value) !== -1;
}
},
watch: {
visible: {
handler: function(newVal) {
if (newVal) {
this.getDepart();
this.search = '';
}
},
immediate: true
}
}
};
</script>
<style scoepd lang="scss">
.dialog {
height: 463px;
position: relative;
.label {
display: inline-block;
width: 100px;
}
.border {
width: 100%;
height: 100%;
padding: 12px;
border: 1px solid #e4e7ed;
}
.content {
display: flex;
align-items: flex-start;
}
.tree {
height: 245px;
overflow-y: auto;
}
}
</style>
<template>
<el-select v-model="departId" placeholder="请选择计费部门" class="select_more" ref="select" @change="handleChange" filterable remote :remote-method="remoteMethod" suffix-icon="el-icon-search">
<div class="infinite-list" ref="infinite-list" v-infinite-scroll="load" style="overflow:auto;max-height:260px">
<el-option v-for="item in data.list" :key="item.accountDepartId" :value="item.accountDepartId" :label="item.accountDepartName">{{ item.accountDepartName }}</el-option>
<p style="text-align: center" :hidden="!loading"><i class="el-icon-loading"></i></p>
</div>
</el-select>
</template>
<script>
export default {
name: 'select-depart',
props: {
data: {
type: Object,
default: () => {}
},
loading: Boolean
},
mounted() {
this.$refs['select'].$el.querySelector('.el-select-dropdown__wrap').style.overflow = 'inherit';
},
data() {
return {
departId: ''
};
},
methods: {
load() {
this.$emit('load');
},
handleChange() {
this.$emit('getDepartId', this.departId);
},
remoteMethod(val) {
this.$refs['infinite-list'].scrollTop = 0;
this.$emit('remote-search', val);
}
}
};
</script>
<style lang="scss" scoped>
.select_more {
display: inline-block;
width: 160px;
position: relative;
// &::before {
// content: '\e778';
// position: absolute;
// font-family: element-icons !important;
// color: #909399;
// right: 11px;
// top: 9px;
// z-index: 2;
// }
}
</style>
<template>
<el-drawer :visible.sync="visible" direction="rtl" :before-close="beforeClose" size="600px" custom-class="drawer">
<template v-slot:title>
<h2>计费规则配置</h2>
</template>
<el-form ref="form" :model="form" class="content" label-width="170px" :rules="rules">
<p class="tip lineh17">开启“多账户模式”后,系统将根据所配置的计费规则将通讯费用按照“独立计费部门”进行汇总核算及扣除费用;开启后次日凌晨生效,并且开启后不允许关闭,请谨慎操作;</p>
<el-form-item label="是否开启多账户模式:" class="mt17 mb17" prop="status">
<el-radio-group v-model="form.status" :disabled="isMoreAccount">
<el-radio :label="0">关闭</el-radio>
<el-radio :label="1">开启</el-radio>
</el-radio-group>
</el-form-item>
<template v-if="form.status">
<hr class="mb10" />
<div class="info mb15">
<i class="iconfont icon-warning-circle-fill icon" />
<div class="lineh22">
<p class="tip">计费规则说明:</p>
<p class="tip">1、会员所属服务门店:由会员所属的服务门店承担费用(最终根据门店所属的“独立计费部门”进行汇总核算和扣除),若产生费用时会员无服务门店,则由总部承担费用;</p>
<p class="tip">2、活动创建人:由此活动的创建人来承担费用(最终根据该管理员所属的“独立计费部门”进行汇总核算和扣除);</p>
<p class="tip">3、导购所在门店:由导购所在的门店承担费用(最终根据门店所属的“独立计费部门”进行汇总核算和扣除);</p>
<p class="tip">4、总部:由“总部”承担费用;</p>
</div>
</div>
<template>
<h2 class="icon-type-title mb20">
<div class="mark" />
短信
</h2>
<el-form-item label="短信营销:" prop="smsPlanRule">
<el-radio-group v-model="form.smsPlanRule">
<el-radio :label="2">会员所属服务门店</el-radio>
<el-radio :label="3">活动创建人</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="智能营销发送短信:" prop="smsEcmRule">
<el-radio-group v-model="form.smsEcmRule">
<el-radio :label="2">会员所属服务门店</el-radio>
<el-radio :label="3">活动创建人</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="会员修改手机号:" prop="smsValidCodeRule">
<el-radio-group v-model="form.smsValidCodeRule">
<el-radio :label="2">会员所属服务门店</el-radio>
<el-radio :label="1">总部</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="会员认证开卡:" prop="smsOpenCardRule">
<el-radio-group v-model="form.smsOpenCardRule">
<el-radio :label="1">总部</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="好办-导购绑定手机号:" prop="smsHaobanRule">
<el-radio-group v-model="form.smsHaobanRule">
<el-radio :label="4">导购所在门店</el-radio>
</el-radio-group>
</el-form-item>
</template>
<hr class="dashed mb20" />
<template>
<h2 class="icon-type-title mb20">
<div class="mark" />
双向呼叫&录音
</h2>
<el-form-item label="双向呼叫&录音:" prop="callRule">
<el-radio-group v-model="form.callRule">
<el-radio :label="4">导购所在门店</el-radio>
<el-radio :label="3">活动创建人</el-radio>
</el-radio-group>
</el-form-item>
</template>
</template>
</el-form>
<div class="demo-drawer__footer footer">
<el-button @click="close">取 消</el-button>
<el-button type="primary" @click="save">保 存</el-button>
</div>
</el-drawer>
</template>
<script>
import { saveAccountRule } from '@/service/api/rechargeApi';
export default {
name: 'setRuleDrawer',
props: {
visible: Boolean,
data: Object
},
data() {
function validate(val, cb, arr) {
if (!arr.includes(val)) return cb(new Error('请选择内容'));
cb();
}
return {
form: {
status: 0, // 是否多账户
smsPlanRule: 2, // 短信营销
smsEcmRule: 2, // 智能引擎
smsValidCodeRule: 1, // 修改手机号
smsOpenCardRule: 1, // 认证
smsHaobanRule: 4, // 好办
callRule: 4 // 双向呼叫
},
isMoreAccount: false,
rules: {
smsPlanRule: [{ required: true, validator: (rule, val, cb) => validate(val, cb, [2, 3]) }],
smsEcmRule: [{ required: true, validator: (rule, val, cb) => validate(val, cb, [2, 3]) }],
smsValidCodeRule: [{ required: true, validator: (rule, val, cb) => validate(val, cb, [2, 1]) }],
smsOpenCardRule: [{ required: true, validator: (rule, val, cb) => validate(val, cb, [1]) }],
smsHaobanRule: [{ required: true, validator: (rule, val, cb) => validate(val, cb, [4]) }],
callRule: [{ required: true, validator: (rule, val, cb) => validate(val, cb, [3, 4]) }]
}
};
},
methods: {
close() {
this.$emit('update:visible', false);
},
save() {
this.$refs['form'].validate(async valid => {
if (valid) {
const options1 = { confirmButtonText: '确认开启', cancelButtonText: '取消', type: 'warning' };
const options2 = { confirmButtonText: '立即设置', cancelButtonText: '稍后设置', type: 'success' };
try {
// 单商户转多商户二次弹窗确认
if (!this.isMoreAccount) await this.$confirm('开启多账户计费模式次日凌晨生效,并且开启', '确认开启多账户模式?', options1);
const para = this.form.status ? this.form : { status: 0 };
await saveAccountRule(para);
// 如果从单商户切换多商户保存了那么就修改isMoreAccount得状态,组件此时不会重新render.同时发送给父组件让父组件重新调用获取商户配置接口
if (para.status == 1 && !this.isMoreAccount) {
this.isMoreAccount = true;
this.$emit('getNewData');
}
try {
await this.$confirm('赶紧去设置不同的计费账户吧,也可点击【计费中心-账户明细】配置不同的计费账户', '保存成功', options2);
this.$router.push(this.$router.push('/recharge/accountDetail'));
} catch (e) {
this.$emit('update:visible', false);
return false;
}
} catch (e) {
return false;
}
}
});
},
extend(to, from) {
for (let key in to) {
to[key] = from[key];
}
},
beforeClose() {
this.$emit('update:visible', false);
}
},
watch: {
data: {
handler: function(newVal) {
this.isMoreAccount = !!newVal.status;
this.extend(this.form, newVal);
this.form['status'] = this.form['status'] || 0;
},
deep: true,
immediate: true
}
}
};
</script>
<style lang="scss" scoped>
.drawer {
position: relative;
}
h2 {
color: #303133;
font-size: 16px;
font-weight: 600;
}
.content {
font-family: PingFangSC-Regular, PingFang SC;
padding: 0 20px;
color: #303133;
.tip {
color: #909399;
font-size: 12px;
}
.lineh17 {
line-height: 17px;
}
.lineh22 {
line-height: 22px;
}
hr {
height: 1px;
background: #e4e7ed;
outline: none;
border: none;
&.dashed {
height: 0;
border: 1px dashed #e4e7ed;
background: none;
}
}
.info {
display: flex;
.icon {
margin-right: 9px;
color: #1890ff;
display: block;
}
}
.icon-type-title {
display: flex;
align-items: center;
height: 20px;
color: #303133;
font-size: 16px;
font-weight: 700;
padding: 10px;
background: #f7f8fa;
.mark {
width: 3px;
height: 14px;
margin-right: 8px;
background-color: #1890ff;
}
}
}
.footer {
box-shadow: 0px -2px 8px 0px rgba(220, 223, 230, 0.6);
padding: 0 20px;
box-sizing: border-box;
height: 56px;
position: absolute;
width: 100%;
display: flex;
justify-content: flex-end;
align-items: center;
bottom: 0;
}
</style>
<template>
<section class="recharge">
<div class="dm-wrap">
日期:<el-date-picker :disabled="loading" :clearable="false" :pickerOptions="pickerOptions" v-model="dateTime" type="daterange" range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期" @change="loadAll(false)"></el-date-picker>
<span class="fz12 gray">* 此处仅支持筛选近半年的统计数据</span>
</div>
<div class="dm-form__wrap">
<h3 class="dm-title__label">趋势分析图</h3>
<div class="text-center fz16" v-if="$route.params.type === 'record'">{{ formatDateTimeByType(dateTime[0], 'yyyy-MM-dd') }}{{ formatDateTimeByType(dateTime[1], 'yyyy-MM-dd') }} 成功存储:{{ sumCount || 0 }} 分钟 总计消费:{{ (sumFee / 100).toFixed(2) }}</div>
<div class="text-center fz16" v-if="$route.params.type === 'video'">{{ formatDateTimeByType(dateTime[0], 'yyyy-MM-dd') }}{{ formatDateTimeByType(dateTime[1], 'yyyy-MM-dd') }} 共消耗流量{{ sumCount }}MB 累计支出{{ sumFee }}</div>
<div class="text-center fz16" v-else>
{{ formatDateTimeByType(dateTime[0], 'yyyy-MM-dd') }}{{ formatDateTimeByType(dateTime[1], 'yyyy-MM-dd') }} 成功发送:国内短信{{ localCount || 0 }}条,国外短信{{ sumInternational || 0 }}
<!-- <template v-if="['sms', 'marketing'].includes($route.params.type)">,其中国内短信{{ localCount || 0 }}条,国外短信{{ sumInternational || 0 }}</template> -->
&nbsp;总计消费:{{ (sumFee / 1000).toFixed(2) }} 元
</div>
<div>
<div id="mountNode" ref="mountNode" v-show="list.length"></div>
<div class="chart--nodata" v-show="!list.length"></div>
</div>
</div>
<div class="dm-form__wrap" v-loading="loading">
<div class="pb22" style="overflow:hidden;line-height:40px;" v-if="$route.params.type === 'video'">
<!-- 视频资费 -->
......@@ -25,16 +7,29 @@
<span class="fz12 gray">* 此处时间跨度不超过7天</span>
<el-button type="primary" class="fr" icon="iconfont icon-icon_yunxiazai fz14 mr5" @click="downloadTrafficCostListExcel">导出记录</el-button>
</div>
<div v-else class="pb22" style="overflow:hidden;line-height:40px;">
<!-- 非视频资费 -->
<!-- <el-select v-if="$route.params.type === 'call'" class="dm-select" v-model="taskType" placeholder="选择发送状态" @change="loadAll(true)">
<el-option v-for="item in taskTypeOptions" :key="item.taskType" :label="item.ecmName" :value="item.taskType"></el-option>
</el-select> -->
<el-input v-model="listParams.searchParam" :style="setInputWidth()" :placeholder="placeholder" clearable @change="loadAll(true)"><i slot="prefix" class="el-input__icon el-icon-search"></i></el-input>
<el-select v-if="$route.params.type === 'sms'" class="dm-select" v-model="listParams.channelType" clearable placeholder="选择类型" @change="loadAll(true)">
<el-option label="会员验证码" value="1"></el-option>
<el-option label="导购验证码" value="2"></el-option>
</el-select>
<div v-else class="pb20" style="overflow:hidden;line-height:40px;">
<div class="flex_between">
<div>
<el-date-picker :disabled="loading" :clearable="false" :pickerOptions="pickerOptions" v-model="dateTime" type="daterange" range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期" @change="loadAll(false)"></el-date-picker>
<el-input v-model="listParams.searchParam" :style="setInputWidth()" :placeholder="placeholder" clearable @change="loadAll()"><i slot="prefix" class="el-input__icon el-icon-search"></i></el-input>
<el-select v-if="$route.params.type === 'sms'" class="dm-select" v-model="listParams.channelType" clearable placeholder="选择类型" @change="loadAll()">
<el-option label="会员验证码" value="1"></el-option>
<el-option label="导购验证码" value="2"></el-option>
</el-select>
<select-depart v-if="isMoreAccount" :data="deparment" @load="load" @getDepartId="getDepartId" @remote-search="remoteSearch" :loading="deparment.loading" />
<span class="fz12 gray">* 此处仅支持筛选近半年的统计数据</span>
</div>
<el-button type="primary" class="btn h32" @click="exportDialog.dialogVisible = true"><i class="iconfont icon-xiazai" style="marginRight:6px;fontSize:14px" />导出</el-button>
</div>
<p class="tips" v-if="$route.params.type === 'video'">共消耗流量{{ sumCount }}MB 累计支出{{ sumFee }}</p>
<p class="tips" v-if="['record', 'call'].includes($route.params.type)">
通话时长:{{ sumCount || 0 }}
<span class="ml10 blod">总计消费:{{ (sumFee / 100).toFixed(2) }}</span>
</p>
<p class="tips" v-else>
成功发送:国内短信{{ localCount || 0 }}条,国外短信{{ sumInternational || 0 }}
<span class="ml10 blod">总计消费:{{ (sumFee / 1000).toFixed(2) }}</span>
</p>
</div>
<!-- 短信营销 -->
<el-table tooltipEffect="light" :data="tableList" style="width:100%" v-if="$route.params.type === 'marketing'">
......@@ -46,18 +41,20 @@
</p>
</template>
</el-table-column>
<el-table-column align="left" width="150" prop="telephone" label="接收号码"></el-table-column>
<el-table-column align="left" width="100" prop="countNum" label="计费条数">
<el-table-column align="left" min-width="120" prop="telephone" label="接收号码"></el-table-column>
<el-table-column align="left" min-width="100" prop="countNum" label="计费条数">
<template slot-scope="scope">{{ scope.row.countNum }}</template>
</el-table-column>
<el-table-column :show-overflow-tooltip="true" align="left" width="320" prop="storeName" label="门店">
<el-table-column :show-overflow-tooltip="true" align="left" min-width="150" prop="storeName" label="门店">
<template slot-scope="scope">
<p>{{ scope.row.storeName }}</p>
<p class="gray">{{ scope.row.storeGroupName }}</p>
</template>
</el-table-column>
<el-table-column label="触发计划名称" :show-overflow-tooltip="true" align="left" width="300" prop="sourceName"></el-table-column>
<el-table-column label="计划创建人" align="left" width="100" prop="sourceCreatorName"></el-table-column>
<el-table-column label="触发计划名称" :show-overflow-tooltip="true" align="left" min-width="110" prop="sourceName"></el-table-column>
<el-table-column label="活动创建人" align="left" min-width="100" prop="sourceCreatorName"></el-table-column>
<el-table-column v-if="isMoreAccount" label="扣费规则" align="left" min-width="100" prop=""></el-table-column>
<el-table-column v-if="isMoreAccount" label="费用归属部门" align="left" min-width="120" prop=""></el-table-column>
<!-- <el-table-column label="模板ID" align="left" width="100" prop="chartsDate"></el-table-column> -->
<el-table-column label="短信内容" align="left" min-width="200" prop="remark">
<template slot-scope="scope">
......@@ -83,8 +80,11 @@
<template slot-scope="scope">
<p>{{ scope.row.storeName }}</p>
<p class="fz13 gray">{{ scope.row.storeGroupName }}</p>
<p v-if="!scope.row.storeName && !scope.row.storeGroupName">--</p>
</template>
</el-table-column>
<el-table-column v-if="isMoreAccount" label="扣费规则" align="left" min-width="100" prop=""></el-table-column>
<el-table-column v-if="isMoreAccount" label="费用归属部门" align="left" min-width="120" prop=""></el-table-column>
<el-table-column :show-overflow-tooltip="true" label="短信内容" align="left" min-width="200" prop="paramInfo">
<template slot-scope="scope">您的验证码是:{{ scope.row.paramInfo }} </template>
</el-table-column>
......@@ -100,7 +100,7 @@
</el-table-column>
</el-table>
<!-- 语音验证码 -->
<el-table tooltipEffect="light" :data="tableList" style="width:100%" v-if="$route.params.type === 'voice'">
<!-- <el-table tooltipEffect="light" :data="tableList" style="width:100%" v-if="$route.params.type === 'voice'">
<el-table-column align="left" prop="createTime" label="发送时间" width="170px">
<template slot-scope="scope">
<p class="cell-time">
......@@ -121,7 +121,7 @@
{{ scope.row.status === 1 ? '成功' : '失败' }}
</template>
</el-table-column>
</el-table>
</el-table> -->
<!-- 双向呼叫 -->
<el-table tooltipEffect="light" :data="tableList" style="width:100%" v-if="$route.params.type === 'call'">
<el-table-column align="left" width="200" prop="createTime" label="呼叫时间">
......@@ -139,7 +139,9 @@
</template>
</el-table-column>
<el-table-column label="触发计划名称" align="left" :show-overflow-tooltip="true" min-width="320" prop="sourceName"></el-table-column>
<el-table-column label="计划创建人" align="left" width="150" prop="sourceCreatorName"></el-table-column>
<el-table-column label="活动创建人" align="left" width="150" prop="sourceCreatorName"></el-table-column>
<el-table-column v-if="isMoreAccount" label="扣费规则" align="left" min-width="100" prop=""></el-table-column>
<el-table-column v-if="isMoreAccount" label="费用归属部门" align="left" min-width="120" prop=""></el-table-column>
<el-table-column align="left" width="150" prop="clerkName" label="主叫">
<template slot-scope="scope">
<p>{{ scope.row.clerkName || '--' }}</p>
......@@ -167,7 +169,7 @@
<template slot-scope="scope">{{ Number(scope.row.callFee / 1000).toFixed(2) }}</template>
</el-table-column>
</el-table>
<!--通话记录-->
<el-table tooltipEffect="light" :data="tableList" style="width:100%" v-if="$route.params.type === 'record'">
<el-table-column align="left" width="160" prop="createTime" label="呼叫时间">
<template slot-scope="scope">
......@@ -184,7 +186,9 @@
</template>
</el-table-column>
<el-table-column label="触发计划名称" align="left" width="350" prop="sourceName" :show-overflow-tooltip="true"></el-table-column>
<el-table-column label="计划创建人" align="left" width="100" prop="sourceCreatorName"></el-table-column>
<el-table-column label="活动创建人" align="left" width="100" prop="sourceCreatorName"></el-table-column>
<el-table-column v-if="isMoreAccount" label="扣费规则" align="left" min-width="100" prop=""></el-table-column>
<el-table-column v-if="isMoreAccount" label="费用归属部门" align="left" min-width="120" prop=""></el-table-column>
<el-table-column align="left" width="150" prop="clerkName" label="主叫">
<template slot-scope="scope">
<p>{{ scope.row.clerkName || '--' }}</p>
......@@ -218,7 +222,7 @@
</el-table-column>
</el-table>
<!-- 视频资费 -->
<el-table tooltipEffect="light" :data="tableList" style="width:100%" v-if="$route.params.type === 'video'">
<!-- <el-table tooltipEffect="light" :data="tableList" style="width:100%" v-if="$route.params.type === 'video'">
<el-table-column align="left" prop="createTime" label="时间">
<template slot-scope="scope">
<p class="cell-time">
......@@ -232,21 +236,33 @@
<el-table-column align="left" prop="trafficCost" label="流量费用支出">
<template slot-scope="scope"> ¥ {{ scope.row.trafficCost }} </template>
</el-table-column>
</el-table>
</el-table> -->
<dm-pagination v-show="tableList.length" background class="dm-pagination" @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page="listParams.currentPage" :page-sizes="[20, 40, 60, 80]" :page-size="listParams.pageSize" layout="total, sizes, prev, pager, next" :total="total"></dm-pagination>
</div>
<vue-gic-export-excel :dialogVisible.sync="dialogVisible" :dataArr="tableList" :type="2" :excelUrl="excelUrl" :params="params" :projectName="projectName"></vue-gic-export-excel>
<!--导出数据-->
<el-dialog title="导出数据" :visible.sync="exportDialog.dialogVisible" width="500">
<div class="export-time text-center">
<h2>选择导出日期范围</h2>
<p class="tip mb10">日期最大可选择范围为3个月,不支持导出当天的数据</p>
<el-date-picker :clearable="false" :pickerOptions="exportDialog.pickerOptions" v-model="exportDialog.time" type="daterange" range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期"></el-date-picker>
<div class="mt40 mb40">
<el-button type="primary" @click="createRepoert">去生成报告</el-button>
</div>
</div>
</el-dialog>
<vue-gic-export-excel :dialogVisible.sync="exportData.dialogVisible" :dataArr="tableList" :type="2" :excelUrl="exportData.excelUrl" :params="exportData.params" :projectName="projectName"></vue-gic-export-excel>
</section>
</template>
<script>
import { messageMarketingChart, messageMarketingPage, smsData, voiceData, callData, recordData, videoPage, videoChartData, downloadTrafficCostListExcel } from '@/service/api/rechargeApi.js';
import { messageMarketingChart, messageMarketingPage, smsData, voiceData, callData, recordData, videoPage, videoChartData, downloadTrafficCostListExcel, getAccountRule, getDepartList } from '@/service/api/rechargeApi.js';
import { formatDateTimeByType } from '@/utils/index.js';
import G2 from '@antv/g2';
import SelectDepart from './components/select-depart.vue';
export default {
name: 'recharge-consume',
components: { SelectDepart },
data() {
return {
list: [],
// eslint-disable-next-line
defaultAvatar: require('../../assets/img/head_default.png'),
formatDateTimeByType,
......@@ -272,8 +288,19 @@ export default {
localCount: 0,
placeholder: '',
pickerOptions: {
disabledDate(val) {
return Date.now() >= val.getTime() + 6 * 30 * 24 * 60 * 60 * 1000;
disabledDate: val => {
// 只能筛选半年之内得数据,并且最大跨度为一个月
const halfYearBefore = Date.now() - 6 * 30 * 24 * 60 * 60 * 1000;
const oneMothods = 30 * 24 * 60 * 60 * 1000;
if (this.minDate) {
const curTime = this.minDate.getTime();
const min = curTime - oneMothods > halfYearBefore ? curTime - oneMothods : halfYearBefore;
return val.getTime() > curTime + oneMothods || val.getTime() < min;
}
return val.getTime() <= halfYearBefore;
},
onPick: ({ maxDate, minDate }) => {
this.minDate = minDate;
}
},
videoListParams: {
......@@ -286,11 +313,52 @@ export default {
projectName: 'marketing', // 当前项目名
dialogVisible: false,
excelUrl: '', // 下载数据的地址
params: {} // 传递的参数
params: {}, // 传递的参数
isMoreAccount: false, // 是否多商户
deparment: {
list: [],
searchParams: '',
status: -1,
pageSize: 20,
currentPage: 1,
loading: false,
isLimit: false, // 是否最后一页
departId: '', // 选中时得id
zbFlag: 1
},
minDate: new Date(Date.now() - 30 * 24 * 60 * 60 * 1000),
exportDialog: {
dialogVisible: false,
pickerOptions: {
disabledDate: val => {
// 只能筛选一年之内得数据,并且最大跨度为三个月
const oneYearBefore = Date.now() - 12 * 30 * 24 * 60 * 60 * 1000;
const threeMothods = 3 * 30 * 24 * 60 * 60 * 1000;
if (this.exportDialog.minDate) {
const curTime = this.exportDialog.minDate.getTime();
const min = curTime - threeMothods > oneYearBefore ? curTime - threeMothods : oneYearBefore;
return val.getTime() > curTime + threeMothods || val.getTime() < min;
}
return val.getTime() <= oneYearBefore;
},
onPick: ({ maxDate, minDate }) => {
this.exportDialog.minDate = minDate;
}
},
time: [Date.now() - 30 * 24 * 60 * 60 * 1000, Date.now()],
minDate: new Date(Date.now() - 30 * 24 * 60 * 60 * 1000)
},
exportData: {
dialogVisible: false,
execUrl: '',
params: {}
}
};
},
created() {
this.loadAll();
this.getDepartmentList();
this.getRule();
this.$store.commit('mutations_breadcrumb', [{ name: '企业管理', path: '' }, { name: '计费中心', path: '/recharge' }, { name: '消费详情', path: '' }]); // eslint-disable-line
},
methods: {
......@@ -326,35 +394,35 @@ export default {
this.listParams.endTime = '';
}
if (this.$route.params.type === 'marketing') {
this.placeholder = '输入手机号码/模板ID/内容/触发计划名称/计划创建人';
this.placeholder = '输入手机号/模板ID/内容/触发计划名称/活动创建人';
if (!onlyList) {
this.marketingCharts();
}
this.marketingList();
}
if (this.$route.params.type === 'sms') {
this.placeholder = '输入手机号';
this.placeholder = '输入手机号';
if (!onlyList) {
this.smsData('charts');
}
this.smsData('list');
}
if (this.$route.params.type === 'voice') {
this.placeholder = '输入手机号';
this.placeholder = '输入手机号';
if (!onlyList) {
this.voiceData('charts');
}
this.voiceData('list');
}
if (this.$route.params.type === 'call') {
this.placeholder = '输入手机号码/触发计划名称/计划创建人';
this.placeholder = '输入手机号/触发计划名称/活动创建人';
if (!onlyList) {
this.callData('charts');
}
this.callData('list');
}
if (this.$route.params.type === 'record') {
this.placeholder = '输入手机号码/触发计划名称/计划创建人';
this.placeholder = '输入手机号/触发计划名称/活动创建人';
if (!onlyList) {
this.recordData('charts');
}
......@@ -382,30 +450,16 @@ export default {
},
async marketingCharts() {
try {
this.list = [];
let res = await messageMarketingChart(this.listParams);
this.sumCount = res.result.sumCount;
this.sumFee = res.result.sumFee;
this.sumInternational = res.result.sumInternational;
this.localCount = this.sumCount - this.sumInternational;
let chartList = res.result.chartList || [];
let list = [];
chartList.map(v => {
list.push({ day: v.chartsDate, name: '消费', temperature: v.fee ? (parseInt(v.fee) / 1000).toFixed(2) : 0 });
});
list.sort((a, b) => {
return a.temperature - b.temperature;
});
this.list = list;
this.$nextTick(_ => {
this.initCharts(list, 'mountNode');
});
} catch (err) {}
},
async smsData(type = 'list') {
this.loading = true;
try {
this.list = [];
let res = await smsData(Object.assign({ ajaxType: type }, this.listParams));
if (type === 'list') {
if (res.errorCode === 0 && res.result.page.result) {
......@@ -419,18 +473,6 @@ export default {
this.sumFee = res.result.sumFee;
this.sumInternational = res.result.sumInternational;
this.localCount = this.sumCount - this.sumInternational;
let chartList = res.result.chartList || [];
let list = [];
chartList.map(v => {
list.push({ day: v.chartsDate, name: '消费', temperature: v.fee ? (parseInt(v.fee) / 1000).toFixed(2) : 0 });
});
list.sort((a, b) => {
return a.temperature - b.temperature;
});
this.list = list;
this.$nextTick(_ => {
this.initCharts(list, 'mountNode');
});
}
} catch (err) {}
this.loading = false;
......@@ -438,7 +480,6 @@ export default {
async voiceData(type = 'list') {
this.loading = true;
try {
this.list = [];
let res = await voiceData(Object.assign({ ajaxType: type }, this.listParams));
if (type === 'list') {
if (res.errorCode === 0 && res.result.page.result) {
......@@ -450,18 +491,6 @@ export default {
} else if (type === 'charts') {
this.sumCount = res.result.sumCount;
this.sumFee = res.result.sumFee;
let chartList = res.result.chartList || [];
let list = [];
chartList.map(v => {
list.push({ day: v.chartsDate, name: '消费', temperature: v.fee ? (parseInt(v.fee) / 1000).toFixed(2) : 0 });
});
list.sort((a, b) => {
return a.temperature - b.temperature;
});
this.list = list;
this.$nextTick(_ => {
this.initCharts(list, 'mountNode');
});
}
} catch (err) {}
this.loading = false;
......@@ -469,7 +498,6 @@ export default {
async callData(type = 'list') {
this.loading = true;
try {
this.list = [];
let res = await callData(Object.assign({ ajaxType: type, taskType: type === 'list' ? this.taskType : '' }, this.listParams));
if (type === 'list') {
if (res.errorCode === 0 && res.result.page.result) {
......@@ -480,20 +508,8 @@ export default {
this.tableList = [];
}
} else if (type === 'charts') {
this.sumCount = res.result.sumCount;
this.sumCount = res.result.times || 0;
this.sumFee = res.result.sumFee;
let chartList = res.result.chartList || [];
let list = [];
chartList.map(v => {
list.push({ day: v.chartsDate, name: '消费', temperature: v.fee ? (parseInt(v.fee) / 1000).toFixed(2) : 0 });
});
list.sort((a, b) => {
return a.temperature - b.temperature;
});
this.list = list;
this.$nextTick(_ => {
this.initCharts(list, 'mountNode');
});
}
} catch (err) {}
this.loading = false;
......@@ -502,7 +518,6 @@ export default {
async videoPage(type = 'list') {
this.loading = true;
try {
this.list = [];
this.videoListParams.startTime = formatDateTimeByType(this.dateTimeVideo[0], 'yyyy-MM-dd');
this.videoListParams.endTime = formatDateTimeByType(this.dateTimeVideo[1], 'yyyy-MM-dd');
if (type === 'list') {
......@@ -518,18 +533,6 @@ export default {
let res = await videoChartData(params);
this.sumCount = res.result.sumCount;
this.sumFee = res.result.sumFee;
let chartList = res.result.chartList || [];
let list = [];
chartList.map(v => {
list.push({ day: v.chartsDate, name: '支出费用', temperature: v.trafficCost || 0 });
});
list.sort((a, b) => {
return a.temperature - b.temperature;
});
this.list = list;
this.$nextTick(_ => {
this.initCharts(list, 'mountNode');
});
}
} catch (err) {}
this.loading = false;
......@@ -558,7 +561,6 @@ export default {
async recordData(type = 'list') {
this.loading = true;
try {
this.list = [];
let res = await recordData(Object.assign({ ajaxType: type, taskType: type === 'list' ? this.taskType : '' }, this.listParams));
if (type === 'list') {
if (res.errorCode === 0 && res.result.page.result) {
......@@ -569,74 +571,84 @@ export default {
this.tableList = [];
}
} else if (type === 'charts') {
this.sumCount = res.result.sumCallTime;
this.sumCount = res.result.sumCallTime || 0;
this.sumFee = res.result.sumFee;
let chartList = res.result.chartList || [];
let list = [];
chartList.map(v => {
list.push({ day: v.chartsDate, name: '录音存储消费', temperature: v.fee ? (parseInt(v.fee) / 100).toFixed(2) : 0 });
});
list.sort((a, b) => {
return a.temperature - b.temperature;
});
this.list = list;
this.$nextTick(_ => {
this.initCharts(list, 'mountNode');
});
}
} catch (err) {}
this.loading = false;
},
//生成折线图
initCharts(data, nodeName) {
//先清空原先图表
const mountNode = this.$refs[nodeName];
const mountNodeChilds = Array.from(mountNode.childNodes);
mountNodeChilds.map(v => {
mountNode.removeChild(v);
});
var chart = new G2.Chart({
container: nodeName,
forceFit: true,
height: 400,
padding: [20, 60, 80, 60]
});
chart.source(data, {
day: {
range: [0, 1]
},
temperature: {
range: [0, 1]
}
});
chart.tooltip({
crosshairs: {
type: 'line'
}
});
chart.axis('temperature', {
label: {
formatter: function formatter(val) {
// console.log(val);
return (isNaN(val) ? '0.00' : val) + '元';
}
}
});
chart
.line()
.position('day*temperature')
.color('name');
chart
.point()
.position('day*temperature')
.color('name')
.size(4)
.shape('circle')
.style({
stroke: '#fff',
lineWidth: 1
});
chart.render();
// 获取计费部门
async getDepartmentList() {
const depart = this.deparment;
depart['loading'] = true;
const para = Object.assign({}, depart);
const typeArr = ['list', 'loading', 'isLimit', 'departId'];
this.deleteAttribute(para, typeArr);
let { result } = await getDepartList(para);
this.setDepartData(depart, result);
},
// 清除接口无用属性
deleteAttribute(obj, typeArr) {
for (const key in obj) {
if (typeArr.includes(key)) delete obj[key];
}
},
// 设置departmentData
setDepartData(obj, result) {
const data = result.result || [];
const total = result.totalCount;
obj['list'].push(...data);
obj['isLimit'] = obj['list'].length >= total; // list得数据量>=数据总数时做出限制
obj['loading'] = false;
},
// 下拉滚动加载
load() {
const depart = this.deparment;
if (depart['isLimit'] || depart['loading']) return;
depart['currentPage']++;
this.getDepartmentList();
},
getDepartId(id) {
// 选中不同下拉得id时,调用对应得表格方法
const depart = this.deparment;
depart['departId'] = id;
this.listParams['currentPage'] = 1;
this.loadAll();
},
// 下拉远程搜索
remoteSearch(val) {
const depart = this.deparment;
depart['searchParams'] = val;
depart['currentPage'] = 1;
depart['isLimit'] = false;
depart['list'] = [];
this.getDepartmentList();
},
// 获取计费规则
async getRule() {
const result = await getAccountRule();
const res = result.result || {};
this.isMoreAccount = !!res.status;
},
// 导出表格数据
cdkeyExport() {
const exportData = this.exportData;
exportData.dialogVisible = true;
// let meth;
const params = {
requestProject: 'marketing',
startTime: formatDateTimeByType(this.exportDialog.time[0], 'yyyy-MM-dd'),
endTime: formatDateTimeByType(this.exportDialog.time[1], 'yyyy-MM-dd')
};
console.log(params);
// exportData.excelUrl = meth;
// exportData.params = params;
},
// 生成报告
createRepoert() {
this.exportDialog.dialogVisible = false;
this.exportData.dialogVisible = true;
this.cdkeyExport();
}
}
};
......@@ -655,4 +667,34 @@ export default {
}
}
}
.tips {
height: 22px;
font-size: 14px;
font-weight: 400;
color: #303133;
line-height: 22px;
margin-top: 16px;
}
.blod {
font-weight: bold;
}
.flex_between {
display: flex;
justify-content: space-between;
}
.export-time {
box-sizing: border-box;
h2 {
padding: 25px 0;
font-weight: 600;
line-height: 22px;
font-size: 16px;
color: #303133;
}
.tip {
font-size: 12px;
font-weight: 400;
color: #909399;
}
}
</style>
......@@ -10,6 +10,7 @@
<el-select v-if="listParams.feeType == 2" style="width:180px" clearable v-model="listParams.deductType" placeholder="全部扣款类型" @change="onSearch">
<el-option v-for="(value, key) in deductTypeList" :key="key" :label="value" :value="key"></el-option>
</el-select>
<select-depart v-if="isMoreAccount" :data="deparment" @load="load" @getDepartId="getDepartId" @remote-search="remoteSearch" :loading="deparment.loading" />
</div>
<el-table tooltipEffect="dark" :data="tableList" style="width: 100%">
<el-table-column align="left" prop="timeEnd" label="操作时间">
......@@ -21,30 +22,20 @@
<el-table-column align="left" prop="transactionId" label="流水号"></el-table-column>
<el-table-column label="记录类型">
<template slot-scope="scope">
{{ textMap[scope.row.feeType] }}
{{ textMap[scope.row.feeType] || '--' }}
</template>
</el-table-column>
<el-table-column label="扣款类型">
<template slot-scope="scope">
{{ deductTypeList[scope.row.deductType] }}
{{ deductTypeList[scope.row.deductType] || '--' }}
</template>
</el-table-column>
<!-- <el-table-column align="left" prop="payType" label="充值方式">
<template slot-scope="scope">
{{ scope.row.payType | filterPayType }}
</template>
</el-table-column> -->
<el-table-column label="金额" align="left" prop="totalFee">
<el-table-column label="余额(元)" align="left" prop="totalFee">
<template slot-scope="scope"> {{ (scope.row.totalFee / 100).toFixed(2) }}</template>
</el-table-column>
<el-table-column align="left" prop="reason" label="原因" show-overflow-tooltip></el-table-column>
<el-table-column label="图片凭证">
<template slot-scope="scope">
<!-- <el-image v-for="(item, index) in getUrls(scope).slice(0, 3)" :key="`${currentPage}-${item}`" style="width: 36px; height: 36px" :src="item" :preview-src-list="getUrls(scope, index)" lazy>
<div slot="placeholder" class="image-slot">
<i class="el-icon-picture-outline"></i>
</div>
</el-image> -->
<viewer :options="options" :images="getUrls(scope)" class="viewer" ref="viewer">
<template #default="scope2">
<img style="width: 36px; height: 36px;margin-left:5px;cursor:pointer" v-for="src in scope2.images" :src="src" :key="src" />
......@@ -57,14 +48,15 @@
</section>
</template>
<script>
import { rechargeRecord } from '@/service/api/rechargeApi.js';
import { rechargeRecord, getDepartList, getAccountRule } from '@/service/api/rechargeApi.js';
import { formatDateTimeByType } from '@/utils/index.js';
import 'viewerjs/dist/viewer.css';
import { component as Viewer } from 'v-viewer';
import SelectDepart from './components/select-depart.vue';
export default {
name: 'recharge-record',
components: { Viewer },
components: { Viewer, SelectDepart },
data() {
return {
textMap: {
......@@ -84,12 +76,26 @@ export default {
deductType: '',
feeType: ''
},
deparment: {
list: [],
searchParams: '',
status: -1,
pageSize: 20,
currentPage: 1,
loading: false,
isLimit: false, // 是否最后一页
departId: '', // 选中时得id
zbFlag: 1
},
total: 0,
deductTypeList: {}
deductTypeList: {},
isMoreAccount: false
};
},
created() {
this.rechargeRecord();
this.getDepartmentList();
this.getRule();
this.$store.commit('mutations_breadcrumb', [{ name: '企业管理', path: '' }, { name: '计费中心', path: '/recharge' }, { name: '记录', path: '' }]); // eslint-disable-line
},
methods: {
......@@ -115,7 +121,6 @@ export default {
this.listParams.currentPage = val;
this.rechargeRecord();
},
async rechargeRecord() {
this.loading = true;
if (this.dateTime) {
......@@ -125,6 +130,7 @@ export default {
this.listParams.beginTime = '';
this.listParams.endTime = '';
}
this.listParams.accountDepartId = this.deparment.departId;
try {
let res = await rechargeRecord(this.listParams);
if (res.errorCode === 0 && res.result.result) {
......@@ -138,6 +144,61 @@ export default {
console.log(err);
}
this.loading = false;
},
// 计费部门下拉
// 获取计费部门
async getDepartmentList() {
const depart = this.deparment;
depart['loading'] = true;
const para = Object.assign({}, depart);
const typeArr = ['list', 'loading', 'isLimit', 'departId'];
this.deleteAttribute(para, typeArr);
let { result } = await getDepartList(para);
this.setDepartData(depart, result);
},
// 清除接口无用属性
deleteAttribute(obj, typeArr) {
for (const key in obj) {
if (typeArr.includes(key)) delete obj[key];
}
},
// 设置departmentData
setDepartData(obj, result) {
const data = result.result || [];
const total = result.totalCount;
obj['list'].push(...data);
obj['isLimit'] = obj['list'].length >= total; // list得数据量>=数据总数时做出限制
obj['loading'] = false;
},
// 下拉滚动加载
load() {
const depart = this.deparment;
if (depart['isLimit'] || depart['loading']) return;
depart['currentPage']++;
this.getDepartmentList();
},
getDepartId(id) {
// 选中不同下拉得id时,调用对应得表格方法
const depart = this.deparment;
depart['departId'] = id;
this.listParams['currentPage'] = 1;
this.rechargeRecord();
},
// 下拉远程搜索
remoteSearch(val) {
const depart = this.deparment;
depart['searchParams'] = val;
depart['currentPage'] = 1;
depart['isLimit'] = false;
depart['list'] = [];
this.getDepartmentList();
},
// 获取计费规则
async getRule() {
const result = await getAccountRule();
const res = result.result || {};
console.log(res);
this.isMoreAccount = !!res.status;
}
},
filters: {
......
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