feat: 新增支付测试小程序与微信支付集成
Some checks failed
Build docker and publish / linux (1.24.5) (push) Failing after 40s
Some checks failed
Build docker and publish / linux (1.24.5) (push) Failing after 40s
feat(pay): 添加支付API基础结构 feat(miniapp): 创建支付测试小程序页面与配置 feat(wechatpay): 配置微信支付参数与证书 fix(guild): 修复成员列表查询条件 docs: 更新代码规范文档与需求文档 style: 统一前后端枚举显示与注释格式 refactor(admin): 重构用户奖励发放接口参数处理 test(title): 添加称号效果参数验证测试
This commit is contained in:
parent
87ad4177b1
commit
6ee627139c
50
.trae/documents/API 与后台管理代码审计与整改计划.md
Normal file
50
.trae/documents/API 与后台管理代码审计与整改计划.md
Normal file
@ -0,0 +1,50 @@
|
||||
## 概览
|
||||
- 后端以 Go 实现,路由集中在 `internal/router/router.go`,分5组:管理端非认证(`/api/admin`登录)、管理端认证(`/api/admin`全量管理接口)、系统管理(`/api`模板兼容)、APP公开(`/api/app`)、APP认证(`/api/app`)。
|
||||
- 管理端处理器分布在 `internal/api/admin/*`,覆盖活动/期次/奖励/抽奖/用户资产/称号/道具卡/优惠券/工会/系统用户角色菜单等。
|
||||
- 前端后台管理位于 `web/admin`,Axios 基础 `baseURL=/api`,API 模块在 `web/admin/src/api/*`。
|
||||
|
||||
## 发现与结论
|
||||
- 路由与前端 URL 基本一致;系统管理模块通过 `baseURL=/api` 访问 `/api/user/list|/api/role/list|/api/v3/system/menus/simple`,与后端 `systemApiRouter` 对齐(`internal/router/router.go:59-167`)。
|
||||
- 前端存在绝对 URL 前缀问题:`web/admin/src/api/reward-grant.ts:50` 使用 `url: '/api/admin/...'`,与 `baseURL=/api` 叠加为重复前缀,应改为相对 `admin/...`。
|
||||
- 奖励发放接口在前端有重复实现:`reward-grant.ts` 与 `player-manage.ts` 同指向 `POST /api/admin/users/{id}/rewards/grant`(后端实现见 `internal/api/admin/users_reward_admin.go:32-75`),建议合并与统一用法。
|
||||
- 认证链路清晰:管理端拦截器校验 JWT、登录哈希与状态(`internal/router/interceptor/admin_auth.go:18-82`);APP 端简化(`internal/router/interceptor/app_auth.go:13-35`)。前端 Axios 统一加 `Authorization` 并防抖登出(`web/admin/src/utils/http/index.ts:65-76,84-92`)。
|
||||
- 分层一致性:多数管理端处理器调用 service 层,但也有直接操作 DAO 的情况(如称号管理 `internal/api/admin/titles_admin.go`),建议将复杂业务(效果校验、用户称号唯一激活切换)沉入 service 保持一致性与复用。
|
||||
- 性能与合理性:
|
||||
- 工作台积分统计扫表累加(`internal/api/admin/dashboard_admin.go:74-84`)可改为 SQL 聚合并按有效期过滤;积分净变动已用流水 SUM(`87-107`),建议两者一致走 SQL。
|
||||
- 新用户概览多表聚合(`224-381`)逻辑合理但查询密集,后续可评估增加索引与批量 UNION/子查询降往返。
|
||||
- API 路由一致性:系统管理接口挂在 `/api` 与管理端其余接口挂在 `/api/admin`,存在认知负担;短期保留兼容,补齐文档;中期评估统一路径策略。
|
||||
|
||||
## 整改实施计划
|
||||
### 阶段1:快速修复
|
||||
- 修复前端绝对 URL 前缀问题:将 `web/admin/src/api/reward-grant.ts:50` 的 `url: '/api/admin/users/${userId}/rewards/grant'` 改为 `url: 'admin/users/${userId}/rewards/grant'`,与其余模块一致。
|
||||
- 合并奖励发放 API:删除/重定向 `reward-grant.ts` 的重复实现,统一在 `player-manage.ts` 暴露 `fetchGrantPlayerReward`,或反之,在 `reward-grant.ts` 统一导出并在 `player-manage.ts` 复用。
|
||||
|
||||
### 阶段2:分层与可维护性
|
||||
- 抽取称号管理核心逻辑到 service:
|
||||
- 将 `AssignUserTitle` 的 upsert 与“仅保留一个激活称号”的切换(`internal/api/admin/titles_admin.go:439-490`)抽到 `internal/service/title`,供管理端与未来 APP 端统一调用。
|
||||
- 将效果参数校验 `validateEffectParams`(`titles_admin.go:296-377`)迁移为可复用的校验器,便于单元测试与前后端一致性。
|
||||
- 优化工作台统计:
|
||||
- 积分总额改为 SQL `SUM(points)` 并过滤过期(替换 `dashboard_admin.go:74-84` 的内存累加),减少 I/O 与CPU。
|
||||
|
||||
### 阶段3:一致性与文档
|
||||
- 路由规范化策略评审:维持 `/api`(系统管理)与 `/api/admin` 并存,新增开发文档条目明确两者用途与历史原因;中期根据前端路由与菜单加载需求评估是否迁移到 `/api/admin/system/*`。
|
||||
- 更新 Swagger 文档:核对 `docs/swagger.yaml`/`docs/swagger.json` 与现状路由一致,补充工作台接口、称号/效果接口、批量发放接口说明。
|
||||
|
||||
### 阶段4:测试与验证
|
||||
- 前端:为 `Axios` 层与关键 API(登录、奖励发放、称号分配)增加最小可运行的集成测试或契约测试;校验 401 防抖与登出行为。
|
||||
- 后端:为称号 service 新增单元测试(效果参数校验、scope 命中、唯一激活切换),为工作台统计新增 SQL 聚合测试。
|
||||
|
||||
## 参考定位
|
||||
- 路由总表:`internal/router/router.go:50-53,56-167,169-188,190-210,213-232`
|
||||
- 绝对 URL 问题:`web/admin/src/api/reward-grant.ts:50`
|
||||
- Axios baseURL:`.env.development:7`、`web/admin/src/utils/http/index.ts:45-48`
|
||||
- 奖励发放后端:`internal/api/admin/users_reward_admin.go:32-75`
|
||||
- 工作台积分统计:`internal/api/admin/dashboard_admin.go:74-84,87-107`
|
||||
- 称号分配逻辑:`internal/api/admin/titles_admin.go:439-490`
|
||||
- 认证拦截器:`internal/router/interceptor/admin_auth.go:18-82`、`internal/router/interceptor/app_auth.go:13-35`
|
||||
|
||||
## 验收标准
|
||||
- 前端所有 API 使用相对路径,无 `/api/` 重复前缀;奖励发放仅一处实现且通过。
|
||||
- 工作台统计在数据量上升时仍稳定(SQL 聚合后端性能可观)。
|
||||
- 称号管理逻辑具备可测试的 service 层,接口行为与前端预期一致。
|
||||
- Swagger 文档与实际路由对齐,前后端调用一目了然。
|
||||
25
.trae/documents/APP端工会审核适配与可选增强.md
Normal file
25
.trae/documents/APP端工会审核适配与可选增强.md
Normal file
@ -0,0 +1,25 @@
|
||||
## 是否变更APP接口
|
||||
- 路由与参数:不变。现有 APP 端接口保持原路径与入参。
|
||||
- `POST /api/app/guilds/:guild_id/members`(加入):internal/api/guild/members_app.go:19-50
|
||||
- `DELETE /api/app/guilds/:guild_id/members/:user_id`(离开):internal/api/guild/members_app.go:52-83
|
||||
- `GET /api/app/guilds/:guild_id/members`(成员列表):internal/api/guild/members_app.go:102-141
|
||||
- 响应结构:不变(仍返回 `{ message }`)。
|
||||
- 行为变化:当工会 `join_mode=1` 时,加入后处于待审状态,不会立即出现在成员列表(服务层变更:internal/service/guild/join.go:13-48;成员列表过滤正式成员:internal/service/guild/members_list.go:9-29)。
|
||||
|
||||
## 客户端适配建议(保持接口不变)
|
||||
- 加入后状态提示:将文案改为“申请已提交,待审核”,并在个人/工会页显示“待审核”状态。
|
||||
- 功能限制:待审期间禁用仅成员可用的操作(如成员互动、贡献等)。
|
||||
- 成员列表:无需变更,列表只显示已通过成员。
|
||||
|
||||
## 可选后端增强(增强体验,仍向后兼容)
|
||||
1) 加入响应增强:在 `POST /api/app/guilds/:guild_id/members` 返回体中新增 `join_status`(0待审/2已通过),便于客户端无额外查询即可知状态。
|
||||
2) 成员自查接口:新增 `GET /api/app/guilds/:guild_id/members/me` 返回当前用户在该工会的 `role/status/join_status`。
|
||||
3) 申请查询接口:新增 `GET /api/app/guilds/:guild_id/applications?user_id=xxx` 用于查询自己的待审记录(只读)。
|
||||
- 以上均为增量字段与新端点,旧客户端不受影响。
|
||||
|
||||
## 验收
|
||||
- 加入审核工会时,APP 加入成功但显示“待审核”,成员列表不包含该用户;审核通过后自动展示。
|
||||
- 保持现有路由与参数不变,无需强制更新客户端。
|
||||
- 若启用增强:客户端通过 `join_status` 或新查询端点准确展示状态。
|
||||
|
||||
如确认,我将按照“保持接口不变 + 提供可选增强”的方式推进服务与(可选)APP接口扩展,并提供前端适配示例。
|
||||
40
.trae/documents/Go 代码规范整改计划.md
Normal file
40
.trae/documents/Go 代码规范整改计划.md
Normal file
@ -0,0 +1,40 @@
|
||||
## 范围
|
||||
- API 层:补齐接口文档注释、统一 req/res 模式、限制为路由与参数控制
|
||||
- Service 层:迁移业务与数据访问、补齐函数级注释、对齐 activity_* 分层命名
|
||||
|
||||
## 工作项
|
||||
1. API 注释补全(Swagger)
|
||||
- 为缺少注释的端点补充 `@Summary/@Description/@Tags/@Param/@Success/@Failure/@Router`:
|
||||
- `internal/api/activity/draw_app.go:16 ExecuteDraw`
|
||||
- `internal/api/admin/draw_simulate.go:36 SimulateIssueDraw`
|
||||
- `internal/api/admin/issue_random_commit.go:21/48/80 Commit/Get/History`
|
||||
- `internal/api/admin/draw_receipt.go:31/77 GetDrawReceipt/GetDrawReceiptByLogID`
|
||||
- `internal/api/admin/verify_draw.go:34 VerifyDrawReceipt`
|
||||
|
||||
2. API 限定职责
|
||||
- 将 API 中直接访问数据库的逻辑迁移到 Service 层:
|
||||
- `internal/api/admin/draw_receipt.go` 查询收据改为调用 `activity.Service.GetDrawReceipt/GetDrawReceiptByLogID`
|
||||
- 保持 API 仅做:参数绑定、鉴权、调用 service、返回 res
|
||||
|
||||
3. 统一 req/res 代码风格
|
||||
- 统一使用 `req := new(XxxRequest)` / `res := new(XxxResponse)`,并在所有端点输出 `ctx.Payload(res)`
|
||||
- 检查并修正个别端点的 `var req` 临时写法
|
||||
|
||||
4. Service 注释与接口规范
|
||||
- 为 `internal/service/activity/activity.go` 的 `Service` 接口所有方法添加函数级注释(功能、参数、返回值)
|
||||
- 为 `internal/service/activity/*.go`(如 `draw_execute.go` 等)关键导出函数添加注释
|
||||
- 保持 `internal/service/user/*.go` 已有中文注释风格的一致性
|
||||
|
||||
5. 分层与命名对齐
|
||||
- 保持并补齐 `internal/api/activity/*` 与 `internal/service/activity/*` 的 `activity_*` 命名映射(CRUD、issue、rewards、draw、logs)
|
||||
- 审核并调整少量不一致的文件/函数命名(如 helper 置于非 API 包或私有化)
|
||||
|
||||
## 验证
|
||||
- 运行 Swagger 生成与本地文档预览,检查所有路由均有完整注释
|
||||
- 运行单元测试/集成冒烟:抽奖、承诺、收据查询、奖励发放
|
||||
- 代码静态检查:确保 API 无直接 DAO 访问、Service 注释完整
|
||||
|
||||
## 交付物
|
||||
- 已修正的 API 与 Service 源码
|
||||
- 通过的 Swagger 文档与接口列表
|
||||
- 简要测试记录(通过用例与关键接口返回示例)
|
||||
40
.trae/documents/修复“添加奖品弹出批量抽奖”与奖品列表优化计划.md
Normal file
40
.trae/documents/修复“添加奖品弹出批量抽奖”与奖品列表优化计划.md
Normal file
@ -0,0 +1,40 @@
|
||||
## 问题定位
|
||||
- 奖品页 `web/admin/src/views/activity/rewards/index.vue` 在工具栏下方无条件渲染了批量抽奖对话框:`<BatchDrawDialog :activity="currentActivity" />`。
|
||||
- 该子组件内部以 `props.activity !== null` 控制 `v-model`,而当前页的 `currentActivity` 始终返回对象(非 null),导致对话框初始即打开,表现为点击“新增奖励”时出现“批量抽奖”。
|
||||
|
||||
## 修复方案
|
||||
- 直接移除奖品页中的 `BatchDrawDialog` 渲染(该页仅做奖励 CRUD,模拟批量抽奖逻辑属于活动管理页/模拟页)。
|
||||
- 若保留(备选):使用受控显示状态,改为 `<BatchDrawDialog v-model="showBatchDrawDialog" :activity="showBatchDrawDialog ? currentActivity : null" />`,并提供单独按钮触发。默认 `showBatchDrawDialog=false`。
|
||||
- 采用“移除”方案,影响文件:`web/admin/src/views/activity/rewards/index.vue`(删除组件引用与相关变量)。
|
||||
|
||||
## 奖品列表优化
|
||||
- 新增展示:
|
||||
- “剩余/总量”列:`quantity / original_qty`
|
||||
- “期望概率”列:基于总权重 `sumWeight` 本地计算 `weight/sumWeight*100`,保留两位小数。
|
||||
- Boss 标签保持,但列名更明确为“Boss”。
|
||||
- 汇总信息:表头上方显示“奖品总数 / 总权重”。
|
||||
- 交互优化:
|
||||
- 编辑校验完善(名称、商品、权重、数量、原始数量、等级为必填/大于零)。
|
||||
- 删除增加二次确认弹窗。
|
||||
- 过滤与排序(前端本地):
|
||||
- 过滤项:名称(模糊)、等级(S/A/B/C)、是否 Boss。
|
||||
- 默认排序:按等级(S→C)+ `sort`。
|
||||
- 数据加载优化:商品列表一次加载并缓存,避免重复请求。
|
||||
|
||||
## 具体改动
|
||||
- 修改文件:`web/admin/src/views/activity/rewards/index.vue`
|
||||
- 删除 `<BatchDrawDialog :activity="currentActivity" />` 与相关 `currentActivity` 计算属性。
|
||||
- 在 `columns` 增加 `剩余/总量`、`期望概率` 两列(期望概率通过本地计算得到)。
|
||||
- 在顶部新增汇总区域(使用 `computed` 基于 `data` 计算)。
|
||||
- 增加简单的本地过滤控件(`ElInput`/`ElSelect`),对展示数据做 `computed` 过滤与排序。
|
||||
- 删除与批量抽奖相关的无用状态(如 `showSimDialog`)。
|
||||
- 删除按钮文案更明确:`批量新增奖励` → `新增奖励`。
|
||||
- 不改后端接口;所有优化为前端表现层。
|
||||
|
||||
## 验收
|
||||
- 进入“奖励”页,点击“新增奖励”只弹奖励编辑对话框,不再出现批量抽奖对话框。
|
||||
- 列表显示“剩余/总量”、“期望概率”,汇总信息正确;过滤与排序生效。
|
||||
- 构建通过,交互校验正常,删除有确认提示。
|
||||
|
||||
## 风险与回滚
|
||||
- 风险低,仅前端视图改动;如需回滚,保留原组件引用并恢复初始布局即可。
|
||||
38
.trae/documents/修复微信支付回调验签失败(支持平台公钥_PUB_KEY_ID).md
Normal file
38
.trae/documents/修复微信支付回调验签失败(支持平台公钥_PUB_KEY_ID).md
Normal file
@ -0,0 +1,38 @@
|
||||
## 问题定位
|
||||
- 现象:POST `/api/pay/wechat/notify` 返回 400,日志提示“certificate [PUB_KEY_ID_0116104396352025041000211519001600] not found in verifier”。
|
||||
- 原因:微信侧请求头 `Wechatpay-Serial` 为 `PUB_KEY_ID_*` 格式,说明该商户已启用“微信支付平台公钥”模式;当前代码仅使用“平台证书”验签(`verifiers.NewSHA256WithRSAVerifier` + `downloader`),导致找不到匹配的证书序列号而验签失败。
|
||||
- 代码位置:
|
||||
- 路由注册:`internal/router/router.go:252`
|
||||
- 回调处理:`internal/api/pay/wechat_notify.go:40-46`(注册证书下载器+证书验签器)
|
||||
- 客户端初始化:`internal/pkg/pay/client.go:32-39`(`WithWechatPayAutoAuthCipher` 使用平台证书模式)
|
||||
|
||||
## 修复方案
|
||||
- 增加“平台公钥”模式配置并切换验签/签名实现:
|
||||
- 在配置新增 `wechatpay.public_key_id` 与 `wechatpay.public_key_path`(商户后台下载的 `pub_key.pem` 与对应 `PUB_KEY_ID_*`)。
|
||||
- 回调验签:当检测到已配置公钥,改用 `verifiers.NewSHA256WithRSAPubkeyVerifier(publicKeyID, publicKey)` 构造 `notify.Handler`;否则保持现有证书模式。
|
||||
- 请求侧签名与自动加密:当检测到已配置公钥,改用 `option.WithWechatPayPublicKeyAuthCipher(mchid, serialNo, privateKey, publicKeyID, publicKey)` 初始化 `core.Client`;否则保持 `WithWechatPayAutoAuthCipher`。
|
||||
- 初始化时机优化:
|
||||
- 将证书下载器注册/公钥加载前置至应用启动阶段,避免首次回调时的“尚未拉取证书”竞态问题。
|
||||
- 兼容策略:
|
||||
- 双模式自动选择:优先检测公钥配置;缺省走证书模式,确保对旧商户不破坏。
|
||||
|
||||
## 实施步骤
|
||||
- 配置:
|
||||
- 在 `configs/*.toml` 的 `[wechatpay]` 增加 `public_key_id` 与 `public_key_path` 键;同时支持环境变量覆盖(如 `WECHAT_PUBLIC_KEY_ID`、`WECHAT_PUBLIC_KEY_PATH`)。
|
||||
- 代码改动:
|
||||
- `internal/api/pay/wechat_notify.go`:根据配置分支选择 `verifiers.NewSHA256WithRSAPubkeyVerifier` 或现有证书验签器;加载公钥使用 `utils.LoadPublicKeyWithPath`。
|
||||
- `internal/pkg/pay/client.go`:根据配置分支选择 `option.WithWechatPayPublicKeyAuthCipher` 或现有证书选项。
|
||||
- 启动流程:在应用初始化位置集中完成下载器注册/公钥加载与复用,不在每次回调时重复注册。
|
||||
|
||||
## 验证步骤
|
||||
- 本地联调:
|
||||
- 使用真实微信回调数据(包含 `Wechatpay-Serial: PUB_KEY_ID_*`)验证回调成功返回 `{"code":"SUCCESS","message":"OK"}`。
|
||||
- 下单→支付→回调链路验证:检查订单状态从 1→2,`paidAt` 按回调 `success_time` 生效(`internal/api/pay/wechat_notify.go:67-71`)。
|
||||
- 日志与安全:
|
||||
- 打印初始化模式(证书/公钥)与关键配置是否完整,避免静默失败。
|
||||
- 不输出密钥/私钥内容,符合安全规范。
|
||||
|
||||
## 额外检查(关于“req 没有写”)
|
||||
- 当前回调处理直接使用 `ctx.Request()` 原始请求体(`internal/api/pay/wechat_notify.go:48`),未提前读取/篡改;若上游中间件读取了 `Body`,需保证使用 `io.NopCloser` 复位请求体后再交给 `notify.Handler`。本次改造同时确认路由链路未破坏请求体传递。
|
||||
|
||||
请确认是否按照“平台公钥”接入(已具备 `pub_key.pem` 与 `PUB_KEY_ID_*`)。确认后我将按上述方案实现代码与配置改动,并完成联调与验证。
|
||||
109
.trae/documents/发货统计SQL设计与字段映射.md
Normal file
109
.trae/documents/发货统计SQL设计与字段映射.md
Normal file
@ -0,0 +1,109 @@
|
||||
## 模型目标
|
||||
- 为运营提供“发货统计”独立表,脱离业务在线查询复杂度,支持导出与审计。
|
||||
- 字段覆盖:产品、价格、发货数量、用户收件信息、物流、订单聚合、盈亏、来源、垫付人、时间。
|
||||
|
||||
## 表结构(DDL)
|
||||
```sql
|
||||
CREATE TABLE IF NOT EXISTS ops_shipping_stats (
|
||||
id BIGINT PRIMARY KEY AUTO_INCREMENT,
|
||||
shipped_at DATETIME NOT NULL COMMENT '发货时间',
|
||||
product_id BIGINT NULL,
|
||||
product_name VARCHAR(255) NOT NULL,
|
||||
product_price_cents BIGINT NOT NULL COMMENT '单位:分',
|
||||
shipped_qty BIGINT NOT NULL,
|
||||
user_id BIGINT NOT NULL,
|
||||
user_name VARCHAR(100) NOT NULL,
|
||||
user_address_text VARCHAR(512) NOT NULL,
|
||||
express_code VARCHAR(64) NULL,
|
||||
express_no VARCHAR(128) NULL,
|
||||
order_id BIGINT NULL,
|
||||
order_no VARCHAR(64) NULL,
|
||||
order_qty BIGINT NULL,
|
||||
order_amount_cents BIGINT NULL COMMENT '单位:分',
|
||||
profit_loss_cents BIGINT NULL COMMENT '单位:分',
|
||||
order_source_type INT NULL,
|
||||
order_source_text VARCHAR(32) NULL,
|
||||
payer VARCHAR(128) NULL COMMENT '垫付人,后续录入或从remark规范解析',
|
||||
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
KEY idx_shipped_at(shipped_at),
|
||||
KEY idx_product(product_id),
|
||||
KEY idx_order(order_id),
|
||||
KEY idx_express(express_no)
|
||||
) COMMENT='运营发货统计';
|
||||
```
|
||||
|
||||
## 明细数据入库(INSERT…SELECT)
|
||||
```sql
|
||||
/* 参数:@start_date, @end_date */
|
||||
INSERT INTO ops_shipping_stats (
|
||||
shipped_at, product_id, product_name, product_price_cents, shipped_qty,
|
||||
user_id, user_name, user_address_text, express_code, express_no,
|
||||
order_id, order_no, order_qty, order_amount_cents, profit_loss_cents,
|
||||
order_source_type, order_source_text, payer
|
||||
)
|
||||
SELECT
|
||||
sr.shipped_at,
|
||||
COALESCE(sr.product_id, oi.product_id) AS product_id,
|
||||
COALESCE(oi.title, p.name) AS product_name,
|
||||
COALESCE(sr.price, oi.price, p.price) AS product_price_cents,
|
||||
sr.quantity AS shipped_qty,
|
||||
ua.user_id AS user_id,
|
||||
ua.name AS user_name,
|
||||
CONCAT(ua.province, ua.city, ua.district, ua.address) AS user_address_text,
|
||||
sr.express_code,
|
||||
sr.express_no,
|
||||
o.id AS order_id,
|
||||
o.order_no AS order_no,
|
||||
os.total_qty AS order_qty,
|
||||
o.actual_amount AS order_amount_cents,
|
||||
/* 盈亏:行价格×发货数量 − 订单实付金额(可根据口径调整) */
|
||||
COALESCE(sr.price, oi.price, p.price) * sr.quantity - o.actual_amount AS profit_loss_cents,
|
||||
o.source_type AS order_source_type,
|
||||
CASE o.source_type WHEN 1 THEN '商城直购' WHEN 2 THEN '抽奖票据' WHEN 3 THEN '其他' ELSE CONCAT('未知-', o.source_type) END AS order_source_text,
|
||||
/* 垫付人:暂为空,后续从 remark 或新增字段填充 */
|
||||
NULL AS payer
|
||||
FROM shipping_records sr
|
||||
LEFT JOIN order_items oi ON oi.id = sr.order_item_id
|
||||
LEFT JOIN products p ON p.id = COALESCE(sr.product_id, oi.product_id)
|
||||
LEFT JOIN orders o ON o.id = sr.order_id
|
||||
LEFT JOIN (
|
||||
SELECT order_id, SUM(quantity) AS total_qty
|
||||
FROM order_items
|
||||
GROUP BY order_id
|
||||
) os ON os.order_id = o.id
|
||||
LEFT JOIN user_addresses ua ON ua.id = COALESCE(sr.address_id, o.user_address_id)
|
||||
WHERE sr.status IN (2,3) /* 已发货/已签收 */
|
||||
AND sr.shipped_at BETWEEN @start_date AND @end_date;
|
||||
```
|
||||
|
||||
## 查询(导出)
|
||||
```sql
|
||||
/* 查询明细(元) */
|
||||
SELECT
|
||||
product_name,
|
||||
ROUND(product_price_cents/100, 2) AS product_price_yuan,
|
||||
shipped_qty,
|
||||
user_name,
|
||||
user_address_text,
|
||||
express_code,
|
||||
express_no,
|
||||
order_no,
|
||||
order_qty,
|
||||
ROUND(order_amount_cents/100, 2) AS order_amount_yuan,
|
||||
ROUND(profit_loss_cents/100, 2) AS profit_loss_yuan,
|
||||
order_source_text,
|
||||
payer,
|
||||
shipped_at
|
||||
FROM ops_shipping_stats
|
||||
WHERE shipped_at BETWEEN @start_date AND @end_date
|
||||
ORDER BY shipped_at DESC;
|
||||
```
|
||||
|
||||
## 口径与可调整项
|
||||
- 盈亏口径:当前为“行价格×发货数量 − 订单实付金额”;如需改为“订单行应付总额(`oi.total_amount`) − 订单实付金额”,可替换。
|
||||
- 下单数量:当前为订单维度总件数(聚合);如需改为行数量,改用 `oi.quantity`。
|
||||
- 垫付人:建议在 `shipping_records` 增加 `payer` 字段或在 `remark` 规范写入 `payer:xxx`,入库时解析填充。
|
||||
|
||||
## 后续实现建议
|
||||
- 管理后台增加导出按钮,直接查询 `ops_shipping_stats` 并输出 CSV/Excel。
|
||||
- 每日定时任务或发货事件触发增量写入,保证统计表实时/准实时更新。
|
||||
78
.trae/documents/发货统计创建表单与来源字段优化方案.md
Normal file
78
.trae/documents/发货统计创建表单与来源字段优化方案.md
Normal file
@ -0,0 +1,78 @@
|
||||
**问题理解**
|
||||
|
||||
* 创建/编辑对话框单列过长,影响填写与可读性。
|
||||
|
||||
* 产品、用户应支持选择(远程搜索)而非手填。
|
||||
|
||||
* 订单来源限定为:淘宝/拼多多/京东/线下;来源类型与文本重复,应仅保留类型并在后端映射文本。
|
||||
|
||||
**前端改造**
|
||||
|
||||
* 表单布局(左右分布):
|
||||
|
||||
* 在 `web/admin/src/views/operations/shipping-stats/index.vue` 的编辑对话框使用 `el-row/el-col` 两列布局,将字段按分组排布:
|
||||
|
||||
* 左列:发货时间、产品选择、单价、发货数量、订单来源类型(下拉)。
|
||||
|
||||
* 右列:用户选择、收件人、地址、快递公司、运单号、订单号。
|
||||
|
||||
* 必填项:`shipped_at`、`product_id`、`user_id`、`shipped_qty`。
|
||||
|
||||
* 产品选择:
|
||||
|
||||
* 远程搜索下拉 `el-select filterable remote` 调用 `GET 'admin/products'`(已有路由:`internal/router/router.go:120`)。
|
||||
|
||||
* 选中后自动填充:`product_id`、`product_name`、`product_price_cents`。
|
||||
|
||||
* 用户选择:
|
||||
|
||||
* 远程搜索下拉调用 `GET 'admin/users'`(已有路由:`internal/router/router.go:129`)。
|
||||
|
||||
* 选中后自动填充:`user_id`、`user_name`;`user_address_text`仍支持手动输入。
|
||||
|
||||
* 订单来源:
|
||||
|
||||
* 将 `order_source_type` 改为下拉选择:`{1: 淘宝, 2: 拼多多, 3: 京东, 4: 线下}`。
|
||||
|
||||
* 移除前端表单中的 `order_source_text` 字段,仅展示文本列(由后端返回)。
|
||||
|
||||
* API 类型调整:
|
||||
|
||||
* 在 `web/admin/src/api/shipping-stats.ts` 的 `CreateShippingStatRequest/UpdateShippingStatRequest` 中删除 `order_source_text` 字段。
|
||||
|
||||
* 列表表格仅显示 `order_source_text`;不显示 `order_source_type` 列(现有实现只显示文本,无需调整)。
|
||||
|
||||
**后端改造**
|
||||
|
||||
* 文段位置:`internal/api/admin/shipping_stats_admin.go`。
|
||||
|
||||
* 文本映射策略:
|
||||
|
||||
* 在创建与更新接口中,根据 `OrderSourceType` 自动映射 `OrderSourceText`:
|
||||
|
||||
* `1→"淘宝"`、`2→"拼多多"`、`3→"京东"`、`4→"线下"`,其他值映射为`"未知"`。
|
||||
|
||||
* 移除对 `order_source_text` 的前端输入依赖:
|
||||
|
||||
* `CreateShippingStat()` 忽略请求体中的 `order_source_text`,统一用映射结果赋值。
|
||||
|
||||
* `ModifyShippingStat()` 若 `OrderSourceType` 变更则同步更新映射文本。
|
||||
|
||||
* 其余字段逻辑不变;保留列表筛选条件与分页排序。
|
||||
|
||||
**交互与体验**
|
||||
|
||||
* 远程搜索均支持关键字匹配与节流,选项标签为 `名称 (ID)`,用户便于确认。
|
||||
|
||||
* 表单左右分布减少滚动,分组更清晰;金额与运单等非必填项可留空。
|
||||
|
||||
**验收标准**
|
||||
|
||||
* 新建/编辑对话框为两列布局,核心字段分组合理,必填校验生效。
|
||||
|
||||
* 产品与用户支持远程搜索选择,选中后自动填充相关字段。
|
||||
|
||||
* 订单来源仅以下拉类型选择,后端返回正确文本,前端只展示文本;不再出现“来源文本/来源类型重复”问题。
|
||||
|
||||
* 列表/详情/增改删功能在管理端登录后完整可用。
|
||||
|
||||
65
.trae/documents/发货统计(ops_shipping_stats)前后端对接与CRUD设计.md
Normal file
65
.trae/documents/发货统计(ops_shipping_stats)前后端对接与CRUD设计.md
Normal file
@ -0,0 +1,65 @@
|
||||
**后端 API 设计**
|
||||
- 路径前缀:`/api/admin/ops_shipping_stats`(管理端鉴权)。参考路由注册与鉴权模式:`internal/router/router.go` 中 Admin 组;处理方式参照 `internal/api/admin/titles_admin.go:28/81/105/135`。
|
||||
- 接口列表:
|
||||
- `GET /api/admin/ops_shipping_stats` 列表查询(分页/筛选/排序)。
|
||||
- `GET /api/admin/ops_shipping_stats/:id` 单条详情。
|
||||
- `POST /api/admin/ops_shipping_stats` 新增。
|
||||
- `PUT /api/admin/ops_shipping_stats/:id` 更新。
|
||||
- `DELETE /api/admin/ops_shipping_stats/:id` 删除。
|
||||
- 入参约定:
|
||||
- 列表查询(Query):`page`、`page_size`、`shipped_start`、`shipped_end`、`product_id`、`product_name`、`user_id`、`user_name`、`express_code`、`express_no`、`order_id`、`order_no`、`order_source_type`、`payer`(字符串枚举),`keyword`(模糊匹配 `product_name/user_name/order_no/express_no`)。
|
||||
- 新增/更新(JSON):`shipped_at`(RFC3339)、`product_id`、`product_name`、`product_price_cents`、`shipped_qty`、`user_id`、`user_name`、`user_address_text`、`express_code`、`express_no`、`order_id`、`order_no`、`order_qty`、`order_amount_cents`、`profit_loss_cents`、`order_source_type`、`order_source_text`、`payer`。
|
||||
- 响应约定:
|
||||
- 列表:`{ page, page_size, total, list: OpsShippingStats[] }`,默认排序 `shipped_at DESC, id DESC`。
|
||||
- 详情:`OpsShippingStats` 完整记录。
|
||||
- 新增/更新/删除:返回受影响记录或 `{ id }`。
|
||||
- 校验与错误:
|
||||
- 参数绑定:`ShouldBindForm`/`ShouldBindJSON`,错误码用 `internal/code` 中 `ParamBindError`、`DatabaseError` 等。
|
||||
- 鉴权:`core.WrapAuthHandler(intc.AdminTokenAuthVerify)`。
|
||||
- DAO 使用:
|
||||
- 读:`h.readDB.OpsShippingStats.WithContext(ctx).ReadDB().Where(...).FindByPage(...)`。
|
||||
- 写:`h.writeDB.OpsShippingStats.WithContext(ctx).Create/Save/Delete`。
|
||||
- 参考模型/DAO:`internal/repository/mysql/model/ops_shipping_stats.gen.go`、`internal/repository/mysql/dao/ops_shipping_stats.gen.go`;全局接入点:`internal/repository/mysql/dao/gen.go`。
|
||||
- 过滤实现:
|
||||
- 时间范围:`Where(dao.OpsShippingStats.ShippedAt.Gte(shipped_start))` 与 `Lte(shipped_end)`。
|
||||
- 模糊匹配:`Like` 多字段 `Or` 组合;精确匹配:ID 字段 `Eq`。
|
||||
- 分页:`FindByPage(offset, limit)` 返回 `(list, total)`。
|
||||
- 索引建议(后续 DBA 执行):
|
||||
- 组合索引:`(shipped_at DESC, product_id)`、`(user_id)`、`(order_no)`、`(express_code, express_no)`,提高查询性能。
|
||||
|
||||
**前端对接与页面**
|
||||
- 菜单与路由:
|
||||
- 本地模式:在 `web/admin/src/router/modules/operations.ts` 的 `children` 增加:`{ path: 'shipping-stats', name: 'ShippingStats', component: '/operations/shipping-stats', meta: { title: '发货统计', roles: ['R_SUPER','R_ADMIN'] } }`。
|
||||
- 接口模式(可选):通过系统菜单表 `menus` 新增对应子菜单;后端 `GET /api/v3/system/menus/simple` 返回;参考 `internal/api/admin/system_menu.go`。
|
||||
- API 客户端:`web/admin/src/api/shipping-stats.ts`
|
||||
- `getList(params)` → `GET 'admin/ops_shipping_stats'`。
|
||||
- `getDetail(id)` → `GET 'admin/ops_shipping_stats/:id'`。
|
||||
- `create(data)` → `POST 'admin/ops_shipping_stats'`。
|
||||
- `update(id, data)` → `PUT 'admin/ops_shipping_stats/:id'`。
|
||||
- `remove(id)` → `DELETE 'admin/ops_shipping_stats/:id'`。
|
||||
- 复用 `src/utils/http/index.ts` 封装(自动附带 `Authorization`、统一错误处理)。
|
||||
- 页面视图:`web/admin/src/views/operations/shipping-stats/index.vue`
|
||||
- 布局:顶部搜索(`ArtSearchBar`),表头工具(`ArtTableHeader`),数据表(`ArtTable`),分页。
|
||||
- 搜索项:时间范围、商品(ID/名称)、用户(ID/名称)、快递公司/单号、订单号、来源类型、付款人、关键字。
|
||||
- 列定义:`shipped_at`、`product_name`、`product_price_cents`(格式化为金额)、`shipped_qty`、`user_name`、`user_address_text`(截断+tooltip)、`express_code`、`express_no`、`order_no`、`order_qty`、`order_amount_cents`(金额)、`profit_loss_cents`(金额/颜色提示)、`order_source_text`、`payer`、操作列(查看/编辑/删除)。
|
||||
- 对话框:新建/编辑(表单项与后端 JSON 契约一致)。
|
||||
- 交互:列表加载用 `useTable`;增改删后按“软刷新/全量刷新”策略更新列表;统一消息提示。
|
||||
- 权限:`meta.roles` 控制访问;按钮权限按需要可用 `authList`。
|
||||
|
||||
**实施步骤**
|
||||
- 后端:
|
||||
- 在 `internal/api/admin` 新增 `shipping_stats_admin.go`,实现 5 个 Handler,仿照 `titles_admin.go` 的风格(入参绑定/读写分离/错误与响应)。
|
||||
- 在 `internal/router/router.go` 的 Admin 鉴权组注册 5 条路由。
|
||||
- 前端:
|
||||
- 添加菜单路由项(本地或接口模式)。
|
||||
- 新建 `src/api/shipping-stats.ts`,封装 5 个方法。
|
||||
- 新建 `src/views/operations/shipping-stats/index.vue` 页面:搜索区、表格列、对话框、分页与刷新逻辑。
|
||||
|
||||
**验收标准**
|
||||
- 管理端已登录用户可访问“运营管理/发货统计”。
|
||||
- 列表支持分页、筛选、排序(默认按发货时间倒序)。
|
||||
- 能创建/编辑/删除记录并实时刷新;可查看详情。
|
||||
- 后端输入校验与统一错误码生效;鉴权与日志记录正常。
|
||||
- 查询性能在典型数据量下良好(建议索引已列出)。
|
||||
|
||||
请确认以上方案,我将按该设计进行代码实现与联调。
|
||||
65
.trae/documents/后台订单管理完善方案.md
Normal file
65
.trae/documents/后台订单管理完善方案.md
Normal file
@ -0,0 +1,65 @@
|
||||
# 目标
|
||||
- 为管理后台补齐“订单管理”全套能力:全局订单列表/详情、筛选与搜索、状态流转(取消/发货/签收/履约)、退款、导出与对账入口;与现有用户维度订单查询保持一致风格。
|
||||
|
||||
# 现状与缺口
|
||||
- 已有:按用户查看订单、运营漏斗统计;无全局订单页与发货、退款、对账的管理操作。
|
||||
- 模型完备:`orders/order_items/shipping_records/user_points_ledger`,但缺少支付域表与真实退款流程;`CreateRefund`占位未挂路由。
|
||||
|
||||
# 后端接口设计
|
||||
- 订单列表(全局):`GET /api/admin/pay/orders`
|
||||
- 筛选:`status(1/2/3/4)`、`source_type(1/2/3)`、`pay_time_range`、`created_range`、`user_id/order_no`、`is_consumed`
|
||||
- 排序:`created_at/paid_at` 倒序
|
||||
- 返回:分页、订单聚合(含金额、状态、支付时间、用户信息摘要)
|
||||
- 订单详情:`GET /api/admin/pay/orders/:order_no`
|
||||
- 返回:订单主信息 + `order_items` + `shipping_records` + 记账流水摘要
|
||||
- 订单备注更新:`PUT /api/admin/pay/orders/:order_no/remark`
|
||||
- 订单取消:`POST /api/admin/pay/orders/:order_no/cancel`
|
||||
- 行为:若`status=1待支付`→置`status=3已取消`/写`cancelled_at`,幂等
|
||||
- 发货单列表:`GET /api/admin/pay/shipments`
|
||||
- 筛选:`status(1待发/2已发/3已签收/4异常)`、`order_no/product_id/express_no`、时间范围
|
||||
- 创建发货:`POST /api/admin/pay/shipments`
|
||||
- 入参:`order_no/order_item_id/address_id/quantity/express_code(optional)`;行为:生成`shipping_records`,将`status=1待发`
|
||||
- 标记已发货:`PUT /api/admin/pay/shipments/:id/ship`
|
||||
- 入参:`express_code/express_no`;行为:置`status=2已发/shipped_at`
|
||||
- 标记已签收:`PUT /api/admin/pay/shipments/:id/receive`
|
||||
- 行为:置`status=3已签收/received_at`
|
||||
- 标记异常:`PUT /api/admin/pay/shipments/:id/abnormal`
|
||||
- 履约完成(虚拟):`PUT /api/admin/pay/orders/:order_no/consume`
|
||||
- 行为:`is_consumed=1`,用于虚拟资产核销;幂等
|
||||
- 退款(对接支付域):
|
||||
- 创建退款:`POST /api/admin/pay/refunds`(保留当前占位实现,后续替换为真实支付退款API)
|
||||
- 查询退款:`GET /api/admin/pay/refunds`、`GET /api/admin/pay/refunds/:refund_no`
|
||||
- 导出:`GET /api/admin/pay/orders/export`(按筛选条件导出CSV/XLSX)
|
||||
- 对账入口:`POST /api/admin/pay/bills/import`(后续结合支付域实现)
|
||||
|
||||
# 服务层与数据层
|
||||
- 新增 `admin` 服务模块:封装订单聚合查询、详情组装、状态流转与发货操作;使用 Gorm 事务与条件更新保证幂等。
|
||||
- DAO 复用:`orders/order_items/shipping_records/user_points_ledger`;索引建议:`orders.order_no`唯一、`shipping_records.express_no`索引。
|
||||
|
||||
# 前端管理页
|
||||
- 新增“订单管理”菜单及页面:
|
||||
- 订单列表页:筛选(状态、来源、时间、用户、订单号、履约)、表格列(订单号、用户、金额、状态、支付时间、来源、履约、备注)、操作(查看、取消、退款、履约、导出)
|
||||
- 订单详情页:订单主信息、订单项、发货记录(创建/发货/签收/异常)、记账流水与日志
|
||||
- 发货管理页:发货单列表,支持搜索与状态更新
|
||||
- API 封装:在 `web/admin/src/api/` 新增 `pay-orders.ts` 与 `shipments.ts`,匹配后端接口
|
||||
|
||||
# 业务与校验
|
||||
- 金额与状态校验:取消仅允许`status=1`;履约仅允许`status=2`;发货需校验行项目数量与地址。
|
||||
- 幂等:状态流转采用条件更新;重复请求直接返回当前状态。
|
||||
- 审计:请求日志已入库(`dblogger`),增加关键操作的业务日志字段。
|
||||
|
||||
# 验收标准
|
||||
- 管理端可全局查看与筛选订单,支持详情、取消、履约、发货、签收、异常标记;
|
||||
- 退款创建与查询可用(占位版本),后续升级为真实退款;
|
||||
- 导出按筛选条件生成数据;
|
||||
- 运营漏斗与新页面数据口径一致;编译与基础联调通过。
|
||||
|
||||
# 实施步骤
|
||||
1. 后端:新增订单与发货相关接口与服务;将退款占位路由挂载;完善查询与分页;幂等与事务。
|
||||
2. 前端:新增API文件与页面组件(列表/详情/发货);接入菜单与路由;UI按现有风格。
|
||||
3. 校验:编译与接口联调、权限校验、筛选与导出验证;漏斗口径确认。
|
||||
4. 留待增强:接入真实支付退款与对账流程;增加订单号唯一索引与支付幂等表。
|
||||
|
||||
# 风险与处理
|
||||
- 真实退款未接入:先保留管理操作占位,避免资金走错;上线前需联通支付域。
|
||||
- 并发与幂等:条件更新控制状态流;失败重试与告警结合现有日志。
|
||||
41
.trae/documents/增加采购价格与对比(ops_shipping_stats)方案.md
Normal file
41
.trae/documents/增加采购价格与对比(ops_shipping_stats)方案.md
Normal file
@ -0,0 +1,41 @@
|
||||
**问题理解**
|
||||
- 需要在发货统计中体现“采购价格”,用于与销售价格/订单金额做对比与分析。
|
||||
- 当前 `ops_shipping_stats` 仅存储销售单价 `product_price_cents`,无采购单价。
|
||||
|
||||
**数据库变更**
|
||||
- 在 `ops_shipping_stats` 表新增字段:
|
||||
- `purchase_price_cents BIGINT NOT NULL DEFAULT 0 COMMENT '采购单价(分)'`
|
||||
- SQL(MySQL):
|
||||
- `ALTER TABLE ops_shipping_stats ADD COLUMN purchase_price_cents BIGINT NOT NULL DEFAULT 0 COMMENT '采购单价(分)';`
|
||||
- 生成代码:执行 gormgen 以更新 Model/DAO(`cmd/gormgen/main.go`,保持原有 DSN 与 `-tables` 参数)。
|
||||
|
||||
**后端改造**
|
||||
- 控制器 `internal/api/admin/shipping_stats_admin.go`:
|
||||
- 创建请求体 `CreateShippingStatRequest` 增加 `purchase_price_cents`(可选)。
|
||||
- 更新请求体 `UpdateShippingStatRequest` 增加 `purchase_price_cents`(可选)。
|
||||
- 保存/更新时写入 `purchase_price_cents`。
|
||||
- 盈亏策略:保持现有“盈亏=销售单价×发货数量 − 订单实付金额”;
|
||||
- 额外提供两个对比维度(不入库,仅响应扩展字段,便于前端展示):
|
||||
- `purchase_total_cents = purchase_price_cents * shipped_qty`
|
||||
- `gross_profit_cents = (product_price_cents - purchase_price_cents) * shipped_qty`
|
||||
- 若你希望持久化以上值,也可扩展表结构,但默认前端计算即可。
|
||||
- 列表与详情响应:保持原结构,新增返回 `purchase_price_cents`(其余对比指标前端计算)。
|
||||
|
||||
**前端改造**
|
||||
- 创建/编辑表单(已两列布局):
|
||||
- 左列补充“采购单价(分)”输入框,默认可为空(为空按0处理)。
|
||||
- 列表新增列:
|
||||
- 采购单价(分→元格式化)
|
||||
- 采购总额(分→元,前端用 `purchase_price_cents * shipped_qty` 计算)
|
||||
- 毛利(分→元,前端用 `(product_price_cents - purchase_price_cents) * shipped_qty` 计算)
|
||||
- 保持“盈亏”列为后端返回值显示(颜色提示不变)。
|
||||
|
||||
**验收标准**
|
||||
- 新建/编辑支持录入采购单价,并正确写入与返回。
|
||||
- 列表能展示采购单价、采购总额和销售对比(毛利),并与现有盈亏并行显示。
|
||||
- 不影响既有筛选、分页与来源类型逻辑。
|
||||
|
||||
**后续可选**
|
||||
- 若采购价格数据来源稳定(如采购单据),可支持远程选择或自动回填;当前先手动录入以快速上线。
|
||||
|
||||
请确认以上方案,通过后我将进行数据库字段添加、代码生成与前后端联调实现。
|
||||
13
.trae/documents/奖品管理操作栏统一为图标标签按钮的优化计划.md
Normal file
13
.trae/documents/奖品管理操作栏统一为图标标签按钮的优化计划.md
Normal file
@ -0,0 +1,13 @@
|
||||
## 目标
|
||||
- 将奖品管理页(Rewards)操作栏的“编辑/删除”文本按钮替换为统一的图标标签按钮(ArtButtonTable),与活动管理、期数管理保持一致风格。
|
||||
|
||||
## 实施点
|
||||
- 修改文件:`web/admin/src/views/activity/rewards/index.vue`
|
||||
- 变更内容:
|
||||
- 引入 `ArtButtonTable` 组件
|
||||
- 将 `actions` 插槽中的 `ElButton` 替换为 `ArtButtonTable type="edit"` 与 `ArtButtonTable type="delete"`
|
||||
- 不改动后端接口与其它逻辑(保留删除确认与编辑弹窗)。
|
||||
|
||||
## 验收
|
||||
- 奖品管理列表操作栏显示统一的图标标签按钮,交互正常(编辑弹窗、删除确认)。
|
||||
- 前端构建通过。
|
||||
46
.trae/documents/完成支付体系全量完善的实施计划.md
Normal file
46
.trae/documents/完成支付体系全量完善的实施计划.md
Normal file
@ -0,0 +1,46 @@
|
||||
## 剩余完善项
|
||||
- 积分恢复比例与幂等:部分/全额退款的积分恢复算法与重复保护
|
||||
- 通知事件持久化与去重:payment_notify_events 入库、processed 标记、重复回调忽略
|
||||
- 退款列表与详情接口:基于 payment_refunds 的查询/分页/导出
|
||||
- 对账入口与差异列表:账单导入、生成 payment_bill_diff、差异查询与导出
|
||||
- 管理端详情字段完善:展示 transaction_id/refund_no/channel/支付方式、累计已退款与可退余额
|
||||
- 前端金额展示统一为元(两位小数),保留分用于接口与导出
|
||||
- 唯一索引与安全校验:核查并补齐关键唯一约束与条件更新
|
||||
|
||||
## 后端改造
|
||||
1) 积分恢复
|
||||
- 算法:恢复积分 = (订单积分抵扣分 × 退款分 ÷ 实付分) ÷ 100;累计不超过订单抵扣积分
|
||||
- 幂等:按订单维度累计已恢复积分对比目标;全额退款一次性恢复剩余积分
|
||||
2) 通知事件
|
||||
- 入库 payment_notify_events(notify_id,event_type,raw,processed=false);处理成功后置 processed=true;重复 notify_id 直接 ACK
|
||||
3) 退款查询
|
||||
- GET /api/admin/pay/refunds:分页筛选(order_no/status/date/range)
|
||||
- GET /api/admin/pay/refunds/:refund_no:详情(refund_no/amount/status/success_time/reason/raw)
|
||||
4) 对账能力
|
||||
- POST /api/admin/pay/bills/import:上传/拉取账单入库 payment_bills
|
||||
- GET /api/admin/pay/bills/diffs:查询差异 payment_bill_diff(缺失/金额不符/多余)
|
||||
5) 订单详情聚合
|
||||
- 交易:最近一笔 payment_transactions(transaction_id/success_time/amount)
|
||||
- 退款:payment_refunds 汇总(refund_no/amount/status/success_time/reason),计算累计已退与可退余额
|
||||
|
||||
## 前端改造
|
||||
- 金额格式化工具:统一元展示(两位小数),表格列与详情区全部使用
|
||||
- 详情页新增字段:transaction_id/refund_no/channel/支付方式;累计已退/可退余额(元)
|
||||
- 退款记录表:元展示;状态中文映射
|
||||
- 对账入口页面:账单导入按钮、差异列表表格、导出
|
||||
|
||||
## 幂等与安全
|
||||
- 幂等键:order_no/out_trade_no/notify_id/refund_no/transaction_id
|
||||
- 唯一索引:核查 payment_* 与 orders.order_no;接口层条件更新与金额校验
|
||||
- 日志与告警:验签失败、金额不一致、退款异常、对账差异
|
||||
|
||||
## 测试与验收
|
||||
- 单元:分↔积分换算、比例恢复、幂等重复、接口参数校验
|
||||
- 集成:预下单→通知→主动退款→详情一致;对账导入与差异生成
|
||||
- 前后端构建与联调通过;数据与展示口径一致
|
||||
|
||||
## 执行顺序
|
||||
1. 后端:积分恢复与幂等、通知事件入库与去重
|
||||
2. 后端:退款查询接口、对账入库与差异接口
|
||||
3. 前端:金额格式化、详情字段与退款/对账页面
|
||||
4. 测试与联调:全链路验证并修正细节
|
||||
16
.trae/documents/完成支付体系剩余执行计划.md
Normal file
16
.trae/documents/完成支付体系剩余执行计划.md
Normal file
@ -0,0 +1,16 @@
|
||||
## 剩余任务
|
||||
- 后端:
|
||||
- 积分恢复比例与幂等:部分退款按比例恢复(累计不超抵扣积分)、全额一次性恢复;增加累计校验避免重复恢复
|
||||
- 通知事件入库与去重:payment_notify_events 入库(notify_id 唯一)、处理成功置 processed=true,重复直接 ACK
|
||||
- 退款查询接口:GET /api/admin/pay/refunds(分页筛选),GET /api/admin/pay/refunds/:refund_no(详情)
|
||||
- 对账接口:POST /api/admin/pay/bills/import(入库账单),GET /api/admin/pay/bills/diffs(差异查询与导出)
|
||||
- 订单详情聚合:补充 transaction_id/success_time 与 payment_refunds 明细,计算累计已退与可退余额
|
||||
- 前端:
|
||||
- 金额统一元格式化展示(两位小数),保留分用于接口与导出
|
||||
- 订单详情补充交易号/退款号/渠道/支付方式,展示累计已退与可退余额(元)
|
||||
- 对账入口页面:账单导入与差异列表
|
||||
- 幂等与安全:核查唯一索引与条件更新,补充异常告警
|
||||
|
||||
## 验收
|
||||
- 覆盖单元与集成测试(换算、比例恢复、幂等、对账差异)
|
||||
- 前后端构建与联调通过;数据持久化与展示口径一致
|
||||
97
.trae/documents/小程序支付与后台订单_退款设计.md
Normal file
97
.trae/documents/小程序支付与后台订单_退款设计.md
Normal file
@ -0,0 +1,97 @@
|
||||
# 目标与范围
|
||||
- 目标:落地微信小程序(JSAPI)支付,完善后台订单管理与退款闭环,支持对账与运营漏斗统计。
|
||||
- 范围:预下单→前端调起→支付回调→订单状态推进→退款申请/执行→账单对账→后台运营管理。
|
||||
|
||||
# 背景与现状
|
||||
- 已有:订单/订单项/积分流水/工作台漏斗统计与请求日志。
|
||||
- 工作台漏斗接口:`internal/api/admin/dashboard_admin.go:754`;路由:`internal/router/router.go:72`。
|
||||
- 用户订单查询:`internal/service/user/orders_list.go:16/40`;系统发放订单与订单号生成:`internal/service/user/reward_grant.go:76/220`。
|
||||
- 缺失:微信支付接入(预下单、SDK、证书/签名)、支付回调与幂等、退款接口与模型、对账流程、订单唯一约束与支付幂等、防重复回调处理。
|
||||
|
||||
# 总体架构
|
||||
- 分层:Controller(HTTP) → Service(业务/状态机) → Repository(Gorm/DAO) → PayClient(微信支付SDK) → Infra(配置、证书、日志、幂等/锁)。
|
||||
- 关键域:
|
||||
- 订单域:`orders/order_items`维持业务订单与行项目。
|
||||
- 支付域:新增`payment_preorders/payment_transactions/payment_refunds/payment_notify_events/payment_bills`。
|
||||
- 记账域:`user_points_ledger`扩展退款恢复落账。
|
||||
|
||||
# 数据模型设计
|
||||
- `payment_preorders` 预下单记录
|
||||
- `id`、`order_id`、`order_no`(唯一) 、`channel`(wechat_jsapi)、`prepay_id`、`out_trade_no`(唯一)、`amount_total`(分)、`payer_openid`、`status`(created/expired/paid)、`notify_url`、`created_at`、`expired_at`。
|
||||
- `payment_transactions` 支付成功交易
|
||||
- `id`、`order_id`、`order_no`(唯一) 、`channel`、`transaction_id`(微信流水号唯一) 、`amount_total`、`success_time`、`raw`(JSON) 、`created_at`。
|
||||
- `payment_refunds` 退款记录
|
||||
- `id`、`order_id`、`order_no`、`refund_no`(唯一) 、`channel`、`status`(submitted/success/abnormal/closed) 、`amount_refund`(分/支持部分退款) 、`reason`、`raw`(JSON) 、`created_at`、`success_time`。
|
||||
- `payment_notify_events` 回调事件
|
||||
- `id`、`notify_id`(唯一) 、`resource_type`、`event_type`、`summary`、`raw`(JSON) 、`processed`(bool) 、`created_at`。
|
||||
- `payment_bills`/`payment_bill_diff` 对账与差异
|
||||
- `bill_date`、`type`(tradebill/refundbill) 、`file_url`、`digest`、`imported`;差异记录`local_tx_id/wechat_tx_id/diff_type/diff_detail`。
|
||||
- 约束与索引:`orders.order_no`唯一索引;`payment_*`关键幂等字段唯一索引;`notify_id/out_trade_no/transaction_id/refund_no`均唯一。
|
||||
|
||||
# 接口设计
|
||||
- App(用户态)
|
||||
- `POST /api/app/pay/wechat/jsapi/preorder`:入参`order_no/openid`,出参`timeStamp/nonceStr/package(pay=prepay_id)/signType/paySign`。
|
||||
- `GET /api/app/orders/:order_no/payment`:查询订单支付状态与交易详情。
|
||||
- Pay通知(平台回调)
|
||||
- `POST /api/pay/wechat/notify`:验签/解密后入库`payment_notify_events`,幂等推进订单状态,写`payment_transactions`,更新`orders.status=2/paid_at`。
|
||||
- Admin(运营态)
|
||||
- `GET /api/admin/pay/orders`:订单列表(筛选:状态、时间、渠道)。
|
||||
- `POST /api/admin/pay/refunds`:创建退款,入参`order_no/amount/reason`;支持全/部分退款。
|
||||
- `GET /api/admin/pay/refunds`/`GET /api/admin/pay/refunds/:refund_no`:查询退款状态。
|
||||
- `POST /api/admin/orders/:order_no/close`:关闭未支付订单(同时调用微信`close`)。
|
||||
- 漏斗统计继续沿用现有接口,支付口径改为“支付交易成功”(来源于transactions)。
|
||||
|
||||
# 业务流程
|
||||
- 预下单(JSAPI)
|
||||
- 校验订单`status=1`与金额,绑定`openid`→调用`/v3/pay/transactions/jsapi`→落库`payment_preorders`并返回客户端调起参数。参考“JSAPI预下单与签名串”[微信支付文档]。
|
||||
- 前端调起
|
||||
- 小程序端`wx.requestPayment`使用服务端返回的`timeStamp/nonceStr/package/signType/paySign`。
|
||||
- 支付回调
|
||||
- 后端接收`notify`→验签(平台证书)与解密→事件幂等检查(`notify_id`)→查询交易状态或直接推进→更新`orders`为已支付,写`payment_transactions`与触发履约逻辑(虚拟商品使用`is_consumed`)。
|
||||
- 失败重试
|
||||
- 回调验签失败/解密失败→告警并拒收;订单未推进→后台定时查询`/v3/pay/transactions/out-trade-no/{out_trade_no}`兜底。
|
||||
- 退款
|
||||
- 管理端创建退款→调用`/v3/refund/domestic/refunds`→落库`payment_refunds`→回调/查询成功后更新为`success`并写`user_points_ledger(action=refund_restore)`与订单状态`4已退款`(支持部分退款:订单保持`2已支付`,以累计退款判断)。
|
||||
- 对账
|
||||
- 每日拉取交易/退款账单→入库`payment_bills`→与本地`payment_transactions/payment_refunds`比对→生成差异`payment_bill_diff`→运营报表与修复。
|
||||
|
||||
# 幂等与一致性
|
||||
- 订单:`order_no`唯一;预下单多次返回同一`prepay_id`;使用`out_trade_no=order_no`作为业务幂等键。
|
||||
- 回调:`notify_id`去重;状态推进使用原子事务与“当前状态→新状态”的校验。
|
||||
- 退款:`refund_no`唯一;重复请求直接返回现有状态。
|
||||
- 事务:Service层统一开启Gorm事务;对并发推进加行级乐观锁或显式`UPDATE ... WHERE status=...`。
|
||||
|
||||
# 配置与密钥管理
|
||||
- `.env`加载:`WECHAT_APPID/WECHAT_MCHID/WECHAT_SERIAL_NO/WECHAT_PRIVATE_KEY_PATH/WECHAT_API_V3_KEY/WECHAT_NOTIFY_URL`。
|
||||
- 证书:落盘PEM私钥与平台证书;SDK负责签名与应答验签。禁止将密钥提交仓库。
|
||||
|
||||
# SDK选型与规范
|
||||
- 后端Go采用官方`/wechatpay-apiv3/wechatpay-go`(API v3,支持签名/验签/回调解密)。
|
||||
- 按微信文档“JSAPI签名串四行格式与RSA签名”构造客户端参数。
|
||||
|
||||
# 安全与合规
|
||||
- 验签必做;来源白名单;请求体原样落库审计。
|
||||
- 金额单位统一“分”;防止小数误差;禁止信任前端金额。
|
||||
- 日志与告警:失败重试/签名失败/金额不一致必须告警。
|
||||
|
||||
# 测试与验收
|
||||
- 单元:签名参数构造、金额边界、幂等推进、退款部分/全额。
|
||||
- 集成:模拟回调事件、订单查询兜底、对账导入与差异比对。
|
||||
- 验收标准:
|
||||
- 小程序端正常拉起并支付成功,后台订单状态正确推进;
|
||||
- 管理端可发起退款并正确落账;
|
||||
- 回调/对账全链路无漏处理且有幂等保障;
|
||||
- 漏斗“支付用户/完成订单”口径与交易/发货一致。
|
||||
|
||||
# 实施步骤
|
||||
1. 引入微信支付Go SDK与配置加载,建立PayClient与证书管理。
|
||||
2. 建表与唯一索引:`orders.order_no`、`payment_*`系列关键字段唯一。
|
||||
3. 实现预下单接口,返回前端调起参数并记录`payment_preorders`。
|
||||
4. 实现支付回调路由:验签/解密→事件入库→幂等推进订单与交易记录。
|
||||
5. 实现退款接口与回调处理;联动`user_points_ledger`落账。
|
||||
6. 实现对账拉取与差异比对;补充运营漏斗统计数据来源为交易表。
|
||||
7. 覆盖测试与监控告警;上线灰度并观察。
|
||||
|
||||
# 参考文档(已核对)
|
||||
- JSAPI预下单与签名串、调起参数与`prepay_id`有效期:`/websites/pay_weixin_qq_doc_v3`
|
||||
- 官方Go SDK:`/wechatpay-apiv3/wechatpay-go`
|
||||
58
.trae/documents/工会审核与成员管理完善.md
Normal file
58
.trae/documents/工会审核与成员管理完善.md
Normal file
@ -0,0 +1,58 @@
|
||||
## 现状与缺口
|
||||
- 审核未落地:`internal/service/guild/join.go:13-48` 加入逻辑固定写 `JoinStatus=2`(同意),未按 `join_mode=1` 走待审流程;接口仅 `JoinGuild/LeaveGuild`,无审批。
|
||||
- 管理端缺踢人:仅成员列表 `internal/api/admin/guild_members.go:42-73`;前端成员页无操作列 `web/admin/src/views/guild/members/index.vue:30-35`。
|
||||
- 角色字段未赋权:虽有 `owner/admin/member`(`internal/repository/mysql/model/guild_members.gen.go:19-23`),但未做权限绑定与校验。
|
||||
|
||||
## 目标与范围
|
||||
- 增加「加入申请与审核」能力:支持待审、通过、拒绝;仅当 `join_mode=1` 生效。
|
||||
- 增加「成员剔除」能力:管理端支持踢人,并记录操作日志。
|
||||
- 基础优化:完善角色权限(会长/官员)、分页/检索一致性、接口与 Swagger 描述对齐。
|
||||
|
||||
## 后端改造
|
||||
- 模型与数据流
|
||||
- 新增表 `guild_join_applications`(推荐方案):字段 `id/guild_id/user_id/apply_time/status(0待审,1通过,2拒绝)/reviewer_id/review_time/remark`。
|
||||
- `JoinGuild` 改造:
|
||||
- 当 `join_mode=1` 时,仅写一条 `applications(pending)`,不立即写 `guild_members`;返回“申请已提交”。
|
||||
- 当 `join_mode=2` 时,维持现状直接入会(`JoinStatus=2`)。
|
||||
- 为兼容保留 24h 离开限制与重复加入校验。
|
||||
- 审批动作:
|
||||
- 通过:写 `guild_members`(`status=1, role=member, start_time=now, join_status=2`),并更新申请为 `approved`;拒绝:仅更新申请为 `rejected`。
|
||||
- 踢人动作:新增服务 `KickMember(ctx, guildID, userID)`,仅将成员 `status=2` 并回写时间;禁止踢会长自身,需先转移会长。
|
||||
- 服务接口(`internal/service/guild/guild.go`)
|
||||
- 新增:`ListApplications/ApproveApplication/RejectApplication/KickMember` 方法。
|
||||
- 控制器与路由(Admin)
|
||||
- `GET /api/admin/guilds/:guild_id/applications` 列表(分页、按状态过滤)。
|
||||
- `POST /api/admin/guilds/:guild_id/applications/:id/approve`、`POST .../reject`。
|
||||
- `DELETE /api/admin/guilds/:guild_id/members/:user_id` 踢人。
|
||||
- 接口均校验:`IsSuper==1`(现行规则),并记录操作日志(`log_operation`)。
|
||||
- 控制器与路由(App,可选)
|
||||
- `POST /api/app/guilds/:guild_id/applications` 提交申请;`GET /api/app/guilds/:guild_id/applications/:id` 查询状态。
|
||||
- 查询一致性
|
||||
- 成员列表 `internal/service/guild/members_list.go:9-29` 保持 `status=1` 过滤;无需受待审数据影响。
|
||||
|
||||
## 管理端前端改造
|
||||
- API 封装(`web/admin/src/api/guild.ts`)
|
||||
- 增加 `fetchGetGuildApplications`、`approveGuildApplication`、`rejectGuildApplication`、`kickGuildMember`。
|
||||
- 成员页(`web/admin/src/views/guild/members/index.vue`)
|
||||
- 增加操作列与「踢出」按钮;调用 `kickGuildMember`,成功后刷新当前页。
|
||||
- 新增「申请审核」页
|
||||
- 列表展示:用户ID、申请时间、状态;提供「通过/拒绝」操作;支持按状态筛选与分页。
|
||||
- 路由与菜单
|
||||
- 在 `web/admin/src/router/modules/guild.ts` 增加“工会申请审核”菜单。
|
||||
|
||||
## 权限与审计
|
||||
- 权限:管理端暂按 `IsSuper==1`;后续可拓展为“会长/官员”在 App 端可审与踢人(基于 `GuildMembers.Role` 校验)。
|
||||
- 审计:所有审批与踢人动作写入 `log_operation`,包含 guild_id/user_id/reviewer_id/动作/结果。
|
||||
|
||||
## 验收标准
|
||||
- 加入审核:当 `join_mode=1` 时,提交申请后在管理端可见;可通过/拒绝;通过后出现在成员列表。
|
||||
- 成员剔除:管理端成员页出现「踢出」操作,成功后该成员不再出现在成员列表;禁止踢会长。
|
||||
- 权限:非超管无法访问管理端相关接口;错误提示与状态码规范。
|
||||
- 文档:Swagger 增补新端点与模型;接口参数与响应示例完整。
|
||||
|
||||
## 风险与兼容
|
||||
- 数据迁移:新增 `guild_join_applications` 表(DDL 与 gorm/gen 代码生成);对现有 `guild_members` 无破坏性更改。
|
||||
- 并发与一致性:审批通过与重复申请的幂等处理;用户离开后重新申请的限制延续。
|
||||
- 回滚策略:所有写操作事务化;失败回滚不影响既有功能。
|
||||
|
||||
请确认以上方案,确认后我将分步骤提交后端与前端改造,并补齐测试与文档。
|
||||
92
.trae/documents/微信支付全量真实接入与后台订单_退款_对账方案.md
Normal file
92
.trae/documents/微信支付全量真实接入与后台订单_退款_对账方案.md
Normal file
@ -0,0 +1,92 @@
|
||||
## 目标
|
||||
- 以微信支付 API v3 的真实流程实现:小程序 JSAPI 预下单→前端调起→支付回调验签与解密→订单推进→退款(全额/部分)→账单拉取与对账→管理端完整的订单/退款展示与操作。
|
||||
- 严格按金额单位“分”、幂等与安全规范落地;所有敏感配置走环境变量与证书文件。
|
||||
|
||||
## 架构与分层
|
||||
- Controller:`internal/api/*`(app 用户态、admin 运营态、pay 通知态)
|
||||
- Service:`internal/service/pay/*`(支付域核心:预下单、推进、退款、对账、状态机与幂等)
|
||||
- Repository:`internal/repository/mysql/*`(新增支付域模型与 DAO,扩展索引)
|
||||
- PayClient:`internal/pkg/pay/*`(微信官方 Go SDK client 初始化、回调验签与解密、账单下载)
|
||||
- Infra:配置与证书、日志、告警、幂等键与重试策略。
|
||||
|
||||
## 数据模型(新增表与索引)
|
||||
1) `payment_preorders`
|
||||
- `id` PK、`order_id`、`order_no`(唯一)、`channel`(`wechat_jsapi`)、`prepay_id`、`out_trade_no`(唯一)、`amount_total`(分)、`payer_openid`、`status`(created/expired/paid)、`notify_url`、`created_at`、`expired_at`
|
||||
- 索引:`order_no` 唯一、`out_trade_no` 唯一
|
||||
2) `payment_transactions`
|
||||
- `id` PK、`order_id`、`order_no`(唯一)、`channel`、`transaction_id`(微信流水号唯一)、`amount_total`、`success_time`、`raw`(JSON)、`created_at`
|
||||
- 索引:`transaction_id` 唯一、`order_no` 唯一
|
||||
3) `payment_refunds`
|
||||
- `id` PK、`order_id`、`order_no`、`refund_no`(唯一)、`channel`、`status`(submitted/success/abnormal/closed)、`amount_refund`(分)、`reason`、`success_time`、`raw`(JSON)、`created_at`
|
||||
- 索引:`refund_no` 唯一、`order_no` 索引
|
||||
4) `payment_notify_events`
|
||||
- `id` PK、`notify_id`(唯一)、`resource_type`、`event_type`、`summary`、`raw`(JSON)、`processed`(bool)、`created_at`
|
||||
- 索引:`notify_id` 唯一
|
||||
5) `payment_bills` / `payment_bill_diff`
|
||||
- 账单拉取与差异记录,支持日级对账
|
||||
6) 订单表约束
|
||||
- `orders.order_no` 唯一索引;基于现有字段建立缺失索引
|
||||
|
||||
## 配置与证书
|
||||
- 环境变量:`WECHAT_APPID/WECHAT_MCHID/WECHAT_SERIAL_NO/WECHAT_PRIVATE_KEY_PATH/WECHAT_API_V3_KEY/WECHAT_NOTIFY_URL`
|
||||
- 证书路径:`./configs/cert/apiclient_key.pem`(商户私钥);平台证书走 SDK 自动下载与缓存(`WithWechatPayAutoAuthCipher`),无需入库
|
||||
- 不提交任何密钥与证书文件到仓库
|
||||
|
||||
## SDK与真实流程
|
||||
1) 预下单(JSAPI):
|
||||
- 初始化 client:`option.WithWechatPayAutoAuthCipher(mchid, serial_no, private_key, api_v3_key)`
|
||||
- 调用 `services/payments/jsapi.JsapiApiService.Prepay`(或 `PrepayWithRequestPayment`)生成 `prepay_id`
|
||||
- 使用四行签名串 RSA-SHA256 构造小程序 `wx.requestPayment` 参数
|
||||
- 落库 `payment_preorders` 与 `out_trade_no=order_no` 幂等
|
||||
2) 支付回调(notify):
|
||||
- 使用 `notify.NewNotifyHandler(api_v3_key, NewSHA256WithRSAVerifier(certificateVisitor))` 验签并解密得到 `payments.Transaction`
|
||||
- 幂等:`notify_id` 去重;如已处理直接 ACK
|
||||
- 状态推进:`WHERE status=1` 条件更新订单为已支付,写入 `payment_transactions`,记录交易时间与原始报文
|
||||
- 失败场景兜底:按 `out_trade_no` 定时查询交易状态
|
||||
3) 退款(真实):
|
||||
- 管理端创建退款:生成 `refund_no` 并调用 SDK `services/refunddomestic`(具体路径以官方包为准),成功回写 `payment_refunds` 与回调推进;支持部分/全额退款
|
||||
- 数据还原:积分恢复按策略记录 `user_points_ledger(action=refund_restore)`;订单全额退款置 `status=4`,部分退款保持 `status=2` 并维护累计退款
|
||||
- 幂等:`refund_no` 唯一;重复请求返回已有结果
|
||||
4) 对账:
|
||||
- 下载交易/退款账单 `GET /v3/bill`;入库 `payment_bills`;比对本地 `payment_transactions/payment_refunds` 生成 `payment_bill_diff`;支持导出与修复流程
|
||||
|
||||
## 后端接口(最终版)
|
||||
- App:
|
||||
- `POST /api/app/pay/wechat/jsapi/preorder`(真实预下单,返回调起参数)
|
||||
- `GET /api/app/orders/:order_no/payment`(查询支付状态)
|
||||
- Pay通知:
|
||||
- `POST /api/pay/wechat/notify`(真实验签与解密,推进订单与交易)
|
||||
- Admin:
|
||||
- 订单列表/详情/备注/取消/履约(已实现基础版,依据真实交易补充支付字段)
|
||||
- `POST /api/admin/pay/refunds`(真实退款)/查询退款列表与详情
|
||||
- 导出与对账入口:`GET /api/admin/pay/orders/export`、`POST /api/admin/pay/bills/import`
|
||||
|
||||
## 前端管理端(orders模块)
|
||||
- 列表与详情中文枚举显示(已完成基础);补充:展示真实 `transaction_id`、`refund_no`、`channel`、`支付方式`
|
||||
- 退款入口:支持部分退款(金额/原因),显示可退余额;展示退款记录(来源 `payment_refunds`)
|
||||
- 对账入口:载入对账结果与差异列表
|
||||
|
||||
## 幂等与安全
|
||||
- 业务幂等键:`order_no`(预下单/订单推进)、`notify_id`(回调)、`refund_no`(退款)
|
||||
- 事务:Service 层统一事务包裹;状态推进以条件更新控制并发
|
||||
- 日志与告警:回调验签失败、金额不一致、退款异常、对账差异均告警
|
||||
|
||||
## 测试与验收
|
||||
- 单元:签名参数构造、金额边界、幂等推进、退款全额/部分、回调解析
|
||||
- 集成:模拟回调事件、订单查询兜底、账单导入与差异比对
|
||||
- 验收标准:
|
||||
- 小程序支付全链路通;订单与交易记录准确;
|
||||
- 管理端退款成功并正确落账与还原;
|
||||
- 对账拉取与差异比对可用;
|
||||
- 漏斗“支付用户/完成订单”口径对齐真实交易。
|
||||
|
||||
## 实施阶段
|
||||
1) 数据库与DAO:新增 `payment_*` 表与索引;生成模型与DAO
|
||||
2) SDK接入:统一初始化 client;预下单返回参数;notify 验签与解密;退款API封装
|
||||
3) 业务实现:订单推进、幂等、退款落账与数据还原
|
||||
4) 前端完善:展示交易/退款字段、可退余额、退款记录;对账入口
|
||||
5) 测试与联调:完成单元与集成测试;灰度上线观测
|
||||
|
||||
## 依赖
|
||||
- 官方 Go SDK:`github.com/wechatpay-apiv3/wechatpay-go`(API v3)
|
||||
- 证书:商户私钥 PEM;平台证书自动下载与缓存
|
||||
28
.trae/documents/排查支付_退款入库异常与对账数据缺失.md
Normal file
28
.trae/documents/排查支付_退款入库异常与对账数据缺失.md
Normal file
@ -0,0 +1,28 @@
|
||||
## 你的现象与原因归纳
|
||||
- 你只看到 `payment_preorders`、`payment_notify_events` 有数据,`payment_refunds` 等为空:
|
||||
- 支付回调写交易记录的地方使用了“静默写入”,失败不会报错:`internal/api/pay/wechat_notify.go:92`(`_ = ...Create(tx)`),导致有回调事件但可能没有交易入库。
|
||||
- 退款创建同样采用“静默写入”:`internal/api/admin/pay_refund_admin.go:79`,即使插入失败,接口仍返回“success”,所以你在前端看到提示,但表里没数据。
|
||||
- 对账管理的数据依赖“导入账单”后才生成差异,未导入则 `payment_bills`、`payment_bill_diff` 为空,属正常(`internal/api/admin/pay_reconcile_admin.go:37-75,154-182`)。
|
||||
|
||||
## 验证与定位步骤
|
||||
1. 查询订单详情聚合的退款:`GET admin/pay/orders/:order_no`(`internal/router/router.go:175`,实现 `internal/api/admin/pay_orders_admin.go:216-226`),确认 `refunds` 列表与 `refundable_amount` 是否合理。
|
||||
2. 查询全局退款列表:`GET admin/pay/refunds?page=1&size=20`(`internal/router/router.go:170`,实现 `internal/api/admin/pay_refund_admin.go:154-181`)。
|
||||
3. 验证 DB 表存在与权限:确认 `payment_refunds`、`payment_transactions` 表存在且可写;如果插入失败,当前代码不会抛错,需要看服务日志是否有“私钥/证书、权限、字段类型”等异常。
|
||||
4. 核验微信支付配置与回调:`configs/fat_configs.toml:34-41` 的 `wechatpay` 配置、回调地址与路由匹配 `internal/router/router.go:252`。
|
||||
|
||||
## 修复方案(代码层)
|
||||
- 将静默写库改为显式错误处理并日志:
|
||||
- 支付交易入库:`internal/api/pay/wechat_notify.go:92` 改为检查返回错误,失败则记录日志并返回错误,避免“有事件无交易”。
|
||||
- 退款入库:`internal/api/admin/pay_refund_admin.go:79` 改为显式处理插入错误;同时在后续积分流水与订单状态更新前确保退款记录已成功写入。
|
||||
- 接口返回语义修正:退款接口在任何一步失败时返回错误,不再统一 `success`,确保前端提示与真实入库一致。
|
||||
|
||||
## 对账管理的正确使用
|
||||
- 在“对账管理”导入每日账单(交易/退款)后再查看差异:前端入口 `web/admin/src/views/reconcile/diff/index.vue:45-76`;后端导入与差异查询分别为 `POST admin/pay/bills/import`、`GET admin/pay/bills/diff`。
|
||||
|
||||
## 交付与测试计划
|
||||
- 修改错误处理后,执行一次完整流程:
|
||||
- 发起支付→收到回调→检查 `payment_transactions` 有记录。
|
||||
- 在订单详情发起退款→检查 `payment_refunds` 有记录,订单状态与积分流水一致。
|
||||
- 导入当天账单→检查 `payment_bill_diff` 能产生差异数据(如金额不一致、缺失条目)。
|
||||
|
||||
我将按以上步骤先进行只读验证(接口与日志路径),随后按修复方案更新入库错误处理并回归测试,确保你在“退款管理”与“对账管理”都能看到数据。
|
||||
44
.trae/documents/支付体系一致性整改计划.md
Normal file
44
.trae/documents/支付体系一致性整改计划.md
Normal file
@ -0,0 +1,44 @@
|
||||
## 审计结论摘要
|
||||
- 金额统一“分”,积分统一“元等值整数(1元=1积分)”;现有退款登记复用积分流水记录“金额/100”,语义混用。
|
||||
- 退款恢复积分在多次部分退款时可能重复恢复导致超恢复;预下单未持久化;前端金额展示直用“分”,体验不佳;订单积分抵扣落账缺失。
|
||||
|
||||
## 整改目标
|
||||
- 明确并统一金额/积分口径:分↔积分换算唯一、无混用。
|
||||
- 完善退款与积分恢复逻辑:部分退款按比例恢复、全额退款一次性恢复;幂等保护。
|
||||
- 持久化支付域关键数据:预下单/交易/退款/通知;订单详情展示真实字段。
|
||||
- 前端金额展示统一为“元”且格式化,枚举与文案一致。
|
||||
|
||||
## 具体改造项
|
||||
1) 数据结构与落账
|
||||
- 新增“货币退款流水”独立记录(或新增表`payment_refunds`并在订单详情聚合展示),将每笔退款金额以“分”记录;保留`user_points_ledger`仅记录“积分”类动作(如`refund_restore`)。
|
||||
- 在支付阶段补充“订单积分扣减流水”(action=`order_deduct`),并回填`orders.points_ledger_id`,与实际抵扣金额对应(分→积分换算)。
|
||||
|
||||
2) 退款恢复与幂等
|
||||
- 部分退款按比例恢复积分:`恢复积分 = 订单积分抵扣(分) × 本次退款金额(分) / 实付金额(分) ÷ 100`(四舍五入),累计不超过订单抵扣积分总额。
|
||||
- 全额退款一次性恢复剩余积分;为`refund_restore`增加去重幂等:唯一键`(order_no, action='refund_restore')`或累计校验。
|
||||
- 订单置“已退款”的条件与部分退款展示:保持`2已支付`,在详情中清晰展示“累计已退款金额(分)”和每笔明细。
|
||||
|
||||
3) 预下单与交易持久化
|
||||
- 引入并使用`payment_preorders/payment_transactions/payment_refunds/payment_notify_events`表:
|
||||
- 预下单:落库`out_trade_no=order_no`与`prepay_id`,回填`orders.pay_preorder_id`。
|
||||
- 交易:回调落库`transaction_id/amount/success_time/raw`,订单推进。
|
||||
- 退款:真实调用后落库`refund_no/amount/status/success_time/raw`,并通过回调/查询推进状态。
|
||||
- 通知事件:记录`notify_id/event_type/raw/processed`作审计与去重。
|
||||
|
||||
4) 前端展示统一
|
||||
- 列表与详情金额展示统一格式化为“元”(保留两位小数),并保留原始分用于导出或接口。
|
||||
- 详情页展示:交易号`transaction_id`、退款号`refund_no`、渠道/支付方式;“可退余额(分/元)”与“累计已退款(分/元)”。
|
||||
|
||||
5) 安全与幂等
|
||||
- 业务幂等键:`order_no`(预下单/订单推进)、`notify_id`(回调)、`refund_no`(退款)。
|
||||
- 数据库唯一索引:`orders.order_no`、`payment_preorders.out_trade_no`、`payment_transactions.transaction_id`、`payment_refunds.refund_no`、`payment_notify_events.notify_id`。
|
||||
|
||||
## 验收与测试
|
||||
- 单元:分↔积分换算正确;部分/全额退款恢复积分计算;幂等重复请求不重复落账。
|
||||
- 集成:真实预下单→回调验证→管理端主动退款→退款落库与恢复积分→订单详情展示一致;账单对账拉取与差异比对基础验证。
|
||||
|
||||
## 执行顺序
|
||||
1. 数据表与DAO:新增支付域表与唯一索引;迁移脚本。
|
||||
2. 业务与服务:预下单持久化、回调入库、退款入库与恢复积分比例算法、幂等保护。
|
||||
3. 前端统一金额展示与详情字段扩展;导出口径对齐。
|
||||
4. 测试与联调:场景覆盖与灰度验证,文案与枚举一致。
|
||||
19
.trae/documents/支付体系最终完善—执行清单.md
Normal file
19
.trae/documents/支付体系最终完善—执行清单.md
Normal file
@ -0,0 +1,19 @@
|
||||
## 后端
|
||||
- 通知事件:入库 payment_notify_events(notify_id 唯一、processed 标记)、重复回调直接 ACK
|
||||
- 积分恢复:部分退款按比例恢复(累计不超订单抵扣积分)、全额退款一次性恢复;幂等累计校验
|
||||
- 退款查询:GET /api/admin/pay/refunds(分页筛选)、GET /api/admin/pay/refunds/:refund_no(详情)
|
||||
- 对账接口:POST /api/admin/pay/bills/import(入库账单)、GET /api/admin/pay/bills/diffs(差异查询/导出)
|
||||
- 详情聚合:补充 transaction_id/success_time 与 payment_refunds 明细,计算累计已退与可退余额
|
||||
|
||||
## 前端
|
||||
- 金额统一元格式化(两位小数);保留分供接口/导出
|
||||
- 详情展示:transaction_id/refund_no/channel/支付方式、累计已退与可退余额(元)
|
||||
- 对账入口:账单导入与差异列表页面
|
||||
|
||||
## 幂等与安全
|
||||
- 幂等键与唯一索引核查:order_no/out_trade_no/notify_id/refund_no/transaction_id
|
||||
- 条件更新控制并发;异常与差异告警
|
||||
|
||||
## 验收
|
||||
- 单元/集成:换算与比例恢复、幂等、预下单→通知→退款→详情一致、对账导入与差异生成
|
||||
- 前后端构建通过,联调验证无误
|
||||
31
.trae/documents/支付域持久化与服务对接执行方案.md
Normal file
31
.trae/documents/支付域持久化与服务对接执行方案.md
Normal file
@ -0,0 +1,31 @@
|
||||
## 目标
|
||||
- 连接已生成的 payment_* 表与 Model/DAO,完成真实微信支付体系:预下单→通知→退款→对账;统一金额/积分;管理端与前端展示一致。
|
||||
|
||||
## 后端改造
|
||||
- 预下单(JSAPI):
|
||||
- 在 `internal/api/user/pay_wechat_app.go` 调用 jsapi 预下单成功后,写入 `payment_preorders(order_id, order_no, out_trade_no=order_no, prepay_id, amount_total, payer_openid, notify_url, status, expired_at)` 并回填 `orders.pay_preorder_id`。
|
||||
- 通知验签与解密:
|
||||
- 在 `internal/api/pay/wechat_notify.go` 验签解密后,写 `payment_notify_events(notify_id, event_type, raw, processed=false)` 并去重;写 `payment_transactions(order_id, order_no, transaction_id, amount_total, success_time, raw)`;条件更新 `orders.status:1→2`,写 `paid_at`。
|
||||
- 后台主动退款:
|
||||
- 在 `internal/api/admin/pay_refund_admin.go` 调用微信退款后,写入 `payment_refunds(order_id, order_no, refund_no, amount_refund, status, success_time, raw, reason)`;
|
||||
- 按比例恢复积分:`恢复积分 = (订单积分抵扣分 × 退款分 ÷ 实付分) ÷ 100`,累计不超过订单抵扣积分;全额退款一次性恢复剩余;全额时置 `orders.status=4`;部分退款保留 `2已支付`。
|
||||
- 对账:
|
||||
- 新增服务在每日任务中拉取 `tradebill/refundbill` 入库 `payment_bills`;对比 `payment_transactions/payment_refunds` 生成 `payment_bill_diff`;导出差异。
|
||||
|
||||
## 管理端接口
|
||||
- 订单详情聚合:从 `payment_transactions/payment_refunds` 汇总展示 `transaction_id/success_time/amount_total` 与 `refund_no/amount_refund/status/success_time/reason`;计算可退余额 `actual_amount - 累计退款`。
|
||||
- 退款列表/详情:`GET /api/admin/pay/refunds`、`GET /api/admin/pay/refunds/:refund_no`。
|
||||
- 对账入口:`POST /api/admin/pay/bills/import`、`GET /api/admin/pay/bills/diffs`。
|
||||
|
||||
## 前端改造
|
||||
- 金额统一以“元”格式化展示(两位小数),接口仍用“分”;
|
||||
- 订单详情增加真实字段(交易号、退款号、渠道/支付方式),展示累计已退与可退余额(元);退款记录按元显示。
|
||||
|
||||
## 幂等与安全
|
||||
- 使用唯一索引与幂等键:`order_no/notify_id/refund_no/transaction_id/out_trade_no`;
|
||||
- 条件更新控制并发;异常与差异告警。
|
||||
|
||||
## 验收与测试
|
||||
- 单元:分↔积分换算、比例恢复、幂等重复;
|
||||
- 集成:预下单→通知→主动退款→详情一致;
|
||||
- 对账:拉取账单并生成差异;前后端构建与联调通过。
|
||||
52
.trae/documents/支付域持久化与退款幂等改造实施方案.md
Normal file
52
.trae/documents/支付域持久化与退款幂等改造实施方案.md
Normal file
@ -0,0 +1,52 @@
|
||||
## 目标
|
||||
- 按真实微信支付流程完善一个完整的支付体系:持久化预下单/交易/退款/通知/对账,统一金额/积分口径,优化退款恢复与幂等,前后端一致展示。
|
||||
|
||||
## 数据库与模型
|
||||
- 新增表(均使用“分”为金额单位):
|
||||
- payment_preorders(id, order_id, order_no[UNIQUE], channel, prepay_id, out_trade_no[UNIQUE], amount_total, payer_openid, status(created/expired/paid), notify_url, created_at, expired_at)
|
||||
- payment_transactions(id, order_id, order_no[UNIQUE], channel, transaction_id[UNIQUE], amount_total, success_time, raw(JSON), created_at)
|
||||
- payment_refunds(id, order_id, order_no, refund_no[UNIQUE], channel, status(submitted/success/abnormal/closed), amount_refund, reason, success_time, raw(JSON), created_at)
|
||||
- payment_notify_events(id, notify_id[UNIQUE], resource_type, event_type, summary, raw(JSON), processed(bool), created_at)
|
||||
- payment_bills(id, bill_date, type(tradebill/refundbill), file_url, digest, imported(bool), created_at)
|
||||
- payment_bill_diff(id, bill_date, diff_type(missing/amount_mismatch/extra), local_tx_id, wechat_tx_id, detail, created_at)
|
||||
- 订单唯一索引:orders.order_no[UNIQUE]
|
||||
- 迁移:生成/应用 GORM 迁移脚本;保持向后兼容(不破坏现有数据)。
|
||||
|
||||
## Repository/DAO
|
||||
- 生成 gorm/gen DAO 与 model 文件;在 internal/repository/mysql/dao, model 下新增 payment_* 的 gen 文件。
|
||||
|
||||
## Service 逻辑
|
||||
- 预下单:
|
||||
- 校验订单 status=1、金额与归属;调用 jsapi.Prepay,落库 payment_preorders(out_trade_no=order_no, prepay_id),回填 orders.pay_preorder_id;返回调起参数(RSA-SHA256 四行签名)。
|
||||
- 支付通知:
|
||||
- 验签解密 payments.Transaction;幂等(notify_id 去重)入库 payment_notify_events;写入 payment_transactions(transaction_id/raw/success_time);orders 条件推进 status:1→2,写 paid_at。
|
||||
- 退款:
|
||||
- 管理端主动退款:生成 refund_no 并调用 refunddomestic.Create,入库 payment_refunds(refund_no/amount/status/raw);
|
||||
- 积分恢复(统一口径):
|
||||
- 部分退款按比例恢复:恢复积分 = (订单积分抵扣分 × 退款分 ÷ 实付分) ÷ 100,累计不超过订单抵扣积分;全额退款一次性恢复剩余积分;幂等校验(累计已恢复积分 vs 目标)。
|
||||
- 订单状态:全额退款置 4已退款;部分退款维持 2已支付,并维护累计已退款金额(聚合 payment_refunds)。
|
||||
- 对账:
|
||||
- 每日下载交易/退款账单(依 SDK);入库 payment_bills;比对 payment_transactions/payment_refunds 生成 payment_bill_diff;提供导出与差异修复入口。
|
||||
|
||||
## Admin 接口
|
||||
- 订单详情:增加展示字段 transaction_id、累计已退款、每笔退款(refund_no/amount/status/时间/原因);可退余额=实付-累计退款。
|
||||
- 退款列表与详情:GET /api/admin/pay/refunds, GET /api/admin/pay/refunds/:refund_no;
|
||||
- 对账:POST /api/admin/pay/bills/import(上传账单并入库);GET /api/admin/pay/bills/diffs(差异列表)。
|
||||
|
||||
## 前端
|
||||
- 金额统一格式化为“元”展示(两位小数),内部接口仍用“分”;
|
||||
- 订单详情页增加 transaction_id/refund_no/channel/支付方式;展示累计已退款与可退余额(元);
|
||||
- 退款记录表展示 refund_no/金额(元)/原因/时间;
|
||||
- 保持中文枚举一致(状态/来源/发货/抽奖)。
|
||||
|
||||
## 幂等与安全
|
||||
- 幂等键:order_no(预下单/推进)、notify_id(回调)、refund_no(退款);
|
||||
- 唯一索引:预下单/交易/退款/通知表关键字段;
|
||||
- 条件更新:WHERE status=... 控制并发;
|
||||
- 日志与告警:验签失败、金额不一致、退款异常、对账差异。
|
||||
|
||||
## 测试与验收
|
||||
- 单元:分↔积分换算、部分/全额退款恢复、幂等重复;
|
||||
- 集成:真实预下单→回调→主动退款→详情一致;
|
||||
- 对账:拉取账单并生成差异;
|
||||
- 验收:管理端全链路可用、金额与积分口径一致、数据持久化完整。
|
||||
22
.trae/documents/澄清与验证「退款管理」与「对账管理」的数据可见性.md
Normal file
22
.trae/documents/澄清与验证「退款管理」与「对账管理」的数据可见性.md
Normal file
@ -0,0 +1,22 @@
|
||||
## 用途说明
|
||||
- 退款管理:全局退款列表与详情,用于按条件检索所有订单的退款记录。前端 `web/admin/src/views/refunds/list/index.vue:21-31` 通过 `fetchAdminRefunds` 请求后端分页列表 `GET admin/pay/refunds`(`internal/router/router.go:169-171`;实现 `internal/api/admin/pay_refund_admin.go:154-181`)。
|
||||
- 对账管理:导入微信日账单后,对本地交易/退款与微信账单进行比对并生成差异明细。入口 `web/admin/src/views/reconcile/diff/index.vue:45-76`;后端 `POST admin/pay/bills/import` 与 `GET admin/pay/bills/diff`(`internal/router/router.go:172-173`;实现 `internal/api/admin/pay_reconcile_admin.go:37-75,154-182`)。
|
||||
|
||||
## 为什么你现在看到的现象成立
|
||||
- 订单管理能看到退款:订单详情直接按该订单号聚合退款记录 `internal/api/admin/pay_orders_admin.go:216-226`,来源同一表 `payment_refunds`。
|
||||
- 退款管理为空:通常由筛选条件或分页导致。该页面有 `order_no/status` 两个筛选项并在挂载时执行一次查询 `web/admin/src/views/refunds/list/index.vue:43-55`;若筛选为非空但不匹配当前退款,列表会显示为空。
|
||||
- 对账管理为空:只有在执行“导入账单”后才会计算并产生差异;未导入则 `payment_bill_diff` 自然为空(计算逻辑 `internal/api/admin/pay_reconcile_admin.go:77-107,109-138`)。
|
||||
|
||||
## 快速验证步骤
|
||||
1. 在“退款管理”清空筛选并点击“刷新”,或直接调用 `GET admin/pay/refunds`(默认分页 20 条)确认返回是否包含最近的退款;该接口由认证组保护,需管理员登录(`internal/router/router.go:169-171`)。
|
||||
2. 如仍为空,检查是否写入了 `payment_refunds`:创建退款的写库在 `internal/api/admin/pay_refund_admin.go:70-80`,列表读取在 `154-181`。
|
||||
3. 在“对账管理”点击“导入账单”:选择“交易账单”或“退款账单”,粘贴当日微信账单的 JSON(页面模型 `web/admin/src/views/reconcile/diff/index.vue:45-76`)。导入后查询 `GET admin/pay/bills/diff` 是否出现差异。
|
||||
|
||||
## 示例导入 JSON(退款账单)
|
||||
- 格式示例:`[{"refund_no":"R202411010001","out_trade_no":"O202411010001","amount_refund":100}]`
|
||||
- 交易账单示例:`[{"transaction_id":"4200001xxx","out_trade_no":"O202411010001","amount_total":100}]`
|
||||
|
||||
## 接下来我将执行
|
||||
- 复核你当前管理端的筛选条件、分页与管理员登录态是否正常。
|
||||
- 指导你导入一份当日测试账单并确认差异计算是否可见。
|
||||
- 如需要,再定位具体退款记录的可见性(按 `order_no/status` 组合查询)。
|
||||
24
.trae/documents/玩家管理列表布局修复.md
Normal file
24
.trae/documents/玩家管理列表布局修复.md
Normal file
@ -0,0 +1,24 @@
|
||||
## 问题定位
|
||||
- 操作列使用 `class="flex gap-2"` 包裹多个按钮(6个),但项目未引入 Tailwind,该类无效,导致按钮按块级元素竖排堆叠,视觉成“蓝色小方块纵向排布”。
|
||||
- 位置:`web/admin/src/views/player-manage/index.vue:202-234`
|
||||
- 其它页普遍使用 `useSlot` 或 `ElSpace` 渲染操作列,避免换行与拥挤(如:`guild/manage/index.vue:185`、`operations/*`)。
|
||||
|
||||
## 修复方案
|
||||
- 将操作列容器从 `h('div', { class: 'flex gap-2' }, [...])` 改为 `h(ElSpace, { wrap: true, size: 6 }, [...])`,保证横向排列且自动换行。
|
||||
- 适当增大操作列宽度,避免拥挤(由 280 调整为 320)。
|
||||
- 如需更一致的实现,后续可将操作列改为 `useSlot: true` + `<template #operation>` 模板渲染,统一风格(本次先用 `ElSpace` 修复)。
|
||||
|
||||
## 具体修改点
|
||||
- 文件:`web/admin/src/views/player-manage/index.vue`
|
||||
- 修改 `columnsFactory()` 中 `operation` 列的 `formatter` 容器为 `ElSpace`(`wrap: true`,`size: 6`)。
|
||||
- 将 `operation` 列 `width` 调整为 `320`。
|
||||
|
||||
## 验收标准
|
||||
- 操作列按钮横向排列,换行时保持等距,不再竖排堆叠。
|
||||
- 表头、分页与表格不发生位移,整页无水平滚动条。
|
||||
- 批量操作按钮(表头左侧)不受影响,交互正常。
|
||||
|
||||
## 影响范围
|
||||
- 仅影响玩家管理页视图渲染;不改动接口、不影响其它模块。
|
||||
|
||||
确认后我将执行上述修改并重新构建前端,提供预览截图/链接以供验收。
|
||||
34
.trae/documents/管理活动页面的表格与操作栏一致性优化计划.md
Normal file
34
.trae/documents/管理活动页面的表格与操作栏一致性优化计划.md
Normal file
@ -0,0 +1,34 @@
|
||||
## 现状审计
|
||||
- 高级表格使用:
|
||||
- 活动管理:`web/admin/src/views/activity/manage/index.vue:10-17` 使用 `ArtTable`(高级表格)
|
||||
- 期数管理:`web/admin/src/views/activity/issues/index.vue:6-14` 使用 `ArtTable`
|
||||
- 奖励管理:`web/admin/src/views/activity/rewards/index.vue:31-37` 使用 `ArtTable`
|
||||
- 操作栏一致性:
|
||||
- 活动管理中“模拟”按钮使用 `ElButton`(`manage/index.vue:32-39`),其余使用 `ArtButtonTable`;风格不一致
|
||||
- 期数管理操作栏统一使用 `ArtButtonTable`(`issues/index.vue:21-33`)
|
||||
- 表格可视宽度:多列设置了固定 `width`(如 `manage/index.vue:291-297`;`issues/index.vue:83-88`),`name` 列使用 `showOverflowTooltip`(`manage/index.vue:292`),导致内容显示受限;未显式启用 `tableLayout="auto"`
|
||||
|
||||
## 优化目标
|
||||
1. 全部使用高级表格组件,启用自适应布局以显示完整内容
|
||||
2. 操作栏统一为图标标签按钮(`ArtButtonTable`),视觉与交互一致
|
||||
3. 表格宽度与列长度自适应:移除过度固定宽度,采用 `minWidth` 与 `tableLayout="auto"`,仅对数值列设定最小宽度;必要时保留溢出提示
|
||||
|
||||
## 具体改动
|
||||
- 活动管理(`web/admin/src/views/activity/manage/index.vue`)
|
||||
- 在 `ArtTable` 上增加 `tableLayout="auto"`
|
||||
- 将 `columnsFactory` 中固定 `width` 改为 `minWidth`(如 `name` 列 `minWidth: 260`,`categoryName` `minWidth: 140`,`actions` `minWidth: 240`),取消 `showOverflowTooltip` 或仅对极长文本保留
|
||||
- 将“模拟抽奖”按钮统一为 `ArtButtonTable`,图标 `ri:shuffle-line`,与现有按钮风格一致(替换 `ElButton`,位置 `index.vue:32-39`)
|
||||
- 期数管理(`web/admin/src/views/activity/issues/index.vue`)
|
||||
- 在 `ArtTable` 上增加 `tableLayout="auto"`
|
||||
- 将列宽改为 `minWidth`,例如:`issue_number` `minWidth: 160`、`status` `minWidth: 110`、`actions` `minWidth: 200`
|
||||
- 已统一使用 `ArtButtonTable`,无需额外调整
|
||||
- 奖励管理(`web/admin/src/views/activity/rewards/index.vue`)
|
||||
- 已是 `ArtTable` 并启用 `tableLayout="auto"`;保留前次优化的汇总与列增强(剩余/总量、期望概率)
|
||||
|
||||
## 验收标准
|
||||
- 三个页面均使用 `ArtTable`,内容在常见分辨率下完整显示(无不必要截断)
|
||||
- 操作栏按钮风格一致,皆为图标标签按钮,间距统一
|
||||
- 表格宽度随容器自适应,长文本列通过 `minWidth` 与布局算法得到更好的展示;必要时保留 tooltip
|
||||
|
||||
## 风险与回滚
|
||||
- 改动为前端表现层,低风险;如需回滚,可恢复原 `width` 配置与旧按钮类型。
|
||||
39
.trae/documents/落地支付域代码生成与迁移执行计划.md
Normal file
39
.trae/documents/落地支付域代码生成与迁移执行计划.md
Normal file
@ -0,0 +1,39 @@
|
||||
## 实施范围
|
||||
- 对接已生成的 payment_* 表与 Model/DAO,落地真实支付服务逻辑与管理端展示
|
||||
|
||||
## 后端服务对接
|
||||
- 预下单(JSAPI)
|
||||
- 写入 `payment_preorders`:`order_id/order_no/out_trade_no=order_no/prepay_id/amount_total/payer_openid/notify_url/status=created/expired_at`
|
||||
- 回填 `orders.pay_preorder_id`;返回四行签名参数给小程序
|
||||
- 支付通知(验签与解密)
|
||||
- 入库 `payment_notify_events`(`notify_id/event_type/raw/processed=false`,幂等去重)
|
||||
- 入库 `payment_transactions`:`order_id/order_no/transaction_id/amount_total/success_time/raw`
|
||||
- 条件更新 `orders.status:1→2`,写 `paid_at`
|
||||
- 后台主动退款(全额/部分)
|
||||
- 生成 `refund_no` 并调用微信退款 API,入库 `payment_refunds`:`order_id/order_no/refund_no/amount_refund/status/success_time/raw/reason`
|
||||
- 积分恢复(统一换算与幂等):
|
||||
- 部分退款:`恢复积分 = (订单积分抵扣分 × 退款分 ÷ 实付分) ÷ 100`,累计不超过订单抵扣积分;幂等以“累计已恢复”校验
|
||||
- 全额退款:一次性恢复剩余积分,置 `orders.status=4`
|
||||
- 对账基础能力
|
||||
- 拉取交易/退款账单入库 `payment_bills`;比对生成 `payment_bill_diff`;导出差异
|
||||
|
||||
## 管理端接口
|
||||
- 订单详情聚合字段:
|
||||
- 交易:`transaction_id/success_time/amount_total`
|
||||
- 退款:累计已退与明细 `refund_no/amount_refund/status/success_time/reason`
|
||||
- 可退余额(分):`actual_amount - 累计退款`
|
||||
- 退款列表与详情:`GET /api/admin/pay/refunds`、`GET /api/admin/pay/refunds/:refund_no`
|
||||
- 对账入口:`POST /api/admin/pay/bills/import`、`GET /api/admin/pay/bills/diffs`
|
||||
|
||||
## 前端展示改造
|
||||
- 金额统一元格式化显示(两位小数),接口仍用分;
|
||||
- 详情页增加展示 `transaction_id/refund_no/channel/支付方式`、累计已退与可退余额(元);退款记录按元显示
|
||||
|
||||
## 幂等与安全
|
||||
- 幂等键:`order_no/notify_id/refund_no`;唯一索引已在表层
|
||||
- 条件更新控制并发;日志与告警覆盖验签失败、金额不一致、退款异常、对账差异
|
||||
|
||||
## 验收与测试
|
||||
- 单元:分↔积分换算、部分/全额退款恢复、幂等重复
|
||||
- 集成:真实预下单→通知→主动退款→详情对齐;对账拉取与差异
|
||||
- 构建与联调通过,数据持久化与展示一致
|
||||
26
.trae/documents/落地支付域持久化与退款幂等(执行计划).md
Normal file
26
.trae/documents/落地支付域持久化与退款幂等(执行计划).md
Normal file
@ -0,0 +1,26 @@
|
||||
## 实施范围
|
||||
- 支付域持久化:预下单/交易/退款/通知/账单与差异表
|
||||
- 退款恢复比例与幂等:统一分↔积分换算,部分/全额退款逻辑
|
||||
- 订单详情与前端金额展示:元格式化与真实字段展示
|
||||
|
||||
## 后端改动
|
||||
- 建表与索引:`payment_preorders/payment_transactions/payment_refunds/payment_notify_events/payment_bills/payment_bill_diff`;唯一键:`order_no/out_trade_no/transaction_id/refund_no/notify_id`
|
||||
- DAO与模型生成:在`internal/repository/mysql/dao, model`新增 payment_* gen 文件
|
||||
- 预下单:落库`payment_preorders`并回填`orders.pay_preorder_id`;返回调起参数
|
||||
- 通知:入库`payment_notify_events/payment_transactions`;`orders`从1→2并写`paid_at`
|
||||
- 退款:入库`payment_refunds`;恢复积分=订单积分抵扣分×退款分/实付分÷100(累计不超抵扣积分);全额退款一次性恢复剩余;订单状态与累计已退款更新
|
||||
- 对账:拉取账单入库`payment_bills`,生成`payment_bill_diff`,接口导出差异
|
||||
|
||||
## 前端改动
|
||||
- 金额统一元格式化展示,内部用分;
|
||||
- 详情展示:`transaction_id/refund_no/channel/支付方式`、累计已退与可退余额(元);退款记录表按元显示
|
||||
|
||||
## 幂等与安全
|
||||
- 幂等键:`order_no/notify_id/refund_no`
|
||||
- 条件更新:`WHERE status=...`;回调与退款入库去重
|
||||
- 日志与告警:验签失败、金额不一致、退款异常、对账差异
|
||||
|
||||
## 验收与测试
|
||||
- 单元:换算、比例恢复、幂等
|
||||
- 集成:预下单→回调→主动退款→详情一致;对账拉取与差异
|
||||
- 前后端构建与联调通过
|
||||
38
.trae/documents/补充证书路径接入与通知验签方案.md
Normal file
38
.trae/documents/补充证书路径接入与通知验签方案.md
Normal file
@ -0,0 +1,38 @@
|
||||
# 目标
|
||||
- 使用你已填写的配置,按“config/cert”中的证书路径完成微信支付真实接入:统一下单+小程序签名+通知验签/解密。
|
||||
|
||||
# 当前配置状况
|
||||
- 已存在 `[wechatpay]` 配置段并支持 ENV 覆盖:`mchid/serial_no/private_key_path/api_v3_key/notify_url`(configs/configs.go)。
|
||||
- 证书路径建议:将商户私钥 `apiclient_key.pem` 放在 `./configs/cert/apiclient_key.pem`,并把 `private_key_path` 配置为该相对路径。
|
||||
|
||||
# 具体改造点
|
||||
- 证书路径接入:
|
||||
- 不新增新的配置段,直接使用 `[wechatpay].private_key_path` 指向 `config/cert` 中的私钥路径。
|
||||
- 若你希望本地校验平台证书(而非自动下载),增加可选字段:`wechatpay.platform_cert_path` 和 `wechatpay.platform_public_key_id`(可留空);否则走自动下载。
|
||||
- 统一下单(JSAPI)按已封装客户端调用:
|
||||
- 读取 `private_key_path` → 初始化 `core.Client`(`WithWechatPayAutoAuthCipher`),自动管理平台证书。
|
||||
- `jsapi.Prepay` 返回 `prepay_id`,之后用已有 `BuildJSAPIParams` 生成 `wx.requestPayment` 所需参数。
|
||||
- 支付通知验签与解密:
|
||||
- 使用 SDK 的平台证书下载管理器:在通知处理处懒加载 `downloader.MgrInstance().RegisterDownloaderWithPrivateKey(mchid, serial_no, private_key, api_v3_key)`。
|
||||
- 用 `notify.NewNotifyHandler(api_v3_key, RSAVerifier(certificateVisitor))` 验签;解密 `resource.ciphertext` 得到 `payments.Transaction`;按 `out_trade_no/success_time` 幂等推进订单。
|
||||
- 条件更新:`WHERE status=1` 防并发重复推进;已支付直接 ACK。
|
||||
|
||||
# 文件与位置(只改后端)
|
||||
- 设置路径:在 `configs/*_configs.toml` 的 `[wechatpay]` 写入 `private_key_path = "./configs/cert/apiclient_key.pem"`(你已填写即可)。
|
||||
- 统一下单:`internal/pkg/pay/client.go` 已使用该路径初始化;无需再改。
|
||||
- 通知验签:`internal/api/pay/wechat_notify.go` 切换为 SDK 验签/解密实现(现在是占位版),引入 downloader + notify.Handler。
|
||||
|
||||
# 验证步骤
|
||||
- 编译通过:`go build ./...`
|
||||
- 预下单:调用 `POST /api/app/pay/wechat/jsapi/preorder` 返回小程序参数;`wx.requestPayment` 可正常调起。
|
||||
- 通知:微信推送或用 SDK 模拟请求,验签成功后订单由待支付→已支付,`paid_at` 为交易成功时间;重复通知幂等。
|
||||
|
||||
# 安全与合规
|
||||
- 私钥路径仅用于服务端签名;不要提交证书文件到仓库。
|
||||
- 金额单位统一为分;仅信任服务端订单金额。
|
||||
|
||||
# 我将执行的改动(收到确认后)
|
||||
1. 在各环境 `*_configs.toml` 确认/写入 `wechatpay.private_key_path` 指向 `config/cert`。
|
||||
2. 将通知处理替换为 SDK 验签/解密版本(保留幂等与条件更新)。
|
||||
3. 增加可选本地平台证书配置支持(如你偏好离线证书验证)。
|
||||
4. 自测:预下单→支付→通知推进,重复通知幂等校验。
|
||||
24
.trae/documents/订单详情枚举与退款闭环完善.md
Normal file
24
.trae/documents/订单详情枚举与退款闭环完善.md
Normal file
@ -0,0 +1,24 @@
|
||||
## 目标
|
||||
- 仅支持后台管理主动发起退款(全额/部分)并记录每笔退款金额与原因
|
||||
- 在订单详情展示“退款记录列表”与“可退款金额”,统一中文枚举显示
|
||||
|
||||
## 后端
|
||||
1) 管理端退款记录补充:在 `POST /api/admin/pay/refunds` 现有逻辑基础上,新增一条 `user_points_ledger(action='refund_amount', points=退款金额, ref_table='orders', ref_id=order_no, remark=reason)` 记录每笔退款金额(不新建表)。
|
||||
2) 订单详情聚合退款:在 `GET /api/admin/pay/orders/:order_no` 增加字段:
|
||||
- `refunds[]`:来源于 `user_points_ledger(action='refund_amount')`;包含金额、原因、时间
|
||||
- `refundable_amount`:`actual_amount - sum(refunds.amount)`(分)
|
||||
3) 边界与幂等:
|
||||
- 仅允许 `status=2已支付` 发起退款;金额需 `>0 且 <= refundable_amount`
|
||||
- 全额退款后置 `status=4已退款`;部分退款维持 `status=2`
|
||||
|
||||
## 前端
|
||||
1) 列表与详情使用中文枚举(已完成基础替换)
|
||||
2) 详情页:
|
||||
- 显示“可退款金额(分)”
|
||||
- 新增“退款记录”表格(金额/原因/时间)
|
||||
- 退款按钮支持部分退款;提交后刷新详情与可退余额
|
||||
|
||||
## 验收
|
||||
- 管理端可主动发起退款并记录到“退款记录”
|
||||
- 详情页展示可退余额与每笔退款信息
|
||||
- 编译与联调通过
|
||||
73
.trae/documents/订单详情枚举展示与退款流程完善方案.md
Normal file
73
.trae/documents/订单详情枚举展示与退款流程完善方案.md
Normal file
@ -0,0 +1,73 @@
|
||||
## 目标
|
||||
- 列表与详情页统一显示“用户、状态、来源、发货状态”等中文枚举名称
|
||||
- 在订单详情提供“退款”入口(支持全额/部分退款),并完善数据还原(积分、库存/履约、优惠券)与幂等、校验流程
|
||||
- 支持用户发起退款申请→后台审核→执行退款的闭环
|
||||
|
||||
## 前端改造
|
||||
### 枚举与显示
|
||||
- 新增/复用前端常量映射:
|
||||
- 订单状态:`{1: 待支付, 2: 已支付, 3: 已取消, 4: 已退款}`
|
||||
- 来源类型:`{1: 商城直购, 2: 抽奖票据, 3: 其他}`
|
||||
- 发货状态:`{1: 待发货, 2: 已发货, 3: 已签收, 4: 异常}`
|
||||
- 抽奖结果:`is_winner(0/1)→否/是`、`level(1/2/3)→S/A/B`
|
||||
- 列表与详情使用映射显示中文名;用户信息显示 `nickname`(无则显示 `用户ID`)
|
||||
|
||||
### 退款入口与交互
|
||||
- 详情页添加“退款”按钮:显示可退款金额(默认 `actual_amount`)、支持部分退款(输入退款金额与原因)
|
||||
- 提交后刷新详情;在详情页增加“退款记录”区块,显示每笔退款状态与时间、原因
|
||||
- 禁用策略:
|
||||
- 已取消或已退款不可再次退款
|
||||
- 货已签收或虚拟已履约需走人工审批(按钮灰显或改为“申请退款”)
|
||||
|
||||
## 后端接口与流程
|
||||
### 管理端
|
||||
- 创建退款:`POST /api/admin/pay/refunds`
|
||||
- 入参:`order_no, amount, reason`
|
||||
- 校验:订单存在且`status=2`;`amount>0 && <= 未退款余额`
|
||||
- 幂等:生成`refund_no`唯一;重复请求返回既有状态
|
||||
- 执行:
|
||||
- 微信真实退款:调用`/v3/refund/domestic/refunds`(成功→记录到`payment_refunds`并落账)
|
||||
- 数据还原:
|
||||
- 积分:写 `user_points_ledger(action=refund_restore, points=points_amount)`(按原抵扣金额,部分退款按比例/策略)
|
||||
- 虚拟履约:若`is_consumed=1`且可撤销,标记撤销或新建“撤销记录”;否则保留人工处理标记
|
||||
- 发货:如已发/已签收不自动撤销,标记异常并提示人工流程(退货入库)
|
||||
- 订单状态:
|
||||
- 全额退款→置`status=4已退款`
|
||||
- 部分退款→保持`status=2已支付`,记录累计退款额(在`payment_refunds`中维护)
|
||||
- 查询退款:`GET /api/admin/pay/refunds`、`GET /api/admin/pay/refunds/:refund_no`
|
||||
- 在订单详情接口增加退款列表聚合(展示退款记录)
|
||||
|
||||
### 用户端退款(申请→审核→执行)
|
||||
- 用户申请:`POST /api/app/orders/:order_no/refund/apply`(金额与原因)
|
||||
- 管理端审核:`POST /api/admin/pay/refunds/:refund_no/approve | /reject`
|
||||
- 审核通过→执行退款(同管理端创建退款);拒绝→记录原因
|
||||
|
||||
## 数据模型建议(支付域)
|
||||
- `payment_refunds`:`refund_no`唯一,`order_id/order_no/channel/status(submitted/success/abnormal/closed)`, `amount_refund`, `reason`, `success_time`, `raw`
|
||||
- 在现有`orders`保持`order_no`唯一;在退款、通知增加幂等校验(`refund_no/notify_id`)
|
||||
|
||||
## 校验与边界
|
||||
- 金额单位统一“分”;仅信任后端计算出的可退余额
|
||||
- 订单来源:抽奖类订单(`source_type=2`)需校验抽奖资产状态(`user_inventory`)是否可撤销
|
||||
- 已签收/履约完成:默认不自动撤销,需人工处理或审批流;在系统中标注并可导出
|
||||
|
||||
## 幂等与一致性
|
||||
- 退款:`refund_no`唯一;重复请求返回现有记录
|
||||
- 回调解密与验签:使用 SDK `notify.Handler`;退款结果通过回调或主动查询推进记录状态
|
||||
- 状态推进使用事务与条件更新(如`WHERE status=2`)保证并发安全
|
||||
|
||||
## 前端展示增强(详情)
|
||||
- 信息区块:订单、活动、付款、订单项、发货、积分、退款记录
|
||||
- 显示中文枚举;金额统一格式化(分→元)或保留分展示
|
||||
- 退款弹窗:输入金额/原因;显示可退款上限与提示规则(履约/签收需审批)
|
||||
|
||||
## 验收标准
|
||||
- 列表与详情均显示中文枚举;详情展示活动与付款信息完整
|
||||
- 管理端可创建退款并正确落账(占位或真实接入);用户申请退款可走审批流程
|
||||
- 幂等与边界校验完善;构建与联调通过
|
||||
|
||||
## 实施步骤
|
||||
1. 前端:新增枚举常量并替换列表/详情的显示;详情页加退款按钮与退款记录展示
|
||||
2. 后端:完善退款创建与查询接口、数据还原逻辑与幂等;补充详情接口聚合退款记录
|
||||
3. 用户端:新增退款申请与后台审核接口(可按你需求分阶段)
|
||||
4. 测试与联调:金额/并发/履约/签收边界;通知与查询兜底
|
||||
24
.trae/documents/运营二维码页面美化与后端类型确认计划.md
Normal file
24
.trae/documents/运营二维码页面美化与后端类型确认计划.md
Normal file
@ -0,0 +1,24 @@
|
||||
## 目标
|
||||
- 美化运营管理的“小程序二维码生成”页面,使布局更现代、交互更顺畅。
|
||||
- 明确并保持后端与前端的 `douyin_id` 为字符串类型。
|
||||
|
||||
## 界面优化
|
||||
- 布局:使用卡片+两栏布局,上方表单,下方预览区域居中;自适应在小屏改为单列。
|
||||
- 交互:
|
||||
- 增加宽度滑块 `ElSlider`,实时控制二维码预览大小
|
||||
- 增加“复制链接”按钮,复制小程序路径 `pages/login/index?...`
|
||||
- 表单校验与提示:必填校验,错误时显示提示消息
|
||||
- 保留“下载PNG”,新增“重置”按钮
|
||||
- 视觉:合并间距、统一按钮风格、预览区居中显示
|
||||
|
||||
## 类型确认
|
||||
- 后端 `miniapp_qrcode` 与 `weixin login` 请求体的 `douyin_id` 保持为 `string`,不做任何数字转换
|
||||
- 前端表单的 `douyin_id` 仅用 `string` 类型
|
||||
|
||||
## 实施
|
||||
- 修改 `web/admin/src/views/operations/miniapp-qrcode/index.vue`:加入滑块、复制链接、重置、表单校验与更优布局
|
||||
- 运行构建验证
|
||||
|
||||
## 验收
|
||||
- 表单必填校验有效,二维码预览与大小调整正常,复制链接与下载可用
|
||||
- `douyin_id` 在前后端均为字符串类型
|
||||
45
.trae/documents/运营生成小程序二维码与登录绑定抖音账号的实现计划.md
Normal file
45
.trae/documents/运营生成小程序二维码与登录绑定抖音账号的实现计划.md
Normal file
@ -0,0 +1,45 @@
|
||||
## 目标
|
||||
- 在运营管理新增“小程序二维码生成”,输入:`invite_code`、`douyin_id`,生成跳转到小程序登录页的二维码。
|
||||
- 在小程序登录 API 支持绑定抖音账号,仅绑定 `douyin_id`。
|
||||
|
||||
## 后端改造
|
||||
- 数据模型
|
||||
- 为 `users` 表新增字段:`douyin_id`(string,唯一,可空)。
|
||||
- 迁移脚本添加唯一索引 `idx_users_douyin_id`;`gorm/gen` 重新生成模型。
|
||||
- 生成二维码接口
|
||||
- 新增管理端接口:`POST /api/admin/miniapp/qrcode`
|
||||
- 请求:`{ invite_code: string, douyin_id: string, width?: number }`
|
||||
- 响应:`{ image_base64: string }`
|
||||
- 实现:使用 `internal/pkg/wechat/qrcode.go` 构造 `path`(示例:`/pages/login?invite_code=xxx&douyin_id=yyy`),校验长度 ≤1024。
|
||||
- 小程序登录绑定
|
||||
- 修改 `internal/api/user/login_app.go` 的 `weixinLoginRequest`:新增可选 `douyin_id`。
|
||||
- service 层 `LoginWeixin`:
|
||||
- 当请求含 `douyin_id` 且当前用户未绑定时,写入 `douyin_id`。
|
||||
- 若 `douyin_id` 已被其他用户绑定,返回冲突错误码;不覆盖已有绑定。
|
||||
|
||||
## 前端改造(运营管理)
|
||||
- 新增页面:`views/operations/miniapp-qrcode/index.vue`
|
||||
- 表单:`invite_code`、`douyin_id`、`width`(默认 430)
|
||||
- 调用 `admin/miniapp/qrcode`,预览与下载二维码。
|
||||
- 小程序前端:登录页读取 query,将 `invite_code` 与 `douyin_id` 随 `code` 一并调用 `POST /api/app/users/weixin/login`。
|
||||
|
||||
## 路由与校验
|
||||
- 路由:新增 admin 路由于 `internal/router/router.go` 管理端认证组。
|
||||
- 输入校验:
|
||||
- `invite_code`:非空、长度/字符集校验
|
||||
- `douyin_id`:长度 1–50,字符集限制(字母数字下划线)
|
||||
- 唯一绑定策略:`douyin_id` 全局唯一;冲突返回明确错误码与提示。
|
||||
|
||||
## 配置
|
||||
- 使用现有 `configs.Wechat.AppID/AppSecret`;复用 `internal/pkg/wechat/qrcode.go`。
|
||||
|
||||
## 验收
|
||||
- 运营页生成二维码成功;扫码进入并完成登录绑定,用户记录含 `douyin_id`。
|
||||
- 登录接口兼容无 `douyin_id` 场景;携带时正确绑定并处理冲突。
|
||||
|
||||
## 变更清单
|
||||
- 后端:新增 `miniapp_qrcode` 管理端接口;修改登录请求体与 service;数据库迁移与模型生成。
|
||||
- 前端:新增运营二维码页面与 API;路由注册。
|
||||
|
||||
## 风险与回滚
|
||||
- 字段新增不影响旧流程;二维码功能为增量,回滚可移除新路由与页面。
|
||||
BIN
build/.DS_Store
vendored
Normal file
BIN
build/.DS_Store
vendored
Normal file
Binary file not shown.
BIN
build/resources/.DS_Store
vendored
Normal file
BIN
build/resources/.DS_Store
vendored
Normal file
Binary file not shown.
@ -1 +1 @@
|
||||
import{d as a,T as s,e as t,h as e,p as m,J as r,m as l,y as c,z as n,l as d,C as x,E as o,f as i}from"./index-ip5gDR6L.js";/* empty css */import{_ as p}from"./index-BOPvNXEP.js";import{E as u}from"./index-opP5No9I.js";const f={class:"page-content !border-0 !bg-transparent min-h-screen flex-cc"},g={class:"flex-cc max-md:!block max-md:text-center"},b={class:"ml-15 w-75 max-md:mx-auto max-md:mt-10 max-md:w-full max-md:text-center"},h={class:"text-xl leading-7 text-g-600 max-md:text-lg"},_=a({__name:"ArtException",props:{data:{}},setup(a){const _=x(),{homePath:j}=s(),v=()=>{_.push(j.value)};return(s,x)=>{const _=p,j=u,w=o("ripple");return i(),t("div",f,[e("div",g,[m(_,{src:a.data.imgUrl,size:"100%",class:"!w-100"},null,8,["src"]),e("div",b,[e("p",h,l(a.data.desc),1),r((i(),c(j,{type:"primary",size:"large",onClick:v,class:"mt-5"},{default:n(()=>[d(l(a.data.btnText),1)]),_:1})),[[w]])])])])}}});export{_};
|
||||
import{d as a,T as s,e as t,h as e,p as m,J as r,m as l,y as c,z as n,l as d,C as x,E as o,f as i}from"./index-DBHnxsy6.js";/* empty css */import{_ as p}from"./index-DuC2vu0m.js";import{E as u}from"./index-r0EsCkBw.js";const f={class:"page-content !border-0 !bg-transparent min-h-screen flex-cc"},g={class:"flex-cc max-md:!block max-md:text-center"},b={class:"ml-15 w-75 max-md:mx-auto max-md:mt-10 max-md:w-full max-md:text-center"},h={class:"text-xl leading-7 text-g-600 max-md:text-lg"},_=a({__name:"ArtException",props:{data:{}},setup(a){const _=x(),{homePath:j}=s(),v=()=>{_.push(j.value)};return(s,x)=>{const _=p,j=u,w=o("ripple");return i(),t("div",f,[e("div",g,[m(_,{src:a.data.imgUrl,size:"100%",class:"!w-100"},null,8,["src"]),e("div",b,[e("p",h,l(a.data.desc),1),r((i(),c(j,{type:"primary",size:"large",onClick:v,class:"mt-5"},{default:n(()=>[d(l(a.data.btnText),1)]),_:1})),[[w]])])])])}}});export{_};
|
||||
@ -1 +1 @@
|
||||
var e=Object.defineProperty,t=Object.defineProperties,s=Object.getOwnPropertyDescriptors,r=Object.getOwnPropertySymbols,a=Object.prototype.hasOwnProperty,o=Object.prototype.propertyIsEnumerable,p=(t,s,r)=>s in t?e(t,s,{enumerable:!0,configurable:!0,writable:!0,value:r}):t[s]=r;import{_ as l}from"./index.vue_vue_type_script_setup_true_lang-CCGboj8m.js";import{d as n,e as c,f as m,p as i,h as d,i as u,m as x,j as b}from"./index-ip5gDR6L.js";const g={class:"title mt-8 text-3xl font-medium !text-g-900 max-md:mt-2.5 max-md:text-2xl"},f={class:"msg mt-5 text-base text-g-600"},_={class:"res mt-7.5 rounded bg-g-200/80 dark:bg-g-300/40 px-7.5 py-5.5 text-left max-md:px-7.5 max-md:py-2.5 [&_p]:flex [&_p]:items-center [&_p]:py-2 [&_p]:text-sm [&_p]:text-[#808695] [&_p_i]:mr-1.5"},y={class:"btn-group mt-12.5"},v=n((j=((e,t)=>{for(var s in t||(t={}))a.call(t,s)&&p(e,s,t[s]);if(r)for(var s of r(t))o.call(t,s)&&p(e,s,t[s]);return e})({},{name:"ArtResultPage"}),t(j,s({__name:"ArtResultPage",props:{type:{default:"success"},title:{default:""},message:{default:""},iconCode:{default:""}},setup:e=>(t,s)=>{const r=l;return m(),c("div",{class:u(["page-content box-border !px-20 py-3.5 text-center max-md:!px-5",e.type])},[i(r,{class:u(["icon size-22 p-2 mt-16 block rounded-full !text-white","success"===e.type?"bg-[#19BE6B]":"bg-[#ED4014]"]),icon:e.iconCode},null,8,["icon","class"]),d("h1",g,x(e.title),1),d("p",f,x(e.message),1),d("div",_,[b(t.$slots,"content")]),d("div",y,[b(t.$slots,"buttons")])],2)}}))));var j;export{v as _};
|
||||
var e=Object.defineProperty,t=Object.defineProperties,s=Object.getOwnPropertyDescriptors,r=Object.getOwnPropertySymbols,a=Object.prototype.hasOwnProperty,o=Object.prototype.propertyIsEnumerable,p=(t,s,r)=>s in t?e(t,s,{enumerable:!0,configurable:!0,writable:!0,value:r}):t[s]=r;import{_ as l}from"./index.vue_vue_type_script_setup_true_lang-DUyHB5jc.js";import{d as n,e as c,f as m,p as i,h as d,i as u,m as x,j as b}from"./index-DBHnxsy6.js";const g={class:"title mt-8 text-3xl font-medium !text-g-900 max-md:mt-2.5 max-md:text-2xl"},f={class:"msg mt-5 text-base text-g-600"},_={class:"res mt-7.5 rounded bg-g-200/80 dark:bg-g-300/40 px-7.5 py-5.5 text-left max-md:px-7.5 max-md:py-2.5 [&_p]:flex [&_p]:items-center [&_p]:py-2 [&_p]:text-sm [&_p]:text-[#808695] [&_p_i]:mr-1.5"},y={class:"btn-group mt-12.5"},v=n((j=((e,t)=>{for(var s in t||(t={}))a.call(t,s)&&p(e,s,t[s]);if(r)for(var s of r(t))o.call(t,s)&&p(e,s,t[s]);return e})({},{name:"ArtResultPage"}),t(j,s({__name:"ArtResultPage",props:{type:{default:"success"},title:{default:""},message:{default:""},iconCode:{default:""}},setup:e=>(t,s)=>{const r=l;return m(),c("div",{class:u(["page-content box-border !px-20 py-3.5 text-center max-md:!px-5",e.type])},[i(r,{class:u(["icon size-22 p-2 mt-16 block rounded-full !text-white","success"===e.type?"bg-[#19BE6B]":"bg-[#ED4014]"]),icon:e.iconCode},null,8,["icon","class"]),d("h1",g,x(e.title),1),d("p",f,x(e.message),1),d("div",_,[b(t.$slots,"content")]),d("div",y,[b(t.$slots,"buttons")])],2)}}))));var j;export{v as _};
|
||||
@ -0,0 +1 @@
|
||||
.unit[data-v-47e87be5]{margin-left:8px;color:#909399}.form-tip[data-v-47e87be5]{margin-top:4px;font-size:12px;color:#909399;line-height:1.4}
|
||||
File diff suppressed because one or more lines are too long
BIN
build/resources/admin/assets/EffectEditDialog-DVCDk-bJ.js.gz
Normal file
BIN
build/resources/admin/assets/EffectEditDialog-DVCDk-bJ.js.gz
Normal file
Binary file not shown.
File diff suppressed because one or more lines are too long
@ -0,0 +1 @@
|
||||
.effect-manager[data-v-f2f200b4]{padding:0}.toolbar[data-v-f2f200b4]{margin-bottom:16px;display:flex;gap:12px}.params-view[data-v-f2f200b4]{display:flex;flex-wrap:wrap;gap:4px}.params-view .el-tag[data-v-f2f200b4]{margin:2px}
|
||||
@ -1 +1 @@
|
||||
var e=Object.defineProperty,r=Object.defineProperties,a=Object.getOwnPropertyDescriptors,t=Object.getOwnPropertySymbols,o=Object.prototype.hasOwnProperty,n=Object.prototype.propertyIsEnumerable,s=(r,a,t)=>a in r?e(r,a,{enumerable:!0,configurable:!0,writable:!0,value:t}):r[a]=t;import{d as l,a as f,o as c,aU as i,aD as u,J as p,aV as b,u as m,e as d,h as v,f as y}from"./index-ip5gDR6L.js";const O={class:"box-border w-full h-full"},h=["src"],j=l((w=((e,r)=>{for(var a in r||(r={}))o.call(r,a)&&s(e,a,r[a]);if(t)for(var a of t(r))n.call(r,a)&&s(e,a,r[a]);return e})({},{name:"IframeView"}),r(w,a({__name:"Iframe",setup(e){const r=u(),a=f(!0),t=f(""),o=f(null);c(()=>{const e=i.getInstance().findByPath(r.path);(null==e?void 0:e.meta)&&(t.value=e.meta.link||"")});const n=()=>{a.value=!1};return(e,r)=>{const s=b;return p((y(),d("div",O,[v("iframe",{ref_key:"iframeRef",ref:o,src:m(t),frameborder:"0",class:"w-full h-full min-h-[calc(100vh-120px)] border-none",onLoad:n},null,40,h)])),[[s,m(a)]])}}}))));var w;export{j as default};
|
||||
var e=Object.defineProperty,r=Object.defineProperties,a=Object.getOwnPropertyDescriptors,t=Object.getOwnPropertySymbols,o=Object.prototype.hasOwnProperty,n=Object.prototype.propertyIsEnumerable,s=(r,a,t)=>a in r?e(r,a,{enumerable:!0,configurable:!0,writable:!0,value:t}):r[a]=t;import{d as l,a as f,o as c,aU as i,aD as u,J as p,aV as b,u as m,e as d,h as v,f as y}from"./index-DBHnxsy6.js";const O={class:"box-border w-full h-full"},h=["src"],j=l((w=((e,r)=>{for(var a in r||(r={}))o.call(r,a)&&s(e,a,r[a]);if(t)for(var a of t(r))n.call(r,a)&&s(e,a,r[a]);return e})({},{name:"IframeView"}),r(w,a({__name:"Iframe",setup(e){const r=u(),a=f(!0),t=f(""),o=f(null);c(()=>{const e=i.getInstance().findByPath(r.path);(null==e?void 0:e.meta)&&(t.value=e.meta.link||"")});const n=()=>{a.value=!1};return(e,r)=>{const s=b;return p((y(),d("div",O,[v("iframe",{ref_key:"iframeRef",ref:o,src:m(t),frameborder:"0",class:"w-full h-full min-h-[calc(100vh-120px)] border-none",onLoad:n},null,40,h)])),[[s,m(a)]])}}}))));var w;export{j as default};
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
BIN
build/resources/admin/assets/RuleConfigDialog-OcWf3YP-.css.gz
Normal file
BIN
build/resources/admin/assets/RuleConfigDialog-OcWf3YP-.css.gz
Normal file
Binary file not shown.
File diff suppressed because one or more lines are too long
BIN
build/resources/admin/assets/RuleConfigDialog-XFo2cxQg.js.gz
Normal file
BIN
build/resources/admin/assets/RuleConfigDialog-XFo2cxQg.js.gz
Normal file
Binary file not shown.
1
build/resources/admin/assets/TitleEditDialog-Cw8i3HNt.js
Normal file
1
build/resources/admin/assets/TitleEditDialog-Cw8i3HNt.js
Normal file
File diff suppressed because one or more lines are too long
@ -0,0 +1 @@
|
||||
.json-editor[data-v-cf7dfd13]{position:relative}.json-editor .el-button[data-v-cf7dfd13]{position:absolute;right:10px;bottom:10px}
|
||||
@ -0,0 +1 @@
|
||||
.user-assignment[data-v-dcfb44a3]{display:flex;flex-direction:column;gap:16px;max-height:70vh;overflow-y:auto}.search-card[data-v-dcfb44a3]{margin-bottom:0}.search-form[data-v-dcfb44a3]{display:flex;flex-wrap:wrap;gap:8px}.card-header[data-v-dcfb44a3]{display:flex;justify-content:space-between;align-items:center}.header-actions[data-v-dcfb44a3]{display:flex;align-items:center;gap:12px}.title-tags[data-v-dcfb44a3]{display:flex;flex-wrap:wrap;gap:4px}.title-icon[data-v-dcfb44a3]{margin-left:2px}.pagination-container[data-v-dcfb44a3]{margin-top:16px;display:flex;justify-content:center}.assignment-config-card[data-v-dcfb44a3]{background-color:#f5f7fa}.unit[data-v-dcfb44a3]{margin-left:8px;color:#909399}.dialog-footer[data-v-dcfb44a3]{display:flex;justify-content:flex-end;gap:12px}
|
||||
File diff suppressed because one or more lines are too long
BIN
build/resources/admin/assets/UserAssignmentDialog-CgQlEmJK.js.gz
Normal file
BIN
build/resources/admin/assets/UserAssignmentDialog-CgQlEmJK.js.gz
Normal file
Binary file not shown.
1
build/resources/admin/assets/_baseIteratee-DRiXPtOg.js
Normal file
1
build/resources/admin/assets/_baseIteratee-DRiXPtOg.js
Normal file
@ -0,0 +1 @@
|
||||
import{bk as n,cz as r,bi as t,b8 as u,cA as i,c7 as e,bx as o,ca as f,bm as a}from"./index-DBHnxsy6.js";import{h as c,i as s}from"./index-nZra831X.js";function v(n){return n==n&&!t(n)}function l(n,r){return function(t){return null!=t&&(t[n]===r&&(void 0!==r||n in Object(t)))}}function b(t){var i=function(n){for(var r=u(n),t=r.length;t--;){var i=r[t],e=n[i];r[t]=[i,e,v(e)]}return r}(t);return 1==i.length&&i[0][2]?l(i[0][0],i[0][1]):function(u){return u===t||function(t,u,i,e){var o=i.length,f=o;if(null==t)return!f;for(t=Object(t);o--;){var a=i[o];if(a[2]?a[1]!==t[a[0]]:!(a[0]in t))return!1}for(;++o<f;){var c=(a=i[o])[0],s=t[c],v=a[1];if(a[2]){if(void 0===s&&!(c in t))return!1}else{var l=new n;if(!r(v,s,3,e,l))return!1}}return!0}(u,0,i)}}function d(n){return i(n)?(r=e(n),function(n){return null==n?void 0:n[r]}):function(n){return function(r){return f(r,n)}}(n);var r}function j(n){return"function"==typeof n?n:null==n?s:"object"==typeof n?a(n)?(t=n[0],u=n[1],i(t)&&v(u)?l(e(t),u):function(n){var i=o(n,t);return void 0===i&&i===u?c(n,t):r(u,i,3)}):b(n):d(n);var t,u}export{j as b};
|
||||
@ -1 +1 @@
|
||||
import{bn as r,cw as t,bs as e,cx as n,cy as o,cz as c,cA as u,cv as a,cB as f}from"./index-ip5gDR6L.js";var s=Object.create,i=function(){function t(){}return function(e){if(!r(e))return{};if(s)return s(e);t.prototype=e;var n=new t;return t.prototype=void 0,n}}();function p(r,t){var e=-1,n=r.length;for(t||(t=Array(n));++e<n;)t[e]=r[e];return t}function v(r,n,o,c){var u=!o;o||(o={});for(var a=-1,f=n.length;++a<f;){var s=n[a],i=void 0;void 0===i&&(i=r[s]),u?t(o,s,i):e(o,s,i)}return o}var l=Object.prototype.hasOwnProperty;function y(t){if(!r(t))return function(r){var t=[];if(null!=r)for(var e in Object(r))t.push(e);return t}(t);var e=n(t),o=[];for(var c in t)("constructor"!=c||!e&&l.call(t,c))&&o.push(c);return o}function b(r){return c(r)?o(r,!0):y(r)}var d=u(Object.getPrototypeOf,Object),h="object"==typeof exports&&exports&&!exports.nodeType&&exports,j=h&&"object"==typeof module&&module&&!module.nodeType&&module,w=j&&j.exports===h?a.Buffer:void 0,x=w?w.allocUnsafe:void 0;function O(r,t){if(t)return r.slice();var e=r.length,n=x?x(e):new r.constructor(e);return r.copy(n),n}function g(r){var t=new r.constructor(r.byteLength);return new f(t).set(new f(r)),t}function m(r,t){var e=t?g(r.buffer):r.buffer;return new r.constructor(e,r.byteOffset,r.length)}function A(r){return"function"!=typeof r.constructor||n(r)?{}:i(d(r))}export{g as a,m as b,v as c,O as d,p as e,d as g,A as i,b as k};
|
||||
import{bi as r,ct as t,bn as e,cu as n,cv as o,cw as u,cx as c,cs as a,cy as f}from"./index-DBHnxsy6.js";var s=Object.create,i=function(){function t(){}return function(e){if(!r(e))return{};if(s)return s(e);t.prototype=e;var n=new t;return t.prototype=void 0,n}}();function p(r,t){var e=-1,n=r.length;for(t||(t=Array(n));++e<n;)t[e]=r[e];return t}function v(r,n,o,u){var c=!o;o||(o={});for(var a=-1,f=n.length;++a<f;){var s=n[a],i=void 0;void 0===i&&(i=r[s]),c?t(o,s,i):e(o,s,i)}return o}var l=Object.prototype.hasOwnProperty;function y(t){if(!r(t))return function(r){var t=[];if(null!=r)for(var e in Object(r))t.push(e);return t}(t);var e=n(t),o=[];for(var u in t)("constructor"!=u||!e&&l.call(t,u))&&o.push(u);return o}function b(r){return u(r)?o(r,!0):y(r)}var d=c(Object.getPrototypeOf,Object),h="object"==typeof exports&&exports&&!exports.nodeType&&exports,j=h&&"object"==typeof module&&module&&!module.nodeType&&module,w=j&&j.exports===h?a.Buffer:void 0,x=w?w.allocUnsafe:void 0;function O(r,t){if(t)return r.slice();var e=r.length,n=x?x(e):new r.constructor(e);return r.copy(n),n}function g(r){var t=new r.constructor(r.byteLength);return new f(t).set(new f(r)),t}function m(r,t){var e=t?g(r.buffer):r.buffer;return new r.constructor(e,r.byteOffset,r.length)}function P(r){return"function"!=typeof r.constructor||n(r)?{}:i(d(r))}export{g as a,m as b,v as c,O as d,p as e,d as g,P as i,b as k};
|
||||
1
build/resources/admin/assets/active-user-CdPYh29t.js
Normal file
1
build/resources/admin/assets/active-user-CdPYh29t.js
Normal file
@ -0,0 +1 @@
|
||||
import{_ as e}from"./active-user.vue_vue_type_script_setup_true_lang-19Sadsu3.js";import"./index-DBHnxsy6.js";import"./useChart-MKcUOr4d.js";import"./operations-CsHdqaV1.js";/* empty css */export{e as default};
|
||||
@ -1 +0,0 @@
|
||||
import{_ as e}from"./active-user.vue_vue_type_script_setup_true_lang-DtO48xU9.js";import"./index-ip5gDR6L.js";import"./useChart-DqpWZdD6.js";export{e as default};
|
||||
@ -0,0 +1 @@
|
||||
var e=Object.defineProperty,t=Object.defineProperties,a=Object.getOwnPropertyDescriptors,s=Object.getOwnPropertySymbols,r=Object.prototype.hasOwnProperty,o=Object.prototype.propertyIsEnumerable,i=(t,a,s)=>a in t?e(t,a,{enumerable:!0,configurable:!0,writable:!0,value:s}):t[a]=s,l=(e,t)=>{for(var a in t||(t={}))r.call(t,a)&&i(e,a,t[a]);if(s)for(var a of s(t))o.call(t,a)&&i(e,a,t[a]);return e};import{d as n,c as d,J as c,aV as u,e as p,f as m,g as h,H as y,a as f,r as g,p as x,S as b,h as v,F as w,G as A,m as L,O as S}from"./index-DBHnxsy6.js";import{u as j,a as k,g as O}from"./useChart-MKcUOr4d.js";import{f as P}from"./operations-CsHdqaV1.js";/* empty css */const B=n((W=l({},{name:"ArtBarChart"}),D={__name:"index",props:{data:{default:()=>[0,0,0,0,0,0,0]},xAxisData:{default:()=>[]},barWidth:{default:"40%"},stack:{type:Boolean,default:!1},borderRadius:{default:4},height:{default:j().chartHeight},loading:{type:Boolean,default:!1},isEmpty:{type:Boolean,default:!1},colors:{default:()=>j().colors},showAxisLabel:{type:Boolean,default:!0},showAxisLine:{type:Boolean,default:!0},showSplitLine:{type:Boolean,default:!0},showTooltip:{type:Boolean,default:!0},showLegend:{type:Boolean,default:!1},legendPosition:{default:"bottom"}},setup(e){const t=e,a=d(()=>Array.isArray(t.data)&&t.data.length>0&&"object"==typeof t.data[0]&&"name"in t.data[0]),s=(e,a)=>e||(void 0!==a?t.colors[a%t.colors.length]:new O.LinearGradient(0,0,0,1,[{offset:0,color:y("--el-color-primary-light-4")},{offset:1,color:y("--el-color-primary")}])),r=e=>new O.LinearGradient(0,0,0,1,[{offset:0,color:e},{offset:1,color:e}]),o=e=>{const a=b();return l({name:e.name,data:e.data,type:"bar",stack:e.stack,itemStyle:(s=e.color,{borderRadius:t.borderRadius,color:"string"==typeof s?r(s):s}),barWidth:e.barWidth||t.barWidth},a);var s},{chartRef:i,getAxisLineStyle:n,getAxisLabelStyle:f,getAxisTickStyle:g,getSplitLineStyle:x,getAnimationConfig:b,getTooltipStyle:v,getLegendStyle:w,getGridWithLegend:A}=k({props:t,checkEmpty:()=>{if(Array.isArray(t.data)&&"number"==typeof t.data[0]){const e=t.data;return!e.length||e.every(e=>0===e)}if(Array.isArray(t.data)&&"object"==typeof t.data[0]){const e=t.data;return!e.length||e.every(e=>{var t;return!(null==(t=e.data)?void 0:t.length)||e.data.every(e=>0===e)})}return!0},watchSources:[()=>t.data,()=>t.xAxisData,()=>t.colors],generateOptions:()=>{const e={grid:A(t.showLegend&&a.value,t.legendPosition,{top:15,right:0,left:0}),tooltip:t.showTooltip?v():void 0,xAxis:{type:"category",data:t.xAxisData,axisTick:g(),axisLine:n(t.showAxisLine),axisLabel:f(t.showAxisLabel)},yAxis:{type:"value",axisLabel:f(t.showAxisLabel),axisLine:n(t.showAxisLine),splitLine:x(t.showSplitLine)}};if(t.showLegend&&a.value&&(e.legend=w(t.legendPosition)),a.value){const a=t.data;e.series=a.map((e,a)=>{const r=s(t.colors[a],a);return o({name:e.name,data:e.data,color:r,barWidth:e.barWidth,stack:t.stack?e.stack||"total":void 0})})}else{const a=t.data,r=s();e.series=[o({data:a,color:r})]}return e}});return(e,a)=>{const s=u;return c((m(),p("div",{ref_key:"chartRef",ref:i,style:h({height:t.height})},null,4)),[[s,t.loading]])}}},t(W,a(D))));var W,D;const T={class:"art-card h-105 p-4 box-border mb-5 max-sm:mb-4"},_={class:"flex-b mt-2"},G={class:"text-2xl text-g-900"},R={class:"text-xs text-g-500"},C=n({__name:"active-user",setup(e){const t=f([]),a=f([]),s=g([{name:"总用户量",num:"0"},{name:"总访问量",num:"0"},{name:"日访问量",num:"0"},{name:"周同比",num:"+0%"}]);return(()=>{return e=this,r=null,o=function*(){try{const e=yield P("30d");t.value=e.chart.map(e=>e.date.slice(5)),a.value=e.chart.map(e=>e.value),s[0].num=String(e.metrics.totalUsers),s[1].num=String(e.metrics.totalVisits),s[2].num=String(e.metrics.dailyVisits),s[3].num=e.metrics.weeklyGrowth}catch(e){S.error("加载用户概述失败")}},new Promise((t,a)=>{var s=e=>{try{l(o.next(e))}catch(t){a(t)}},i=e=>{try{l(o.throw(e))}catch(t){a(t)}},l=e=>e.done?t(e.value):Promise.resolve(e.value).then(s,i);l((o=o.apply(e,r)).next())});var e,r,o})(),(e,r)=>{const o=B;return m(),p("div",T,[x(o,{class:"box-border p-2",barWidth:"50%",height:"13.7rem",showAxisLine:!1,data:a.value,xAxisData:t.value},null,8,["data","xAxisData"]),r[0]||(r[0]=b('<div class="ml-1"><h3 class="mt-5 text-lg font-medium">用户概述</h3><p class="mt-1 text-sm">比上周 <span class="text-success font-medium">+23%</span></p><p class="mt-1 text-sm">我们为您创建了多个选项,可将它们组合在一起并定制为像素完美的页面</p></div>',1)),v("div",_,[(m(!0),p(w,null,A(s,(e,t)=>(m(),p("div",{class:"flex-1",key:t},[v("p",G,L(e.num),1),v("p",R,L(e.name),1)]))),128))])])}}});export{C as _};
|
||||
@ -1 +0,0 @@
|
||||
var e=Object.defineProperty,t=Object.defineProperties,a=Object.getOwnPropertyDescriptors,s=Object.getOwnPropertySymbols,o=Object.prototype.hasOwnProperty,r=Object.prototype.propertyIsEnumerable,i=(t,a,s)=>a in t?e(t,a,{enumerable:!0,configurable:!0,writable:!0,value:s}):t[a]=s,l=(e,t)=>{for(var a in t||(t={}))o.call(t,a)&&i(e,a,t[a]);if(s)for(var a of s(t))r.call(t,a)&&i(e,a,t[a]);return e};import{d as n,c as d,J as c,aV as p,e as u,f as m,g as f,H as h,p as y,S as g,h as x,F as b,G as v,m as A}from"./index-ip5gDR6L.js";import{u as L,a as w,g as k}from"./useChart-DqpWZdD6.js";const S=n((j=l({},{name:"ArtBarChart"}),O={__name:"index",props:{data:{default:()=>[0,0,0,0,0,0,0]},xAxisData:{default:()=>[]},barWidth:{default:"40%"},stack:{type:Boolean,default:!1},borderRadius:{default:4},height:{default:L().chartHeight},loading:{type:Boolean,default:!1},isEmpty:{type:Boolean,default:!1},colors:{default:()=>L().colors},showAxisLabel:{type:Boolean,default:!0},showAxisLine:{type:Boolean,default:!0},showSplitLine:{type:Boolean,default:!0},showTooltip:{type:Boolean,default:!0},showLegend:{type:Boolean,default:!1},legendPosition:{default:"bottom"}},setup(e){const t=e,a=d(()=>Array.isArray(t.data)&&t.data.length>0&&"object"==typeof t.data[0]&&"name"in t.data[0]),s=(e,a)=>e||(void 0!==a?t.colors[a%t.colors.length]:new k.LinearGradient(0,0,0,1,[{offset:0,color:h("--el-color-primary-light-4")},{offset:1,color:h("--el-color-primary")}])),o=e=>new k.LinearGradient(0,0,0,1,[{offset:0,color:e},{offset:1,color:e}]),r=e=>{const a=b();return l({name:e.name,data:e.data,type:"bar",stack:e.stack,itemStyle:(s=e.color,{borderRadius:t.borderRadius,color:"string"==typeof s?o(s):s}),barWidth:e.barWidth||t.barWidth},a);var s},{chartRef:i,getAxisLineStyle:n,getAxisLabelStyle:y,getAxisTickStyle:g,getSplitLineStyle:x,getAnimationConfig:b,getTooltipStyle:v,getLegendStyle:A,getGridWithLegend:L}=w({props:t,checkEmpty:()=>{if(Array.isArray(t.data)&&"number"==typeof t.data[0]){const e=t.data;return!e.length||e.every(e=>0===e)}if(Array.isArray(t.data)&&"object"==typeof t.data[0]){const e=t.data;return!e.length||e.every(e=>{var t;return!(null==(t=e.data)?void 0:t.length)||e.data.every(e=>0===e)})}return!0},watchSources:[()=>t.data,()=>t.xAxisData,()=>t.colors],generateOptions:()=>{const e={grid:L(t.showLegend&&a.value,t.legendPosition,{top:15,right:0,left:0}),tooltip:t.showTooltip?v():void 0,xAxis:{type:"category",data:t.xAxisData,axisTick:g(),axisLine:n(t.showAxisLine),axisLabel:y(t.showAxisLabel)},yAxis:{type:"value",axisLabel:y(t.showAxisLabel),axisLine:n(t.showAxisLine),splitLine:x(t.showSplitLine)}};if(t.showLegend&&a.value&&(e.legend=A(t.legendPosition)),a.value){const a=t.data;e.series=a.map((e,a)=>{const o=s(t.colors[a],a);return r({name:e.name,data:e.data,color:o,barWidth:e.barWidth,stack:t.stack?e.stack||"total":void 0})})}else{const a=t.data,o=s();e.series=[r({data:a,color:o})]}return e}});return(e,a)=>{const s=p;return c((m(),u("div",{ref_key:"chartRef",ref:i,style:f({height:t.height})},null,4)),[[s,t.loading]])}}},t(j,a(O))));var j,O;const B={class:"art-card h-105 p-4 box-border mb-5 max-sm:mb-4"},P={class:"flex-b mt-2"},W={class:"text-2xl text-g-900"},_={class:"text-xs text-g-500"},D=n({__name:"active-user",setup(e){const t=["1月","2月","3月","4月","5月","6月","7月","8月","9月"],a=[160,100,150,80,190,100,175,120,160],s=[{name:"总用户量",num:"32k"},{name:"总访问量",num:"128k"},{name:"日访问量",num:"1.2k"},{name:"周同比",num:"+5%"}];return(e,o)=>{const r=S;return m(),u("div",B,[y(r,{class:"box-border p-2",barWidth:"50%",height:"13.7rem",showAxisLine:!1,data:a,xAxisData:t}),o[0]||(o[0]=g('<div class="ml-1"><h3 class="mt-5 text-lg font-medium">用户概述</h3><p class="mt-1 text-sm">比上周 <span class="text-success font-medium">+23%</span></p><p class="mt-1 text-sm">我们为您创建了多个选项,可将它们组合在一起并定制为像素完美的页面</p></div>',1)),x("div",P,[(m(),u(b,null,v(s,(e,t)=>x("div",{class:"flex-1",key:t},[x("p",W,A(e.num),1),x("p",_,A(e.name),1)])),64))])])}}});export{D as _};
|
||||
@ -1 +1 @@
|
||||
var e=Object.defineProperty,r=Object.getOwnPropertySymbols,t=Object.prototype.hasOwnProperty,a=Object.prototype.propertyIsEnumerable,o=(r,t,a)=>t in r?e(r,t,{enumerable:!0,configurable:!0,writable:!0,value:a}):r[t]=a;import{b5 as s}from"./index-ip5gDR6L.js";function n(e){return n=this,i=null,c=function*(){const n=((e,s)=>{for(var n in s||(s={}))t.call(s,n)&&o(e,n,s[n]);if(r)for(var n of r(s))a.call(s,n)&&o(e,n,s[n]);return e})({page:1,page_size:20},e||{});try{const e=yield s.get({url:"app/activities",params:n,showErrorMessage:!1});return{records:e.list.map(e=>({id:e.id,name:e.name,categoryName:e.category_name,status:e.status,priceDraw:e.price_draw,isBoss:e.is_boss})),total:e.total,current:e.page,size:e.page_size}}catch(i){return{records:[],total:0,current:n.page,size:n.page_size}}},new Promise((e,r)=>{var t=e=>{try{o(c.next(e))}catch(t){r(t)}},a=e=>{try{o(c.throw(e))}catch(t){r(t)}},o=r=>r.done?e(r.value):Promise.resolve(r.value).then(t,a);o((c=c.apply(n,i)).next())});var n,i,c}export{n as f};
|
||||
var e=Object.defineProperty,r=Object.getOwnPropertySymbols,t=Object.prototype.hasOwnProperty,a=Object.prototype.propertyIsEnumerable,o=(r,t,a)=>t in r?e(r,t,{enumerable:!0,configurable:!0,writable:!0,value:a}):r[t]=a;import{b4 as s}from"./index-DBHnxsy6.js";function n(e){return n=this,i=null,c=function*(){const n=((e,s)=>{for(var n in s||(s={}))t.call(s,n)&&o(e,n,s[n]);if(r)for(var n of r(s))a.call(s,n)&&o(e,n,s[n]);return e})({page:1,page_size:20},e||{});try{const e=yield s.get({url:"app/activities",params:n,showErrorMessage:!1});return{records:e.list.map(e=>({id:e.id,name:e.name,categoryName:e.category_name,status:e.status,priceDraw:e.price_draw,isBoss:e.is_boss})),total:e.total,current:e.page,size:e.page_size}}catch(i){return{records:[],total:0,current:n.page,size:n.page_size}}},new Promise((e,r)=>{var t=e=>{try{o(c.next(e))}catch(t){r(t)}},a=e=>{try{o(c.throw(e))}catch(t){r(t)}},o=r=>r.done?e(r.value):Promise.resolve(r.value).then(t,a);o((c=c.apply(n,i)).next())});var n,i,c}export{n as f};
|
||||
@ -1 +1 @@
|
||||
import{d as t,a as e,r as l,o as a,e as s,f as r,h as o,m as n,F as i,G as c,l as d,i as g,p as x,z as v,n as f}from"./index-ip5gDR6L.js";/* empty css */import{f as p,a as u}from"./operations-Cj-RMt0B.js";import{E as m}from"./index-D1xXI5gl.js";import"./use-form-common-props-BwBs_jod.js";const b={class:"art-card h-140 p-5 mb-5 max-sm:mb-4"},h={class:"h-[calc(100%-40px)]"},y={class:"grid grid-cols-4 gap-4 mb-6"},w={class:"text-center p-3 bg-blue-50 rounded-lg"},C={class:"text-2xl font-bold text-blue-600"},R={class:"text-center p-3 bg-green-50 rounded-lg"},S={class:"text-2xl font-bold text-green-600"},j={class:"text-center p-3 bg-yellow-50 rounded-lg"},P={class:"text-2xl font-bold text-yellow-600"},$={class:"text-center p-3 bg-purple-50 rounded-lg"},k={class:"text-2xl font-bold text-purple-600"},T={class:"h-60 mb-4"},_={class:"overflow-auto"},A={class:"w-full text-sm"},F={class:"py-2"},W={class:"flex items-center"},z={class:"py-2"},E={class:"py-2"},G={class:"py-2"},L={class:"py-2"},N=t({__name:"activity-lottery",setup(t){const N=e(),B=l({totalActivities:0,totalParticipants:0,totalDraws:0,winnerCount:0,overallWinRate:0,costControl:0}),D=l([]),M=e(!1),O=t=>t.winRate>2?"success":t.winRate>1?"warning":"info",q=t=>t.winRate>2?"高中奖率":t.winRate>1?"中等中奖率":"低中奖率",H=(t,e)=>({1:`rgba(251, 191, 36, ${e})`,2:`rgba(156, 163, 175, ${e})`,3:`rgba(251, 146, 60, ${e})`,4:`rgba(96, 165, 250, ${e})`,5:`rgba(52, 211, 153, ${e})`}[t]||`rgba(156, 163, 175, ${e})`),I=()=>{return t=this,e=null,l=function*(){M.value=!0;try{const[t,e]=yield Promise.all([p("7d"),u("7d")]);Object.assign(B,t),D.splice(0,D.length,...e),f(()=>{(()=>{if(!N.value||0===D.length)return;const t=N.value,e=t.getContext("2d");if(!e)return;e.clearRect(0,0,t.width,t.height);const l=40,a=t.width-80,s=t.height-80,r=Math.max(...D.map(t=>t.winnerCount)),o=a/D.length*.6,n=a/D.length*.4;D.forEach((t,a)=>{const i=l+a*(o+n)+n/2,c=t.winnerCount/r*s,d=l+s-c,g=e.createLinearGradient(i,d+c,i,d);g.addColorStop(0,H(t.level,.8)),g.addColorStop(1,H(t.level,1)),e.fillStyle=g,e.fillRect(i,d,o,c),e.fillStyle="#333",e.font="12px sans-serif",e.textAlign="center",e.fillText(t.winnerCount.toString(),i+o/2,d-5),e.fillText(t.levelName,i+o/2,l+s+20)}),e.strokeStyle="#e0e0e0",e.lineWidth=1,e.beginPath(),e.moveTo(l,l),e.lineTo(l,l+s),e.lineTo(l+a,l+s),e.stroke()})()})}catch(t){}finally{M.value=!1}},new Promise((a,s)=>{var r=t=>{try{n(l.next(t))}catch(e){s(e)}},o=t=>{try{n(l.throw(t))}catch(e){s(e)}},n=t=>t.done?a(t.value):Promise.resolve(t.value).then(r,o);n((l=l.apply(t,e)).next())});var t,e,l};return a(()=>{I()}),(t,e)=>{const l=m;return r(),s("div",b,[e[5]||(e[5]=o("div",{class:"art-card-header"},[o("div",{class:"title"},[o("h4",null,"活动抽奖效果分析"),o("p",null,"优化中奖概率,控制活动成本")])],-1)),o("div",h,[o("div",y,[o("div",w,[o("div",C,n(B.totalActivities),1),e[0]||(e[0]=o("div",{class:"text-sm text-g-500"},"活动总数",-1))]),o("div",R,[o("div",S,n((a=B.totalParticipants,a>=1e4?(a/1e4).toFixed(1)+"w":a>=1e3?(a/1e3).toFixed(1)+"k":a.toString())),1),e[1]||(e[1]=o("div",{class:"text-sm text-g-500"},"参与人数",-1))]),o("div",j,[o("div",P,n(B.overallWinRate)+"%",1),e[2]||(e[2]=o("div",{class:"text-sm text-g-500"},"整体中奖率",-1))]),o("div",$,[o("div",k,n(B.costControl)+"%",1),e[3]||(e[3]=o("div",{class:"text-sm text-g-500"},"成本控制",-1))])]),o("div",T,[o("canvas",{ref_key:"chartRef",ref:N,width:"400",height:"240"},null,512)]),o("div",_,[o("table",A,[e[4]||(e[4]=o("thead",null,[o("tr",{class:"border-b border-g-200"},[o("th",{class:"text-left py-2"},"奖级"),o("th",{class:"text-left py-2"},"中奖人数"),o("th",{class:"text-left py-2"},"中奖率"),o("th",{class:"text-left py-2"},"成本"),o("th",{class:"text-left py-2"},"状态")])],-1)),o("tbody",null,[(r(!0),s(i,null,c(D,t=>{return r(),s("tr",{key:t.level,class:"border-b border-g-100 hover:bg-g-50"},[o("td",F,[o("div",W,[o("span",{class:g(["w-6 h-6 rounded-full flex items-center justify-center text-white text-xs font-bold mr-2",(e=t.level,{1:"bg-gradient-to-r from-yellow-400 to-yellow-300",2:"bg-gradient-to-r from-gray-400 to-gray-300",3:"bg-gradient-to-r from-orange-400 to-orange-300",4:"bg-gradient-to-r from-blue-400 to-blue-300",5:"bg-gradient-to-r from-green-400 to-green-300"}[e]||"bg-gradient-to-r from-gray-400 to-gray-300")])},n(t.level),3),d(" "+n(t.levelName),1)])]),o("td",z,n(t.winnerCount)+"人",1),o("td",E,n(t.winRate)+"%",1),o("td",G,"¥"+n(t.cost.toLocaleString()),1),o("td",L,[x(l,{type:O(t),size:"small"},{default:v(()=>[d(n(q(t)),1)]),_:2},1032,["type"])])]);var e}),128))])])])])]);var a}}});export{N as default};
|
||||
import{d as t,a as e,r as l,o as a,e as s,f as r,h as o,m as n,F as i,G as c,l as d,i as g,p as x,z as v,n as f}from"./index-DBHnxsy6.js";/* empty css */import{a as p,b as u}from"./operations-CsHdqaV1.js";import{E as m}from"./index-D2YpA_om.js";import"./use-form-common-props-BOfz9xGR.js";const b={class:"art-card h-140 p-5 mb-5 max-sm:mb-4"},h={class:"h-[calc(100%-40px)]"},y={class:"grid grid-cols-4 gap-4 mb-6"},w={class:"text-center p-3 bg-blue-50 rounded-lg"},C={class:"text-2xl font-bold text-blue-600"},R={class:"text-center p-3 bg-green-50 rounded-lg"},S={class:"text-2xl font-bold text-green-600"},j={class:"text-center p-3 bg-yellow-50 rounded-lg"},P={class:"text-2xl font-bold text-yellow-600"},$={class:"text-center p-3 bg-purple-50 rounded-lg"},k={class:"text-2xl font-bold text-purple-600"},T={class:"h-60 mb-4"},_={class:"overflow-auto"},A={class:"w-full text-sm"},E={class:"py-2"},F={class:"flex items-center"},W={class:"py-2"},z={class:"py-2"},G={class:"py-2"},L={class:"py-2"},N=t({__name:"activity-lottery",setup(t){const N=e(),D=l({totalActivities:0,totalParticipants:0,totalDraws:0,winnerCount:0,overallWinRate:0,costControl:0}),M=l([]),O=e(!1),U=t=>t.winRate>2?"success":t.winRate>1?"warning":"info",q=t=>t.winRate>2?"高中奖率":t.winRate>1?"中等中奖率":"低中奖率",B=(t,e)=>({1:`rgba(251, 191, 36, ${e})`,2:`rgba(156, 163, 175, ${e})`,3:`rgba(251, 146, 60, ${e})`,4:`rgba(96, 165, 250, ${e})`,5:`rgba(52, 211, 153, ${e})`}[t]||`rgba(156, 163, 175, ${e})`),H=()=>{return t=this,e=null,l=function*(){O.value=!0;try{const[t,e]=yield Promise.all([p("7d"),u("7d")]);Object.assign(D,t),M.splice(0,M.length,...e),f(()=>{(()=>{if(!N.value||0===M.length)return;const t=N.value,e=t.getContext("2d");if(!e)return;e.clearRect(0,0,t.width,t.height);const l=40,a=t.width-80,s=t.height-80,r=Math.max(...M.map(t=>t.winnerCount)),o=a/M.length*.6,n=a/M.length*.4;M.forEach((t,a)=>{const i=l+a*(o+n)+n/2,c=t.winnerCount/r*s,d=l+s-c,g=e.createLinearGradient(i,d+c,i,d);g.addColorStop(0,B(t.level,.8)),g.addColorStop(1,B(t.level,1)),e.fillStyle=g,e.fillRect(i,d,o,c),e.fillStyle="#333",e.font="12px sans-serif",e.textAlign="center",e.fillText(t.winnerCount.toString(),i+o/2,d-5),e.fillText(t.levelName,i+o/2,l+s+20)}),e.strokeStyle="#e0e0e0",e.lineWidth=1,e.beginPath(),e.moveTo(l,l),e.lineTo(l,l+s),e.lineTo(l+a,l+s),e.stroke()})()})}catch(t){}finally{O.value=!1}},new Promise((a,s)=>{var r=t=>{try{n(l.next(t))}catch(e){s(e)}},o=t=>{try{n(l.throw(t))}catch(e){s(e)}},n=t=>t.done?a(t.value):Promise.resolve(t.value).then(r,o);n((l=l.apply(t,e)).next())});var t,e,l};return a(()=>{H()}),(t,e)=>{const l=m;return r(),s("div",b,[e[5]||(e[5]=o("div",{class:"art-card-header"},[o("div",{class:"title"},[o("h4",null,"活动抽奖效果分析"),o("p",null,"优化中奖概率,控制活动成本")])],-1)),o("div",h,[o("div",y,[o("div",w,[o("div",C,n(D.totalActivities),1),e[0]||(e[0]=o("div",{class:"text-sm text-g-500"},"活动总数",-1))]),o("div",R,[o("div",S,n((a=D.totalParticipants,a>=1e4?(a/1e4).toFixed(1)+"w":a>=1e3?(a/1e3).toFixed(1)+"k":a.toString())),1),e[1]||(e[1]=o("div",{class:"text-sm text-g-500"},"参与人数",-1))]),o("div",j,[o("div",P,n(D.overallWinRate)+"%",1),e[2]||(e[2]=o("div",{class:"text-sm text-g-500"},"整体中奖率",-1))]),o("div",$,[o("div",k,n(D.costControl)+"%",1),e[3]||(e[3]=o("div",{class:"text-sm text-g-500"},"成本控制",-1))])]),o("div",T,[o("canvas",{ref_key:"chartRef",ref:N,width:"400",height:"240"},null,512)]),o("div",_,[o("table",A,[e[4]||(e[4]=o("thead",null,[o("tr",{class:"border-b border-g-200"},[o("th",{class:"text-left py-2"},"奖级"),o("th",{class:"text-left py-2"},"中奖人数"),o("th",{class:"text-left py-2"},"中奖率"),o("th",{class:"text-left py-2"},"成本"),o("th",{class:"text-left py-2"},"状态")])],-1)),o("tbody",null,[(r(!0),s(i,null,c(M,t=>{return r(),s("tr",{key:t.level,class:"border-b border-g-100 hover:bg-g-50"},[o("td",E,[o("div",F,[o("span",{class:g(["w-6 h-6 rounded-full flex items-center justify-center text-white text-xs font-bold mr-2",(e=t.level,{1:"bg-gradient-to-r from-yellow-400 to-yellow-300",2:"bg-gradient-to-r from-gray-400 to-gray-300",3:"bg-gradient-to-r from-orange-400 to-orange-300",4:"bg-gradient-to-r from-blue-400 to-blue-300",5:"bg-gradient-to-r from-green-400 to-green-300"}[e]||"bg-gradient-to-r from-gray-400 to-gray-300")])},n(t.level),3),d(" "+n(t.levelName),1)])]),o("td",W,n(t.winnerCount)+"人",1),o("td",z,n(t.winRate)+"%",1),o("td",G,"¥"+n(t.cost.toLocaleString()),1),o("td",L,[x(l,{type:U(t),size:"small"},{default:v(()=>[d(n(q(t)),1)]),_:2},1032,["type"])])]);var e}),128))])])])])]);var a}}});export{N as default};
|
||||
@ -1 +0,0 @@
|
||||
import{_ as i}from"./activity-prize-analysis.vue_vue_type_script_setup_true_lang-D1MExuFt.js";import"./index-ip5gDR6L.js";import"./el-progress-DSvqWOWu.js";/* empty css *//* empty css *//* empty css *//* empty css *//* empty css */import"./operations-Cj-RMt0B.js";import"./index-Br3btmwp.js";import"./index-C9KXwj2c.js";import"./index-fnD_mPvE.js";import"./index-CHAf6FpU.js";import"./use-form-item-BDb3ZgPk.js";import"./use-form-common-props-BwBs_jod.js";import"./index-DbRz3Zuu.js";import"./index-D1xXI5gl.js";import"./token-DWNpOE8r.js";import"./index-D-10FtUD.js";import"./scroll-ChxXkPhM.js";import"./debounce-B9s63_mG.js";import"./index-Jz5GfFad.js";import"./vnode-6FKeug3R.js";export{i as default};
|
||||
@ -0,0 +1 @@
|
||||
import{_ as o}from"./activity-prize-analysis.vue_vue_type_script_setup_true_lang-bThIk24K.js";import"./index-DBHnxsy6.js";import"./el-progress-DcK0UPpw.js";/* empty css *//* empty css *//* empty css *//* empty css *//* empty css */import"./operations-CsHdqaV1.js";import"./index-CHZ9Ls9_.js";import"./index-B99ckkYy.js";import"./index-nZra831X.js";import"./index-MOpuiu9Z.js";import"./use-form-item-ByasanrX.js";import"./use-form-common-props-BOfz9xGR.js";import"./index-sqhzrW2O.js";import"./index-D2YpA_om.js";import"./token-DWNpOE8r.js";import"./index-DZO7eApA.js";import"./scroll-Cceui3yC.js";import"./debounce-CkazOn_9.js";import"./_baseIteratee-DRiXPtOg.js";import"./index-BC2pRl3S.js";import"./vnode-CqxKQBn0.js";export{o as default};
|
||||
File diff suppressed because one or more lines are too long
@ -0,0 +1 @@
|
||||
.search-card[data-v-82eaff85]{margin-bottom:16px}[data-v-82eaff85] .el-card__body{padding-bottom:0}
|
||||
1
build/resources/admin/assets/activity-search-61nDqo2V.js
Normal file
1
build/resources/admin/assets/activity-search-61nDqo2V.js
Normal file
@ -0,0 +1 @@
|
||||
var e=Object.defineProperty,a=Object.getOwnPropertySymbols,l=Object.prototype.hasOwnProperty,t=Object.prototype.propertyIsEnumerable,o=(a,l,t)=>l in a?e(a,l,{enumerable:!0,configurable:!0,writable:!0,value:t}):a[l]=t,s=(e,s)=>{for(var r in s||(s={}))l.call(s,r)&&o(e,r,s[r]);if(a)for(var r of a(s))t.call(s,r)&&o(e,r,s[r]);return e};import{d as r,a as i,w as p,E as u,y as d,f as m,z as n,p as c,u as f,B as j,e as v,F as _,G as b,J as y,l as x,ab as h,aW as V}from"./index-DBHnxsy6.js";/* empty css *//* empty css *//* empty css *//* empty css *//* empty css *//* empty css *//* empty css *//* empty css *//* empty css *//* empty css */import{a as g}from"./adminActivities-C7lwI2HP.js";import{E,a as w}from"./index-zIv9y_XZ.js";import{E as O}from"./index-BIIDnOvF.js";import{E as C}from"./index-B5f0OwDI.js";import{E as P}from"./index-qSQlj0RO.js";import{E as k,a as I}from"./index-CHZ9Ls9_.js";import{E as J}from"./index-Bi8tRqjc.js";import{E as U}from"./index-r0EsCkBw.js";import{E as z}from"./index-Bh7aAHNM.js";import{_ as B}from"./_plugin-vue_export-helper-BCo6x5W8.js";import"./use-form-common-props-BOfz9xGR.js";import"./index-DZO7eApA.js";import"./index-MOpuiu9Z.js";import"./index-nZra831X.js";import"./use-form-item-ByasanrX.js";import"./_initCloneObject-7oWMW-Ot.js";import"./index-B99ckkYy.js";import"./index-sqhzrW2O.js";import"./index-D2YpA_om.js";import"./token-DWNpOE8r.js";import"./scroll-Cceui3yC.js";import"./debounce-CkazOn_9.js";import"./_baseIteratee-DRiXPtOg.js";import"./index-BC2pRl3S.js";import"./vnode-CqxKQBn0.js";import"./index-DfWQjCuY.js";const F=B(r({__name:"activity-search",props:{modelValue:{}},emits:["update:modelValue","search","reset"],setup(e,{emit:a}){const l=e,t=a,o=i([]),r=i(s({},l.modelValue));p(()=>l.modelValue,(e,a)=>{JSON.stringify(e)!==JSON.stringify(a)&&(r.value=s({},e))},{deep:!0});let B=null;p(r,e=>{B&&clearTimeout(B),B=setTimeout(()=>{t("update:modelValue",s({},e))},100)},{deep:!0});const F=e=>{return a=this,l=null,t=function*(){if(e&&0===o.value.length)try{const e=yield g();o.value=e.list||[]}catch(a){}},new Promise((e,o)=>{var s=e=>{try{i(t.next(e))}catch(a){o(a)}},r=e=>{try{i(t.throw(e))}catch(a){o(a)}},i=a=>a.done?e(a.value):Promise.resolve(a.value).then(s,r);i((t=t.apply(a,l)).next())});var a,l,t},G=()=>{t("search",r.value)},S=()=>{r.value={name:void 0,category_id:void 0,status:void 0,is_boss:void 0},t("reset")};return(a,l)=>{const t=u("ripple");return m(),d(f(z),{class:"search-card",shadow:"never"},{default:n(()=>[c(f(E),{ref:"formRef",model:e.modelValue,"label-width":"80px"},{default:n(()=>[c(f(O),{gutter:20},{default:n(()=>[c(f(C),{span:6},{default:n(()=>[c(f(w),{label:"活动名称",prop:"name"},{default:n(()=>[c(f(P),{modelValue:r.value.name,"onUpdate:modelValue":l[0]||(l[0]=e=>r.value.name=e),placeholder:"请输入活动名称",clearable:"",onKeyup:j(G,["enter"])},null,8,["modelValue"])]),_:1})]),_:1}),c(f(C),{span:6},{default:n(()=>[c(f(w),{label:"分类",prop:"category_id"},{default:n(()=>[c(f(k),{modelValue:r.value.category_id,"onUpdate:modelValue":l[1]||(l[1]=e=>r.value.category_id=e),placeholder:"请选择分类",clearable:"",onVisibleChange:F},{default:n(()=>[(m(!0),v(_,null,b(o.value,e=>(m(),d(f(I),{key:e.id,label:e.name,value:e.id},null,8,["label","value"]))),128))]),_:1},8,["modelValue"])]),_:1})]),_:1}),c(f(C),{span:6},{default:n(()=>[c(f(w),{label:"状态",prop:"status"},{default:n(()=>[c(f(k),{modelValue:r.value.status,"onUpdate:modelValue":l[2]||(l[2]=e=>r.value.status=e),placeholder:"请选择状态",clearable:""},{default:n(()=>[c(f(I),{value:1,label:"进行中"}),c(f(I),{value:2,label:"下线"})]),_:1},8,["modelValue"])]),_:1})]),_:1}),c(f(C),{span:6},{default:n(()=>[c(f(w),{label:"Boss活动",prop:"is_boss"},{default:n(()=>[c(f(k),{modelValue:r.value.is_boss,"onUpdate:modelValue":l[3]||(l[3]=e=>r.value.is_boss=e),placeholder:"请选择",clearable:""},{default:n(()=>[c(f(I),{value:1,label:"是"}),c(f(I),{value:0,label:"否"})]),_:1},8,["modelValue"])]),_:1})]),_:1}),c(f(C),{span:4},{default:n(()=>[c(f(w),{"label-width":"0"},{default:n(()=>[c(f(J),null,{default:n(()=>[y((m(),d(f(U),{type:"primary",onClick:G},{default:n(()=>[c(f(h),{class:"mr-1"},{default:n(()=>[c(f(V))]),_:1}),l[4]||(l[4]=x(" 搜索 ",-1))]),_:1})),[[t]]),y((m(),d(f(U),{onClick:S},{default:n(()=>[...l[5]||(l[5]=[x("重置",-1)])]),_:1})),[[t]])]),_:1})]),_:1})]),_:1})]),_:1})]),_:1},8,["model"])]),_:1})}}}),[["__scopeId","data-v-82eaff85"]]);export{F as default};
|
||||
@ -1 +0,0 @@
|
||||
.search-card[data-v-00083c50]{margin-bottom:16px}[data-v-00083c50] .el-card__body{padding-bottom:0}
|
||||
@ -1 +0,0 @@
|
||||
var e=Object.defineProperty,a=Object.getOwnPropertySymbols,l=Object.prototype.hasOwnProperty,t=Object.prototype.propertyIsEnumerable,o=(a,l,t)=>l in a?e(a,l,{enumerable:!0,configurable:!0,writable:!0,value:t}):a[l]=t,s=(e,s)=>{for(var r in s||(s={}))l.call(s,r)&&o(e,r,s[r]);if(a)for(var r of a(s))t.call(s,r)&&o(e,r,s[r]);return e};import{d as r,a as i,w as u,E as d,y as p,f as m,z as n,p as c,u as f,B as j,e as v,F as b,G as _,J as y,l as h,ab as x,a$ as V}from"./index-ip5gDR6L.js";/* empty css *//* empty css *//* empty css *//* empty css *//* empty css *//* empty css *//* empty css *//* empty css *//* empty css */import{a as g}from"./adminActivities-d_jfLDps.js";import{E,a as w}from"./index-CxGOZAw2.js";import{E as O}from"./index-Ca1H-sCf.js";import{E as k}from"./index-D5xppMej.js";import{E as P}from"./index-DfrOVkw6.js";import{E as C,a as U}from"./index-Br3btmwp.js";import{E as J}from"./index-BY5Lb5w1.js";import{E as S}from"./index-opP5No9I.js";import{E as $}from"./index-BBSYL4g8.js";import{_ as B}from"./_plugin-vue_export-helper-BCo6x5W8.js";import"./use-form-common-props-BwBs_jod.js";import"./index-D-10FtUD.js";import"./index-CHAf6FpU.js";import"./index-fnD_mPvE.js";import"./use-form-item-BDb3ZgPk.js";import"./_initCloneObject-DwsgJamq.js";import"./index-C9KXwj2c.js";import"./index-DbRz3Zuu.js";import"./index-D1xXI5gl.js";import"./token-DWNpOE8r.js";import"./scroll-ChxXkPhM.js";import"./debounce-B9s63_mG.js";import"./index-Jz5GfFad.js";import"./vnode-6FKeug3R.js";import"./index-D-iZIej9.js";const I=B(r({__name:"activity-search",props:{modelValue:{}},emits:["update:modelValue","search","reset"],setup(e,{emit:a}){const l=e,t=a,o=i([]),r=i(s({},l.modelValue));u(()=>l.modelValue,(e,a)=>{JSON.stringify(e)!==JSON.stringify(a)&&(r.value=s({},e))},{deep:!0});let B=null;u(r,e=>{B&&clearTimeout(B),B=setTimeout(()=>{t("update:modelValue",s({},e))},100)},{deep:!0});const I=e=>{return a=this,l=null,t=function*(){if(e&&0===o.value.length)try{const e=yield g();o.value=e.list||[]}catch(a){}},new Promise((e,o)=>{var s=e=>{try{i(t.next(e))}catch(a){o(a)}},r=e=>{try{i(t.throw(e))}catch(a){o(a)}},i=a=>a.done?e(a.value):Promise.resolve(a.value).then(s,r);i((t=t.apply(a,l)).next())});var a,l,t},K=()=>{t("search",r.value)},N=()=>{r.value={name:void 0,category_id:void 0,status:void 0,is_boss:void 0},t("reset")};return(a,l)=>{const t=d("ripple");return m(),p(f($),{class:"search-card",shadow:"never"},{default:n(()=>[c(f(E),{ref:"formRef",model:e.modelValue,"label-width":"80px"},{default:n(()=>[c(f(O),{gutter:20},{default:n(()=>[c(f(k),{span:6},{default:n(()=>[c(f(w),{label:"活动名称",prop:"name"},{default:n(()=>[c(f(P),{modelValue:r.value.name,"onUpdate:modelValue":l[0]||(l[0]=e=>r.value.name=e),placeholder:"请输入活动名称",clearable:"",onKeyup:j(K,["enter"])},null,8,["modelValue"])]),_:1})]),_:1}),c(f(k),{span:6},{default:n(()=>[c(f(w),{label:"分类",prop:"category_id"},{default:n(()=>[c(f(C),{modelValue:r.value.category_id,"onUpdate:modelValue":l[1]||(l[1]=e=>r.value.category_id=e),placeholder:"请选择分类",clearable:"",onVisibleChange:I},{default:n(()=>[(m(!0),v(b,null,_(o.value,e=>(m(),p(f(U),{key:e.id,label:e.name,value:e.id},null,8,["label","value"]))),128))]),_:1},8,["modelValue"])]),_:1})]),_:1}),c(f(k),{span:6},{default:n(()=>[c(f(w),{label:"状态",prop:"status"},{default:n(()=>[c(f(C),{modelValue:r.value.status,"onUpdate:modelValue":l[2]||(l[2]=e=>r.value.status=e),placeholder:"请选择状态",clearable:""},{default:n(()=>[c(f(U),{value:1,label:"进行中"}),c(f(U),{value:2,label:"下线"})]),_:1},8,["modelValue"])]),_:1})]),_:1}),c(f(k),{span:6},{default:n(()=>[c(f(w),{label:"Boss活动",prop:"is_boss"},{default:n(()=>[c(f(C),{modelValue:r.value.is_boss,"onUpdate:modelValue":l[3]||(l[3]=e=>r.value.is_boss=e),placeholder:"请选择",clearable:""},{default:n(()=>[c(f(U),{value:1,label:"是"}),c(f(U),{value:0,label:"否"})]),_:1},8,["modelValue"])]),_:1})]),_:1}),c(f(k),{span:4},{default:n(()=>[c(f(w),{"label-width":"0"},{default:n(()=>[c(f(J),null,{default:n(()=>[y((m(),p(f(S),{type:"primary",onClick:K},{default:n(()=>[c(f(x),{class:"mr-1"},{default:n(()=>[c(f(V))]),_:1}),l[4]||(l[4]=h(" 搜索 ",-1))]),_:1})),[[t]]),y((m(),p(f(S),{onClick:N},{default:n(()=>[...l[5]||(l[5]=[h("重置",-1)])]),_:1})),[[t]])]),_:1})]),_:1})]),_:1})]),_:1})]),_:1},8,["model"])]),_:1})}}}),[["__scopeId","data-v-00083c50"]]);export{I as default};
|
||||
@ -1 +0,0 @@
|
||||
import{_ as o}from"./add-coupon-dialog.vue_vue_type_script_setup_true_lang-IOaUwwXz.js";import"./index-ip5gDR6L.js";/* empty css *//* empty css *//* empty css *//* empty css *//* empty css *//* empty css *//* empty css *//* empty css */import"./coupons-DhPZnSZd.js";import"./index-CxGOZAw2.js";import"./use-form-common-props-BwBs_jod.js";import"./index-D-10FtUD.js";import"./index-CHAf6FpU.js";import"./index-fnD_mPvE.js";import"./use-form-item-BDb3ZgPk.js";import"./_initCloneObject-DwsgJamq.js";import"./index-Br3btmwp.js";import"./index-C9KXwj2c.js";import"./index-DbRz3Zuu.js";import"./index-D1xXI5gl.js";import"./token-DWNpOE8r.js";import"./scroll-ChxXkPhM.js";import"./debounce-B9s63_mG.js";import"./index-Jz5GfFad.js";import"./vnode-6FKeug3R.js";import"./index-opP5No9I.js";import"./index-D-iZIej9.js";import"./index-BpP4itD-.js";import"./index-D17cYxP7.js";import"./use-dialog-Cj8YneNA.js";import"./index-D6zsJoyl.js";import"./refs-Cw5r5QN8.js";export{o as default};
|
||||
@ -0,0 +1 @@
|
||||
import{_ as o}from"./add-coupon-dialog.vue_vue_type_script_setup_true_lang-C0dOKfzY.js";import"./index-DBHnxsy6.js";/* empty css *//* empty css *//* empty css *//* empty css *//* empty css *//* empty css *//* empty css *//* empty css */import"./coupons-BX1h_qVI.js";import"./index-zIv9y_XZ.js";import"./use-form-common-props-BOfz9xGR.js";import"./index-DZO7eApA.js";import"./index-MOpuiu9Z.js";import"./index-nZra831X.js";import"./use-form-item-ByasanrX.js";import"./_initCloneObject-7oWMW-Ot.js";import"./index-CHZ9Ls9_.js";import"./index-B99ckkYy.js";import"./index-sqhzrW2O.js";import"./index-D2YpA_om.js";import"./token-DWNpOE8r.js";import"./scroll-Cceui3yC.js";import"./debounce-CkazOn_9.js";import"./_baseIteratee-DRiXPtOg.js";import"./index-BC2pRl3S.js";import"./vnode-CqxKQBn0.js";import"./index-r0EsCkBw.js";import"./index-DfWQjCuY.js";import"./index-CtUPMuT5.js";import"./index-DeCbFeva.js";import"./use-dialog-yhiLZsn9.js";import"./index-DvlZWusB.js";import"./refs-Cw5r5QN8.js";export{o as default};
|
||||
@ -1 +1 @@
|
||||
var e=(e,a,l)=>new Promise((o,t)=>{var i=e=>{try{r(l.next(e))}catch(a){t(a)}},s=e=>{try{r(l.throw(e))}catch(a){t(a)}},r=e=>e.done?o(e.value):Promise.resolve(e.value).then(i,s);r((l=l.apply(e,a)).next())});import{d as a,a as l,r as o,w as t,o as i,y as s,f as r,z as d,p as u,u as n,e as p,F as m,G as c,l as v}from"./index-ip5gDR6L.js";/* empty css *//* empty css *//* empty css *//* empty css *//* empty css *//* empty css *//* empty css *//* empty css */import{c as f}from"./coupons-DhPZnSZd.js";import{E as y,a as b}from"./index-CxGOZAw2.js";import{E as j,a as h}from"./index-Br3btmwp.js";import{E as g}from"./index-opP5No9I.js";import{E as _}from"./index-BpP4itD-.js";const x=a({__name:"add-coupon-dialog",props:{visible:{type:Boolean}},emits:["update:visible","submit"],setup(a,{emit:x}){const I=a,w=x,k=l(),V=l(!1),E=l([]),C=o({couponId:null});t(()=>I.visible,e=>{});const $={couponId:[{required:!0,message:"请选择优惠券",trigger:"change"}]},z=()=>e(this,null,function*(){var e;try{yield null==(e=k.value)?void 0:e.validate(),V.value=!0,w("submit",{coupon_id:C.couponId})}catch(a){}finally{V.value=!1}}),A=()=>{w("update:visible",!1)},M=()=>{var e;C.couponId=null,null==(e=k.value)||e.clearValidate()};return i(()=>e(this,null,function*(){try{const e=yield f.getList({status:1,page:1,page_size:100});E.value=Array.isArray(e.list)?e.list.map(e=>({id:e.id,name:e.name})):[]}catch(e){E.value=[]}})),(e,l)=>(r(),s(n(_),{"model-value":a.visible,title:"发放优惠券",width:"400px","close-on-click-modal":!1,"onUpdate:modelValue":l[1]||(l[1]=e=>w("update:visible",e)),onClosed:M},{footer:d(()=>[u(n(g),{onClick:A},{default:d(()=>[...l[2]||(l[2]=[v("取消",-1)])]),_:1}),u(n(g),{type:"primary",loading:V.value,onClick:z},{default:d(()=>[...l[3]||(l[3]=[v(" 确定 ",-1)])]),_:1},8,["loading"])]),default:d(()=>[u(n(y),{ref_key:"formRef",ref:k,model:C,rules:$,"label-width":"80px"},{default:d(()=>[u(n(b),{label:"优惠券",prop:"couponId"},{default:d(()=>[u(n(j),{modelValue:C.couponId,"onUpdate:modelValue":l[0]||(l[0]=e=>C.couponId=e),placeholder:"请选择优惠券",filterable:"",style:{width:"100%"}},{default:d(()=>[(r(!0),p(m,null,c(E.value,e=>(r(),s(n(h),{key:e.id,label:`${e.name}(ID: ${e.id})`,value:e.id},null,8,["label","value"]))),128))]),_:1},8,["modelValue"])]),_:1})]),_:1},8,["model"])]),_:1},8,["model-value"]))}});export{x as _};
|
||||
var e=(e,a,l)=>new Promise((o,t)=>{var i=e=>{try{r(l.next(e))}catch(a){t(a)}},s=e=>{try{r(l.throw(e))}catch(a){t(a)}},r=e=>e.done?o(e.value):Promise.resolve(e.value).then(i,s);r((l=l.apply(e,a)).next())});import{d as a,a as l,r as o,w as t,o as i,y as s,f as r,z as d,p as u,u as n,e as p,F as m,G as c,l as v}from"./index-DBHnxsy6.js";/* empty css *//* empty css *//* empty css *//* empty css *//* empty css *//* empty css *//* empty css *//* empty css */import{c as f}from"./coupons-BX1h_qVI.js";import{E as y,a as b}from"./index-zIv9y_XZ.js";import{E as j,a as g}from"./index-CHZ9Ls9_.js";import{E as h}from"./index-r0EsCkBw.js";import{E as _}from"./index-CtUPMuT5.js";const x=a({__name:"add-coupon-dialog",props:{visible:{type:Boolean}},emits:["update:visible","submit"],setup(a,{emit:x}){const I=a,w=x,k=l(),V=l(!1),C=l([]),E=o({couponId:null});t(()=>I.visible,e=>{});const A={couponId:[{required:!0,message:"请选择优惠券",trigger:"change"}]},z=()=>e(this,null,function*(){var e;try{yield null==(e=k.value)?void 0:e.validate(),V.value=!0,w("submit",{coupon_id:E.couponId})}catch(a){}finally{V.value=!1}}),B=()=>{w("update:visible",!1)},P=()=>{var e;E.couponId=null,null==(e=k.value)||e.clearValidate()};return i(()=>e(this,null,function*(){try{const e=yield f.getList({status:1,page:1,page_size:100});C.value=Array.isArray(e.list)?e.list.map(e=>({id:e.id,name:e.name})):[]}catch(e){C.value=[]}})),(e,l)=>(r(),s(n(_),{"model-value":a.visible,title:"发放优惠券",width:"400px","close-on-click-modal":!1,"onUpdate:modelValue":l[1]||(l[1]=e=>w("update:visible",e)),onClosed:P},{footer:d(()=>[u(n(h),{onClick:B},{default:d(()=>[...l[2]||(l[2]=[v("取消",-1)])]),_:1}),u(n(h),{type:"primary",loading:V.value,onClick:z},{default:d(()=>[...l[3]||(l[3]=[v(" 确定 ",-1)])]),_:1},8,["loading"])]),default:d(()=>[u(n(y),{ref_key:"formRef",ref:k,model:E,rules:A,"label-width":"80px"},{default:d(()=>[u(n(b),{label:"优惠券",prop:"couponId"},{default:d(()=>[u(n(j),{modelValue:E.couponId,"onUpdate:modelValue":l[0]||(l[0]=e=>E.couponId=e),placeholder:"请选择优惠券",filterable:"",style:{width:"100%"}},{default:d(()=>[(r(!0),p(m,null,c(C.value,e=>(r(),s(n(g),{key:e.id,label:`${e.name}(ID: ${e.id})`,value:e.id},null,8,["label","value"]))),128))]),_:1},8,["modelValue"])]),_:1})]),_:1},8,["model"])]),_:1},8,["model-value"]))}});export{x as _};
|
||||
@ -0,0 +1 @@
|
||||
import{_ as i}from"./add-item-card-dialog.vue_vue_type_script_setup_true_lang-r-95eO1C.js";import"./index-DBHnxsy6.js";/* empty css *//* empty css *//* empty css *//* empty css *//* empty css *//* empty css *//* empty css *//* empty css *//* empty css *//* empty css */import"./itemCards-BHUYJ5ez.js";import"./index-zIv9y_XZ.js";import"./use-form-common-props-BOfz9xGR.js";import"./index-DZO7eApA.js";import"./index-MOpuiu9Z.js";import"./index-nZra831X.js";import"./use-form-item-ByasanrX.js";import"./_initCloneObject-7oWMW-Ot.js";import"./index-CHZ9Ls9_.js";import"./index-B99ckkYy.js";import"./index-sqhzrW2O.js";import"./index-D2YpA_om.js";import"./token-DWNpOE8r.js";import"./scroll-Cceui3yC.js";import"./debounce-CkazOn_9.js";import"./_baseIteratee-DRiXPtOg.js";import"./index-BC2pRl3S.js";import"./vnode-CqxKQBn0.js";import"./index-B4jq9Ja2.js";import"./index-qSQlj0RO.js";import"./index-CuWUnlSZ.js";import"./index-r0EsCkBw.js";import"./index-DfWQjCuY.js";import"./index-CtUPMuT5.js";import"./index-DeCbFeva.js";import"./use-dialog-yhiLZsn9.js";import"./index-DvlZWusB.js";import"./refs-Cw5r5QN8.js";export{i as default};
|
||||
@ -1 +0,0 @@
|
||||
import{_ as i}from"./add-item-card-dialog.vue_vue_type_script_setup_true_lang-BbAo8kLg.js";import"./index-ip5gDR6L.js";/* empty css *//* empty css *//* empty css *//* empty css *//* empty css *//* empty css *//* empty css *//* empty css *//* empty css *//* empty css */import"./itemCards-C_dE3q5e.js";import"./index-CxGOZAw2.js";import"./use-form-common-props-BwBs_jod.js";import"./index-D-10FtUD.js";import"./index-CHAf6FpU.js";import"./index-fnD_mPvE.js";import"./use-form-item-BDb3ZgPk.js";import"./_initCloneObject-DwsgJamq.js";import"./index-Br3btmwp.js";import"./index-C9KXwj2c.js";import"./index-DbRz3Zuu.js";import"./index-D1xXI5gl.js";import"./token-DWNpOE8r.js";import"./scroll-ChxXkPhM.js";import"./debounce-B9s63_mG.js";import"./index-Jz5GfFad.js";import"./vnode-6FKeug3R.js";import"./index-HtXsSizM.js";import"./index-DfrOVkw6.js";import"./index-BbcTNOqF.js";import"./index-opP5No9I.js";import"./index-D-iZIej9.js";import"./index-BpP4itD-.js";import"./index-D17cYxP7.js";import"./use-dialog-Cj8YneNA.js";import"./index-D6zsJoyl.js";import"./refs-Cw5r5QN8.js";export{i as default};
|
||||
@ -1 +0,0 @@
|
||||
var e=(e,a,l)=>new Promise((t,i)=>{var r=e=>{try{s(l.next(e))}catch(a){i(a)}},o=e=>{try{s(l.throw(e))}catch(a){i(a)}},s=e=>e.done?t(e.value):Promise.resolve(e.value).then(r,o);s((l=l.apply(e,a)).next())});import{d as a,a as l,r as t,w as i,o as r,y as o,f as s,z as d,p as u,u as n,e as m,F as p,G as c,l as v}from"./index-ip5gDR6L.js";/* empty css *//* empty css *//* empty css *//* empty css *//* empty css *//* empty css *//* empty css *//* empty css *//* empty css *//* empty css */import{i as y}from"./itemCards-C_dE3q5e.js";import{E as f,a as b}from"./index-CxGOZAw2.js";import{E as j,a as g}from"./index-Br3btmwp.js";import{E as h}from"./index-HtXsSizM.js";import{E as _}from"./index-opP5No9I.js";import{E as x}from"./index-BpP4itD-.js";const q=a({__name:"add-item-card-dialog",props:{visible:{type:Boolean}},emits:["update:visible","submit"],setup(a,{emit:q}){const I=a,V=q,w=l(),k=l(!1),E=l([]),C=t({cardId:null,quantity:1});i(()=>I.visible,e=>{});const U={cardId:[{required:!0,message:"请选择道具卡",trigger:"change"}],quantity:[{required:!0,message:"请输入数量",trigger:"change"}]},$=()=>e(this,null,function*(){var e;try{yield null==(e=w.value)?void 0:e.validate(),k.value=!0,V("submit",{card_id:C.cardId,quantity:C.quantity})}catch(a){}finally{k.value=!1}}),z=()=>{V("update:visible",!1)},A=()=>{var e;C.cardId=null,C.quantity=1,null==(e=w.value)||e.clearValidate()};return r(()=>e(this,null,function*(){try{const e=yield y.getList({page:1,page_size:100});E.value=Array.isArray(e.list)?e.list.map(e=>({id:e.id,name:e.name})):[]}catch(e){E.value=[]}})),(e,l)=>(s(),o(n(x),{"model-value":a.visible,title:"分配道具卡",width:"420px","close-on-click-modal":!1,"onUpdate:modelValue":l[2]||(l[2]=e=>V("update:visible",e)),onClosed:A},{footer:d(()=>[u(n(_),{onClick:z},{default:d(()=>[...l[3]||(l[3]=[v("取消",-1)])]),_:1}),u(n(_),{type:"primary",loading:k.value,onClick:$},{default:d(()=>[...l[4]||(l[4]=[v("确定",-1)])]),_:1},8,["loading"])]),default:d(()=>[u(n(f),{ref_key:"formRef",ref:w,model:C,rules:U,"label-width":"80px"},{default:d(()=>[u(n(b),{label:"道具卡",prop:"cardId"},{default:d(()=>[u(n(j),{modelValue:C.cardId,"onUpdate:modelValue":l[0]||(l[0]=e=>C.cardId=e),placeholder:"请选择道具卡",filterable:"",style:{width:"100%"}},{default:d(()=>[(s(!0),m(p,null,c(E.value,e=>(s(),o(n(g),{key:e.id,label:`${e.name}(ID: ${e.id})`,value:e.id},null,8,["label","value"]))),128))]),_:1},8,["modelValue"])]),_:1}),u(n(b),{label:"数量",prop:"quantity"},{default:d(()=>[u(n(h),{modelValue:C.quantity,"onUpdate:modelValue":l[1]||(l[1]=e=>C.quantity=e),min:1,max:100},null,8,["modelValue"])]),_:1})]),_:1},8,["model"])]),_:1},8,["model-value"]))}});export{q as _};
|
||||
@ -0,0 +1 @@
|
||||
var e=(e,a,l)=>new Promise((t,i)=>{var r=e=>{try{s(l.next(e))}catch(a){i(a)}},o=e=>{try{s(l.throw(e))}catch(a){i(a)}},s=e=>e.done?t(e.value):Promise.resolve(e.value).then(r,o);s((l=l.apply(e,a)).next())});import{d as a,a as l,r as t,w as i,o as r,y as o,f as s,z as d,p as u,u as n,e as m,F as p,G as c,l as v}from"./index-DBHnxsy6.js";/* empty css *//* empty css *//* empty css *//* empty css *//* empty css *//* empty css *//* empty css *//* empty css *//* empty css *//* empty css */import{i as y}from"./itemCards-BHUYJ5ez.js";import{E as f,a as b}from"./index-zIv9y_XZ.js";import{E as g,a as j}from"./index-CHZ9Ls9_.js";import{E as h}from"./index-B4jq9Ja2.js";import{E as _}from"./index-r0EsCkBw.js";import{E as x}from"./index-CtUPMuT5.js";const q=a({__name:"add-item-card-dialog",props:{visible:{type:Boolean}},emits:["update:visible","submit"],setup(a,{emit:q}){const I=a,V=q,w=l(),k=l(!1),C=l([]),E=t({cardId:null,quantity:1});i(()=>I.visible,e=>{});const A={cardId:[{required:!0,message:"请选择道具卡",trigger:"change"}],quantity:[{required:!0,message:"请输入数量",trigger:"change"}]},U=()=>e(this,null,function*(){var e;try{yield null==(e=w.value)?void 0:e.validate(),k.value=!0,V("submit",{card_id:E.cardId,quantity:E.quantity})}catch(a){}finally{k.value=!1}}),$=()=>{V("update:visible",!1)},z=()=>{var e;E.cardId=null,E.quantity=1,null==(e=w.value)||e.clearValidate()};return r(()=>e(this,null,function*(){try{const e=yield y.getList({page:1,page_size:100});C.value=Array.isArray(e.list)?e.list.map(e=>({id:e.id,name:e.name})):[]}catch(e){C.value=[]}})),(e,l)=>(s(),o(n(x),{"model-value":a.visible,title:"分配道具卡",width:"420px","close-on-click-modal":!1,"onUpdate:modelValue":l[2]||(l[2]=e=>V("update:visible",e)),onClosed:z},{footer:d(()=>[u(n(_),{onClick:$},{default:d(()=>[...l[3]||(l[3]=[v("取消",-1)])]),_:1}),u(n(_),{type:"primary",loading:k.value,onClick:U},{default:d(()=>[...l[4]||(l[4]=[v("确定",-1)])]),_:1},8,["loading"])]),default:d(()=>[u(n(f),{ref_key:"formRef",ref:w,model:E,rules:A,"label-width":"80px"},{default:d(()=>[u(n(b),{label:"道具卡",prop:"cardId"},{default:d(()=>[u(n(g),{modelValue:E.cardId,"onUpdate:modelValue":l[0]||(l[0]=e=>E.cardId=e),placeholder:"请选择道具卡",filterable:"",style:{width:"100%"}},{default:d(()=>[(s(!0),m(p,null,c(C.value,e=>(s(),o(n(j),{key:e.id,label:`${e.name}(ID: ${e.id})`,value:e.id},null,8,["label","value"]))),128))]),_:1},8,["modelValue"])]),_:1}),u(n(b),{label:"数量",prop:"quantity"},{default:d(()=>[u(n(h),{modelValue:E.quantity,"onUpdate:modelValue":l[1]||(l[1]=e=>E.quantity=e),min:1,max:100},null,8,["modelValue"])]),_:1})]),_:1},8,["model"])]),_:1},8,["model-value"]))}});export{q as _};
|
||||
@ -0,0 +1 @@
|
||||
import{_ as i}from"./add-points-dialog.vue_vue_type_script_setup_true_lang-B99Uz4mM.js";import"./index-DBHnxsy6.js";/* empty css *//* empty css *//* empty css *//* empty css *//* empty css *//* empty css *//* empty css *//* empty css *//* empty css *//* empty css */import"./index-zIv9y_XZ.js";import"./use-form-common-props-BOfz9xGR.js";import"./index-DZO7eApA.js";import"./index-MOpuiu9Z.js";import"./index-nZra831X.js";import"./use-form-item-ByasanrX.js";import"./_initCloneObject-7oWMW-Ot.js";import"./index-B4jq9Ja2.js";import"./index-qSQlj0RO.js";import"./index-CuWUnlSZ.js";import"./index-CHZ9Ls9_.js";import"./index-B99ckkYy.js";import"./index-sqhzrW2O.js";import"./index-D2YpA_om.js";import"./token-DWNpOE8r.js";import"./scroll-Cceui3yC.js";import"./debounce-CkazOn_9.js";import"./_baseIteratee-DRiXPtOg.js";import"./index-BC2pRl3S.js";import"./vnode-CqxKQBn0.js";import"./index-r0EsCkBw.js";import"./index-DfWQjCuY.js";import"./index-CtUPMuT5.js";import"./index-DeCbFeva.js";import"./use-dialog-yhiLZsn9.js";import"./index-DvlZWusB.js";import"./refs-Cw5r5QN8.js";export{i as default};
|
||||
@ -1 +0,0 @@
|
||||
import{_ as i}from"./add-points-dialog.vue_vue_type_script_setup_true_lang-BG76LgaZ.js";import"./index-ip5gDR6L.js";/* empty css *//* empty css *//* empty css *//* empty css *//* empty css *//* empty css *//* empty css *//* empty css *//* empty css *//* empty css */import"./index-CxGOZAw2.js";import"./use-form-common-props-BwBs_jod.js";import"./index-D-10FtUD.js";import"./index-CHAf6FpU.js";import"./index-fnD_mPvE.js";import"./use-form-item-BDb3ZgPk.js";import"./_initCloneObject-DwsgJamq.js";import"./index-HtXsSizM.js";import"./index-DfrOVkw6.js";import"./index-BbcTNOqF.js";import"./index-Br3btmwp.js";import"./index-C9KXwj2c.js";import"./index-DbRz3Zuu.js";import"./index-D1xXI5gl.js";import"./token-DWNpOE8r.js";import"./scroll-ChxXkPhM.js";import"./debounce-B9s63_mG.js";import"./index-Jz5GfFad.js";import"./vnode-6FKeug3R.js";import"./index-opP5No9I.js";import"./index-D-iZIej9.js";import"./index-BpP4itD-.js";import"./index-D17cYxP7.js";import"./use-dialog-Cj8YneNA.js";import"./index-D6zsJoyl.js";import"./refs-Cw5r5QN8.js";export{i as default};
|
||||
@ -1 +1 @@
|
||||
import{d as e,a,r as l,w as i,y as r,f as o,z as t,p as d,u as s,l as n}from"./index-ip5gDR6L.js";/* empty css *//* empty css *//* empty css *//* empty css *//* empty css *//* empty css *//* empty css *//* empty css *//* empty css *//* empty css */import{E as m,a as p}from"./index-CxGOZAw2.js";import{E as u}from"./index-HtXsSizM.js";import{E as v,a as c}from"./index-Br3btmwp.js";import{E as f}from"./index-DfrOVkw6.js";import{E as b}from"./index-opP5No9I.js";import{E as k}from"./index-BpP4itD-.js";const _=e({__name:"add-points-dialog",props:{visible:{type:Boolean}},emits:["update:visible","submit"],setup(e,{emit:_}){const j=e,y=_,h=a(),g=a(!1),x=l({points:null,kind:"admin_add",remark:""});i(()=>j.visible,e=>{});const V={points:[{required:!0,message:"请输入积分数量",trigger:"blur"}],kind:[{required:!0,message:"请选择积分类型",trigger:"change"}]},w=()=>{return e=this,a=null,l=function*(){var e;try{yield null==(e=h.value)?void 0:e.validate(),g.value=!0,y("submit",{points:x.points,kind:x.kind,remark:x.remark})}catch(a){}finally{g.value=!1}},new Promise((i,r)=>{var o=e=>{try{d(l.next(e))}catch(a){r(a)}},t=e=>{try{d(l.throw(e))}catch(a){r(a)}},d=e=>e.done?i(e.value):Promise.resolve(e.value).then(o,t);d((l=l.apply(e,a)).next())});var e,a,l},E=()=>{y("update:visible",!1)},U=()=>{var e;x.points=null,x.kind="admin_add",x.remark="",null==(e=h.value)||e.clearValidate()};return(a,l)=>(o(),r(s(k),{"model-value":e.visible,title:"增加积分",width:"400px","close-on-click-modal":!1,"onUpdate:modelValue":l[3]||(l[3]=e=>y("update:visible",e)),onClosed:U},{footer:t(()=>[d(s(b),{onClick:E},{default:t(()=>[...l[4]||(l[4]=[n("取消",-1)])]),_:1}),d(s(b),{type:"primary",loading:g.value,onClick:w},{default:t(()=>[...l[5]||(l[5]=[n(" 确定 ",-1)])]),_:1},8,["loading"])]),default:t(()=>[d(s(m),{ref_key:"formRef",ref:h,model:x,rules:V,"label-width":"80px"},{default:t(()=>[d(s(p),{label:"积分数量",prop:"points"},{default:t(()=>[d(s(u),{modelValue:x.points,"onUpdate:modelValue":l[0]||(l[0]=e=>x.points=e),placeholder:"请输入积分数量",min:1,precision:0,style:{width:"100%"}},null,8,["modelValue"])]),_:1}),d(s(p),{label:"积分类型",prop:"kind"},{default:t(()=>[d(s(v),{modelValue:x.kind,"onUpdate:modelValue":l[1]||(l[1]=e=>x.kind=e),placeholder:"请选择积分类型",style:{width:"100%"}},{default:t(()=>[d(s(c),{label:"管理员增加",value:"admin_add"}),d(s(c),{label:"活动奖励",value:"activity_reward"}),d(s(c),{label:"签到奖励",value:"sign_reward"}),d(s(c),{label:"消费返还",value:"consume_return"})]),_:1},8,["modelValue"])]),_:1}),d(s(p),{label:"备注",prop:"remark"},{default:t(()=>[d(s(f),{modelValue:x.remark,"onUpdate:modelValue":l[2]||(l[2]=e=>x.remark=e),type:"textarea",placeholder:"请输入备注",rows:3},null,8,["modelValue"])]),_:1})]),_:1},8,["model"])]),_:1},8,["model-value"]))}});export{_};
|
||||
import{d as e,a,r as l,w as i,y as r,f as o,z as t,p as d,u as s,l as n}from"./index-DBHnxsy6.js";/* empty css *//* empty css *//* empty css *//* empty css *//* empty css *//* empty css *//* empty css *//* empty css *//* empty css *//* empty css */import{E as m,a as p}from"./index-zIv9y_XZ.js";import{E as u}from"./index-B4jq9Ja2.js";import{E as v,a as c}from"./index-CHZ9Ls9_.js";import{E as f}from"./index-qSQlj0RO.js";import{E as b}from"./index-r0EsCkBw.js";import{E as k}from"./index-CtUPMuT5.js";const _=e({__name:"add-points-dialog",props:{visible:{type:Boolean}},emits:["update:visible","submit"],setup(e,{emit:_}){const j=e,y=_,g=a(),h=a(!1),x=l({points:null,kind:"admin_add",remark:""});i(()=>j.visible,e=>{});const V={points:[{required:!0,message:"请输入积分数量",trigger:"blur"}],kind:[{required:!0,message:"请选择积分类型",trigger:"change"}]},w=()=>{return e=this,a=null,l=function*(){var e;try{yield null==(e=g.value)?void 0:e.validate(),h.value=!0,y("submit",{points:x.points,kind:x.kind,remark:x.remark})}catch(a){}finally{h.value=!1}},new Promise((i,r)=>{var o=e=>{try{d(l.next(e))}catch(a){r(a)}},t=e=>{try{d(l.throw(e))}catch(a){r(a)}},d=e=>e.done?i(e.value):Promise.resolve(e.value).then(o,t);d((l=l.apply(e,a)).next())});var e,a,l},E=()=>{y("update:visible",!1)},C=()=>{var e;x.points=null,x.kind="admin_add",x.remark="",null==(e=g.value)||e.clearValidate()};return(a,l)=>(o(),r(s(k),{"model-value":e.visible,title:"增加积分",width:"400px","close-on-click-modal":!1,"onUpdate:modelValue":l[3]||(l[3]=e=>y("update:visible",e)),onClosed:C},{footer:t(()=>[d(s(b),{onClick:E},{default:t(()=>[...l[4]||(l[4]=[n("取消",-1)])]),_:1}),d(s(b),{type:"primary",loading:h.value,onClick:w},{default:t(()=>[...l[5]||(l[5]=[n(" 确定 ",-1)])]),_:1},8,["loading"])]),default:t(()=>[d(s(m),{ref_key:"formRef",ref:g,model:x,rules:V,"label-width":"80px"},{default:t(()=>[d(s(p),{label:"积分数量",prop:"points"},{default:t(()=>[d(s(u),{modelValue:x.points,"onUpdate:modelValue":l[0]||(l[0]=e=>x.points=e),placeholder:"请输入积分数量",min:1,precision:0,style:{width:"100%"}},null,8,["modelValue"])]),_:1}),d(s(p),{label:"积分类型",prop:"kind"},{default:t(()=>[d(s(v),{modelValue:x.kind,"onUpdate:modelValue":l[1]||(l[1]=e=>x.kind=e),placeholder:"请选择积分类型",style:{width:"100%"}},{default:t(()=>[d(s(c),{label:"管理员增加",value:"admin_add"}),d(s(c),{label:"活动奖励",value:"activity_reward"}),d(s(c),{label:"签到奖励",value:"sign_reward"}),d(s(c),{label:"消费返还",value:"consume_return"})]),_:1},8,["modelValue"])]),_:1}),d(s(p),{label:"备注",prop:"remark"},{default:t(()=>[d(s(f),{modelValue:x.remark,"onUpdate:modelValue":l[2]||(l[2]=e=>x.remark=e),type:"textarea",placeholder:"请输入备注",rows:3},null,8,["modelValue"])]),_:1})]),_:1},8,["model"])]),_:1},8,["model-value"]))}});export{_};
|
||||
1
build/resources/admin/assets/adminActivities-C7lwI2HP.js
Normal file
1
build/resources/admin/assets/adminActivities-C7lwI2HP.js
Normal file
@ -0,0 +1 @@
|
||||
import{b4 as i}from"./index-DBHnxsy6.js";function s(s){return i.post({url:"admin/activities",params:s})}function t(s,t){return i.put({url:`admin/activities/${s}`,params:t})}function r(s){return i.del({url:`admin/activities/${s}`})}function a(s){return i.get({url:`admin/activities/${s}`})}function n(s,t=1,r=20){return i.get({url:`admin/activities/${s}/issues`,params:{page:t,page_size:r}})}function e(s,t){return i.post({url:`admin/activities/${s}/issues`,params:t})}function u(s,t,r){return i.put({url:`admin/activities/${s}/issues/${t}`,params:r})}function c(s,t){return i.del({url:`admin/activities/${s}/issues/${t}`})}function m(s,t){return i.get({url:`admin/activities/${s}/issues/${t}/rewards`})}function o(s,t,r){return i.post({url:`admin/activities/${s}/issues/${t}/rewards`,params:{rewards:r}})}function d(s,t,r,a){return i.put({url:`admin/activities/${s}/issues/${t}/rewards/${r}`,params:a})}function $(s,t,r){return i.del({url:`admin/activities/${s}/issues/${t}/rewards/${r}`})}function l(){return i.get({url:"admin/activity_categories"})}function p(s,t){return i.post({url:`admin/activities/${s}/issues/${t}/commit_random`})}function f(s,t){return i.get({url:`admin/activities/${s}/issues/${t}/commit_random`,showErrorMessage:!1})}function v(s,t){return i.get({url:`admin/activities/${s}/issues/${t}/commit_random/history`})}function g(s,t,r){return i.post({url:`admin/activities/${s}/issues/${t}/simulate_draw`,params:r,timeout:6e4})}function w(s,t,r){return i.post({url:`admin/activities/${s}/issues/${t}/batch_draw`,params:r,timeout:6e4})}function _(s,t,r){return i.post({url:`admin/activities/${s}/issues/${t}/verify_draw`,params:r})}function h(s){return i.get({url:`admin/draw_receipts/${s}`})}function b(s){return i.get({url:`admin/draw_receipts/log/${s}`})}function y(s){return i.get({url:"admin/users",params:s})}export{l as a,t as b,e as c,c as d,s as e,r as f,a as g,o as h,f as i,v as j,p as k,n as l,d as m,m as n,$ as o,y as p,w as q,h as r,g as s,b as t,u,_ as v};
|
||||
@ -1 +0,0 @@
|
||||
import{b5 as i}from"./index-ip5gDR6L.js";function s(s){return i.post({url:"admin/activities",params:s})}function t(s,t){return i.put({url:`admin/activities/${s}`,params:t})}function a(s){return i.del({url:`admin/activities/${s}`})}function r(s){return i.get({url:`admin/activities/${s}`})}function e(s,t=1,a=20){return i.get({url:`admin/activities/${s}/issues`,params:{page:t,page_size:a}})}function n(s,t){return i.post({url:`admin/activities/${s}/issues`,params:t})}function u(s,t,a){return i.put({url:`admin/activities/${s}/issues/${t}`,params:a})}function c(s,t){return i.del({url:`admin/activities/${s}/issues/${t}`})}function d(s,t){return i.get({url:`admin/activities/${s}/issues/${t}/rewards`})}function m(s,t,a){return i.post({url:`admin/activities/${s}/issues/${t}/rewards`,params:{rewards:a}})}function o(s,t,a,r){return i.put({url:`admin/activities/${s}/issues/${t}/rewards/${a}`,params:r})}function $(s,t,a){return i.del({url:`admin/activities/${s}/issues/${t}/rewards/${a}`})}function l(){return i.get({url:"admin/activity_categories"})}export{l as a,t as b,n as c,c as d,s as e,a as f,r as g,m as h,o as i,d as j,$ as k,e as l,u};
|
||||
@ -0,0 +1 @@
|
||||
import{_ as i}from"./assign-title-dialog.vue_vue_type_script_setup_true_lang-ChRt4Uun.js";import"./index-DBHnxsy6.js";/* empty css *//* empty css *//* empty css *//* empty css *//* empty css *//* empty css *//* empty css *//* empty css *//* empty css *//* empty css */import"./titles-Bs2uXvDP.js";import"./index-zIv9y_XZ.js";import"./use-form-common-props-BOfz9xGR.js";import"./index-DZO7eApA.js";import"./index-MOpuiu9Z.js";import"./index-nZra831X.js";import"./use-form-item-ByasanrX.js";import"./_initCloneObject-7oWMW-Ot.js";import"./index-CHZ9Ls9_.js";import"./index-B99ckkYy.js";import"./index-sqhzrW2O.js";import"./index-D2YpA_om.js";import"./token-DWNpOE8r.js";import"./scroll-Cceui3yC.js";import"./debounce-CkazOn_9.js";import"./_baseIteratee-DRiXPtOg.js";import"./index-BC2pRl3S.js";import"./vnode-CqxKQBn0.js";import"./index-CS6jHv1e.js";import"./index-r0EsCkBw.js";import"./index-DfWQjCuY.js";import"./index-qSQlj0RO.js";import"./index-CuWUnlSZ.js";import"./index-CtUPMuT5.js";import"./index-DeCbFeva.js";import"./use-dialog-yhiLZsn9.js";import"./index-DvlZWusB.js";import"./refs-Cw5r5QN8.js";export{i as default};
|
||||
@ -0,0 +1 @@
|
||||
var e=(e,l,a)=>new Promise((t,i)=>{var r=e=>{try{s(a.next(e))}catch(l){i(l)}},o=e=>{try{s(a.throw(e))}catch(l){i(l)}},s=e=>e.done?t(e.value):Promise.resolve(e.value).then(r,o);s((a=a.apply(e,l)).next())});import{d as l,a,r as t,o as i,y as r,f as o,z as s,p as d,u as m,e as p,F as u,G as n,l as f}from"./index-DBHnxsy6.js";/* empty css *//* empty css *//* empty css *//* empty css *//* empty css *//* empty css *//* empty css *//* empty css *//* empty css *//* empty css */import{titlesApi as v}from"./titles-Bs2uXvDP.js";import{E as c,a as x}from"./index-zIv9y_XZ.js";import{E as y,a as j}from"./index-CHZ9Ls9_.js";import{E as b}from"./index-CS6jHv1e.js";import{E as h}from"./index-qSQlj0RO.js";import{E as _}from"./index-r0EsCkBw.js";import{E as g}from"./index-CtUPMuT5.js";const k=l({__name:"assign-title-dialog",props:{visible:{type:Boolean}},emits:["update:visible","submit"],setup(l,{emit:k}){const V=k,A=a(),I=a(!1),w=a([]),E=t({titleId:null,expiresAt:"",remark:""}),C={titleId:[{required:!0,message:"请选择称号",trigger:"change"}]},U=()=>e(this,null,function*(){var e;try{yield null==(e=A.value)?void 0:e.validate(),I.value=!0;const l={title_id:E.titleId};E.expiresAt&&(l.expires_at=E.expiresAt),E.remark&&(l.remark=E.remark),V("submit",l)}catch(l){}finally{I.value=!1}}),Y=()=>{V("update:visible",!1)},D=()=>{var e;E.titleId=null,E.expiresAt="",E.remark="",null==(e=A.value)||e.clearValidate()};return i(()=>e(this,null,function*(){try{const e=yield v.getList({page:1,page_size:100});w.value=Array.isArray(e.list)?e.list.map(e=>({id:e.id,name:e.name})):[]}catch(e){w.value=[]}})),(e,a)=>(o(),r(m(g),{"model-value":l.visible,title:"分配称号",width:"460px","close-on-click-modal":!1,"onUpdate:modelValue":a[3]||(a[3]=e=>V("update:visible",e)),onClosed:D},{footer:s(()=>[d(m(_),{onClick:Y},{default:s(()=>[...a[4]||(a[4]=[f("取消",-1)])]),_:1}),d(m(_),{type:"primary",loading:I.value,onClick:U},{default:s(()=>[...a[5]||(a[5]=[f("确定",-1)])]),_:1},8,["loading"])]),default:s(()=>[d(m(c),{ref_key:"formRef",ref:A,model:E,rules:C,"label-width":"90px"},{default:s(()=>[d(m(x),{label:"称号",prop:"titleId"},{default:s(()=>[d(m(y),{modelValue:E.titleId,"onUpdate:modelValue":a[0]||(a[0]=e=>E.titleId=e),placeholder:"请选择称号",filterable:"",style:{width:"100%"}},{default:s(()=>[(o(!0),p(u,null,n(w.value,e=>(o(),r(m(j),{key:e.id,label:`${e.name}(ID: ${e.id})`,value:e.id},null,8,["label","value"]))),128))]),_:1},8,["modelValue"])]),_:1}),d(m(x),{label:"过期时间",prop:"expiresAt"},{default:s(()=>[d(m(b),{modelValue:E.expiresAt,"onUpdate:modelValue":a[1]||(a[1]=e=>E.expiresAt=e),type:"datetime","value-format":"YYYY-MM-DDTHH:mm:ssZ",placeholder:"可选,默认为永久",style:{width:"100%"}},null,8,["modelValue"])]),_:1}),d(m(x),{label:"备注",prop:"remark"},{default:s(()=>[d(m(h),{modelValue:E.remark,"onUpdate:modelValue":a[2]||(a[2]=e=>E.remark=e),placeholder:"可选备注",maxlength:"100"},null,8,["modelValue"])]),_:1})]),_:1},8,["model"])]),_:1},8,["model-value"]))}});export{k as _};
|
||||
@ -0,0 +1 @@
|
||||
import{_ as i}from"./batch-draw-dialog.vue_vue_type_script_setup_true_lang-D6SWKrJK.js";import"./index-DBHnxsy6.js";/* empty css *//* empty css */import"./el-pagination-CkwA8__y.js";import"./index-CHZ9Ls9_.js";import"./index-B99ckkYy.js";import"./index-nZra831X.js";import"./index-MOpuiu9Z.js";import"./use-form-item-ByasanrX.js";import"./use-form-common-props-BOfz9xGR.js";import"./index-sqhzrW2O.js";import"./index-D2YpA_om.js";import"./token-DWNpOE8r.js";import"./index-DZO7eApA.js";import"./scroll-Cceui3yC.js";import"./debounce-CkazOn_9.js";import"./_baseIteratee-DRiXPtOg.js";import"./index-BC2pRl3S.js";import"./vnode-CqxKQBn0.js";import"./index-qSQlj0RO.js";import"./index-DfWQjCuY.js";/* empty css *//* empty css *//* empty css *//* empty css *//* empty css *//* empty css */import"./el-table-column-DWU1JSNJ.js";import"./_initCloneObject-7oWMW-Ot.js";import"./isArrayLikeObject-BJDhNHaT.js";import"./raf-CXqF1NB1.js";import"./index-DKaQAdbW.js";/* empty css */import"./el-tooltip-l0sNRNKZ.js";/* empty css *//* empty css *//* empty css *//* empty css */import"./el-step-DZviJjCE.js";import"./index-y_oew012.js";/* empty css */import"./adminActivities-C7lwI2HP.js";import"./index-zIv9y_XZ.js";import"./index-BIIDnOvF.js";import"./index-B5f0OwDI.js";import"./index-r0EsCkBw.js";import"./index-CtUPMuT5.js";import"./index-DeCbFeva.js";import"./use-dialog-yhiLZsn9.js";import"./index-DvlZWusB.js";import"./refs-Cw5r5QN8.js";export{i as default};
|
||||
File diff suppressed because one or more lines are too long
Binary file not shown.
1
build/resources/admin/assets/card-D34vavgk.css
Normal file
1
build/resources/admin/assets/card-D34vavgk.css
Normal file
@ -0,0 +1 @@
|
||||
.el-card{--el-card-border-color: var(--el-border-color-light);--el-card-border-radius: 4px;--el-card-padding: 20px;--el-card-bg-color: var(--el-fill-color-blank)}.el-card{border-radius:var(--el-card-border-radius);border:1px solid var(--el-card-border-color);background-color:var(--el-card-bg-color);overflow:hidden;color:var(--el-text-color-primary);transition:var(--el-transition-duration)}.el-card.is-always-shadow{box-shadow:var(--el-box-shadow-light)}.el-card.is-hover-shadow:hover,.el-card.is-hover-shadow:focus{box-shadow:var(--el-box-shadow-light)}.el-card__header{padding:calc(var(--el-card-padding) - 2px) var(--el-card-padding);border-bottom:1px solid var(--el-card-border-color);box-sizing:border-box}.el-card__body{padding:var(--el-card-padding)}.el-card__footer{padding:calc(var(--el-card-padding) - 2px) var(--el-card-padding);border-top:1px solid var(--el-card-border-color);box-sizing:border-box}
|
||||
@ -1 +0,0 @@
|
||||
import{_ as e}from"./card-list.vue_vue_type_script_setup_true_lang-Dm1x1mF4.js";import"./index-ip5gDR6L.js";/* empty css *//* empty css */import"./index.vue_vue_type_script_setup_true_lang-CCGboj8m.js";/* empty css */import"./dashboard-C-Wq9kTO.js";import"./index-D5xppMej.js";import"./index-Ca1H-sCf.js";export{e as default};
|
||||
1
build/resources/admin/assets/card-list-Dkk_zimK.js
Normal file
1
build/resources/admin/assets/card-list-Dkk_zimK.js
Normal file
@ -0,0 +1 @@
|
||||
import{_ as t}from"./card-list.vue_vue_type_script_setup_true_lang-CibUUuFD.js";import"./index-DBHnxsy6.js";/* empty css *//* empty css */import"./index.vue_vue_type_script_setup_true_lang-DUyHB5jc.js";/* empty css */import"./dashboard-efoygTEr.js";import"./index-B5f0OwDI.js";import"./index-BIIDnOvF.js";export{t as default};
|
||||
@ -1 +1 @@
|
||||
import{d as e,c as a,b6 as t,aC as s,b7 as u,w as r,aH as n,e as l,f as i,m as o,i as d,n as c,r as m,y as v,z as p,F as g,G as f,h as x,p as h,O as b}from"./index-ip5gDR6L.js";/* empty css *//* empty css */import{_}from"./index.vue_vue_type_script_setup_true_lang-CCGboj8m.js";/* empty css */import{f as y}from"./dashboard-C-Wq9kTO.js";import{E as j}from"./index-D5xppMej.js";import{E as w}from"./index-Ca1H-sCf.js";const C="easeOutExpo",S=e({__name:"index",props:{target:{default:0},duration:{default:2e3},autoStart:{type:Boolean,default:!0},decimals:{default:0},decimal:{default:"."},separator:{default:""},prefix:{default:""},suffix:{default:""},easing:{default:C},disabled:{type:Boolean,default:!1}},emits:["started","finished","paused","reset"],setup(e,{expose:m,emit:v}){const p=Number.EPSILON,g=e,f=v,x=(e,a,t)=>Number.isFinite(e)?e:t,h=(e,a,t)=>Math.max(a,Math.min(e,t)),b=a(()=>x(g.target,0,0)),_=a(()=>h(x(g.duration,0,2e3),100,6e4)),y=a(()=>h(x(g.decimals,0,0),0,10)),j=a(()=>{const e=g.easing;return e in t?e:C}),w=s(0),S=s(b.value),F=s(!1),N=s(!1),V=s(0),M=u(w,{duration:_,transition:a(()=>t[j.value]),onStarted:()=>{F.value=!0,N.value=!1,f("started",S.value)},onFinished:()=>{F.value=!1,N.value=!1,f("finished",S.value)}}),P=a(()=>{const e=N.value?V.value:M.value;if(!Number.isFinite(e))return`${g.prefix}0${g.suffix}`;const a=((e,a,t,s)=>{let u=a>0?e.toFixed(a):Math.floor(e).toString();if("."!==t&&u.includes(".")&&(u=u.replace(".",t)),s){const e=u.split(t);e[0]=e[0].replace(/\B(?=(\d{3})+(?!\d))/g,s),u=e.join(t)}return u})(e,y.value,g.decimal,g.separator);return`${g.prefix}${a}${g.suffix}`}),$=()=>{N.value=!1,V.value=0},E=e=>{if(g.disabled)return;const a=void 0!==e?e:S.value;Number.isFinite(a)&&(S.value=a,(e=>{const a=N.value?V.value:M.value;return Math.abs(a-e)<p})(a)||(N.value&&(w.value=V.value,$()),c(()=>{w.value=a})))},O=()=>{(F.value||N.value)&&(w.value=0,$(),f("paused",0))};return r(b,e=>{g.autoStart&&!g.disabled?E(e):S.value=e},{immediate:g.autoStart&&!g.disabled}),r(()=>g.disabled,e=>{e&&F.value&&O()}),n(()=>{F.value&&O()}),m({start:E,pause:()=>{F.value&&!N.value&&(N.value=!0,V.value=M.value,w.value=V.value,f("paused",V.value))},reset:(e=0)=>{const a=x(e,0,0);w.value=a,S.value=a,$(),f("reset")},stop:O,setTarget:e=>{Number.isFinite(e)&&(S.value=e,!F.value&&!g.autoStart||g.disabled||E(e))},get isRunning(){return F.value},get isPaused(){return N.value},get currentValue(){return N.value?V.value:M.value},get targetValue(){return S.value},get progress(){const e=N.value?V.value:M.value,a=S.value;return 0===a?0===e?1:0:Math.abs(e/a)}}),(e,a)=>(i(),l("span",{class:d(["text-g-900 tabular-nums",F.value?"transition-opacity duration-300 ease-in-out":""])},o(P.value),3))}}),F={class:"art-card relative flex flex-col justify-center h-35 px-5 mb-5 max-sm:mb-4"},N={class:"text-g-700 text-sm"},V={class:"flex-c mt-1"},M={class:"absolute top-0 bottom-0 right-5 m-auto size-12.5 rounded-xl flex-cc bg-theme/10"},P=e({__name:"card-list",props:{range:{}},setup(e){const a=e,t=m([{des:"道具卡销量",icon:"ri:shopping-bag-3-line",startVal:0,duration:1e3,num:0,change:"+0%"},{des:"活动抽奖次数",icon:"ri:fire-line",startVal:0,duration:1e3,num:0,change:"+0%"},{des:"新用户注册数",icon:"ri:user-add-line",startVal:0,duration:1e3,num:0,change:"+0%"},{des:"用户总积分",icon:"ri:coin-line",startVal:0,duration:1e3,num:0,change:"+0%"}]);return r(()=>a.range,()=>{return e=this,s=null,u=function*(){try{const e=yield y(a.range);t[0].num=e.itemCardSales,t[0].change=e.itemCardChange,t[1].num=e.drawCount,t[1].change=e.drawChange,t[2].num=e.newUsers,t[2].change=e.newUserChange,t[3].num=e.totalPoints,t[3].change=e.pointsChange}catch(e){b.error("获取卡片数据失败")}},new Promise((a,t)=>{var r=e=>{try{l(u.next(e))}catch(a){t(a)}},n=e=>{try{l(u.throw(e))}catch(a){t(a)}},l=e=>e.done?a(e.value):Promise.resolve(e.value).then(r,n);l((u=u.apply(e,s)).next())});var e,s,u},{immediate:!0}),(e,a)=>{const s=S,u=_,r=j,n=w;return i(),v(n,{gutter:20,class:"flex"},{default:p(()=>[(i(!0),l(g,null,f(t,(e,t)=>(i(),v(r,{key:t,sm:12,md:6,lg:6},{default:p(()=>[x("div",F,[x("span",N,o(e.des),1),h(s,{class:"text-[26px] font-medium mt-2",target:e.num,duration:1300},null,8,["target"]),x("div",V,[a[0]||(a[0]=x("span",{class:"text-xs text-g-600"},"较上周",-1)),x("span",{class:d(["ml-1 text-xs font-semibold",[-1===e.change.indexOf("+")?"text-danger":"text-success"]])},o(e.change),3)]),x("div",M,[h(u,{icon:e.icon,class:"text-xl text-theme"},null,8,["icon"])])])]),_:2},1024))),128))]),_:1})}}});export{P as _};
|
||||
import{d as e,c as a,b1 as t,aC as s,b2 as u,w as r,aH as n,e as l,f as i,m as o,i as d,n as c,r as m,y as v,z as p,F as g,G as f,h as x,p as h,O as b}from"./index-DBHnxsy6.js";/* empty css *//* empty css */import{_}from"./index.vue_vue_type_script_setup_true_lang-DUyHB5jc.js";/* empty css */import{f as y}from"./dashboard-efoygTEr.js";import{E as j}from"./index-B5f0OwDI.js";import{E as S}from"./index-BIIDnOvF.js";const w="easeOutExpo",C=e({__name:"index",props:{target:{default:0},duration:{default:2e3},autoStart:{type:Boolean,default:!0},decimals:{default:0},decimal:{default:"."},separator:{default:""},prefix:{default:""},suffix:{default:""},easing:{default:w},disabled:{type:Boolean,default:!1}},emits:["started","finished","paused","reset"],setup(e,{expose:m,emit:v}){const p=Number.EPSILON,g=e,f=v,x=(e,a,t)=>Number.isFinite(e)?e:t,h=(e,a,t)=>Math.max(a,Math.min(e,t)),b=a(()=>x(g.target,0,0)),_=a(()=>h(x(g.duration,0,2e3),100,6e4)),y=a(()=>h(x(g.decimals,0,0),0,10)),j=a(()=>{const e=g.easing;return e in t?e:w}),S=s(0),C=s(b.value),F=s(!1),N=s(!1),V=s(0),M=u(S,{duration:_,transition:a(()=>t[j.value]),onStarted:()=>{F.value=!0,N.value=!1,f("started",C.value)},onFinished:()=>{F.value=!1,N.value=!1,f("finished",C.value)}}),P=a(()=>{const e=N.value?V.value:M.value;if(!Number.isFinite(e))return`${g.prefix}0${g.suffix}`;const a=((e,a,t,s)=>{let u=a>0?e.toFixed(a):Math.floor(e).toString();if("."!==t&&u.includes(".")&&(u=u.replace(".",t)),s){const e=u.split(t);e[0]=e[0].replace(/\B(?=(\d{3})+(?!\d))/g,s),u=e.join(t)}return u})(e,y.value,g.decimal,g.separator);return`${g.prefix}${a}${g.suffix}`}),$=()=>{N.value=!1,V.value=0},E=e=>{if(g.disabled)return;const a=void 0!==e?e:C.value;Number.isFinite(a)&&(C.value=a,(e=>{const a=N.value?V.value:M.value;return Math.abs(a-e)<p})(a)||(N.value&&(S.value=V.value,$()),c(()=>{S.value=a})))},O=()=>{(F.value||N.value)&&(S.value=0,$(),f("paused",0))};return r(b,e=>{g.autoStart&&!g.disabled?E(e):C.value=e},{immediate:g.autoStart&&!g.disabled}),r(()=>g.disabled,e=>{e&&F.value&&O()}),n(()=>{F.value&&O()}),m({start:E,pause:()=>{F.value&&!N.value&&(N.value=!0,V.value=M.value,S.value=V.value,f("paused",V.value))},reset:(e=0)=>{const a=x(e,0,0);S.value=a,C.value=a,$(),f("reset")},stop:O,setTarget:e=>{Number.isFinite(e)&&(C.value=e,!F.value&&!g.autoStart||g.disabled||E(e))},get isRunning(){return F.value},get isPaused(){return N.value},get currentValue(){return N.value?V.value:M.value},get targetValue(){return C.value},get progress(){const e=N.value?V.value:M.value,a=C.value;return 0===a?0===e?1:0:Math.abs(e/a)}}),(e,a)=>(i(),l("span",{class:d(["text-g-900 tabular-nums",F.value?"transition-opacity duration-300 ease-in-out":""])},o(P.value),3))}}),F={class:"art-card relative flex flex-col justify-center h-35 px-5 mb-5 max-sm:mb-4"},N={class:"text-g-700 text-sm"},V={class:"flex-c mt-1"},M={class:"absolute top-0 bottom-0 right-5 m-auto size-12.5 rounded-xl flex-cc bg-theme/10"},P=e({__name:"card-list",props:{range:{}},setup(e){const a=e,t=m([{des:"道具卡销量",icon:"ri:shopping-bag-3-line",startVal:0,duration:1e3,num:0,change:"+0%"},{des:"活动抽奖次数",icon:"ri:fire-line",startVal:0,duration:1e3,num:0,change:"+0%"},{des:"新用户注册数",icon:"ri:user-add-line",startVal:0,duration:1e3,num:0,change:"+0%"},{des:"用户总积分",icon:"ri:coin-line",startVal:0,duration:1e3,num:0,change:"+0%"}]);return r(()=>a.range,()=>{return e=this,s=null,u=function*(){try{const e=yield y(a.range);t[0].num=e.itemCardSales,t[0].change=e.itemCardChange,t[1].num=e.drawCount,t[1].change=e.drawChange,t[2].num=e.newUsers,t[2].change=e.newUserChange,t[3].num=e.totalPoints,t[3].change=e.pointsChange}catch(e){b.error("获取卡片数据失败")}},new Promise((a,t)=>{var r=e=>{try{l(u.next(e))}catch(a){t(a)}},n=e=>{try{l(u.throw(e))}catch(a){t(a)}},l=e=>e.done?a(e.value):Promise.resolve(e.value).then(r,n);l((u=u.apply(e,s)).next())});var e,s,u},{immediate:!0}),(e,a)=>{const s=C,u=_,r=j,n=S;return i(),v(n,{gutter:20,class:"flex"},{default:p(()=>[(i(!0),l(g,null,f(t,(e,t)=>(i(),v(r,{key:t,sm:12,md:6,lg:6},{default:p(()=>[x("div",F,[x("span",N,o(e.des),1),h(s,{class:"text-[26px] font-medium mt-2",target:e.num,duration:1300},null,8,["target"]),x("div",V,[a[0]||(a[0]=x("span",{class:"text-xs text-g-600"},"较上周",-1)),x("span",{class:d(["ml-1 text-xs font-semibold",[-1===e.change.indexOf("+")?"text-danger":"text-success"]])},o(e.change),3)]),x("div",M,[h(u,{icon:e.icon,class:"text-xl text-theme"},null,8,["icon"])])])]),_:2},1024))),128))]),_:1})}}});export{P as _};
|
||||
@ -1 +0,0 @@
|
||||
var e=Object.defineProperty,a=Object.getOwnPropertySymbols,t=Object.prototype.hasOwnProperty,o=Object.prototype.propertyIsEnumerable,r=(a,t,o)=>t in a?e(a,t,{enumerable:!0,configurable:!0,writable:!0,value:o}):a[t]=o,s=(e,s)=>{for(var l in s||(s={}))t.call(s,l)&&r(e,l,s[l]);if(a)for(var l of a(s))o.call(s,l)&&r(e,l,s[l]);return e};import{d as l,a as p,w as i,E as m,y as d,f as u,z as n,p as f,u as j,B as c,J as b,l as _,ab as v,a$ as x}from"./index-ip5gDR6L.js";/* empty css *//* empty css *//* empty css *//* empty css *//* empty css *//* empty css *//* empty css *//* empty css *//* empty css */import{E as y,a as h}from"./index-CxGOZAw2.js";import{E as V}from"./index-Ca1H-sCf.js";import{E as g}from"./index-D5xppMej.js";import{E}from"./index-DfrOVkw6.js";import{E as O,a as w}from"./index-Br3btmwp.js";import{E as k}from"./index-BY5Lb5w1.js";import{E as C}from"./index-opP5No9I.js";import{E as J}from"./index-BBSYL4g8.js";import{_ as P}from"./_plugin-vue_export-helper-BCo6x5W8.js";import"./use-form-common-props-BwBs_jod.js";import"./index-D-10FtUD.js";import"./index-CHAf6FpU.js";import"./index-fnD_mPvE.js";import"./use-form-item-BDb3ZgPk.js";import"./_initCloneObject-DwsgJamq.js";import"./index-C9KXwj2c.js";import"./index-DbRz3Zuu.js";import"./index-D1xXI5gl.js";import"./token-DWNpOE8r.js";import"./scroll-ChxXkPhM.js";import"./debounce-B9s63_mG.js";import"./index-Jz5GfFad.js";import"./vnode-6FKeug3R.js";import"./index-D-iZIej9.js";const S=P(l({__name:"category-search",props:{modelValue:{}},emits:["update:modelValue","search","reset"],setup(e,{emit:a}){const t=e,o=a,r=p(s({},t.modelValue));i(()=>t.modelValue,(e,a)=>{JSON.stringify(e)!==JSON.stringify(a)&&(r.value=s({},e))},{deep:!0});let l=null;i(r,e=>{l&&clearTimeout(l),l=setTimeout(()=>{o("update:modelValue",s({},e))},100)},{deep:!0});const P=()=>{o("search",r.value)},S=()=>{r.value={name:void 0,status:void 0},o("reset")};return(a,t)=>{const o=m("ripple");return u(),d(j(J),{class:"search-card",shadow:"never"},{default:n(()=>[f(j(y),{ref:"formRef",model:e.modelValue,"label-width":"80px"},{default:n(()=>[f(j(V),{gutter:20},{default:n(()=>[f(j(g),{span:8},{default:n(()=>[f(j(h),{label:"分类名称",prop:"name"},{default:n(()=>[f(j(E),{modelValue:r.value.name,"onUpdate:modelValue":t[0]||(t[0]=e=>r.value.name=e),placeholder:"请输入分类名称",clearable:"",onKeyup:c(P,["enter"])},null,8,["modelValue"])]),_:1})]),_:1}),f(j(g),{span:6},{default:n(()=>[f(j(h),{label:"状态",prop:"status"},{default:n(()=>[f(j(O),{modelValue:r.value.status,"onUpdate:modelValue":t[1]||(t[1]=e=>r.value.status=e),placeholder:"请选择状态",clearable:""},{default:n(()=>[f(j(w),{value:1,label:"启用"}),f(j(w),{value:2,label:"禁用"})]),_:1},8,["modelValue"])]),_:1})]),_:1}),f(j(g),{span:6},{default:n(()=>[f(j(h),{"label-width":"0"},{default:n(()=>[f(j(k),null,{default:n(()=>[b((u(),d(j(C),{type:"primary",onClick:P},{default:n(()=>[f(j(v),{class:"mr-1"},{default:n(()=>[f(j(x))]),_:1}),t[2]||(t[2]=_(" 搜索 ",-1))]),_:1})),[[o]]),b((u(),d(j(C),{onClick:S},{default:n(()=>[...t[3]||(t[3]=[_("重置",-1)])]),_:1})),[[o]])]),_:1})]),_:1})]),_:1})]),_:1})]),_:1},8,["model"])]),_:1})}}}),[["__scopeId","data-v-7c3709d1"]]);export{S as default};
|
||||
@ -0,0 +1 @@
|
||||
.search-card[data-v-9b5715ff]{margin-bottom:16px}[data-v-9b5715ff] .el-card__body{padding-bottom:0}
|
||||
1
build/resources/admin/assets/category-search-CzNFlBJ5.js
Normal file
1
build/resources/admin/assets/category-search-CzNFlBJ5.js
Normal file
@ -0,0 +1 @@
|
||||
var e=Object.defineProperty,a=Object.getOwnPropertySymbols,t=Object.prototype.hasOwnProperty,o=Object.prototype.propertyIsEnumerable,r=(a,t,o)=>t in a?e(a,t,{enumerable:!0,configurable:!0,writable:!0,value:o}):a[t]=o,s=(e,s)=>{for(var l in s||(s={}))t.call(s,l)&&r(e,l,s[l]);if(a)for(var l of a(s))o.call(s,l)&&r(e,l,s[l]);return e};import{d as l,a as p,w as i,E as m,y as d,f as u,z as n,p as f,u as j,B as c,J as b,l as _,ab as x,aW as v}from"./index-DBHnxsy6.js";/* empty css *//* empty css *//* empty css *//* empty css *//* empty css *//* empty css *//* empty css *//* empty css *//* empty css *//* empty css */import{E as y,a as E}from"./index-zIv9y_XZ.js";import{E as V}from"./index-BIIDnOvF.js";import{E as h}from"./index-B5f0OwDI.js";import{E as g}from"./index-qSQlj0RO.js";import{E as O,a as w}from"./index-CHZ9Ls9_.js";import{E as C}from"./index-Bi8tRqjc.js";import{E as I}from"./index-r0EsCkBw.js";import{E as J}from"./index-Bh7aAHNM.js";import{_ as k}from"./_plugin-vue_export-helper-BCo6x5W8.js";import"./use-form-common-props-BOfz9xGR.js";import"./index-DZO7eApA.js";import"./index-MOpuiu9Z.js";import"./index-nZra831X.js";import"./use-form-item-ByasanrX.js";import"./_initCloneObject-7oWMW-Ot.js";import"./index-B99ckkYy.js";import"./index-sqhzrW2O.js";import"./index-D2YpA_om.js";import"./token-DWNpOE8r.js";import"./scroll-Cceui3yC.js";import"./debounce-CkazOn_9.js";import"./_baseIteratee-DRiXPtOg.js";import"./index-BC2pRl3S.js";import"./vnode-CqxKQBn0.js";import"./index-DfWQjCuY.js";const z=k(l({__name:"category-search",props:{modelValue:{}},emits:["update:modelValue","search","reset"],setup(e,{emit:a}){const t=e,o=a,r=p(s({},t.modelValue));i(()=>t.modelValue,(e,a)=>{JSON.stringify(e)!==JSON.stringify(a)&&(r.value=s({},e))},{deep:!0});let l=null;i(r,e=>{l&&clearTimeout(l),l=setTimeout(()=>{o("update:modelValue",s({},e))},100)},{deep:!0});const k=()=>{o("search",r.value)},z=()=>{r.value={name:void 0,status:void 0},o("reset")};return(a,t)=>{const o=m("ripple");return u(),d(j(J),{class:"search-card",shadow:"never"},{default:n(()=>[f(j(y),{ref:"formRef",model:e.modelValue,"label-width":"80px"},{default:n(()=>[f(j(V),{gutter:20},{default:n(()=>[f(j(h),{span:8},{default:n(()=>[f(j(E),{label:"分类名称",prop:"name"},{default:n(()=>[f(j(g),{modelValue:r.value.name,"onUpdate:modelValue":t[0]||(t[0]=e=>r.value.name=e),placeholder:"请输入分类名称",clearable:"",onKeyup:c(k,["enter"])},null,8,["modelValue"])]),_:1})]),_:1}),f(j(h),{span:6},{default:n(()=>[f(j(E),{label:"状态",prop:"status"},{default:n(()=>[f(j(O),{modelValue:r.value.status,"onUpdate:modelValue":t[1]||(t[1]=e=>r.value.status=e),placeholder:"请选择状态",clearable:""},{default:n(()=>[f(j(w),{value:1,label:"启用"}),f(j(w),{value:2,label:"禁用"})]),_:1},8,["modelValue"])]),_:1})]),_:1}),f(j(h),{span:6},{default:n(()=>[f(j(E),{"label-width":"0"},{default:n(()=>[f(j(C),null,{default:n(()=>[b((u(),d(j(I),{type:"primary",onClick:k},{default:n(()=>[f(j(x),{class:"mr-1"},{default:n(()=>[f(j(v))]),_:1}),t[2]||(t[2]=_(" 搜索 ",-1))]),_:1})),[[o]]),b((u(),d(j(I),{onClick:z},{default:n(()=>[...t[3]||(t[3]=[_("重置",-1)])]),_:1})),[[o]])]),_:1})]),_:1})]),_:1})]),_:1})]),_:1},8,["model"])]),_:1})}}}),[["__scopeId","data-v-9b5715ff"]]);export{z as default};
|
||||
@ -1 +0,0 @@
|
||||
.search-card[data-v-7c3709d1]{margin-bottom:16px}[data-v-7c3709d1] .el-card__body{padding-bottom:0}
|
||||
@ -1 +0,0 @@
|
||||
import{b as r}from"./index-CxGOZAw2.js";function n(n){return r(n,5)}export{n as c};
|
||||
1
build/resources/admin/assets/cloneDeep-zs6l5dp3.js
Normal file
1
build/resources/admin/assets/cloneDeep-zs6l5dp3.js
Normal file
@ -0,0 +1 @@
|
||||
import{b as n}from"./index-zIv9y_XZ.js";function r(r){return n(r,5)}export{r as c};
|
||||
File diff suppressed because one or more lines are too long
Binary file not shown.
1
build/resources/admin/assets/col-yED17g82.css
Normal file
1
build/resources/admin/assets/col-yED17g82.css
Normal file
File diff suppressed because one or more lines are too long
BIN
build/resources/admin/assets/col-yED17g82.css.gz
Normal file
BIN
build/resources/admin/assets/col-yED17g82.css.gz
Normal file
Binary file not shown.
File diff suppressed because one or more lines are too long
@ -1 +0,0 @@
|
||||
.form-tip[data-v-d9eb48ea]{margin-left:8px;color:#909399;font-size:12px}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user