From 5aad85110814cb1fdb746ee1e73c65e6647eacda Mon Sep 17 00:00:00 2001
From: zhenhui <1276357500@qq.com>
Date: Tue, 21 Jul 2026 15:46:01 +0800
Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=AF=8F=E6=97=A5=E6=8E=A8?=
=?UTF-8?q?=E9=80=81=E7=9A=84=E5=AF=BC=E5=87=BA=E5=92=8C=E6=8E=92=E5=BA=8F?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
app/admin/controller/mall/DailyPush.php | 73 +++++++++++++++++++
web/src/lang/backend/en/mall/dailyPush.ts | 14 ++++
web/src/lang/backend/zh-cn/mall/dailyPush.ts | 14 ++++
.../views/backend/mall/dailyPush/index.vue | 24 ++++--
4 files changed, 119 insertions(+), 6 deletions(-)
diff --git a/app/admin/controller/mall/DailyPush.php b/app/admin/controller/mall/DailyPush.php
index 68dd280..01cf9bf 100644
--- a/app/admin/controller/mall/DailyPush.php
+++ b/app/admin/controller/mall/DailyPush.php
@@ -4,6 +4,7 @@ namespace app\admin\controller\mall;
use Throwable;
use app\common\library\MallDailyPushBackfill;
+use app\common\library\MallDailyPushExport;
use app\common\controller\Backend;
use support\Response;
use Webman\Http\Request;
@@ -35,6 +36,11 @@ class DailyPush extends Backend
'create_time',
];
+ /**
+ * exportCount 与 export 共用导出权限
+ */
+ protected array $noNeedPermission = ['exportCount'];
+
public function initialize(): void
{
parent::initialize();
@@ -59,6 +65,73 @@ class DailyPush extends Backend
return $this->_index();
}
+ /**
+ * 导出筛选结果条数预览
+ */
+ public function exportCount(Request $request): Response
+ {
+ $response = $this->initializeBackend($request);
+ if ($response !== null) {
+ return $response;
+ }
+
+ if (!$this->auth->check('mall/dailyPush/export')) {
+ return $this->error(__('You have no permission'), [], 401);
+ }
+
+ try {
+ [$where, $alias] = $this->buildExportQueryParts();
+ $count = $this->model->alias($alias)->where($where)->count();
+ $exporter = new MallDailyPushExport();
+ return $this->success('', [
+ 'count' => $count,
+ 'max_limit' => $exporter->getMaxExportLimit(),
+ ]);
+ } catch (Throwable $e) {
+ return $this->error($e->getMessage());
+ }
+ }
+
+ /**
+ * 导出 Excel(CSV 流式写入,兼容 Excel 打开)
+ */
+ public function export(Request $request): Response
+ {
+ $response = $this->initializeBackend($request);
+ if ($response !== null) {
+ return $response;
+ }
+
+ $fieldsRaw = $request->post('fields', $request->get('fields', []));
+ $fields = is_array($fieldsRaw) ? $fieldsRaw : explode(',', strval($fieldsRaw));
+ $limitRaw = strval($request->post('export_limit', $request->get('export_limit', '1000')));
+ if (!is_numeric($limitRaw)) {
+ return $this->error(__('Parameter error'));
+ }
+ $limit = intval($limitRaw);
+ if ($limit < 1) {
+ return $this->error(__('Parameter error'));
+ }
+
+ try {
+ [$where, $alias, $order] = $this->buildExportQueryParts();
+ $lang = strval($request->header('think-lang', 'zh-cn'));
+ $exporter = new MallDailyPushExport();
+ return $exporter->export($this->model, $where, $alias, $order, $fields, $limit, $lang);
+ } catch (Throwable $e) {
+ return $this->error($e->getMessage());
+ }
+ }
+
+ /**
+ * @return array{0: array, 1: array, 2: array}
+ */
+ private function buildExportQueryParts(): array
+ {
+ [$where, $alias, , $order] = $this->queryBuilder();
+ return [$where, $alias, $order];
+ }
+
/**
* 按历史 mall_daily_push 记录回补用户信息
*/
diff --git a/web/src/lang/backend/en/mall/dailyPush.ts b/web/src/lang/backend/en/mall/dailyPush.ts
index 297b4c5..c74ec2d 100644
--- a/web/src/lang/backend/en/mall/dailyPush.ts
+++ b/web/src/lang/backend/en/mall/dailyPush.ts
@@ -9,4 +9,18 @@ export default {
lifetime_total_withdraw: 'Lifetime total withdraw',
create_time: 'Created at',
'quick Search Fields': 'ID',
+ export_excel: 'Export Excel',
+ export_title: 'Export daily push data',
+ export_tip: 'Exports data using the current filters and sort order. The server writes in batches to avoid freezing on large datasets.',
+ export_fields: 'Export fields',
+ export_select_all: 'Select all',
+ export_clear_all: 'Clear all',
+ export_limit: 'Export limit',
+ export_all_matched: 'All matched',
+ export_matched_count: '{count} record(s) match the current filters',
+ export_actual_count: 'will export {count} record(s)',
+ export_large_warning: 'Large export may take a while. Maximum {max} records per export.',
+ export_confirm: 'Start export',
+ export_success: 'Successfully exported {count} record(s)',
+ export_failed: 'Export failed, please try again later',
}
diff --git a/web/src/lang/backend/zh-cn/mall/dailyPush.ts b/web/src/lang/backend/zh-cn/mall/dailyPush.ts
index f5c6cfb..dd72ec0 100644
--- a/web/src/lang/backend/zh-cn/mall/dailyPush.ts
+++ b/web/src/lang/backend/zh-cn/mall/dailyPush.ts
@@ -9,5 +9,19 @@ export default {
lifetime_total_withdraw: '历史总提现',
create_time: '创建时间',
'quick Search Fields': 'ID',
+ export_excel: 'Excel导出',
+ export_title: '导出每日推送数据',
+ export_tip: '将按当前列表筛选条件与排序导出数据;采用服务端分批写入,避免一次性加载导致卡顿。',
+ export_fields: '导出字段',
+ export_select_all: '全选',
+ export_clear_all: '清空',
+ export_limit: '导出条数',
+ export_all_matched: '全部匹配',
+ export_matched_count: '当前筛选条件下共 {count} 条',
+ export_actual_count: '实际将导出 {count} 条',
+ export_large_warning: '导出条数较多,可能需要等待较长时间;单次最多导出 {max} 条。',
+ export_confirm: '开始导出',
+ export_success: '已成功导出 {count} 条数据',
+ export_failed: '导出失败,请稍后重试',
}
diff --git a/web/src/views/backend/mall/dailyPush/index.vue b/web/src/views/backend/mall/dailyPush/index.vue
index 1522b98..382ff6f 100644
--- a/web/src/views/backend/mall/dailyPush/index.vue
+++ b/web/src/views/backend/mall/dailyPush/index.vue
@@ -6,20 +6,31 @@