From 4fbc4500fdaf297176273529f36663f78a07c16a Mon Sep 17 00:00:00 2001 From: zhenhui <1276357500@qq.com> Date: Tue, 21 Jul 2026 16:44:39 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=A2=9E=E5=8A=A0=E7=AC=AC?= =?UTF-8?q?=E4=B8=89=E6=96=B9=E3=80=90=E6=AF=8F=E6=97=A5=E6=8E=A8=E9=80=81?= =?UTF-8?q?=E3=80=91=E5=8E=9F=E5=A7=8B=E6=95=B0=E6=8D=AE=E8=AE=B0=E5=BD=95?= =?UTF-8?q?=E6=97=A5=E5=BF=97.log?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env-example | 2 + app/admin/controller/mall/Address.php | 177 ++++++++++-------- app/admin/controller/mall/DailyPush.php | 6 +- app/api/controller/v1/Playx.php | 17 +- app/common/library/MallDailyPushExport.php | 51 +++-- app/common/library/MallDailyPushRawLogger.php | 70 +++++++ app/common/model/MallAddress.php | 121 ++++++++++-- composer.json | 3 +- config/playx.php | 2 + web/src/lang/backend/en/mall/address.ts | 1 + web/src/lang/backend/en/mall/dailyPush.ts | 6 +- web/src/lang/backend/zh-cn/mall/address.ts | 1 + web/src/lang/backend/zh-cn/mall/dailyPush.ts | 6 +- web/src/views/backend/mall/address/index.vue | 20 ++ .../views/backend/mall/address/popupForm.vue | 4 +- .../backend/mall/dailyPush/exportDialog.vue | 27 +-- 16 files changed, 371 insertions(+), 143 deletions(-) create mode 100644 app/common/library/MallDailyPushRawLogger.php diff --git a/.env-example b/.env-example index b0e8944..cf21271 100644 --- a/.env-example +++ b/.env-example @@ -31,6 +31,8 @@ PLAYX_RETURN_RATIO=0.1 PLAYX_UNLOCK_RATIO=0.1 # Daily Push 签名校验密钥(HMAC,建议从部署系统注入,避免写入代码/仓库) PLAYX_DAILY_PUSH_SECRET= +# 第三方每日推送原始日志保留天数(runtime/logs/daily_push_raw,默认 30) +PLAYX_DAILY_PUSH_RAW_LOG_DAYS=30 # 合作方回调 JWT 验签密钥(HS256,与对端私发密钥一致;与上一项可同时配置,则两种均需通过) PLAYX_PARTNER_JWT_SECRET= # Agent authtoken(/api/v1/authToken)JWT 签名密钥;留空则使用下方 buildadmin.token.key diff --git a/app/admin/controller/mall/Address.php b/app/admin/controller/mall/Address.php index b22f316..f7182f1 100644 --- a/app/admin/controller/mall/Address.php +++ b/app/admin/controller/mall/Address.php @@ -1,74 +1,103 @@ -model = new \app\common\model\MallAddress(); - } - - /** - * 查看 - * @throws Throwable - */ - public function index(\Webman\Http\Request $request): \support\Response - { - $response = $this->initializeBackend($request); - if ($response !== null) { - return $response; - } - - if ($request->get('select') || $request->post('select')) { - $this->_select(); - return $this->success(); - } - - /** - * 1. withJoin 不可使用 alias 方法设置表别名,别名将自动使用关联模型名称(小写下划线命名规则) - * 2. 以下的别名设置了主表别名,同时便于拼接查询参数等 - * 3. paginate 数据集可使用链式操作 each(function($item, $key) {}) 遍历处理 - */ - list($where, $alias, $limit, $order) = $this->queryBuilder(); - $res = $this->model - ->with(['playxUserAsset' => function ($query) { - $query->field('id,username'); - }]) - ->visible(['playxUserAsset' => ['username']]) - ->alias($alias) - ->where($where) - ->order($order) - ->paginate($limit); - - return $this->success('', [ - 'list' => $res->items(), - 'total' => $res->total(), - 'remark' => get_route_remark(), - ]); - } - - /** - * 若需重写查看、编辑、删除等方法,请复制 @see \app\admin\library\traits\Backend 中对应的方法至此进行重写 - */ -} \ No newline at end of file +model = new MallAddress(); + } + + /** + * 查看 + * @throws Throwable + */ + public function index(\Webman\Http\Request $request): \support\Response + { + $response = $this->initializeBackend($request); + if ($response !== null) { + return $response; + } + + if ($request->get('select') || $request->post('select')) { + $this->_select(); + return $this->success(); + } + + list($where, $alias, $limit, $order) = $this->queryBuilder(); + $res = $this->model + ->with(['playxUserAsset' => function ($query) { + $query->field('id,username'); + }]) + ->visible(['playxUserAsset' => ['username']]) + ->alias($alias) + ->where($where) + ->order($order) + ->paginate($limit); + + return $this->success('', [ + 'list' => $res->items(), + 'total' => $res->total(), + 'remark' => get_route_remark(), + ]); + } + + public function edit(\Webman\Http\Request $request): \support\Response + { + $response = $this->initializeBackend($request); + if ($response !== null) { + return $response; + } + + if ($request->method() === 'POST') { + $post = $request->post(); + $regionOrigin = trim(strval($post['region_origin'] ?? '')); + if (isset($post['region']) && $regionOrigin !== '' && MallAddress::isAreaIdList($regionOrigin)) { + $displayOrigin = MallAddress::resolveAreaNames($regionOrigin); + if (MallAddress::normalizeRegion($post['region']) === MallAddress::normalizeRegion($displayOrigin)) { + $request->setPost(array_merge($post, ['region' => $regionOrigin])); + } + } + return $this->_edit(); + } + + $pk = $this->model->getPk(); + $id = $request->get($pk); + $row = $this->model->find($id); + if (!$row) { + return $this->error(__('Record not found')); + } + + $dataLimitAdminIds = $this->getDataLimitAdminIds(); + if ($dataLimitAdminIds && !in_array($row[$this->dataLimitField], $dataLimitAdminIds)) { + return $this->error(__('You have no permission')); + } + + $rowData = $row->toArray(); + $rowData['region_origin'] = $row->getData('region'); + + return $this->success('', ['row' => $rowData]); + } +} diff --git a/app/admin/controller/mall/DailyPush.php b/app/admin/controller/mall/DailyPush.php index 01cf9bf..c832ad8 100644 --- a/app/admin/controller/mall/DailyPush.php +++ b/app/admin/controller/mall/DailyPush.php @@ -93,7 +93,7 @@ class DailyPush extends Backend } /** - * 导出 Excel(CSV 流式写入,兼容 Excel 打开) + * 导出 Excel(XLSX 流式写入) */ public function export(Request $request): Response { @@ -104,12 +104,12 @@ class DailyPush extends Backend $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'))); + $limitRaw = strval($request->post('export_limit', $request->get('export_limit', '0'))); if (!is_numeric($limitRaw)) { return $this->error(__('Parameter error')); } $limit = intval($limitRaw); - if ($limit < 1) { + if ($limit < 0) { return $this->error(__('Parameter error')); } diff --git a/app/api/controller/v1/Playx.php b/app/api/controller/v1/Playx.php index 61f0a7f..5939466 100644 --- a/app/api/controller/v1/Playx.php +++ b/app/api/controller/v1/Playx.php @@ -15,6 +15,7 @@ use app\common\model\MallSession; use app\common\model\MallOrder; use app\common\model\MallUserAsset; use app\common\library\MallPlayxRatios; +use app\common\library\MallDailyPushRawLogger; use app\common\model\MallAddress; use support\think\Db; use Webman\Http\Request; @@ -188,13 +189,15 @@ class Playx extends Api return $response; } + $rawBody = $request->rawBody(); $body = $request->post(); - if (empty($body)) { - $raw = $request->rawBody(); - if ($raw) { - $body = json_decode($raw, true) ?? []; - } + if (empty($body) && $rawBody !== '') { + $body = json_decode($rawBody, true) ?? []; } + if ($rawBody === '') { + $rawBody = json_encode($body, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); + } + MallDailyPushRawLogger::log($request, $rawBody); $secret = config('playx.daily_push_secret', ''); if ($secret !== '') { @@ -1101,7 +1104,7 @@ SQL; $phone = trim(strval($request->post('phone', ''))); $receiverName = trim(strval($request->post('receiver_name', ''))); - $region = $request->post('region', ''); + $region = MallAddress::normalizeRegion($request->post('region', '')); $detailAddress = trim(strval($request->post('detail_address', ''))); $defaultSetting = strval($request->post('default_setting', '0')) === '1' ? 1 : 0; @@ -1171,7 +1174,7 @@ SQL; $updates['receiver_name'] = trim(strval($request->post('receiver_name', ''))); } if ($request->post('region', null) !== null) { - $updates['region'] = $request->post('region', ''); + $updates['region'] = MallAddress::normalizeRegion($request->post('region', '')); } if ($request->post('detail_address', null) !== null) { $updates['detail_address'] = trim(strval($request->post('detail_address', ''))); diff --git a/app/common/library/MallDailyPushExport.php b/app/common/library/MallDailyPushExport.php index 785a10d..2040089 100644 --- a/app/common/library/MallDailyPushExport.php +++ b/app/common/library/MallDailyPushExport.php @@ -5,15 +5,19 @@ declare(strict_types=1); namespace app\common\library; use app\common\model\MallDailyPush; +use OpenSpout\Common\Entity\Row; +use OpenSpout\Writer\XLSX\Options; +use OpenSpout\Writer\XLSX\Writer; use support\Response; use Throwable; /** - * 每日推送数据导出(流式 CSV,兼容 Excel 打开) + * 每日推送数据导出(OpenSpout 流式 XLSX) */ class MallDailyPushExport { - public const MAX_EXPORT_LIMIT = 100000; + /** 0 表示不限制条数,导出全部匹配数据 */ + public const UNLIMITED_EXPORT = 0; public const CHUNK_SIZE = 2000; @@ -64,28 +68,34 @@ class MallDailyPushExport throw new \InvalidArgumentException(__('Parameter error')); } - $limit = max(1, min(self::MAX_EXPORT_LIMIT, $limit)); + if ($limit < 0) { + throw new \InvalidArgumentException(__('Parameter error')); + } + + @set_time_limit(0); $exportDir = runtime_path('export'); if (!is_dir($exportDir) && !mkdir($exportDir, 0755, true) && !is_dir($exportDir)) { throw new \RuntimeException('Failed to create export directory'); } - $filepath = $exportDir . DIRECTORY_SEPARATOR . 'daily_push_' . date('YmdHis') . '_' . bin2hex(random_bytes(4)) . '.csv'; + $filepath = $exportDir . DIRECTORY_SEPARATOR . 'daily_push_' . date('YmdHis') . '_' . bin2hex(random_bytes(4)) . '.xlsx'; $labels = $this->resolveFieldLabels($lang); - $handle = fopen($filepath, 'wb'); - if ($handle === false) { - throw new \RuntimeException('Failed to create export file'); - } + + $options = new Options(); + $options->setTempFolder($exportDir); + $writer = new Writer($options); try { - fwrite($handle, "\xEF\xBB\xBF"); - fputcsv($handle, $this->buildHeaderRow($fields, $labels)); + $writer->openToFile($filepath); + $writer->addRow(Row::fromValues($this->buildHeaderRow($fields, $labels))); $exported = 0; $page = 1; - while ($exported < $limit) { - $batchSize = min(self::CHUNK_SIZE, $limit - $exported); + $unlimited = $limit === self::UNLIMITED_EXPORT; + + while ($unlimited || $exported < $limit) { + $batchSize = $unlimited ? self::CHUNK_SIZE : min(self::CHUNK_SIZE, $limit - $exported); $rows = $model ->field($fields) ->alias($alias) @@ -99,9 +109,9 @@ class MallDailyPushExport } foreach ($rows as $row) { - fputcsv($handle, $this->formatRow($row->toArray(), $fields)); + $writer->addRow(Row::fromValues($this->formatRow($row->toArray(), $fields))); $exported++; - if ($exported >= $limit) { + if (!$unlimited && $exported >= $limit) { break; } } @@ -111,22 +121,27 @@ class MallDailyPushExport } $page++; } + + $writer->close(); } catch (Throwable $e) { - fclose($handle); + try { + $writer->close(); + } catch (Throwable) { + // ignore + } if (is_file($filepath)) { @unlink($filepath); } throw $e; } - fclose($handle); register_shutdown_function(static function () use ($filepath): void { if (is_file($filepath)) { @unlink($filepath); } }); - $filename = 'daily_push_' . date('YmdHis') . '.csv'; + $filename = 'daily_push_' . date('YmdHis') . '.xlsx'; return (new Response())->file($filepath, $filename); } @@ -157,7 +172,7 @@ class MallDailyPushExport public function getMaxExportLimit(): int { - return self::MAX_EXPORT_LIMIT; + return self::UNLIMITED_EXPORT; } /** diff --git a/app/common/library/MallDailyPushRawLogger.php b/app/common/library/MallDailyPushRawLogger.php new file mode 100644 index 0000000..8eedb21 --- /dev/null +++ b/app/common/library/MallDailyPushRawLogger.php @@ -0,0 +1,70 @@ + date('Y-m-d H:i:s'), + 'ip' => $request->getRealIp(), + 'method' => $request->method(), + 'path' => $request->path(), + 'headers' => [ + 'X-Request-Id' => strval($request->header('X-Request-Id', '')), + 'X-Timestamp' => strval($request->header('X-Timestamp', '')), + 'Content-Type' => strval($request->header('Content-Type', '')), + ], + 'raw' => $rawBody, + ]; + + $logFile = $dir . DIRECTORY_SEPARATOR . 'daily_push_' . date('Y-m-d') . '.log'; + file_put_contents( + $logFile, + json_encode($entry, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) . PHP_EOL, + FILE_APPEND | LOCK_EX + ); + + self::cleanup($dir); + } + + private static function cleanup(string $dir): void + { + $retentionDays = intval(config('playx.daily_push_raw_log_days', self::DEFAULT_RETENTION_DAYS)); + if ($retentionDays <= 0) { + return; + } + + $expireBefore = time() - ($retentionDays * 86400); + $files = glob($dir . DIRECTORY_SEPARATOR . 'daily_push_*.log'); + if ($files === false) { + return; + } + + foreach ($files as $file) { + if (is_file($file) && filemtime($file) < $expireBefore) { + @unlink($file); + } + } + } +} diff --git a/app/common/model/MallAddress.php b/app/common/model/MallAddress.php index 1c3a75b..ee66411 100644 --- a/app/common/model/MallAddress.php +++ b/app/common/model/MallAddress.php @@ -3,6 +3,7 @@ namespace app\common\model; use app\common\model\traits\TimestampInteger; +use support\think\Db; use support\think\Model; /** @@ -23,37 +24,117 @@ class MallAddress extends Model 'region_text', ]; - - public function getregionAttr($value): array + /** + * 表单展示:文本地区返回规范化字符串;历史地区 ID 转为名称(英文逗号拼接) + */ + public function getregionAttr($value): string { - if ($value === '' || $value === null) return []; - if (!is_array($value)) { - return explode(',', $value); + if ($value === '' || $value === null) { + return ''; } - return $value; + if (is_array($value)) { + return self::normalizeRegion($value); + } + $region = trim(strval($value)); + if ($region === '') { + return ''; + } + if (self::isAreaIdList($region)) { + return self::resolveAreaNames($region); + } + return self::normalizeRegion($region); } + /** + * 入库:统一为英文逗号分隔(与 API / 原 city 组件存储格式一致) + */ public function setregionAttr($value): string { - return is_array($value) ? implode(',', $value) : $value; + return self::normalizeRegion($value); } public function getregionTextAttr($value, $row): string { - if ($row['region'] === '' || $row['region'] === null) return ''; - $region = $row['region']; - $ids = $region; - if (!is_array($ids)) { - $ids = explode(',', (string) $ids); - } - $ids = array_values(array_filter(array_map('trim', $ids), static function ($s) { - return $s !== ''; - })); - if (empty($ids)) { + $region = $row['region'] ?? ''; + if ($region === '' || $region === null) { return ''; } - $cityNames = \support\think\Db::name('area')->whereIn('id', $ids)->column('name'); - return $cityNames ? implode(',', $cityNames) : ''; + if (is_array($region)) { + return self::normalizeRegion($region); + } + $region = trim(strval($region)); + if ($region === '') { + return ''; + } + if (self::isAreaIdList($region)) { + return self::resolveAreaNames($region); + } + return self::normalizeRegion($region); + } + + /** + * 将地区规范为英文逗号分隔字符串(数组或「省,市,区」文本) + */ + public static function normalizeRegion(mixed $value): string + { + if (is_array($value)) { + $parts = []; + foreach ($value as $item) { + $part = trim(strval($item)); + if ($part !== '') { + $parts[] = $part; + } + } + return implode(',', $parts); + } + + $region = trim(strval($value)); + if ($region === '') { + return ''; + } + + $region = str_replace(',', ',', $region); + $parts = explode(',', $region); + $normalized = []; + foreach ($parts as $part) { + $part = trim($part); + if ($part !== '') { + $normalized[] = $part; + } + } + + return implode(',', $normalized); + } + + public static function isAreaIdList(string $region): bool + { + $parts = array_values(array_filter(array_map('trim', explode(',', $region)), static function ($part) { + return $part !== ''; + })); + if ($parts === []) { + return false; + } + foreach ($parts as $part) { + if (!ctype_digit($part)) { + return false; + } + } + return true; + } + + public static function resolveAreaNames(string $region): string + { + $ids = array_values(array_filter(array_map('trim', explode(',', $region)), static function ($part) { + return $part !== ''; + })); + if ($ids === []) { + return ''; + } + $cityNames = Db::name('area')->whereIn('id', $ids)->column('name'); + if (!$cityNames) { + return self::normalizeRegion($region); + } + return implode(',', $cityNames); } public function playxUserAsset(): \think\model\relation\BelongsTo @@ -83,4 +164,4 @@ class MallAddress extends Model 'receiver_address' => $receiverAddress, ]; } -} \ No newline at end of file +} diff --git a/composer.json b/composer.json index a8a2d18..7e13ce6 100644 --- a/composer.json +++ b/composer.json @@ -43,7 +43,8 @@ "firebase/php-jwt": "^7.0", "guzzlehttp/guzzle": "^7.10", "robthree/twofactorauth": "^3.0", - "bacon/bacon-qr-code": "^3.1" + "bacon/bacon-qr-code": "^3.1", + "openspout/openspout": "^4.28" }, "suggest": { "ext-event": "For better performance. " diff --git a/config/playx.php b/config/playx.php index d5a8939..cecd36a 100644 --- a/config/playx.php +++ b/config/playx.php @@ -13,6 +13,8 @@ return [ 'points_to_cash_ratio' => floatval(env('PLAYX_POINTS_TO_CASH_RATIO', '0.1')), // Daily Push 签名校验(PlayX 调用商城时使用) 'daily_push_secret' => strval(env('PLAYX_DAILY_PUSH_SECRET', '')), + /** 第三方每日推送原始日志保留天数(runtime/logs/daily_push_raw) */ + 'daily_push_raw_log_days' => intval(env('PLAYX_DAILY_PUSH_RAW_LOG_DAYS', '30')), /** * 合作方 JWT 验签密钥(HS256)。非空时:dailyPush 等回调需带 Authorization: Bearer * 仅写入部署环境变量,勿提交仓库。 diff --git a/web/src/lang/backend/en/mall/address.ts b/web/src/lang/backend/en/mall/address.ts index db8dda0..7b493d1 100644 --- a/web/src/lang/backend/en/mall/address.ts +++ b/web/src/lang/backend/en/mall/address.ts @@ -5,6 +5,7 @@ export default { receiver_name: 'receiver name', phone: 'phone', region: 'region', + region_tip: 'Separate levels with commas, e.g. Kuala Lumpur,KLCC', detail_address: 'detail_address', default_setting: 'Default address', 'default_setting 0': '--', diff --git a/web/src/lang/backend/en/mall/dailyPush.ts b/web/src/lang/backend/en/mall/dailyPush.ts index c74ec2d..e0c69f5 100644 --- a/web/src/lang/backend/en/mall/dailyPush.ts +++ b/web/src/lang/backend/en/mall/dailyPush.ts @@ -11,15 +11,15 @@ export default { '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_tip: 'Exports filtered and sorted data as Excel (.xlsx). The server writes in batches and supports exporting all matched records.', export_fields: 'Export fields', export_select_all: 'Select all', export_clear_all: 'Clear all', - export_limit: 'Export limit', + export_limit: 'Export limit (0 = all matched)', 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_large_warning: 'Large export may take a while. Please keep this page open.', 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/address.ts b/web/src/lang/backend/zh-cn/mall/address.ts index 9c07ede..1eb4e7c 100644 --- a/web/src/lang/backend/zh-cn/mall/address.ts +++ b/web/src/lang/backend/zh-cn/mall/address.ts @@ -5,6 +5,7 @@ export default { receiver_name: '收货人', phone: '电话', region: '地区', + region_tip: '多个层级请用英文逗号分隔,如:广东省,广州市,天河区', detail_address: '详细地址', default_setting: '默认地址', 'default_setting 0': '--', diff --git a/web/src/lang/backend/zh-cn/mall/dailyPush.ts b/web/src/lang/backend/zh-cn/mall/dailyPush.ts index dd72ec0..7d2ee36 100644 --- a/web/src/lang/backend/zh-cn/mall/dailyPush.ts +++ b/web/src/lang/backend/zh-cn/mall/dailyPush.ts @@ -11,15 +11,15 @@ export default { 'quick Search Fields': 'ID', export_excel: 'Excel导出', export_title: '导出每日推送数据', - export_tip: '将按当前列表筛选条件与排序导出数据;采用服务端分批写入,避免一次性加载导致卡顿。', + export_tip: '将按当前列表筛选条件与排序导出为 Excel(.xlsx);服务端分批写入,支持导出全部匹配数据。', export_fields: '导出字段', export_select_all: '全选', export_clear_all: '清空', - export_limit: '导出条数', + export_limit: '导出条数(0 表示全部匹配)', export_all_matched: '全部匹配', export_matched_count: '当前筛选条件下共 {count} 条', export_actual_count: '实际将导出 {count} 条', - export_large_warning: '导出条数较多,可能需要等待较长时间;单次最多导出 {max} 条。', + export_large_warning: '导出条数较多,可能需要等待较长时间,请勿关闭页面。', export_confirm: '开始导出', export_success: '已成功导出 {count} 条数据', export_failed: '导出失败,请稍后重试', diff --git a/web/src/views/backend/mall/address/index.vue b/web/src/views/backend/mall/address/index.vue index 5202bbb..5c40508 100644 --- a/web/src/views/backend/mall/address/index.vue +++ b/web/src/views/backend/mall/address/index.vue @@ -50,6 +50,17 @@ const formatRegion = (raw: string) => { return s.replace(/[,,\s]+/g, ',') } +const normalizeRegionInput = (raw: unknown) => { + const s = String(raw ?? '').trim() + if (!s) return '' + return s + .replace(/,/g, ',') + .split(',') + .map((part) => part.trim()) + .filter(Boolean) + .join(',') +} + /** * baTable 内包含了表格的所有数据且数据具备响应性,然后通过 provide 注入给了后代组件 */ @@ -145,6 +156,15 @@ const baTable = new baTableClass( }, { defaultItems: {}, + }, + { + onSubmit: () => { + const items = baTable.form.items + if (items && items.region !== undefined && items.region !== null) { + items.region = normalizeRegionInput(items.region) + } + return true + }, } ) diff --git a/web/src/views/backend/mall/address/popupForm.vue b/web/src/views/backend/mall/address/popupForm.vue index a27975b..01c8c7c 100644 --- a/web/src/views/backend/mall/address/popupForm.vue +++ b/web/src/views/backend/mall/address/popupForm.vue @@ -53,10 +53,10 @@ />
{{ t('mall.dailyPush.export_limit') }}
- +
{{ preset }} - + {{ t('mall.dailyPush.export_all_matched') }}
{{ t('mall.dailyPush.export_matched_count', { count: matchedCount }) }} - + ,{{ t('mall.dailyPush.export_actual_count', { count: actualExportCount }) }}
- - {{ t('mall.dailyPush.export_large_warning', { max: maxLimit }) }} + + {{ t('mall.dailyPush.export_large_warning') }} @@ -84,7 +84,6 @@ const visible = defineModel({ default: false }) const exporting = ref(false) const countLoading = ref(false) const matchedCount = ref(0) -const maxLimit = ref(100000) const limitPresets = [1000, 5000, 10000, 50000] const fieldOptions = computed(() => [ @@ -101,10 +100,15 @@ const fieldOptions = computed(() => [ const form = reactive({ fields: fieldOptions.value.map((item) => item.value), - exportLimit: 10000, + exportLimit: 0, }) -const actualExportCount = computed(() => Math.min(form.exportLimit, matchedCount.value, maxLimit.value)) +const actualExportCount = computed(() => { + if (form.exportLimit <= 0) { + return matchedCount.value + } + return Math.min(form.exportLimit, matchedCount.value) +}) const selectAllFields = () => { form.fields = fieldOptions.value.map((item) => item.value) @@ -115,7 +119,7 @@ const clearFields = () => { } const useMatchedCount = () => { - form.exportLimit = Math.min(matchedCount.value || 1, maxLimit.value) + form.exportLimit = 0 } const buildExportParams = () => { @@ -123,7 +127,7 @@ const buildExportParams = () => { return { ...filter, fields: form.fields, - export_limit: actualExportCount.value, + export_limit: form.exportLimit <= 0 ? 0 : form.exportLimit, } } @@ -136,7 +140,6 @@ const loadMatchedCount = async () => { params: baTable.table.filter || {}, }) matchedCount.value = res.data.count - maxLimit.value = res.data.max_limit } catch { matchedCount.value = baTable.table.total || 0 } finally { @@ -197,7 +200,7 @@ const submitExport = async () => { const disposition = String(response.headers['content-disposition'] || '') const filenameMatch = disposition.match(/filename="?([^";]+)"?/i) - const filename = filenameMatch?.[1] || `daily_push_${Date.now()}.csv` + const filename = filenameMatch?.[1] || `daily_push_${Date.now()}.xlsx` downloadBlob(blob, filename) ElNotification({