feat: 将推广费页签迁移到营销账户账单
This commit is contained in:
parent
8c38175318
commit
ad9d09aafe
@ -914,7 +914,7 @@ class Pages extends Controller
|
||||
* 支持的营销账户平台列表
|
||||
*/
|
||||
protected const MARKETING_PLATFORMS = [
|
||||
'taobao-alipay', 'taobao-wechat', 'pinduoduo',
|
||||
'promotion-marketing', 'taobao-alipay', 'taobao-wechat', 'pinduoduo',
|
||||
'jd-wallet', 'douyin', 'dewu', 'vipshop',
|
||||
'jd-promotion', 'douyin-promotion', 'dewu-promotion', 'vip-promotion',
|
||||
];
|
||||
@ -9341,6 +9341,11 @@ class Pages extends Controller
|
||||
return null;
|
||||
}
|
||||
|
||||
// 推广费页面由 pages.js 挂载共享 AccountBills 组件,服务端不重复查询。
|
||||
if ($tab === 'promotion-marketing') {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (in_array($tab, self::PROMOTION_MARKETING_TABS, true)) {
|
||||
return app(PlatformPromotionAccountPageData::class)->content($tab);
|
||||
}
|
||||
|
||||
@ -554,13 +554,13 @@ $navigation = [
|
||||
['slug' => 'dewu', 'title' => '得物账户'],
|
||||
['slug' => 'vipshop', 'title' => '唯品会MP账户'],
|
||||
['slug' => 'vipshop-jx', 'title' => '唯品会JX账户'],
|
||||
['slug' => 'promotion-marketing', 'title' => '推广费(营销账户)'],
|
||||
],
|
||||
],
|
||||
[
|
||||
'slug' => 'marketing-account-bills',
|
||||
'title' => '店铺营销账户账单',
|
||||
'tabs' => [
|
||||
['slug' => 'promotion-marketing', 'title' => '推广费(营销账户)'],
|
||||
['slug' => 'taobao-alipay', 'title' => '淘系支付宝账户'],
|
||||
['slug' => 'taobao-wechat', 'title' => '淘系微信账户'],
|
||||
['slug' => 'pinduoduo', 'title' => '拼多多账户'],
|
||||
|
||||
@ -1,6 +1,12 @@
|
||||
<template>
|
||||
<div class="table-workspace">
|
||||
<div class="tw-stats" :class="{ 'tw-stats--promotion-only': isPromotionMarketing }">
|
||||
<div class="table-workspace" :class="{ 'table-workspace--compact': compact }">
|
||||
<div
|
||||
class="tw-stats"
|
||||
:class="{
|
||||
'tw-stats--promotion-only': isPromotionMarketing && !compact,
|
||||
'tw-stats--promotion-compact': compact,
|
||||
}"
|
||||
>
|
||||
<div
|
||||
v-for="stat in stats"
|
||||
:key="stat.label"
|
||||
@ -15,6 +21,7 @@
|
||||
type="button"
|
||||
class="tw-promotion-link"
|
||||
aria-label="查看店铺明细"
|
||||
:disabled="compact && (loadError || loading)"
|
||||
@click="openPromotionDetails"
|
||||
>查看明细 →</button>
|
||||
</div>
|
||||
@ -133,7 +140,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<form class="tw-filter" @submit.prevent="applyFilters">
|
||||
<form v-if="!compact" class="tw-filter" @submit.prevent="applyFilters">
|
||||
<label>店铺
|
||||
<el-select v-if="usesMirrorOptions && !filterOptionsError" v-model="filters.store_name" filterable clearable :loading="filterOptionsLoading" placeholder="全部店铺" aria-label="店铺筛选" popper-class="account-bill-filter-options" no-match-text="没有匹配的店铺" no-data-text="暂无店铺" @change="applyFilters">
|
||||
<el-option v-for="shop in filterOptions.shops" :key="shop" :label="shop" :value="shop" />
|
||||
@ -174,7 +181,7 @@
|
||||
<p v-if="filterOptionsError" class="tw-filter-error" role="alert">筛选选项加载失败,可输入名称查询。<button type="button" class="tw-button" @click="fetchFilterOptions">重试选项</button></p>
|
||||
<p v-if="filtersDirty && !loading" class="tw-note" role="status">筛选条件已修改,查询后可导出对应结果。</p>
|
||||
</form>
|
||||
<section class="tw-panel" :aria-label="`${columnsTitle || '账户'}账单明细`">
|
||||
<section v-if="!compact" class="tw-panel" :aria-label="`${columnsTitle || '账户'}账单明细`">
|
||||
<div v-if="isJdBillTab && sync" class="tw-note">来源:{{ sync.source }} · 已接入月份:{{ (sync.periods || []).join('、') }} · 导入时间:{{ sync.last_succeeded_at }}</div>
|
||||
<div v-if="loadError" class="tw-alert" role="alert"><span>账单加载失败,请重试</span><button type="button" @click="fetch">重新加载</button></div>
|
||||
<div class="tw-toolbar">
|
||||
@ -185,7 +192,7 @@
|
||||
<button type="button" class="tw-button" :disabled="!canExport || exporting" @click="exportRows"><span class="material-icons-outlined" aria-hidden="true">download</span>{{ exporting ? '提交中…' : (exportMode === 'page-visible' ? '导出当前页' : '导出全部筛选结果') }}</button>
|
||||
<button type="button" class="tw-button" @click="openExportModal"><span class="material-icons-outlined" aria-hidden="true">assignment</span>导出进度</button>
|
||||
</template>
|
||||
<table-column-settings :columns="sourceColumns" :user-key="userKey" page-key="store-receiving-bills" :tab-key="billType" table-key="bill-details" @change="configuredColumns = $event" />
|
||||
<table-column-settings :columns="sourceColumns" :user-key="userKey" :page-key="pageKey" :tab-key="billType" table-key="bill-details" @change="configuredColumns = $event" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -262,11 +269,13 @@ const DATE_FILTER_LABELS = {'jd-transactions':'费用结算时间','jingxi':'费
|
||||
const dateConfig = {dateFormat:'Y-m-d',locale:Mandarin,disableMobile:true,allowInput:false,onReady:(_,value,picker)=>picker.calendarContainer.classList.add('account-bill-calendar')};
|
||||
// 默认账期=本月:不填日期时查全表(几百万行)会很慢,默认落到当月既快又符合日常用法。
|
||||
const currentMonthRange = () => { const now = new Date(); const year = now.getFullYear(); const month = now.getMonth() + 1; const pad = value => String(value).padStart(2, '0'); const lastDay = new Date(year, month, 0).getDate(); return { start: `${year}-${pad(month)}-01`, end: `${year}-${pad(month)}-${pad(lastDay)}` }; };
|
||||
const emptyFilters = billType => ({store_name:'',ledger_remark:'',bill_item:'',...currentMonthRange(),keyword:'',keyword_match:'contains'});
|
||||
const filtersForRange=(start,end)=>{const range=currentMonthRange();return {store_name:'',ledger_remark:'',bill_item:'',start:start||range.start,end:end||range.end,keyword:'',keyword_match:'contains'};};
|
||||
export default {
|
||||
name:'AccountBills', components:{ TableColumnSettings, flatPicker, [Select.name]:Select, [Option.name]:Option }, props:{ apiBase:{type:String,required:true}, billType:{type:String,required:true}, progressBase:{type:String,default:''}, userKey:{type:[String,Number],required:true} },
|
||||
data(){return{loading:false,loadError:false,sync:null,rows:[],sourceColumns:[],configuredColumns:[],columnsTitle:'',sourceGroups:[],summary:{income:0,expense:0,balance:0,count:0},total:0,currentPage:1,lastPage:1,perPage:20,filters:emptyFilters(this.billType),queryFilters:emptyFilters(this.billType),resultFilters:null,filterOptions:{shops:[],bill_items:[],ledger_remarks:[]},filterOptionsLoading:false,filterOptionsError:false,requestSerial:0,promotionRequestSerial:0,sort:{key:'occurred_at',dir:'desc'},exportMode:'filtered-visible',exporting:false,exportModalOpen:false,exportTasks:[],exportHint:'导出在后台生成,成功后点「下载」;期间可以继续查看其它页面。',exportTimer:null,promotionModalOpen:false,promotionExpenseTab:'platform',promotionExpenseLoading:false,promotionExpenseError:false,promotionStoreSummary:[],promotionExpenseSummary:{expense:0,count:0},promotionExpenseMessage:'',promotionPlatformFilter:'',promotionStoreFilter:'',promotionNoteFilter:'',promotionNoteTimer:null};},
|
||||
data(){return{loading:false,loadError:false,sync:null,rows:[],sourceColumns:[],configuredColumns:[],columnsTitle:'',sourceGroups:[],summary:{income:0,expense:0,balance:0,count:0},total:0,currentPage:1,lastPage:1,perPage:20,filters:emptyFilters(this.billType),queryFilters:emptyFilters(this.billType),resultFilters:null,filterOptions:{shops:[],bill_items:[],ledger_remarks:[]},filterOptionsLoading:false,filterOptionsError:false,requestSerial:0,promotionRequestSerial:0,sort:{key:'occurred_at',dir:'desc'},exportMode:'filtered-visible',exporting:false,exportModalOpen:false,exportTasks:[],exportHint:'导出在后台生成,成功后点「下载」;期间可以继续查看其它页面。',exportTimer:null,promotionModalOpen:false,promotionExpenseTab:'platform',promotionExpenseLoading:false,promotionExpenseError:false,promotionStoreSummary:[],promotionExpenseSummary:{expense:0,count:0},promotionExpenseMessage:'',promotionPlatformFilter:'',promotionStoreFilter:''};},
|
||||
name:'AccountBills', components:{ TableColumnSettings, flatPicker, [Select.name]:Select, [Option.name]:Option }, props:{ apiBase:{type:String,required:true}, billType:{type:String,required:true}, progressBase:{type:String,default:''}, userKey:{type:[String,Number],required:true}, pageKey:{type:String,default:'store-receiving-bills'}, compact:{type:Boolean,default:false}, initialStart:{type:String,default:''}, initialEnd:{type:String,default:''} },
|
||||
data(){
|
||||
const filters=filtersForRange(this.initialStart,this.initialEnd);
|
||||
return{loading:false,loadError:false,sync:null,rows:[],sourceColumns:[],configuredColumns:[],columnsTitle:'',sourceGroups:[],summary:{income:0,expense:0,balance:0,count:0},total:0,currentPage:1,lastPage:1,perPage:this.compact?1:20,filters,queryFilters:{...filters},resultFilters:null,filterOptions:{shops:[],bill_items:[],ledger_remarks:[]},filterOptionsLoading:false,filterOptionsError:false,requestSerial:0,promotionRequestSerial:0,sort:{key:'occurred_at',dir:'desc'},exportMode:'filtered-visible',exporting:false,exportModalOpen:false,exportTasks:[],exportHint:'导出在后台生成,成功后点「下载」;期间可以继续查看其它页面。',exportTimer:null,promotionModalOpen:false,promotionExpenseTab:'platform',promotionExpenseLoading:false,promotionExpenseError:false,promotionStoreSummary:[],promotionExpenseSummary:{expense:0,count:0},promotionExpenseMessage:'',promotionPlatformFilter:'',promotionStoreFilter:'',promotionNoteFilter:'',promotionNoteTimer:null};
|
||||
},
|
||||
computed:{
|
||||
isDouyin(){return this.billType==='douyin';},
|
||||
isPromotionMarketing(){return this.billType==='promotion-marketing';},
|
||||
@ -289,7 +298,7 @@ export default {
|
||||
visibleGroups(){return this.visibleColumns.reduce((groups,column)=>{if(!column.group)return groups;const last=groups[groups.length-1];if(last&&last.label===column.group)last.span+=1;else groups.push({label:column.group,span:1});return groups;},[]);},
|
||||
stats(){
|
||||
if(this.isPromotionMarketing){
|
||||
return [{label:'推广费(营销账户)',value:this.moneyWan(this.promotionExpenseSummary.expense),note:`${this.promotionExpenseSummary.count} 笔支出 · 按当前筛选`,className:'is-expense',action:true}];
|
||||
return [{label:'推广费(营销账户)',value:this.loadError?'—':this.moneyWan(this.promotionExpenseSummary.expense),note:this.loadError?'加载失败,请刷新页面重试':`${this.promotionExpenseSummary.count} 笔支出 · 按当前筛选`,className:'is-expense',action:true}];
|
||||
}
|
||||
const netLabel=JD_BILL_TABS.concat(['vipshop-jx']).indexOf(this.billType)!==-1;
|
||||
const items=[
|
||||
@ -307,13 +316,13 @@ export default {
|
||||
promotionFilteredExpenseTotal(){return this.promotionFilteredStoreRows.reduce((sum,row)=>sum+Number(row.amount||0),0);},
|
||||
rangeStart(){return this.total===0?0:(this.currentPage-1)*this.perPage+1;},rangeEnd(){return Math.min(this.currentPage*this.perPage,this.total);}
|
||||
},
|
||||
mounted(){if(this.usesMirrorOptions)this.fetchFilterOptions();this.fetch();},
|
||||
mounted(){if(this.usesMirrorOptions&&!this.compact)this.fetchFilterOptions();this.fetch();},
|
||||
beforeDestroy(){if(this.promotionNoteTimer)window.clearTimeout(this.promotionNoteTimer);},
|
||||
methods:{
|
||||
async fetchFilterOptions(){this.filterOptionsLoading=true;this.filterOptionsError=false;try{const response=await window.axios.get(`${this.apiBase}/filter-options`,{params:{start:this.filters.start,end:this.filters.end}});const body=response.data||{};this.filterOptions={shops:Array.isArray(body.shops)?body.shops:[],bill_items:Array.isArray(body.bill_items)?body.bill_items:[],ledger_remarks:Array.isArray(body.ledger_remarks)?body.ledger_remarks:[]};}catch(error){this.filterOptionsError=true;}finally{this.filterOptionsLoading=false;}},
|
||||
async fetch(){const requestId=++this.requestSerial;const requestedFilters={...this.queryFilters};this.loading=true;this.loadError=false;try{const response=await window.axios.get(this.apiBase,{params:{...requestedFilters,sort:this.sort.key,dir:this.sort.dir,limit:this.perPage,page:this.currentPage}});if(requestId!==this.requestSerial)return;const body=response.data||{};this.resultFilters=requestedFilters;this.sync=body.sync||null;this.rows=body.data||[];this.total=body.total||0;this.currentPage=body.current_page||1;this.lastPage=body.last_page||1;this.perPage=body.per_page||this.perPage;this.summary=body.summary||{income:0,expense:0,balance:0,count:0};if(this.isPromotionMarketing){this.promotionExpenseSummary={expense:Number(this.summary.expense||0),count:Number(this.summary.count||0)};}if(body.columns){this.columnsTitle=body.columns.title||'';this.sourceGroups=body.columns.groups||[];this.sourceColumns=this.attachGroups(body.columns.columns||body.columns||[],this.sourceGroups);}}catch(error){if(requestId!==this.requestSerial)return;this.rows=[];this.total=0;this.sync=null;this.summary={income:0,expense:0,balance:0,count:0};this.loadError=true;}finally{if(requestId===this.requestSerial)this.loading=false;}},
|
||||
attachGroups(columns,groups){let index=0;return columns.map((column,columnIndex)=>{while(groups[index]&&columnIndex>=groups.slice(0,index+1).reduce((sum,group)=>sum+Number(group.span||0),0))index+=1;return{...column,key:column.field||`column-${columnIndex}`,group:groups[index]?groups[index].label:''};});},
|
||||
applyFilters(){if(this.invalidDateRange)return;this.queryFilters={...this.filters};this.currentPage=1;return this.fetch();},resetFilters(){this.filters=emptyFilters(this.billType);return this.applyFilters();},changePage(page){this.currentPage=page;this.fetch();this.$nextTick(()=>{if(this.$refs.scroll)this.$refs.scroll.scrollTop=0;});},
|
||||
applyFilters(){if(this.invalidDateRange)return;this.queryFilters={...this.filters};this.currentPage=1;return this.fetch();},resetFilters(){this.filters=filtersForRange();return this.applyFilters();},changePage(page){this.currentPage=page;this.fetch();this.$nextTick(()=>{if(this.$refs.scroll)this.$refs.scroll.scrollTop=0;});},
|
||||
openDatePicker(refName){const picker=this.$refs[refName];if(picker&&picker.fp)picker.fp.open();},
|
||||
async fetchPromotionExpenseDetails(filters=null){
|
||||
const requestId=++this.promotionRequestSerial;
|
||||
@ -326,15 +335,10 @@ export default {
|
||||
if(requestId!==this.promotionRequestSerial)return;
|
||||
const body=response.data||{};
|
||||
this.promotionStoreSummary=Array.isArray(body.store_summary)?body.store_summary:[];
|
||||
this.promotionExpenseSummary={
|
||||
expense:Number((body.summary||{}).expense||0),
|
||||
count:Number((body.summary||{}).count||0),
|
||||
};
|
||||
this.promotionExpenseMessage=body.message||'';
|
||||
}catch(error){
|
||||
if(requestId!==this.promotionRequestSerial)return;
|
||||
this.promotionStoreSummary=[];
|
||||
this.promotionExpenseSummary={expense:0,count:0};
|
||||
this.promotionExpenseError=true;
|
||||
}finally{
|
||||
if(requestId===this.promotionRequestSerial)this.promotionExpenseLoading=false;
|
||||
@ -525,9 +529,15 @@ export default {
|
||||
|
||||
<style scoped>
|
||||
.tw-stats--promotion-only{grid-template-columns:minmax(0,1fr)}
|
||||
.table-workspace--compact,.tw-stats--promotion-compact{display:contents}
|
||||
.tw-stats--promotion-compact .tw-stat{padding:.75rem 1rem;border-color:#e5e7eb;border-radius:.5rem}
|
||||
.tw-stats--promotion-compact .tw-label{color:#6b7280;font-size:.75rem;font-weight:500}
|
||||
.tw-stats--promotion-compact .tw-value{margin-top:.25rem;font-size:1.125rem;font-weight:600}
|
||||
.tw-stats--promotion-compact .tw-note{margin-top:.125rem;font-size:.625rem}
|
||||
.tw-stat--promotion{display:flex;min-height:5.25rem;flex-direction:column}
|
||||
.tw-promotion-link{margin-top:auto;align-self:flex-end;padding:0;border:0;background:transparent;color:#856632;font:500 .75rem/1.5 inherit;text-decoration:underline;text-underline-offset:2px;cursor:pointer}
|
||||
.tw-promotion-link:hover{color:#6a5129}
|
||||
.tw-promotion-link:disabled{cursor:not-allowed;opacity:.5}
|
||||
.tw-promotion-link:focus-visible{outline:none;box-shadow:0 0 0 3px rgba(133,102,50,.2);border-radius:.25rem}
|
||||
.tw-promotion-modal{width:min(980px,100%)}
|
||||
.tw-promotion-modal .tw-modal__head{gap:1rem}
|
||||
|
||||
37
resources/assets/js/views/business/pages.js
vendored
37
resources/assets/js/views/business/pages.js
vendored
@ -8,6 +8,8 @@
|
||||
* - taobao-wechat / pinduoduo / jd-wallet / jd-transactions / jingxi / dewu / vipshop / vipshop-jx / promotion-marketing
|
||||
* → 通用实现 AccountBills(表 ec_account_bills,按 bill_type 区分)
|
||||
* - douyin → AccountBills 展示共享原始账单镜像中的头条放心购渠道
|
||||
* 店铺营销账户账单(marketing-account-bills)的推广费页签复用 AccountBills;
|
||||
* 淘系支付宝页签同时挂载其紧凑汇总卡和同一个店铺明细弹窗。
|
||||
*/
|
||||
|
||||
require('./../../bootstrap');
|
||||
@ -17,7 +19,7 @@ import TaobaoAlipayBills from './../../components/business/TaobaoAlipayBills.vue
|
||||
import AccountBills from './../../components/business/AccountBills.vue';
|
||||
|
||||
const GENERIC_TABS = ['taobao-wechat', 'pinduoduo', 'jd-wallet', 'jd-transactions', 'jingxi', 'douyin', 'dewu', 'vipshop', 'vipshop-jx', 'promotion-marketing'];
|
||||
const STORE_RECEIVING_TABS = ['taobao-alipay', 'taobao-wechat', 'pinduoduo', 'jd-wallet', 'jd-transactions', 'jingxi', 'douyin', 'dewu', 'vipshop', 'vipshop-jx', 'promotion-marketing'];
|
||||
const STORE_RECEIVING_TABS = ['taobao-alipay', 'taobao-wechat', 'pinduoduo', 'jd-wallet', 'jd-transactions', 'jingxi', 'douyin', 'dewu', 'vipshop', 'vipshop-jx'];
|
||||
|
||||
function pageContext(pathname = window.location.pathname) {
|
||||
const parts = (pathname || '').split('/').filter(Boolean);
|
||||
@ -53,12 +55,16 @@ function resolveComponent(context) {
|
||||
return { Component: TaobaoAlipayBills, props: { apiBase: alipayBase, exportBase, progressBase } };
|
||||
}
|
||||
|
||||
// 店铺收款账户账单 › 其它账户类型(通用实现)
|
||||
if (context.page === 'store-receiving-bills' && GENERIC_TABS.indexOf(context.tab) !== -1) {
|
||||
// 店铺收款账户账单 › 其它账户类型,以及店铺营销账户账单 › 推广费(通用实现)
|
||||
if (
|
||||
(context.page === 'store-receiving-bills' || context.page === 'marketing-account-bills')
|
||||
&& GENERIC_TABS.indexOf(context.tab) !== -1
|
||||
&& (context.page === 'store-receiving-bills' || context.tab === 'promotion-marketing')
|
||||
) {
|
||||
const progressBase = context.companyId
|
||||
? `/${context.companyId}/ecommerce-finance/account-bills/export-progress`
|
||||
: '/ecommerce-finance/account-bills/export-progress';
|
||||
return { Component: AccountBills, props: { apiBase: accountBase, billType: context.tab, progressBase } };
|
||||
return { Component: AccountBills, props: { apiBase: accountBase, billType: context.tab, progressBase, pageKey: context.page } };
|
||||
}
|
||||
|
||||
return null;
|
||||
@ -172,3 +178,26 @@ if (mount) {
|
||||
|
||||
updateActiveTab(initialContext.tab);
|
||||
}
|
||||
|
||||
document.querySelectorAll('[data-promotion-expense-summary]').forEach((mount) => {
|
||||
const userKey = mount.dataset.userKey || 'anonymous';
|
||||
|
||||
/* eslint-disable no-new */
|
||||
new Vue({
|
||||
el: mount,
|
||||
|
||||
render(h) {
|
||||
return h(AccountBills, {
|
||||
props: {
|
||||
apiBase: mount.dataset.apiBase,
|
||||
billType: 'promotion-marketing',
|
||||
userKey,
|
||||
pageKey: 'marketing-account-bills',
|
||||
compact: true,
|
||||
initialStart: mount.dataset.start || '',
|
||||
initialEnd: mount.dataset.end || '',
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
@ -40,8 +40,19 @@
|
||||
|
||||
<div class="w-full">
|
||||
{{-- 汇总卡:当前筛选条件下的收入/支出/净额/笔数,金额单位为万元 --}}
|
||||
@if ($hasSummary)
|
||||
<div class="mb-4 grid grid-cols-2 gap-3 sm:grid-cols-4">
|
||||
<div class="mb-4 grid grid-cols-2 gap-3 sm:grid-cols-5">
|
||||
<div
|
||||
class="rounded-lg border border-gray-200 bg-white px-4 py-3"
|
||||
data-promotion-expense-summary
|
||||
data-api-base="{{ route('ecommerce-finance.account-bills.index', ['bill_type' => 'promotion-marketing']) }}"
|
||||
data-start="{{ $filters['from'] ?? '' }}"
|
||||
data-end="{{ $filters['to'] ?? '' }}"
|
||||
data-user-key="{{ user()->id }}"
|
||||
>
|
||||
<div class="text-xs font-medium text-gray-500">推广费(营销账户)</div>
|
||||
<div class="mt-1 text-lg font-semibold tabular-nums text-gray-500">加载中…</div>
|
||||
</div>
|
||||
@if ($hasSummary)
|
||||
<div class="rounded-lg border border-gray-200 bg-white px-4 py-3">
|
||||
<div class="text-xs font-medium text-gray-500">收入合计(万元)</div>
|
||||
<div class="mt-1 text-lg font-semibold text-green-600 tabular-nums">
|
||||
@ -66,8 +77,8 @@
|
||||
{{ number_format((int) ($summary['count'] ?? 0)) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
@endif
|
||||
</div>
|
||||
|
||||
{{-- 筛选器:按店铺 + 收支类型 + 交易日期范围 --}}
|
||||
@php
|
||||
|
||||
@ -0,0 +1,6 @@
|
||||
<section class="business-content-section" aria-label="推广费(营销账户)">
|
||||
<div
|
||||
class="business-blank-page"
|
||||
data-table-preferences-user="{{ user()->id }}"
|
||||
></div>
|
||||
</section>
|
||||
@ -1,6 +1,10 @@
|
||||
@php
|
||||
$isMarketingAccountBillsPage = ($section['slug'] ?? null) === 'billing-transactions'
|
||||
&& ($page['slug'] ?? null) === 'marketing-account-bills';
|
||||
$isPromotionMarketingBillsTab = $isMarketingAccountBillsPage
|
||||
&& ($activeTab['slug'] ?? null) === 'promotion-marketing';
|
||||
$isPromotionMarketingSummaryTab = $isMarketingAccountBillsPage
|
||||
&& ($activeTab['slug'] ?? null) === 'taobao-alipay';
|
||||
$isMarketingAccountBalancePage = ($section['slug'] ?? null) === 'billing-transactions'
|
||||
&& ($page['slug'] ?? null) === 'marketing-account-balance';
|
||||
$isStoreReceivingAccountsPage = ($section['slug'] ?? null) === 'fund-management'
|
||||
@ -981,6 +985,9 @@
|
||||
'business-tab-link',
|
||||
'is-active' => $activeTab['slug'] === $tab['slug'],
|
||||
])
|
||||
@if ($isMarketingAccountBillsPage && $tab['slug'] === 'promotion-marketing')
|
||||
data-marketing-account-bills-full-page
|
||||
@endif
|
||||
@if ($activeTab['slug'] === $tab['slug']) aria-current="page" @endif
|
||||
>
|
||||
<span class="material-icons-outlined text-xl" aria-hidden="true">
|
||||
@ -1461,6 +1468,9 @@
|
||||
@if ($isMarketingAccountBillsPage)
|
||||
@push('scripts_start')
|
||||
<script src="{{ asset('public/js/business/marketing-account-bills.min.js') }}?id={{ md5_file(public_path('js/business/marketing-account-bills.min.js')) }}"></script>
|
||||
@if ($isPromotionMarketingBillsTab || $isPromotionMarketingSummaryTab)
|
||||
<script src="{{ asset('public/js/business/pages.min.js') }}?id={{ md5_file(public_path('js/business/pages.min.js')) }}"></script>
|
||||
@endif
|
||||
@endpush
|
||||
@elseif (($page['slug'] ?? null) === 'store-receiving-bills')
|
||||
@push('scripts_start')
|
||||
|
||||
@ -4,30 +4,32 @@ namespace Tests\Feature\Business;
|
||||
|
||||
use Mockery;
|
||||
use Modules\EcommerceFinance\Repositories\PromotionMarketingExpenseRepository;
|
||||
use Modules\EcommerceFinance\Repositories\TmallTaobaoSpreadBillRepository;
|
||||
use Tests\Feature\FeatureTestCase;
|
||||
|
||||
class PromotionMarketingAccountBillsTest extends FeatureTestCase
|
||||
{
|
||||
public function test_promotion_marketing_tab_is_next_to_vipshop_jx(): void
|
||||
public function test_promotion_marketing_tab_is_first_inside_marketing_account_bills(): void
|
||||
{
|
||||
$pages = collect(config('business_navigation'))
|
||||
->firstWhere('slug', 'billing-transactions')['pages'];
|
||||
$tabs = collect($pages)->firstWhere('slug', 'store-receiving-bills')['tabs'];
|
||||
$slugs = array_column($tabs, 'slug');
|
||||
$vipshopJxIndex = array_search('vipshop-jx', $slugs, true);
|
||||
$receivingTabs = collect($pages)->firstWhere('slug', 'store-receiving-bills')['tabs'];
|
||||
$marketingTabs = collect($pages)->firstWhere('slug', 'marketing-account-bills')['tabs'];
|
||||
|
||||
$this->assertNotFalse($vipshopJxIndex);
|
||||
$this->assertSame('promotion-marketing', $slugs[$vipshopJxIndex + 1] ?? null);
|
||||
$this->assertSame('推广费(营销账户)', $tabs[$vipshopJxIndex + 1]['title'] ?? null);
|
||||
$this->assertNotContains('promotion-marketing', array_column($receivingTabs, 'slug'));
|
||||
$this->assertSame('promotion-marketing', $marketingTabs[0]['slug'] ?? null);
|
||||
$this->assertSame('推广费(营销账户)', $marketingTabs[0]['title'] ?? null);
|
||||
$this->assertSame('taobao-alipay', $marketingTabs[1]['slug'] ?? null);
|
||||
|
||||
$this->loginAs()
|
||||
->get(route('business.pages.show', [
|
||||
'section' => 'billing-transactions',
|
||||
'page' => 'store-receiving-bills',
|
||||
'page' => 'marketing-account-bills',
|
||||
'tab' => 'promotion-marketing',
|
||||
]))
|
||||
->assertOk()
|
||||
->assertSeeText('推广费(营销账户)');
|
||||
->assertSeeText('推广费(营销账户)')
|
||||
->assertSee('class="business-blank-page"', false);
|
||||
}
|
||||
|
||||
public function test_promotion_marketing_page_and_details_share_contract(): void
|
||||
@ -101,6 +103,40 @@ class PromotionMarketingAccountBillsTest extends FeatureTestCase
|
||||
->assertJsonPath('store_summary.0.amount', 12.34);
|
||||
}
|
||||
|
||||
public function test_taobao_alipay_tab_mounts_the_promotion_card_before_income(): void
|
||||
{
|
||||
$repository = Mockery::mock(TmallTaobaoSpreadBillRepository::class);
|
||||
$repository->shouldReceive('usesBusinessMirror')->andReturnTrue();
|
||||
$repository->shouldReceive('paginate')->once()->andReturn(['rows' => [], 'total' => 0]);
|
||||
$repository->shouldReceive('summary')->once()->andReturn([
|
||||
'income_total' => 100,
|
||||
'expense_total' => 0,
|
||||
'net' => 100,
|
||||
'count' => 1,
|
||||
]);
|
||||
$repository->shouldReceive('storeOptions')->once()->andReturn([]);
|
||||
$this->app->instance(TmallTaobaoSpreadBillRepository::class, $repository);
|
||||
|
||||
$response = $this->loginAs()->get(route('business.pages.show', [
|
||||
'section' => 'billing-transactions',
|
||||
'page' => 'marketing-account-bills',
|
||||
'tab' => 'taobao-alipay',
|
||||
]))->assertOk();
|
||||
|
||||
$html = $response->getContent();
|
||||
$promotionPosition = strpos($html, 'data-promotion-expense-summary');
|
||||
$incomePosition = strpos($html, '收入合计(万元)');
|
||||
|
||||
$this->assertNotFalse($promotionPosition);
|
||||
$this->assertNotFalse($incomePosition);
|
||||
$this->assertLessThan($incomePosition, $promotionPosition);
|
||||
$response
|
||||
->assertSee('public/js/business/pages.min.js', false)
|
||||
->assertSee(route('ecommerce-finance.account-bills.index', [
|
||||
'bill_type' => 'promotion-marketing',
|
||||
]), false);
|
||||
}
|
||||
|
||||
public function test_promotion_marketing_store_options_come_from_the_same_date_range(): void
|
||||
{
|
||||
$repository = Mockery::mock(PromotionMarketingExpenseRepository::class);
|
||||
|
||||
@ -16,7 +16,7 @@ class AccountBillsTableTest extends TestCase
|
||||
$this->assertStringContainsString('.tw-date.is-stable-date .tw-date-control', $source);
|
||||
$this->assertStringContainsString("['douyin','pinduoduo','dewu','taobao-wechat','jd-transactions','jingxi','jd-wallet','vipshop','vipshop-jx','promotion-marketing'].indexOf(this.billType)!==-1", $source);
|
||||
$this->assertStringContainsString('v-if="usesMirrorOptions && !filterOptionsError"', $source);
|
||||
$this->assertStringContainsString('page-key="store-receiving-bills"', $source);
|
||||
$this->assertStringContainsString(':page-key="pageKey"', $source);
|
||||
$this->assertStringContainsString(':tab-key="billType"', $source);
|
||||
$this->assertStringContainsString("value=\"page-visible\"", $source);
|
||||
$this->assertStringContainsString("value=\"filtered-visible\"", $source);
|
||||
@ -34,7 +34,11 @@ class AccountBillsTableTest extends TestCase
|
||||
$source,
|
||||
);
|
||||
$this->assertStringContainsString(
|
||||
"const STORE_RECEIVING_TABS = ['taobao-alipay', 'taobao-wechat', 'pinduoduo', 'jd-wallet', 'jd-transactions', 'jingxi', 'douyin', 'dewu', 'vipshop', 'vipshop-jx', 'promotion-marketing'];",
|
||||
"const STORE_RECEIVING_TABS = ['taobao-alipay', 'taobao-wechat', 'pinduoduo', 'jd-wallet', 'jd-transactions', 'jingxi', 'douyin', 'dewu', 'vipshop', 'vipshop-jx'];",
|
||||
$source,
|
||||
);
|
||||
$this->assertStringContainsString(
|
||||
"context.page === 'store-receiving-bills' || context.tab === 'promotion-marketing'",
|
||||
$source,
|
||||
);
|
||||
}
|
||||
@ -73,7 +77,23 @@ class AccountBillsTableTest extends TestCase
|
||||
$this->assertStringContainsString('promotionFilteredStoreRows', $source);
|
||||
$this->assertStringContainsString('tw-stats--promotion-only', $source);
|
||||
$this->assertStringContainsString('.tw-stats--promotion-only{grid-template-columns:minmax(0,1fr)}', $source);
|
||||
$this->assertStringContainsString('tw-stats--promotion-compact', $source);
|
||||
$this->assertStringContainsString('table-workspace--compact', $source);
|
||||
$this->assertStringContainsString('class="tw-date-icon"', $source);
|
||||
$this->assertStringContainsString("if(this.isPromotionMarketing){\n return [{label:'推广费(营销账户)'", $source);
|
||||
}
|
||||
|
||||
public function test_taobao_marketing_tab_places_the_promotion_card_before_income(): void
|
||||
{
|
||||
$source = file_get_contents(__DIR__ . '/../../../resources/views/banking/marketing_taobao_alipay_bills/partials/embedded-table.blade.php');
|
||||
|
||||
$promotionPosition = strpos($source, 'data-promotion-expense-summary');
|
||||
$incomePosition = strpos($source, '收入合计(万元)');
|
||||
|
||||
$this->assertNotFalse($promotionPosition);
|
||||
$this->assertNotFalse($incomePosition);
|
||||
$this->assertLessThan($incomePosition, $promotionPosition);
|
||||
$this->assertStringContainsString('sm:grid-cols-5', $source);
|
||||
$this->assertStringContainsString('data-api-base=', $source);
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user