|
@@ -0,0 +1,256 @@
|
|
|
|
|
+<!--
|
|
|
|
|
+ PC 收入统计:与小程序 kdIncome 同源 /stat-kd/profile(StatKdClass::eachChannelIncome)。
|
|
|
|
|
+ timeType 切换账单时间(bill=payTime) / 发货时间(send=sendTime);明细弹窗看渠道分类和员工收款。
|
|
|
|
|
+-->
|
|
|
|
|
+<template>
|
|
|
|
|
+ <div class="shop-list">
|
|
|
|
|
+ <x-crud @load="onLoad">
|
|
|
|
|
+ <template #slot-date-select>
|
|
|
|
|
+ <el-button-group style="margin-right:12px;">
|
|
|
|
|
+ <el-button size="mini" :type="timeType === 'bill' ? 'primary' : 'default'" @click="changeTimeType('bill')">账单时间</el-button>
|
|
|
|
|
+ <el-button size="mini" :type="timeType === 'send' ? 'primary' : 'default'" @click="changeTimeType('send')">发货时间</el-button>
|
|
|
|
|
+ </el-button-group>
|
|
|
|
|
+ <el-date-picker v-model="currentDateList" type="daterange" align="right" unlink-panels range-separator="至" start-placeholder="开始日期" @change="getDate" value-format="yyyy-MM-dd"
|
|
|
|
|
+ end-placeholder="结束日期" :picker-options="pickerOptions" size="mini"> </el-date-picker>
|
|
|
|
|
+ </template>
|
|
|
|
|
+
|
|
|
|
|
+ <template #slot-column-option="{scope}">
|
|
|
|
|
+ <el-button size="mini" type="primary" @click="getDetail(scope.row)">明细</el-button>
|
|
|
|
|
+ </template>
|
|
|
|
|
+
|
|
|
|
|
+ <template #table-column-amount="{scope}">
|
|
|
|
|
+ <span>{{ formatNum(scope.row.amount) }}</span>
|
|
|
|
|
+ </template>
|
|
|
|
|
+
|
|
|
|
|
+ <template #table-column-num="{scope}">
|
|
|
|
|
+ <span>{{ formatNum(scope.row.num) }}</span>
|
|
|
|
|
+ </template>
|
|
|
|
|
+
|
|
|
|
|
+ <template #slot-total-amount>
|
|
|
|
|
+ <div class="income-total-wrap">
|
|
|
|
|
+ <div class="income-total-nums">
|
|
|
|
|
+ <span style="font-size:15px;font-weight:bold;">合计 ¥{{ formatNum(totalIncome) }}</span>
|
|
|
|
|
+ <span v-if="Number(payCodeIncome) > 0" style="font-size:15px;font-weight:bold;margin-left:15px;color:#e74c3c;">以上还要减掉重复登记的收款码收入 ¥{{ formatNum(payCodeIncome) }}</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div v-if="staffAmountList.length" class="staff-list">
|
|
|
|
|
+ <div v-for="(it, idx) in staffAmountList" :key="idx" class="staff-row">
|
|
|
|
|
+ <span>{{ it.staffName || '未命名' }}</span>
|
|
|
|
|
+ <span>数量 {{ formatNum(it.num) }}</span>
|
|
|
|
|
+ <span>¥{{ formatNum(it.amount) }}</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </x-crud>
|
|
|
|
|
+
|
|
|
|
|
+ <el-dialog title="明细" :visible.sync="showDetail" width="560px">
|
|
|
|
|
+ <el-table v-if="detailClassList.length" :data="detailClassList" border size="mini" style="margin-bottom:16px;">
|
|
|
|
|
+ <el-table-column prop="name" label="员工" align="center"></el-table-column>
|
|
|
|
|
+ <el-table-column label="笔数" align="center">
|
|
|
|
|
+ <template slot-scope="scope">{{ formatNum(scope.row.num) }}笔</template>
|
|
|
|
|
+ </el-table-column>
|
|
|
|
|
+ <el-table-column label="金额" align="center">
|
|
|
|
|
+ <template slot-scope="scope">¥{{ formatNum(scope.row.amount) }}</template>
|
|
|
|
|
+ </el-table-column>
|
|
|
|
|
+ </el-table>
|
|
|
|
|
+ <el-table v-if="detailCategoryList.length" :data="detailCategoryList" border size="mini">
|
|
|
|
|
+ <el-table-column prop="name" label="分类" align="center"></el-table-column>
|
|
|
|
|
+ <el-table-column label="笔数" align="center">
|
|
|
|
|
+ <template slot-scope="scope">{{ formatNum(scope.row.num) }}笔</template>
|
|
|
|
|
+ </el-table-column>
|
|
|
|
|
+ <el-table-column label="金额" align="center">
|
|
|
|
|
+ <template slot-scope="scope">¥{{ formatNum(scope.row.amount) }}</template>
|
|
|
|
|
+ </el-table-column>
|
|
|
|
|
+ </el-table>
|
|
|
|
|
+ <div v-if="!detailClassList.length && !detailCategoryList.length" style="color:#999;text-align:center;">暂无明细</div>
|
|
|
|
|
+ </el-dialog>
|
|
|
|
|
+ </div>
|
|
|
|
|
+</template>
|
|
|
|
|
+<script>
|
|
|
|
|
+import { mapGetters } from 'vuex';
|
|
|
|
|
+export default {
|
|
|
|
|
+ data() {
|
|
|
|
|
+ return {
|
|
|
|
|
+ crud: null,
|
|
|
|
|
+ pickerOptions: {
|
|
|
|
|
+ shortcuts: [{
|
|
|
|
|
+ text: '最近一周',
|
|
|
|
|
+ onClick(picker) {
|
|
|
|
|
+ const end = new Date();
|
|
|
|
|
+ const start = new Date();
|
|
|
|
|
+ start.setTime(start.getTime() - 3600 * 1000 * 24 * 7);
|
|
|
|
|
+ picker.$emit('pick', [start, end]);
|
|
|
|
|
+ }
|
|
|
|
|
+ }, {
|
|
|
|
|
+ text: '最近一个月',
|
|
|
|
|
+ onClick(picker) {
|
|
|
|
|
+ const end = new Date();
|
|
|
|
|
+ const start = new Date();
|
|
|
|
|
+ start.setTime(start.getTime() - 3600 * 1000 * 24 * 30);
|
|
|
|
|
+ picker.$emit('pick', [start, end]);
|
|
|
|
|
+ }
|
|
|
|
|
+ }, {
|
|
|
|
|
+ text: '最近三个月',
|
|
|
|
|
+ onClick(picker) {
|
|
|
|
|
+ const end = new Date();
|
|
|
|
|
+ const start = new Date();
|
|
|
|
|
+ start.setTime(start.getTime() - 3600 * 1000 * 24 * 90);
|
|
|
|
|
+ picker.$emit('pick', [start, end]);
|
|
|
|
|
+ }
|
|
|
|
|
+ }]
|
|
|
|
|
+ },
|
|
|
|
|
+ currentDateList: '',
|
|
|
|
|
+ startTime: '',
|
|
|
|
|
+ endTime: '',
|
|
|
|
|
+ searchTime: '',
|
|
|
|
|
+ // bill=账单日 payTime(默认) / send=发货 sendTime
|
|
|
|
|
+ timeType: 'bill',
|
|
|
|
|
+ totalIncome: 0,
|
|
|
|
|
+ payCodeIncome: 0,
|
|
|
|
|
+ staffAmountList: [],
|
|
|
|
|
+ showDetail: false,
|
|
|
|
|
+ detailClassList: [],
|
|
|
|
|
+ detailCategoryList: []
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ computed: {
|
|
|
|
|
+ ...mapGetters(['shopInfo']),
|
|
|
|
|
+ /** 当前筛选口径文案 */
|
|
|
|
|
+ timeTypeName() {
|
|
|
|
|
+ return this.timeType === 'send' ? '发货时间' : '账单时间'
|
|
|
|
|
+ },
|
|
|
|
|
+ },
|
|
|
|
|
+ methods: {
|
|
|
|
|
+ /** 数字展示:不用 truthy,避免 0 被当成空 */
|
|
|
|
|
+ formatNum(val) {
|
|
|
|
|
+ const n = parseFloat(val);
|
|
|
|
|
+ return isNaN(n) ? 0 : n;
|
|
|
|
|
+ },
|
|
|
|
|
+ /** 对象转数组,接口 list/class/category 可能是关联数组 */
|
|
|
|
|
+ toList(raw) {
|
|
|
|
|
+ if (!raw) {
|
|
|
|
|
+ return []
|
|
|
|
|
+ }
|
|
|
|
|
+ if (Array.isArray(raw)) {
|
|
|
|
|
+ return raw
|
|
|
|
|
+ }
|
|
|
|
|
+ return Object.keys(raw).map((k) => raw[k])
|
|
|
|
|
+ },
|
|
|
|
|
+ /** 明细只展示有笔数或金额的行,避免空分类占位 */
|
|
|
|
|
+ filterDetailRows(raw) {
|
|
|
|
|
+ return this.toList(raw).filter((row) => {
|
|
|
|
|
+ return this.formatNum(row.num) > 0 || this.formatNum(row.amount) !== 0
|
|
|
|
|
+ })
|
|
|
|
|
+ },
|
|
|
|
|
+ /** 与小程序 kdIncome 对齐的查询参数 */
|
|
|
|
|
+ queryParams(extra) {
|
|
|
|
|
+ return {
|
|
|
|
|
+ searchTime: this.searchTime,
|
|
|
|
|
+ startTime: this.startTime,
|
|
|
|
|
+ endTime: this.endTime,
|
|
|
|
|
+ timeType: this.timeType,
|
|
|
|
|
+ contain: 0,
|
|
|
|
|
+ ...(extra || {})
|
|
|
|
|
+ };
|
|
|
|
|
+ },
|
|
|
|
|
+ /** 切换账单时间 / 发货时间并刷新 */
|
|
|
|
|
+ changeTimeType(type) {
|
|
|
|
|
+ if (this.timeType === type) {
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+ this.timeType = type
|
|
|
|
|
+ if (this.app) {
|
|
|
|
|
+ this.app.refresh(this.queryParams())
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ /** 打开渠道明细:员工收款 + 批发/零售等分类 */
|
|
|
|
|
+ getDetail(row) {
|
|
|
|
|
+ this.detailClassList = this.filterDetailRows(row.class)
|
|
|
|
|
+ this.detailCategoryList = this.filterDetailRows(row.category)
|
|
|
|
|
+ this.showDetail = true
|
|
|
|
|
+ },
|
|
|
|
|
+ onLoad({ ctx, app }) {
|
|
|
|
|
+ this.app = app;
|
|
|
|
|
+ ctx.service({
|
|
|
|
|
+ page: () => {
|
|
|
|
|
+ return this.$service.statKd.getProfile(this.queryParams()).then((res) => {
|
|
|
|
|
+ this.totalIncome = this.formatNum(res.totalIncome)
|
|
|
|
|
+ this.payCodeIncome = this.formatNum(res.payCodeIncome)
|
|
|
|
|
+ this.staffAmountList = this.toList(res.staffAmountList)
|
|
|
|
|
+ return {
|
|
|
|
|
+ list: this.toList(res.list)
|
|
|
|
|
+ };
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+ .set('table', {
|
|
|
|
|
+ columns: [
|
|
|
|
|
+ {
|
|
|
|
|
+ label: '渠道',
|
|
|
|
|
+ prop: 'name',
|
|
|
|
|
+ align: 'center',
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ label: '笔数',
|
|
|
|
|
+ prop: 'num',
|
|
|
|
|
+ align: 'center',
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ label: '金额',
|
|
|
|
|
+ prop: 'amount',
|
|
|
|
|
+ align: 'center',
|
|
|
|
|
+ },
|
|
|
|
|
+ ],
|
|
|
|
|
+ op: {
|
|
|
|
|
+ visible: true,
|
|
|
|
|
+ props: {
|
|
|
|
|
+ width: 120,
|
|
|
|
|
+ align: 'center',
|
|
|
|
|
+ fixed: 'right',
|
|
|
|
|
+ label: '操作'
|
|
|
|
|
+ },
|
|
|
|
|
+ layout: ['slot-column-option']
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+ .set('layout', [
|
|
|
|
|
+ ['slot-date-select', 'flex1'],
|
|
|
|
|
+ ['data-table'],
|
|
|
|
|
+ ['slot-total-amount', 'flex1']
|
|
|
|
|
+ ])
|
|
|
|
|
+ .done();
|
|
|
|
|
+ app.refresh();
|
|
|
|
|
+ },
|
|
|
|
|
+ getDate() {
|
|
|
|
|
+ this.searchTime = 'custom'
|
|
|
|
|
+ this.startTime = this.currentDateList[0]
|
|
|
|
|
+ this.endTime = this.currentDateList[1]
|
|
|
|
|
+ this.app.refresh(this.queryParams());
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+};
|
|
|
|
|
+</script>
|
|
|
|
|
+
|
|
|
|
|
+<style lang="stylus" scoped>
|
|
|
|
|
+.shop-list {
|
|
|
|
|
+ height: 100%;
|
|
|
|
|
+}
|
|
|
|
|
+.income-total-wrap {
|
|
|
|
|
+ display: block;
|
|
|
|
|
+ width: 100%;
|
|
|
|
|
+}
|
|
|
|
|
+.income-total-nums {
|
|
|
|
|
+ display: block;
|
|
|
|
|
+ line-height: 1.6;
|
|
|
|
|
+}
|
|
|
|
|
+.staff-list {
|
|
|
|
|
+ margin-top: 12px;
|
|
|
|
|
+}
|
|
|
|
|
+.staff-row {
|
|
|
|
|
+ font-size: 13px;
|
|
|
|
|
+ color: #333;
|
|
|
|
|
+ margin-top: 6px;
|
|
|
|
|
+}
|
|
|
|
|
+.staff-row span {
|
|
|
|
|
+ margin-right: 16px;
|
|
|
|
|
+}
|
|
|
|
|
+</style>
|