增加计费列表收货时间查询
This commit is contained in:
+4
@@ -40,6 +40,10 @@ public class BatchAddIncomeBillFeeRequest implements Serializable
|
||||
private String beginBizDate;
|
||||
private String endBizDate;
|
||||
|
||||
/** Receive/pickup time range (pickup_date), inclusive. */
|
||||
private String beginPickupDate;
|
||||
private String endPickupDate;
|
||||
|
||||
/** Surcharge config id (prc_surcharge.sc_id). */
|
||||
@NotNull(message = "scId required")
|
||||
private Long scId;
|
||||
|
||||
+3
@@ -25,4 +25,7 @@ public class ChargeWeightTemplateRequest implements Serializable
|
||||
private List<Integer> orderStatuses;
|
||||
private String beginBizDate;
|
||||
private String endBizDate;
|
||||
/** Receive/pickup time range (pickup_date), inclusive. */
|
||||
private String beginPickupDate;
|
||||
private String endPickupDate;
|
||||
}
|
||||
|
||||
+1
-1
@@ -56,7 +56,7 @@ public class PrcBillingBillListExportRow implements Serializable
|
||||
@Excel(name = "计费时间", width = 20, dateFormat = "yyyy-MM-dd HH:mm:ss")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date bizDate;
|
||||
@Excel(name = "提货时间", width = 20, dateFormat = "yyyy-MM-dd HH:mm:ss")
|
||||
@Excel(name = "收货时间", width = 20, dateFormat = "yyyy-MM-dd HH:mm:ss")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date pickupDate;
|
||||
@Excel(name = "销售员", width = 12)
|
||||
|
||||
@@ -31,4 +31,7 @@ public class RecalcBillPriceRequest implements Serializable
|
||||
private List<Integer> billingFailStatuses;
|
||||
private String beginBizDate;
|
||||
private String endBizDate;
|
||||
/** Receive/pickup time range (pickup_date), inclusive. */
|
||||
private String beginPickupDate;
|
||||
private String endPickupDate;
|
||||
}
|
||||
|
||||
@@ -630,11 +630,6 @@ public class EsBillSearchStore implements IBillSearchStore
|
||||
filters.add(wrap);
|
||||
}
|
||||
|
||||
private String escapeWildcard(String value)
|
||||
{
|
||||
return value.replace("\\", "\\\\").replace("*", "\\*").replace("?", "\\?");
|
||||
}
|
||||
|
||||
private long readTotal(Object total)
|
||||
{
|
||||
if (total instanceof JSONObject)
|
||||
|
||||
+31
-2
@@ -681,6 +681,7 @@ public class PrcOrderBillServiceImpl implements IPrcOrderBillService
|
||||
request.getCustomerCodes(), request.getProductCodes(), request.getCountryCode(),
|
||||
request.getCreditArrearsStatus(), request.getOrderStatuses(), request.getBillingFailStatuses(),
|
||||
request.getBeginBizDate(), request.getEndBizDate(),
|
||||
request.getBeginPickupDate(), request.getEndPickupDate(),
|
||||
"price.bill.recalc.filterRequired", "price.bill.recalc.scopeRequired",
|
||||
"price.bill.recalc.selectedRequired");
|
||||
if (bills.isEmpty())
|
||||
@@ -1276,6 +1277,7 @@ public class PrcOrderBillServiceImpl implements IPrcOrderBillService
|
||||
request.getCustomerCodes(), request.getProductCodes(), request.getCountryCode(),
|
||||
request.getCreditArrearsStatus(), request.getOrderStatuses(), request.getBillingFailStatuses(),
|
||||
request.getBeginBizDate(), request.getEndBizDate(),
|
||||
request.getBeginPickupDate(), request.getEndPickupDate(),
|
||||
"price.bill.fee.batch.filterRequired", "price.bill.fee.batch.scopeRequired",
|
||||
"price.bill.fee.batch.selectedRequired");
|
||||
}
|
||||
@@ -1290,6 +1292,7 @@ public class PrcOrderBillServiceImpl implements IPrcOrderBillService
|
||||
List<String> customerCodesRaw, List<String> productCodesRaw, String countryCodeRaw,
|
||||
String creditArrearsStatusRaw, List<Integer> orderStatusesRaw, List<Integer> billingFailStatusesRaw,
|
||||
String beginBizDateRaw, String endBizDateRaw,
|
||||
String beginPickupDateRaw, String endPickupDateRaw,
|
||||
String msgFilter, String msgScope, String msgSelected)
|
||||
{
|
||||
String scopeMode = StringUtils.nvl(scopeModeRaw, "").trim().toUpperCase();
|
||||
@@ -1311,6 +1314,8 @@ public class PrcOrderBillServiceImpl implements IPrcOrderBillService
|
||||
: countryCodeRaw.trim().toUpperCase();
|
||||
String beginBizDate = StringUtils.isEmpty(beginBizDateRaw) ? null : beginBizDateRaw.trim();
|
||||
String endBizDate = StringUtils.isEmpty(endBizDateRaw) ? null : endBizDateRaw.trim();
|
||||
String beginPickupDate = StringUtils.isEmpty(beginPickupDateRaw) ? null : beginPickupDateRaw.trim();
|
||||
String endPickupDate = StringUtils.isEmpty(endPickupDateRaw) ? null : endPickupDateRaw.trim();
|
||||
boolean hasFilter = (orderNos != null && !orderNos.isEmpty())
|
||||
|| (trackingNos != null && !trackingNos.isEmpty())
|
||||
|| (transferNos != null && !transferNos.isEmpty())
|
||||
@@ -1321,7 +1326,9 @@ public class PrcOrderBillServiceImpl implements IPrcOrderBillService
|
||||
|| StringUtils.isNotEmpty(creditArrearsStatus)
|
||||
|| StringUtils.isNotEmpty(countryCode)
|
||||
|| StringUtils.isNotEmpty(beginBizDate)
|
||||
|| StringUtils.isNotEmpty(endBizDate);
|
||||
|| StringUtils.isNotEmpty(endBizDate)
|
||||
|| StringUtils.isNotEmpty(beginPickupDate)
|
||||
|| StringUtils.isNotEmpty(endPickupDate);
|
||||
if (!hasFilter)
|
||||
{
|
||||
throw new ServiceException(MessageUtils.message(msgFilter));
|
||||
@@ -1356,6 +1363,14 @@ public class PrcOrderBillServiceImpl implements IPrcOrderBillService
|
||||
{
|
||||
query.getParams().put("endBizDate", endBizDate);
|
||||
}
|
||||
if (beginPickupDate != null)
|
||||
{
|
||||
query.getParams().put("beginPickupDate", beginPickupDate);
|
||||
}
|
||||
if (endPickupDate != null)
|
||||
{
|
||||
query.getParams().put("endPickupDate", endPickupDate);
|
||||
}
|
||||
}
|
||||
List<PrcOrderBill> list = self.selectBillList(query);
|
||||
return list == null ? new ArrayList<PrcOrderBill>() : list;
|
||||
@@ -2754,6 +2769,10 @@ public class PrcOrderBillServiceImpl implements IPrcOrderBillService
|
||||
: request.getBeginBizDate().trim();
|
||||
String endBizDate = StringUtils.isEmpty(request.getEndBizDate()) ? null
|
||||
: request.getEndBizDate().trim();
|
||||
String beginPickupDate = StringUtils.isEmpty(request.getBeginPickupDate()) ? null
|
||||
: request.getBeginPickupDate().trim();
|
||||
String endPickupDate = StringUtils.isEmpty(request.getEndPickupDate()) ? null
|
||||
: request.getEndPickupDate().trim();
|
||||
boolean hasFilter = (orderNos != null && !orderNos.isEmpty())
|
||||
|| (trackingNos != null && !trackingNos.isEmpty())
|
||||
|| (transferNos != null && !transferNos.isEmpty())
|
||||
@@ -2762,7 +2781,9 @@ public class PrcOrderBillServiceImpl implements IPrcOrderBillService
|
||||
|| (productCodes != null && !productCodes.isEmpty())
|
||||
|| StringUtils.isNotEmpty(creditArrearsStatus)
|
||||
|| StringUtils.isNotEmpty(beginBizDate)
|
||||
|| StringUtils.isNotEmpty(endBizDate);
|
||||
|| StringUtils.isNotEmpty(endBizDate)
|
||||
|| StringUtils.isNotEmpty(beginPickupDate)
|
||||
|| StringUtils.isNotEmpty(endPickupDate);
|
||||
if (!hasFilter)
|
||||
{
|
||||
throw new ServiceException("请先设置列表筛选条件");
|
||||
@@ -2784,6 +2805,14 @@ public class PrcOrderBillServiceImpl implements IPrcOrderBillService
|
||||
{
|
||||
query.getParams().put("endBizDate", endBizDate);
|
||||
}
|
||||
if (beginPickupDate != null)
|
||||
{
|
||||
query.getParams().put("beginPickupDate", beginPickupDate);
|
||||
}
|
||||
if (endPickupDate != null)
|
||||
{
|
||||
query.getParams().put("endPickupDate", endPickupDate);
|
||||
}
|
||||
List<PrcOrderBill> list = self.selectBillList(query);
|
||||
if (list == null || list.isEmpty())
|
||||
{
|
||||
|
||||
@@ -123,11 +123,13 @@ function taskDone(task, options) {
|
||||
* @param {string} [options.filePrefix='export']
|
||||
* @param {string} [options.loadingText]
|
||||
* @param {(task: object) => void} [options.onProgress]
|
||||
* @param {number} [options.pollIntervalMs=800]
|
||||
* @param {boolean} [options.showLoading=true] full-screen ElLoading; set false when page shows its own float progress
|
||||
* @param {number} [options.pollIntervalMs=1000]
|
||||
* @param {number} [options.maxWaitMs]
|
||||
* @param {string[]} [options.successStatus]
|
||||
* @param {string[]} [options.failStatus]
|
||||
* @param {boolean} [options.showSuccessMessage=true]
|
||||
* @param {boolean} [options.showErrorMessage=true]
|
||||
*/
|
||||
export async function exportAsync(options = {}) {
|
||||
const { submitUrl, taskUrlPrefix, downloadUrlPrefix } = resolveAsyncUrls(options)
|
||||
@@ -152,11 +154,15 @@ export async function exportAsync(options = {}) {
|
||||
|
||||
const filePrefix = options.filePrefix || 'export'
|
||||
const loadingText = options.loadingText || '\u6b63\u5728\u5bfc\u51fa\uff0c\u8bf7\u7a0d\u5019'
|
||||
const pollIntervalMs = options.pollIntervalMs || 800
|
||||
const pollIntervalMs = options.pollIntervalMs || 1000
|
||||
const maxWaitMs = options.maxWaitMs || 30 * 60 * 1000
|
||||
const showSuccessMessage = options.showSuccessMessage !== false
|
||||
const showErrorMessage = options.showErrorMessage !== false
|
||||
const useLoading = options.showLoading !== false
|
||||
|
||||
const loading = ElLoading.service({ text: loadingText, background: 'rgba(0, 0, 0, 0.7)' })
|
||||
const loading = useLoading
|
||||
? ElLoading.service({ text: loadingText, background: 'rgba(0, 0, 0, 0.7)' })
|
||||
: null
|
||||
try {
|
||||
const submitRes = await request({
|
||||
url: submitUrl,
|
||||
@@ -167,15 +173,23 @@ export async function exportAsync(options = {}) {
|
||||
})
|
||||
const taskId = pickTaskId(submitRes && submitRes.data)
|
||||
if (!taskId) throw new Error('missing export taskId')
|
||||
if (typeof options.onTaskId === 'function') options.onTaskId(String(taskId))
|
||||
|
||||
const started = Date.now()
|
||||
let task = null
|
||||
let lastSig = ''
|
||||
while (Date.now() - started < maxWaitMs) {
|
||||
const hidden = typeof document !== 'undefined' && document.visibilityState === 'hidden'
|
||||
const res = await request({ url: taskUrlPrefix + String(taskId), method: 'get' })
|
||||
task = res.data || {}
|
||||
if (typeof options.onProgress === 'function') options.onProgress(task)
|
||||
const pct = Number(task.percent)
|
||||
if (!Number.isNaN(pct) && pct >= 0) {
|
||||
const st = String(task.status || task.exportStatus || '').toUpperCase()
|
||||
const sig = [st, pct, task.message, task.rowCount].join('|')
|
||||
if (typeof options.onProgress === 'function' && sig !== lastSig) {
|
||||
lastSig = sig
|
||||
options.onProgress(task)
|
||||
}
|
||||
if (loading && !Number.isNaN(pct) && pct >= 0) {
|
||||
loading.setText(loadingText + ' ' + Math.min(100, pct) + '%')
|
||||
}
|
||||
const state = taskDone(task, options)
|
||||
@@ -183,7 +197,7 @@ export async function exportAsync(options = {}) {
|
||||
if (state === 'FAIL') {
|
||||
throw new Error(task.error || task.remark || task.message || 'export failed')
|
||||
}
|
||||
await sleep(pollIntervalMs)
|
||||
await sleep(hidden ? 2500 : pollIntervalMs)
|
||||
}
|
||||
if (!task || taskDone(task, options) !== 'SUCCESS') {
|
||||
throw new Error('export timeout')
|
||||
@@ -204,10 +218,10 @@ export async function exportAsync(options = {}) {
|
||||
if (showSuccessMessage) ElMessage.success('\u5bfc\u51fa\u6210\u529f')
|
||||
return { task, taskId }
|
||||
} catch (e) {
|
||||
ElMessage.error((e && e.message) || '\u5bfc\u51fa\u5931\u8d25')
|
||||
if (showErrorMessage) ElMessage.error((e && e.message) || '\u5bfc\u51fa\u5931\u8d25')
|
||||
return null
|
||||
} finally {
|
||||
loading.close()
|
||||
if (loading) loading.close()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -68,7 +68,7 @@ export const BILLING_BILL_EXPORT_FIELDS = [
|
||||
{ prop: 'customerActiveStatusLabel', label: '客户状态' },
|
||||
{ prop: 'creditArrearsStatusLabel', label: '额度欠费' },
|
||||
{ prop: 'bizDate', label: '计费时间' },
|
||||
{ prop: 'pickupDate', label: '提货时间' },
|
||||
{ prop: 'pickupDate', label: '收货时间' },
|
||||
{ prop: 'belongSaler', label: '销售员' },
|
||||
{ prop: 'settlementMan', label: '结算员' },
|
||||
{ prop: 'serviceMan', label: '客服员' }
|
||||
|
||||
@@ -1,5 +1,20 @@
|
||||
<template>
|
||||
<div class="app-container billing-bill">
|
||||
<div v-if="exportFloatVisible" class="retry-status-float export-status-float">
|
||||
<el-tooltip :content="exportFloatTooltip" placement="bottom">
|
||||
<el-button class="retry-status-button" type="warning" plain size="small" @click="openExportProgress()">
|
||||
<span class="retry-progress-ring" :style="{ '--retry-progress': exportProgressDegree + 'deg' }" aria-hidden="true">
|
||||
<span class="retry-progress-value">{{ exportProgressText }}</span>
|
||||
</span>
|
||||
<span class="retry-status-text">
|
||||
<strong>{{ exportFloatTitle }}</strong>
|
||||
<small>{{ exportFloatSummary }}</small>
|
||||
</span>
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
|
||||
|
||||
<basic-table
|
||||
ref="tableRef"
|
||||
:columns="columns"
|
||||
@@ -157,6 +172,8 @@
|
||||
type="warning"
|
||||
plain
|
||||
icon="Download"
|
||||
:loading="exportLoading"
|
||||
:disabled="exportLoading"
|
||||
v-hasPermi="['price:billing:export']"
|
||||
@click="handleExport"
|
||||
>导出</el-button>
|
||||
@@ -266,6 +283,32 @@
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog
|
||||
v-model="exportProgressVisible"
|
||||
:title="exportDialogTitle"
|
||||
width="480px"
|
||||
append-to-body
|
||||
:close-on-click-modal="false"
|
||||
:close-on-press-escape="true"
|
||||
:show-close="true"
|
||||
>
|
||||
<el-progress
|
||||
:percentage="exportProgressPercent"
|
||||
:status="exportProgressStatus"
|
||||
:stroke-width="14"
|
||||
striped
|
||||
:striped-flow="exportLoading"
|
||||
/>
|
||||
<div class="export-progress__msg">{{ exportProgressMessage }}</div>
|
||||
<div class="export-progress__text">
|
||||
导出行数 {{ exportProgressRowCount }}
|
||||
</div>
|
||||
<p v-if="exportLoading" class="export-progress__hint">导出在后台进行,可收起弹窗继续操作;请勿关闭或刷新本页</p>
|
||||
<template #footer>
|
||||
<el-button v-if="exportLoading" @click="exportProgressVisible = false">收起</el-button>
|
||||
<el-button v-else type="primary" @click="exportProgressVisible = false">关闭</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
<excel-export-field-dialog
|
||||
ref="exportFieldRef"
|
||||
:fields="BILLING_BILL_EXPORT_FIELDS"
|
||||
@@ -319,6 +362,78 @@ const productOptions = computed(() =>
|
||||
.map(p => ({ code: p.productCode, name: p.productName || p.productCode }))
|
||||
)
|
||||
|
||||
const exportLoading = ref(false)
|
||||
const exportFinished = ref(false)
|
||||
const exportProgressVisible = ref(false)
|
||||
const exportProgressPercent = ref(0)
|
||||
const exportProgressMessage = ref('')
|
||||
const exportProgressStatus = ref(undefined)
|
||||
const exportProgressRowCount = ref(0)
|
||||
const exportTaskId = ref('')
|
||||
let exportFloatHideTimer = null
|
||||
|
||||
const exportFloatVisible = computed(() => exportLoading.value || exportFinished.value)
|
||||
const exportProgressDegree = computed(() => Math.round(((exportProgressPercent.value || 0) / 100) * 360))
|
||||
const exportProgressText = computed(() => (exportProgressPercent.value || 0) + '%')
|
||||
const exportDialogTitle = computed(() => {
|
||||
if (exportLoading.value) return '异步导出进行中'
|
||||
if (exportFinished.value) return '异步导出完成'
|
||||
return '异步导出'
|
||||
})
|
||||
const exportFloatTitle = computed(() => {
|
||||
if (exportLoading.value) return '导出中'
|
||||
if (exportProgressStatus.value === 'exception') return '导出失败'
|
||||
if (exportFinished.value) return '导出完成'
|
||||
return '导出'
|
||||
})
|
||||
const exportFloatSummary = computed(() => {
|
||||
const pct = exportProgressPercent.value || 0
|
||||
const rows = exportProgressRowCount.value || 0
|
||||
if (rows > 0) return pct + '% / ' + rows + ' 行'
|
||||
return pct + '%'
|
||||
})
|
||||
const exportFloatTooltip = computed(() => exportProgressMessage.value || '点击查看详情')
|
||||
|
||||
function openExportProgress() {
|
||||
exportProgressVisible.value = true
|
||||
}
|
||||
|
||||
function scheduleHideExportFloat() {
|
||||
if (exportFloatHideTimer) {
|
||||
clearTimeout(exportFloatHideTimer)
|
||||
exportFloatHideTimer = null
|
||||
}
|
||||
exportFloatHideTimer = window.setTimeout(() => {
|
||||
if (!exportLoading.value && exportFinished.value) {
|
||||
exportFinished.value = false
|
||||
}
|
||||
exportFloatHideTimer = null
|
||||
}, 8000)
|
||||
}
|
||||
|
||||
function applyExportProgress(t) {
|
||||
const pct = Math.min(100, Number(t && t.percent) || 0)
|
||||
exportProgressPercent.value = pct
|
||||
exportProgressRowCount.value = Number(t && t.rowCount) || 0
|
||||
const st = String((t && t.status) || '').toUpperCase()
|
||||
if (st === 'PENDING') {
|
||||
exportProgressMessage.value = (t && t.message) || '排队中...'
|
||||
exportProgressStatus.value = undefined
|
||||
} else if (st === 'RUNNING') {
|
||||
exportProgressMessage.value = (t && t.message) || ('导出中 ' + pct + '%')
|
||||
exportProgressStatus.value = undefined
|
||||
} else if (st === 'SUCCESS') {
|
||||
exportProgressMessage.value = (t && t.message) || '导出完成,正在下载'
|
||||
exportProgressStatus.value = 'success'
|
||||
exportProgressPercent.value = 100
|
||||
} else if (st === 'FAIL' || st === 'FAILED') {
|
||||
exportProgressMessage.value = (t && (t.error || t.message)) || '导出失败'
|
||||
exportProgressStatus.value = 'exception'
|
||||
} else {
|
||||
exportProgressMessage.value = (t && t.message) || ''
|
||||
}
|
||||
}
|
||||
|
||||
const tableRef = ref()
|
||||
const exportFieldRef = ref()
|
||||
const tableHeight = ref(480)
|
||||
@@ -457,7 +572,7 @@ const columns = [
|
||||
{ label: '额度欠费', prop: 'creditArrearsStatus', align: 'center', width: 100, slot: 'creditArrearsStatus' },
|
||||
// { label: '退费状态', prop: 'refundStatus', align: 'center', width: 100, slot: 'refundStatus' },
|
||||
{ label: '计费时间', prop: 'bizDate', align: 'center', width: 170, parseTime: true },
|
||||
{ label: '提货时间', prop: 'pickupDate', align: 'center', width: 170, parseTime: true },
|
||||
{ label: '收货时间', prop: 'pickupDate', align: 'center', width: 170, parseTime: true },
|
||||
{ label: '销售员', prop: 'belongSaler', minWidth: 100, showOverflowTooltip: true },
|
||||
{ label: '结算员', prop: 'settlementMan', minWidth: 100, showOverflowTooltip: true },
|
||||
{ label: '客服员', prop: 'serviceMan', minWidth: 100, showOverflowTooltip: true },
|
||||
@@ -530,20 +645,35 @@ const searchColumns = [
|
||||
// 只选日期时自动补全为当天 00:00:00 ~ 23:59:59
|
||||
defaultTime: [new Date(2000, 0, 1, 0, 0, 0), new Date(2000, 0, 1, 23, 59, 59)]
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '收货时间',
|
||||
prop: 'pickupDateRange',
|
||||
component: 'datetimerange',
|
||||
colSpan: 2,
|
||||
props: {
|
||||
clearable: true,
|
||||
defaultTime: [new Date(2000, 0, 1, 0, 0, 0), new Date(2000, 0, 1, 23, 59, 59)]
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
function buildBillQuery(params) {
|
||||
const { bizDateRange, orderIdentifiers: rawOrderIdentifiers, ...rest } = params || {}
|
||||
const { bizDateRange, pickupDateRange, orderIdentifiers: rawOrderIdentifiers, ...rest } = params || {}
|
||||
const query = { ...rest }
|
||||
const orderIdentifiers = parseOrderIdentifiers(rawOrderIdentifiers)
|
||||
if (orderIdentifiers.length) query.orderIdentifiers = orderIdentifiers
|
||||
const rangeParams = {}
|
||||
if (Array.isArray(bizDateRange) && (bizDateRange[0] || bizDateRange[1])) {
|
||||
query.params = {
|
||||
...(query.params || {}),
|
||||
beginBizDate: bizDateRange[0] || null,
|
||||
endBizDate: bizDateRange[1] || null
|
||||
rangeParams.beginBizDate = bizDateRange[0] || null
|
||||
rangeParams.endBizDate = bizDateRange[1] || null
|
||||
}
|
||||
if (Array.isArray(pickupDateRange) && (pickupDateRange[0] || pickupDateRange[1])) {
|
||||
rangeParams.beginPickupDate = pickupDateRange[0] || null
|
||||
rangeParams.endPickupDate = pickupDateRange[1] || null
|
||||
}
|
||||
if (Object.keys(rangeParams).length) {
|
||||
query.params = { ...(query.params || {}), ...rangeParams }
|
||||
}
|
||||
return query
|
||||
}
|
||||
@@ -557,11 +687,25 @@ function queryList(params) {
|
||||
}
|
||||
|
||||
async function handleExport() {
|
||||
if (exportLoading.value) return
|
||||
const params = tableRef.value?.getQueryParams?.() || {}
|
||||
if (!validateOrderIdentifierLimit(params.orderIdentifiers)) return
|
||||
const fields = await exportFieldRef.value?.open?.()
|
||||
if (!fields || !fields.length) return
|
||||
return exportAsyncFromTable({
|
||||
if (exportFloatHideTimer) {
|
||||
clearTimeout(exportFloatHideTimer)
|
||||
exportFloatHideTimer = null
|
||||
}
|
||||
exportLoading.value = true
|
||||
exportFinished.value = false
|
||||
exportProgressVisible.value = false
|
||||
exportProgressPercent.value = 0
|
||||
exportProgressRowCount.value = 0
|
||||
exportProgressStatus.value = undefined
|
||||
exportProgressMessage.value = '导出任务已提交'
|
||||
exportTaskId.value = ''
|
||||
try {
|
||||
const result = await exportAsyncFromTable({
|
||||
baseUrl: '/price/billing/bill/export',
|
||||
tableRef: tableRef.value,
|
||||
normalize: (raw) => {
|
||||
@@ -570,8 +714,39 @@ async function handleExport() {
|
||||
return q
|
||||
},
|
||||
filePrefix: 'billing_bill',
|
||||
requireFilter: true
|
||||
requireFilter: true,
|
||||
showLoading: false,
|
||||
showSuccessMessage: true,
|
||||
showErrorMessage: true,
|
||||
onTaskId: (id) => { exportTaskId.value = id },
|
||||
onProgress: (task) => applyExportProgress(task || {})
|
||||
})
|
||||
if (result && result.task) {
|
||||
applyExportProgress({ ...(result.task || {}), status: 'SUCCESS', percent: 100 })
|
||||
exportFinished.value = true
|
||||
scheduleHideExportFloat()
|
||||
} else if (!exportTaskId.value) {
|
||||
exportFinished.value = false
|
||||
exportProgressPercent.value = 0
|
||||
exportProgressMessage.value = ''
|
||||
exportProgressStatus.value = undefined
|
||||
} else if (exportProgressStatus.value !== 'exception') {
|
||||
exportProgressStatus.value = 'exception'
|
||||
exportProgressMessage.value = exportProgressMessage.value || '导出失败'
|
||||
exportFinished.value = true
|
||||
scheduleHideExportFloat()
|
||||
} else {
|
||||
exportFinished.value = true
|
||||
scheduleHideExportFloat()
|
||||
}
|
||||
} catch (e) {
|
||||
exportProgressStatus.value = 'exception'
|
||||
exportProgressMessage.value = (e && e.message) || '导出失败'
|
||||
exportFinished.value = true
|
||||
scheduleHideExportFloat()
|
||||
} finally {
|
||||
exportLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -645,14 +820,15 @@ function hasBatchFeeQuery(q) {
|
||||
|| hasArr(q.productCodes)
|
||||
|| hasArr(q.billingFailStatuses)
|
||||
|| hasArr(q.bizDateRange)
|
||||
|| hasArr(q.pickupDateRange)
|
||||
|| !!(q.creditArrearsStatus && String(q.creditArrearsStatus).trim())
|
||||
}
|
||||
|
||||
function openBatchFee() {
|
||||
const q = tableRef.value?.getQueryParams?.() || {}
|
||||
if (!validateOrderIdentifierLimit(q.orderIdentifiers)) return
|
||||
const { pageNum, pageSize, bizDateRange, ...rest } = q
|
||||
if (!hasBatchFeeQuery({ ...rest, bizDateRange })) {
|
||||
const { pageNum, pageSize, bizDateRange, pickupDateRange, ...rest } = q
|
||||
if (!hasBatchFeeQuery({ ...rest, bizDateRange, pickupDateRange })) {
|
||||
ElMessage.warning('请先设置列表查询条件')
|
||||
return
|
||||
}
|
||||
@@ -664,6 +840,10 @@ function openBatchFee() {
|
||||
listQuery.beginBizDate = bizDateRange[0] || null
|
||||
listQuery.endBizDate = bizDateRange[1] || null
|
||||
}
|
||||
if (Array.isArray(pickupDateRange) && (pickupDateRange[0] || pickupDateRange[1])) {
|
||||
listQuery.beginPickupDate = pickupDateRange[0] || null
|
||||
listQuery.endPickupDate = pickupDateRange[1] || null
|
||||
}
|
||||
batchFeeListQuery.value = listQuery
|
||||
batchFeeSelectedIds.value = tableRef.value?.getSelectionIds?.() || []
|
||||
batchFeeOpen.value = true
|
||||
@@ -676,8 +856,8 @@ function onBatchFeeSuccess() {
|
||||
function buildRecalcPayload() {
|
||||
const q = tableRef.value?.getQueryParams?.() || {}
|
||||
if (!validateOrderIdentifierLimit(q.orderIdentifiers)) return null
|
||||
const { pageNum, pageSize, bizDateRange, ...rest } = q
|
||||
if (!hasBatchFeeQuery({ ...rest, bizDateRange })) {
|
||||
const { pageNum, pageSize, bizDateRange, pickupDateRange, ...rest } = q
|
||||
if (!hasBatchFeeQuery({ ...rest, bizDateRange, pickupDateRange })) {
|
||||
ElMessage.warning('请先设置列表查询条件')
|
||||
return null
|
||||
}
|
||||
@@ -699,6 +879,10 @@ function buildRecalcPayload() {
|
||||
payload.beginBizDate = bizDateRange[0] || null
|
||||
payload.endBizDate = bizDateRange[1] || null
|
||||
}
|
||||
if (Array.isArray(pickupDateRange) && (pickupDateRange[0] || pickupDateRange[1])) {
|
||||
payload.beginPickupDate = pickupDateRange[0] || null
|
||||
payload.endPickupDate = pickupDateRange[1] || null
|
||||
}
|
||||
return payload
|
||||
}
|
||||
|
||||
@@ -775,7 +959,7 @@ function onChargeWeightUpdated() {
|
||||
|
||||
function buildChargeWeightTemplateQuery() {
|
||||
const q = tableRef.value?.getQueryParams?.() || {}
|
||||
const { pageNum, pageSize, bizDateRange, ...rest } = q
|
||||
const { pageNum, pageSize, bizDateRange, pickupDateRange, ...rest } = q
|
||||
const payload = {
|
||||
fillByFilter: !!cwFillByFilter.value,
|
||||
orderNos: rest.orderNos || [],
|
||||
@@ -791,6 +975,10 @@ function buildChargeWeightTemplateQuery() {
|
||||
payload.beginBizDate = bizDateRange[0] || null
|
||||
payload.endBizDate = bizDateRange[1] || null
|
||||
}
|
||||
if (Array.isArray(pickupDateRange) && (pickupDateRange[0] || pickupDateRange[1])) {
|
||||
payload.beginPickupDate = pickupDateRange[0] || null
|
||||
payload.endPickupDate = pickupDateRange[1] || null
|
||||
}
|
||||
return payload
|
||||
}
|
||||
|
||||
@@ -999,4 +1187,67 @@ function refundTagType(status) {
|
||||
width: 100%;
|
||||
}
|
||||
.order-identifier-search { width: 100%; }
|
||||
.billing-bill .retry-status-float {
|
||||
position: fixed;
|
||||
top: 92px;
|
||||
right: 24px;
|
||||
z-index: 3001;
|
||||
}
|
||||
.billing-bill .retry-status-button {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
min-width: 200px;
|
||||
min-height: 68px;
|
||||
padding: 6px 16px 6px 7px;
|
||||
border-radius: 36px;
|
||||
}
|
||||
.billing-bill .retry-progress-ring {
|
||||
position: relative;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex: 0 0 54px;
|
||||
width: 54px;
|
||||
height: 54px;
|
||||
border-radius: 50%;
|
||||
background: conic-gradient(
|
||||
var(--el-color-warning) 0deg var(--retry-progress, 45deg),
|
||||
var(--el-color-warning-light-8) var(--retry-progress, 45deg) 360deg
|
||||
);
|
||||
}
|
||||
.billing-bill .retry-progress-ring::after {
|
||||
position: absolute;
|
||||
inset: 5px;
|
||||
content: '';
|
||||
border-radius: 50%;
|
||||
background: var(--el-bg-color);
|
||||
}
|
||||
.billing-bill .retry-progress-value {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
color: var(--el-color-warning);
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
line-height: 1;
|
||||
}
|
||||
.billing-bill .retry-status-text {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 3px;
|
||||
line-height: 1.2;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.billing-bill .retry-status-text strong {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
}
|
||||
.billing-bill .retry-status-text small {
|
||||
color: var(--el-text-color-secondary);
|
||||
font-size: 11px;
|
||||
}
|
||||
.export-progress__msg { margin-top: 12px; color: var(--el-text-color-regular); }
|
||||
.export-progress__text { margin-top: 8px; font-size: 13px; color: var(--el-text-color-secondary); }
|
||||
.export-progress__hint { margin-top: 12px; font-size: 12px; color: var(--el-color-warning); }
|
||||
</style>
|
||||
|
||||
@@ -1,5 +1,19 @@
|
||||
<template>
|
||||
<div class="app-container order-bill-query">
|
||||
<div v-if="exportFloatVisible" class="retry-status-float export-status-float" :class="{ 'is-offset': rebillFloatVisible }">
|
||||
<el-tooltip :content="exportFloatTooltip" placement="bottom">
|
||||
<el-button class="retry-status-button" type="warning" plain size="small" @click="openExportProgress()">
|
||||
<span class="retry-progress-ring" :style="{ '--retry-progress': exportProgressDegree + 'deg' }" aria-hidden="true">
|
||||
<span class="retry-progress-value">{{ exportProgressText }}</span>
|
||||
</span>
|
||||
<span class="retry-status-text">
|
||||
<strong>{{ exportFloatTitle }}</strong>
|
||||
<small>{{ exportFloatSummary }}</small>
|
||||
</span>
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
|
||||
<div v-if="rebillFloatVisible" class="retry-status-float">
|
||||
<el-tooltip :content="rebillFloatTooltip" placement="bottom">
|
||||
<el-button class="retry-status-button" type="primary" plain size="small" @click="openRebillProgress()">
|
||||
@@ -58,6 +72,8 @@
|
||||
type="warning"
|
||||
plain
|
||||
icon="Download"
|
||||
:loading="exportLoading"
|
||||
:disabled="exportLoading"
|
||||
v-hasPermi="['price:orderBill:export']"
|
||||
@click="handleExport"
|
||||
>导出</el-button>
|
||||
@@ -208,6 +224,32 @@
|
||||
<el-button v-else type="primary" @click="rebillProgressVisible = false">关闭</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
<el-dialog
|
||||
v-model="exportProgressVisible"
|
||||
:title="exportDialogTitle"
|
||||
width="480px"
|
||||
append-to-body
|
||||
:close-on-click-modal="false"
|
||||
:close-on-press-escape="true"
|
||||
:show-close="true"
|
||||
>
|
||||
<el-progress
|
||||
:percentage="exportProgressPercent"
|
||||
:status="exportProgressStatus"
|
||||
:stroke-width="14"
|
||||
striped
|
||||
:striped-flow="exportLoading"
|
||||
/>
|
||||
<div class="rebill-progress__msg">{{ exportProgressMessage }}</div>
|
||||
<div class="rebill-progress__text">
|
||||
导出行数 {{ exportProgressRowCount }}
|
||||
</div>
|
||||
<p v-if="exportLoading" class="rebill-progress__hint">导出在后台进行,可收起弹窗继续操作;请勿关闭或刷新本页</p>
|
||||
<template #footer>
|
||||
<el-button v-if="exportLoading" @click="exportProgressVisible = false">收起</el-button>
|
||||
<el-button v-else type="primary" @click="exportProgressVisible = false">关闭</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
<order-source-sync-log-drawer ref="logDrawerRef" />
|
||||
<order-bill-product-check-drawer ref="productCheckDrawerRef" />
|
||||
|
||||
@@ -233,6 +275,78 @@ const exportFieldRef = ref()
|
||||
const logDrawerRef = ref()
|
||||
const productCheckDrawerRef = ref()
|
||||
const checkLoading = ref(false)
|
||||
const exportLoading = ref(false)
|
||||
const exportFinished = ref(false)
|
||||
const exportProgressVisible = ref(false)
|
||||
const exportProgressPercent = ref(0)
|
||||
const exportProgressMessage = ref('')
|
||||
const exportProgressStatus = ref(undefined)
|
||||
const exportProgressRowCount = ref(0)
|
||||
const exportTaskId = ref('')
|
||||
let exportFloatHideTimer = null
|
||||
|
||||
const exportFloatVisible = computed(() => exportLoading.value || exportFinished.value)
|
||||
const exportProgressDegree = computed(() => Math.round(((exportProgressPercent.value || 0) / 100) * 360))
|
||||
const exportProgressText = computed(() => (exportProgressPercent.value || 0) + '%')
|
||||
const exportDialogTitle = computed(() => {
|
||||
if (exportLoading.value) return '异步导出进行中'
|
||||
if (exportFinished.value) return '异步导出完成'
|
||||
return '异步导出'
|
||||
})
|
||||
const exportFloatTitle = computed(() => {
|
||||
if (exportLoading.value) return '导出中'
|
||||
if (exportProgressStatus.value === 'exception') return '导出失败'
|
||||
if (exportFinished.value) return '导出完成'
|
||||
return '导出'
|
||||
})
|
||||
const exportFloatSummary = computed(() => {
|
||||
const pct = exportProgressPercent.value || 0
|
||||
const rows = exportProgressRowCount.value || 0
|
||||
if (rows > 0) return pct + '% / ' + rows + ' 行'
|
||||
return pct + '%'
|
||||
})
|
||||
const exportFloatTooltip = computed(() => exportProgressMessage.value || '点击查看详情')
|
||||
|
||||
function openExportProgress() {
|
||||
exportProgressVisible.value = true
|
||||
}
|
||||
|
||||
function scheduleHideExportFloat() {
|
||||
if (exportFloatHideTimer) {
|
||||
clearTimeout(exportFloatHideTimer)
|
||||
exportFloatHideTimer = null
|
||||
}
|
||||
exportFloatHideTimer = window.setTimeout(() => {
|
||||
if (!exportLoading.value && exportFinished.value) {
|
||||
exportFinished.value = false
|
||||
}
|
||||
exportFloatHideTimer = null
|
||||
}, 8000)
|
||||
}
|
||||
|
||||
function applyExportProgress(t) {
|
||||
const pct = Math.min(100, Number(t && t.percent) || 0)
|
||||
exportProgressPercent.value = pct
|
||||
exportProgressRowCount.value = Number(t && t.rowCount) || 0
|
||||
const st = String((t && t.status) || '').toUpperCase()
|
||||
if (st === 'PENDING') {
|
||||
exportProgressMessage.value = (t && t.message) || '排队中...'
|
||||
exportProgressStatus.value = undefined
|
||||
} else if (st === 'RUNNING') {
|
||||
exportProgressMessage.value = (t && t.message) || ('导出中 ' + pct + '%')
|
||||
exportProgressStatus.value = undefined
|
||||
} else if (st === 'SUCCESS') {
|
||||
exportProgressMessage.value = (t && t.message) || '导出完成,正在下载'
|
||||
exportProgressStatus.value = 'success'
|
||||
exportProgressPercent.value = 100
|
||||
} else if (st === 'FAIL' || st === 'FAILED') {
|
||||
exportProgressMessage.value = (t && (t.error || t.message)) || '导出失败'
|
||||
exportProgressStatus.value = 'exception'
|
||||
} else {
|
||||
exportProgressMessage.value = (t && t.message) || ''
|
||||
}
|
||||
}
|
||||
|
||||
const batchLoading = ref(false)
|
||||
const rebillFinished = ref(false)
|
||||
const rebillProgressVisible = ref(false)
|
||||
@@ -516,11 +630,25 @@ function queryList(params) {
|
||||
}
|
||||
|
||||
async function handleExport() {
|
||||
if (exportLoading.value) return
|
||||
const params = tableRef.value?.getQueryParams?.() || {}
|
||||
if (!validateOrderIdentifierLimit(params.orderIdentifiers)) return
|
||||
const fields = await exportFieldRef.value?.open?.()
|
||||
if (!fields || !fields.length) return
|
||||
return exportAsyncFromTable({
|
||||
if (exportFloatHideTimer) {
|
||||
clearTimeout(exportFloatHideTimer)
|
||||
exportFloatHideTimer = null
|
||||
}
|
||||
exportLoading.value = true
|
||||
exportFinished.value = false
|
||||
exportProgressVisible.value = false
|
||||
exportProgressPercent.value = 0
|
||||
exportProgressRowCount.value = 0
|
||||
exportProgressStatus.value = undefined
|
||||
exportProgressMessage.value = '导出任务已提交'
|
||||
exportTaskId.value = ''
|
||||
try {
|
||||
const result = await exportAsyncFromTable({
|
||||
baseUrl: '/price/billing/orderBill/export',
|
||||
tableRef: tableRef.value,
|
||||
normalize: (raw) => {
|
||||
@@ -529,8 +657,40 @@ async function handleExport() {
|
||||
return q
|
||||
},
|
||||
filePrefix: 'order_bill',
|
||||
requireFilter: true
|
||||
requireFilter: true,
|
||||
showLoading: false,
|
||||
showSuccessMessage: true,
|
||||
showErrorMessage: true,
|
||||
onTaskId: (id) => { exportTaskId.value = id },
|
||||
onProgress: (task) => applyExportProgress(task || {})
|
||||
})
|
||||
if (result && result.task) {
|
||||
applyExportProgress({ ...(result.task || {}), status: 'SUCCESS', percent: 100 })
|
||||
exportFinished.value = true
|
||||
scheduleHideExportFloat()
|
||||
} else if (!exportTaskId.value) {
|
||||
// filter rejected / cancelled before task created
|
||||
exportFinished.value = false
|
||||
exportProgressPercent.value = 0
|
||||
exportProgressMessage.value = ''
|
||||
exportProgressStatus.value = undefined
|
||||
} else if (exportProgressStatus.value !== 'exception') {
|
||||
exportProgressStatus.value = 'exception'
|
||||
exportProgressMessage.value = exportProgressMessage.value || '导出失败'
|
||||
exportFinished.value = true
|
||||
scheduleHideExportFloat()
|
||||
} else {
|
||||
exportFinished.value = true
|
||||
scheduleHideExportFloat()
|
||||
}
|
||||
} catch (e) {
|
||||
exportProgressStatus.value = 'exception'
|
||||
exportProgressMessage.value = (e && e.message) || '导出失败'
|
||||
exportFinished.value = true
|
||||
scheduleHideExportFloat()
|
||||
} finally {
|
||||
exportLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -889,6 +1049,12 @@ onBeforeUnmount(() => {
|
||||
.rebill-progress__msg { margin-top: 12px; color: var(--el-text-color-regular); }
|
||||
.rebill-progress__text { margin-top: 8px; font-size: 13px; color: var(--el-text-color-secondary); }
|
||||
.rebill-progress__hint { margin-top: 12px; font-size: 12px; color: var(--el-color-warning); }
|
||||
.order-bill-query .export-status-float {
|
||||
top: 92px;
|
||||
}
|
||||
.order-bill-query .export-status-float.is-offset {
|
||||
top: 178px;
|
||||
}
|
||||
.order-bill-query .retry-status-float {
|
||||
position: fixed;
|
||||
top: 92px;
|
||||
|
||||
Reference in New Issue
Block a user