{"role":"user","message":{"content":[{"type":"text","text":"\n在 /Users/ray/Documents/project/www/slot/backend/slot_admin_vue 这个 Vue 管理后台前端工程里,需要梳理\"活动配置\"的编辑表单实现,特别是 activity_type / 活动类型字段相关的渲染。\n\n请回答以下问题,并标出具体的文件路径与代码片段(包含行号):\n\n1. 活动列表与编辑页面入口(路由、列表组件、编辑组件)所在文件路径。\n2. 活动类型枚举在前端是怎么定义的?目前已经支持哪些 type 值(数字)?类型 11 现在是否已经有定义?是怎么显示的(select 选项 label)?\n3. 编辑表单里\"活动类型\"切换后,会根据不同 type 渲染什么不同的子表单?现在已有的类型(比如 1,2,3 ...)是怎么做分支渲染的,是 v-if、动态组件、还是 schema 驱动?请给出代码示例。\n4. 活动详情 / 编辑接口前端是怎么请求的(API 文件路径、URL)?提交时的字段结构是什么样?\n5. 活动配置(config)字段在 form 中是平铺的还是放在嵌套 JSON 里?后端 activity 表是否有专门的 config / extra 字段?\n6. 类型 11(首充前免费余额定格与分档释放)在前端有任何残留代码 / 注释 / 占位吗?\n\n直接读 backend/slot_admin_vue/src 下面的代码,不要修改任何东西。返回时请用 markdown 列表形式给出关键文件路径、关键代码片段(用代码块包裹,附上文件路径和行号),以及你的总结结论。\n"}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"梳理活动配置编辑表单,先从路由、活动类型枚举与相关组件入手。\n\n[REDACTED]"},{"type":"tool_use","name":"updateCurrentStep","input":{"current_step":"Exploring activity form code"}},{"type":"tool_use","name":"Grep","input":{"pattern":"activity","path":"/Users/ray/Documents/project/www/slot/backend/slot_admin_vue/src","glob":"**/router/**","-i":true}},{"type":"tool_use","name":"Glob","input":{"target_directory":"/Users/ray/Documents/project/www/slot/backend/slot_admin_vue/src","glob_pattern":"**/*activit*"}},{"type":"tool_use","name":"Grep","input":{"pattern":"activity_type|activityType|ACTIVITY_TYPE","path":"/Users/ray/Documents/project/www/slot/backend/slot_admin_vue/src"}}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"[REDACTED]"},{"type":"tool_use","name":"Grep","input":{"pattern":"activity_type|activity","path":"/Users/ray/Documents/project/www/slot/backend/slot_admin_vue/src","glob":"**/*.{js,ts,vue,json}"}},{"type":"tool_use","name":"Read","input":{"path":"/Users/ray/Documents/project/www/slot/backend/slot_admin_vue/src/views/game/activity/edit.vue"}},{"type":"tool_use","name":"Read","input":{"path":"/Users/ray/Documents/project/www/slot/backend/slot_admin_vue/src/views/game/activity/index.vue"}},{"type":"tool_use","name":"Read","input":{"path":"/Users/ray/Documents/project/www/slot/backend/slot_admin_vue/src/api/game/activity.js"}}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"[REDACTED]"},{"type":"tool_use","name":"Grep","input":{"pattern":"activity|activity_type","path":"/Users/ray/Documents/project/www/slot/backend/slot_admin_vue","glob":"**/*.{js,ts,json,vue}"}},{"type":"tool_use","name":"Grep","input":{"pattern":"\\b11\\b|type.*11|首充|free_credits|定格|分档","path":"/Users/ray/Documents/project/www/slot/backend/slot_admin_vue","-i":true}},{"type":"tool_use","name":"Glob","input":{"target_directory":"/Users/ray/Documents/project/www/slot/backend/slot_admin_vue","glob_pattern":"**/router/**"}}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"[REDACTED]"},{"type":"tool_use","name":"Grep","input":{"pattern":"game/activity|activity","path":"/Users/ray/Documents/project/www/slot/backend/slot_admin_vue/src/router"}},{"type":"tool_use","name":"Grep","input":{"pattern":"activity_type","path":"/Users/ray/Documents/project/www/slot/backend"}},{"type":"tool_use","name":"Grep","input":{"pattern":"sa-select|dict","path":"/Users/ray/Documents/project/www/slot/backend/slot_admin_vue/src/components","glob":"**/*","head_limit":30}},{"type":"tool_use","name":"Glob","input":{"target_directory":"/Users/ray/Documents/project/www/slot/backend/slot_admin_vue/src","glob_pattern":"**/sa-select/**"}}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"[REDACTED]"},{"type":"tool_use","name":"Read","input":{"path":"/Users/ray/Documents/project/www/slot/backend/slot_admin_vue/src/router/index.js"}},{"type":"tool_use","name":"Read","input":{"path":"/Users/ray/Documents/project/www/slot/backend/slot_admin_vue/src/components/sa-select/index.vue"}},{"type":"tool_use","name":"Grep","input":{"pattern":"activity_type|ActivityType","path":"/Users/ray/Documents/project/www/slot/backend/slot_admin"}},{"type":"tool_use","name":"Grep","input":{"pattern":"formData\\.type|type ===|type ==|type !==","path":"/Users/ray/Documents/project/www/slot/backend/slot_admin_vue/src/views/game/activity"}}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"[REDACTED]"},{"type":"tool_use","name":"Grep","input":{"pattern":"dict|activity","path":"/Users/ray/Documents/project/www/slot/backend/slot_admin_vue/src/store"}},{"type":"tool_use","name":"Read","input":{"path":"/Users/ray/Documents/project/www/slot/backend/slot_admin_vue/src/router/webRouter.js","limit":80}},{"type":"tool_use","name":"Glob","input":{"target_directory":"/Users/ray/Documents/project/www/slot/backend/slot_admin","glob_pattern":"**/*Activity*"}},{"type":"tool_use","name":"Grep","input":{"pattern":"type.*11|TYPE.*11|activity.*11","path":"/Users/ray/Documents/project/www/slot/backend/slot_admin","-i":true}}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"[REDACTED]"},{"type":"tool_use","name":"Read","input":{"path":"/Users/ray/Documents/project/www/slot/backend/slot_admin/app/game/controller/ActivityController.php"}},{"type":"tool_use","name":"Read","input":{"path":"/Users/ray/Documents/project/www/slot/backend/slot_admin/app/game/validate/ActivityValidate.php"}},{"type":"tool_use","name":"Read","input":{"path":"/Users/ray/Documents/project/www/slot/backend/slot_admin_vue/src/store/modules/dict.js"}},{"type":"tool_use","name":"Grep","input":{"pattern":"activity_type|game/activity","path":"/Users/ray/Documents/project/www/slot/backend"}}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"[REDACTED]"},{"type":"tool_use","name":"Grep","input":{"pattern":"activity_type","path":"/Users/ray/Documents/project/www/slot","glob":"**/*.{sql,php,js,vue,ts}","head_limit":50}},{"type":"tool_use","name":"Grep","input":{"pattern":"class ActivityService|ext_config|ActivityModel","path":"/Users/ray/Documents/project/www/slot/slot_lib"}},{"type":"tool_use","name":"Read","input":{"path":"/Users/ray/Documents/project/www/slot/backend/slot_admin_vue/src/store/modules/user.js","limit":120}},{"type":"tool_use","name":"Grep","input":{"pattern":"game/activity|views/game/activity","path":"/Users/ray/Documents/project/www/slot/backend/slot_admin_vue"}}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"[REDACTED]"},{"type":"tool_use","name":"Read","input":{"path":"/Users/ray/Documents/project/www/slot/slot_lib/src/services/ActivityService.php"}},{"type":"tool_use","name":"Read","input":{"path":"/Users/ray/Documents/project/www/slot/backend/slot_admin_vue/src/store/modules/user.js","offset":115}},{"type":"tool_use","name":"Grep","input":{"pattern":"ActivityType|ACTIVITY_TYPE|activity.*type.*=.*11|type.*11","path":"/Users/ray/Documents/project/www/slot","glob":"**/*.{php,ts,js}","head_limit":40}},{"type":"tool_use","name":"Grep","input":{"pattern":"ext_config|activity","path":"/Users/ray/Documents/project/www/slot/slot_hub","glob":"**/*Activity*","head_limit":30}}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"[REDACTED]"},{"type":"tool_use","name":"Read","input":{"path":"/Users/ray/Documents/project/www/slot/slot_lib/src/common/const/Consts.php","limit":80}},{"type":"tool_use","name":"Grep","input":{"pattern":"ACTIVITY_TYPE|type.*10|type.*11","path":"/Users/ray/Documents/project/www/slot/slot_lib"}},{"type":"tool_use","name":"Grep","input":{"pattern":"ext_config|activity","path":"/Users/ray/Documents/project/www/slot/slot_console","glob":"**/*activity*","head_limit":40}},{"type":"tool_use","name":"Grep","input":{"pattern":"free_credits|定格|分档|type.*11","path":"/Users/ray/Documents/project/www/slot/backend/slot_admin_vue"}}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"[REDACTED]"},{"type":"tool_use","name":"Grep","input":{"pattern":"ACTIVITY_TYPE.*10|type.*=.*10|破产","path":"/Users/ray/Documents/project/www/slot","glob":"**/*.{php,vue,js}","head_limit":30}},{"type":"tool_use","name":"Grep","input":{"pattern":"activity_type","path":"/Users/ray/Documents/project/www/slot/slot_console"}},{"type":"tool_use","name":"Glob","input":{"target_directory":"/Users/ray/Documents/project/www/slot/slot_console","glob_pattern":"**/*Activity*Model*"}},{"type":"tool_use","name":"Read","input":{"path":"/Users/ray/Documents/project/www/slot/backend/slot_admin_vue/src/api/common.js","limit":50}}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"[REDACTED]"},{"type":"tool_use","name":"Read","input":{"path":"/Users/ray/Documents/project/www/slot/slot_console/app/model/common/RechargeGiftConfigModel.php"}},{"type":"tool_use","name":"Read","input":{"path":"/Users/ray/Documents/project/www/slot/slot_console/app/model/common/ActivityConfigModel.php"}},{"type":"tool_use","name":"Grep","input":{"pattern":"dictAll|dict/all","path":"/Users/ray/Documents/project/www/slot/backend/slot_admin_vue"}},{"type":"tool_use","name":"Grep","input":{"pattern":"free_credits|FREE_CREDITS|type.*11|TYPE.*11","path":"/Users/ray/Documents/project/www/slot/slot_console"}}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"[REDACTED]"},{"type":"tool_use","name":"Grep","input":{"pattern":"activity_type|'activity_type'","path":"/Users/ray/Documents/project/www/slot","glob":"**/*.sql","head_limit":30}},{"type":"tool_use","name":"Glob","input":{"target_directory":"/Users/ray/Documents/project/www/slot/slot_console","glob_pattern":"**/*Activity*Controller*"}},{"type":"tool_use","name":"Grep","input":{"pattern":"getPageList|function add|function update|ext_config","path":"/Users/ray/Documents/project/www/slot/slot_console","glob":"**/innerapi/**Activity*"}}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"[REDACTED]"},{"type":"tool_use","name":"Read","input":{"path":"/Users/ray/Documents/project/www/slot/slot_console/app/innerapi/controller/ActivityController.php"}},{"type":"tool_use","name":"Grep","input":{"pattern":"recharge_gift_config|ext_config","path":"/Users/ray/Documents/project/www/slot/slot_console/db"}},{"type":"tool_use","name":"Grep","input":{"pattern":"game/activity|activity/index","path":"/Users/ray/Documents/project/www/slot/backend","glob":"**/*.sql","head_limit":20}}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"[REDACTED]"},{"type":"tool_use","name":"Grep","input":{"pattern":"addActivityConfig|updateActivityConfig|ext_config","path":"/Users/ray/Documents/project/www/slot/slot_console/app/entity/activity/ActivityConfigEntity.php"}},{"type":"tool_use","name":"Read","input":{"path":"/Users/ray/Documents/project/www/slot/slot_console/app/entity/activity/ActivityConfigEntity.php","limit":120}},{"type":"tool_use","name":"Grep","input":{"pattern":"s_recharge_gift_config","path":"/Users/ray/Documents/project/www/slot","glob":"**/*.sql","head_limit":15}}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"[REDACTED]"},{"type":"tool_use","name":"Read","input":{"path":"/Users/ray/Documents/project/www/slot/slot_console/app/entity/activity/ActivityConfigEntity.php","offset":480,"limit":120}},{"type":"tool_use","name":"Grep","input":{"pattern":"activity_type|首冲|破产|兑换码|Free Credits","path":"/Users/ray/Documents/project/www/slot/backend/slot_admin","glob":"**/*","head_limit":30}},{"type":"tool_use","name":"Read","input":{"path":"/Users/ray/Documents/project/www/slot/backend/slot_admin_vue/src/views/game/advertisement/index.vue","limit":80}}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"[REDACTED]"},{"type":"tool_use","name":"Grep","input":{"pattern":"activity_type","path":"/Users/ray/Documents/project/www/slot/backend/slot_admin/plugin"}},{"type":"tool_use","name":"Grep","input":{"pattern":"details|read","path":"/Users/ray/Documents/project/www/slot/backend/slot_admin_vue/src/api/game/activity.js"}},{"type":"tool_use","name":"Grep","input":{"pattern":"formData\\.type|type === 11|type == 11","path":"/Users/ray/Documents/project/www/slot/backend/slot_admin_vue"}},{"type":"tool_use","name":"Read","input":{"path":"/Users/ray/Documents/project/www/slot/docs/requirements/首充前免费余额定格与分档释放需求文档.md","limit":80}}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"[REDACTED]"},{"type":"tool_use","name":"Grep","input":{"pattern":"ext_config|type.*11|后台|管理","path":"/Users/ray/Documents/project/www/slot/docs/requirements/首充前免费余额定格与分档释放需求文档.md","head_limit":40}},{"type":"tool_use","name":"Read","input":{"path":"/Users/ray/Documents/project/www/slot/backend/slot_admin_vue/src/views/game/recharge/first/edit.vue.bak","limit":80}},{"type":"tool_use","name":"Grep","input":{"pattern":"dictAll|dict_data|activity_type","path":"/Users/ray/Documents/project/www/slot/backend/slot_admin","glob":"**/*.php","head_limit":25}}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"[REDACTED]"},{"type":"tool_use","name":"Read","input":{"path":"/Users/ray/Documents/project/www/slot/docs/requirements/首充前免费余额定格与分档释放需求文档.md","offset":1040,"limit":50}},{"type":"tool_use","name":"Grep","input":{"pattern":"ext_config|release_tier|first_cash","path":"/Users/ray/Documents/project/www/slot/slot_console/app/api/logic/FreeCreditsLogic.php","head_limit":30}},{"type":"tool_use","name":"Grep","input":{"pattern":"ext_config","path":"/Users/ray/Documents/project/www/slot/slot_console/config/free_credits.php"}},{"type":"tool_use","name":"Read","input":{"path":"/Users/ray/Documents/project/www/slot/slot_console/config/free_credits.php"}}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"[REDACTED]"},{"type":"tool_use","name":"Glob","input":{"target_directory":"/Users/ray/Documents/project/www/slot/backend/slot_admin_vue/src/views/game/advertisement","glob_pattern":"**/*"}},{"type":"tool_use","name":"Grep","input":{"pattern":"type.*5|红包","path":"/Users/ray/Documents/project/www/slot/backend/slot_admin_vue/src/views/game/activity"}},{"type":"tool_use","name":"Read","input":{"path":"/Users/ray/Documents/project/www/slot/backend/slot_admin_vue/src/api/common.js","offset":95,"limit":15}}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"[REDACTED]"},{"type":"tool_use","name":"Read","input":{"path":"/Users/ray/Documents/project/www/slot/backend/slot_admin_vue/src/views/game/advertisement/edit.vue","limit":40}},{"type":"tool_use","name":"Grep","input":{"pattern":"game/activity","path":"/Users/ray/Documents/project/www/slot","glob":"**/*.{sql,json,md}","head_limit":15}}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"[REDACTED]"},{"type":"tool_use","name":"Read","input":{"path":"/Users/ray/Documents/project/www/slot/backend/slot_admin_vue/src/views/game/advertisement/index.vue","offset":80}}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"[REDACTED]"},{"type":"tool_use","name":"Read","input":{"path":"/Users/ray/Documents/project/www/slot/slot_console/app/api/logic/FreeCreditsLogic.php","offset":750,"limit":60}},{"type":"tool_use","name":"updateCurrentStep","input":{"current_step":"Compiling activity form report","final_summary":"活动配置在 `slot_admin_vue` 中由列表页 + 弹窗编辑组成,路由来自后台菜单动态注册;活动类型通过字典 `activity_type` 下拉展示,子表单用 `v-if` 按 type 分支。类型 11(Free Credits)在后端 `RechargeGiftConfigModel` 已定义,但前端无任何分支、占位或 `ext_config` 编辑 UI。","completed_subtitle":"Mapped activity form implementation"}}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"[REDACTED]"},{"type":"tool_use","name":"Read","input":{"path":"/Users/ray/Documents/project/www/slot/slot_console/app/api/logic/FreeCreditsLogic.php","offset":20,"limit":25}}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"[REDACTED]"},{"type":"tool_use","name":"Grep","input":{"pattern":"dictList|dict\\[","path":"/Users/ray/Documents/project/www/slot/backend/slot_admin_vue/src/components","head_limit":20}},{"type":"tool_use","name":"Read","input":{"path":"/Users/ray/Documents/project/www/slot/backend/slot_admin/plugin/saimulti/app/controller/AdminCommonController.php","offset":38,"limit":40}}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"[REDACTED]"},{"type":"tool_use","name":"Grep","input":{"pattern":"value|label","path":"/Users/ray/Documents/project/www/slot/backend/slot_admin/plugin/saimulti/app/model/system/SystemDictData.php"}},{"type":"tool_use","name":"Grep","input":{"pattern":"getLabel","path":"/Users/ray/Documents/project/www/slot/backend/slot_admin_vue/src/utils/tool.js","-A":5}}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"以下为对 `backend/slot_admin_vue/src` 的梳理结论(只读代码,未改任何文件)。\n\n---\n\n## 1. 活动列表与编辑页面入口\n\n### 路由(动态菜单,非静态路由表)\n\n- 静态路由只有 `layout` / `login` / 404,见 `/Users/ray/Documents/project/www/slot/backend/slot_admin_vue/src/router/webRouter.js`。\n- **活动页路由由登录后后台菜单动态注入**:`component` 字段映射到 `views/${component}.vue`。\n\n```141:161:/Users/ray/Documents/project/www/slot/backend/slot_admin_vue/src/store/modules/user.js\nconst views = import.meta.glob('../../views/**/**.vue')\n// ...\nconst filterAsyncRouter = (routerMap) => {\n // ...\n const route = {\n path: item.path,\n name: item.name,\n // ...\n component: views[`../../views/${item.component}.vue`]\n }\n```\n\n活动配置页面对应组件路径应为 **`game/activity/index`**(菜单里 `component` 配这个值时),路由 `name` 由后台菜单决定(常见为 `game/activity/index`)。\n\n### 列表页\n\n- **路径**:`/Users/ray/Documents/project/www/slot/backend/slot_admin_vue/src/views/game/activity/index.vue`\n- 使用 `sa-table` + `api.getPageList`;新增/编辑通过子组件弹窗。\n\n```60:61:/Users/ray/Documents/project/www/slot/backend/slot_admin_vue/src/views/game/activity/index.vue\n \n \n```\n\n```111:135:/Users/ray/Documents/project/www/slot/backend/slot_admin_vue/src/views/game/activity/index.vue\n add: {\n show: true,\n auth: ['/game/activity/add'],\n func: async () => {\n editRef.value?.open()\n },\n },\n edit: {\n show: true,\n // ...\n func: async (record) => {\n record.show_time = [\n record.show_start_time,\n record.show_end_time,\n ];\n const d = {...record};\n d.goods = record.goods?.map((item) => {\n return {...item};\n });\n editRef.value?.open('edit')\n editRef.value?.setFormData(d)\n },\n },\n```\n\n**说明**:编辑**没有单独详情接口**,直接用列表行 `record` 回填弹窗。\n\n### 编辑组件(Modal 弹窗,非独立路由页)\n\n- **路径**:`/Users/ray/Documents/project/www/slot/backend/slot_admin_vue/src/views/game/activity/edit.vue`\n- 由 `index.vue` 以 `` 引入,`defineExpose({ open, setFormData })`。\n\n### 相关 API 封装\n\n- **路径**:`/Users/ray/Documents/project/www/slot/backend/slot_admin_vue/src/api/game/activity.js`\n\n---\n\n## 2. 活动类型枚举在前端如何定义?支持哪些 type?type 11?\n\n### 前端没有本地 TS/JS 枚举\n\n- 表单字段名是 **`type`**(不是 `activity_type`)。\n- 下拉用字典码 **`activity_type`**,数据来自登录后 `dictAll` 接口,存入 Pinia。\n\n```31:33:/Users/ray/Documents/project/www/slot/backend/slot_admin_vue/src/views/game/activity/edit.vue\n \n \n```\n\n```18:21:/Users/ray/Documents/project/www/slot/backend/slot_admin_vue/src/store/modules/dict.js\n async initData() {\n const { data } = await commonApi.dictAll()\n this.data = data\n```\n\n```99:103:/Users/ray/Documents/project/www/slot/backend/slot_admin_vue/src/api/common.js\n dictAll() {\n return request({\n url: '/saimulti/system/dictAll',\n method: 'get'\n```\n\n```5:6:/Users/ray/Documents/project/www/slot/backend/slot_admin_vue/src/components/sa-select/index.vue\n :options=\"dictList[props.dict] ?? []\"\n```\n\n选项格式为 `{ value, label }`(见 `tool.getLabel`)。\n\n### 代码里能推断的 type 含义(来自分支逻辑 + 后端注释)\n\n| type | 前端是否有专门 UI | 含义(后端/注释) |\n|------|------------------|-------------------|\n| 1,2,3,4 | 通用「赠送配置」卡片 | 首充/二充/三充/普通 |\n| 5 | 列表侧被后端过滤 | 红包拉新(管理端 `getPageList` 排除 type=5) |\n| 6 | `ext_config.gift_amount` + 赠送卡片百分比 | 每日优惠 |\n| 7,8 | `onlyGift`:隐藏商品类型 | 注册赠送 / 下载赠送 |\n| 9 | 频道链接、兑换表等 | 兑换码 |\n| 10 | 破产保护字段块 | 破产保护 |\n| **11** | **无** | 首充前 Free Credits(仅后端已定义) |\n\n`slot_lib` 常量只到 9:\n\n```15:25:/Users/ray/Documents/project/www/slot/slot_lib/src/common/const/Consts.php\n const ACTIVITY_TYPE_FIRST_RECHARGE = 1; //活动类型标识 首次充值\n const ACTIVITY_TYPE_SECOND_RECHARGE = 2; //活动类型标识 第二次充值\n const ACTIVITY_TYPE_THIRD_RECHARGE = 3; //活动类型标识 第三次充值\n const ACTIVITY_TYPE_COMMON = 4; //活动类型标识 普通类型\n const ACTIVITY_TYPE_RED_PACKET = 5; //活动类型标识 红包拉新\n const ACTIVITY_TYPE_DISCOUNT = 6; //活动类型标识 每日优惠\n const ACTIVITY_TYPE_REG = 7; //活动类型标识 注册赠送\n const ACTIVITY_TYPE_DOWNLOAD = 8; //活动类型标识 下载赠送\n const ACTIVITY_TYPE_EXCHANGE_CODE = 9; //活动类型标识 兑换码赠送\n```\n\n后端 `RechargeGiftConfigModel` 另有 10、11:\n\n```68:71:/Users/ray/Documents/project/www/slot/slot_console/app/model/common/RechargeGiftConfigModel.php\n const TYPE_BRANKRUPTCY = 10; // 破产保护\n\n /** 首充前免费余额定格与分档释放;配置存 ext_config,玩家状态见 free_credits_* 表 */\n const TYPE_FREE_CREDITS = 11;\n```\n\n### type 11 在前端字典里有没有?\n\n- **`src` 内没有任何 `activity_type` 的硬编码条目**,`activity_type` 在仓库 SQL/前端里都搜不到静态定义。\n- **是否在 select 里出现,取决于 DB `sm_system_dict_data` 是否配置了 value=`11` 的项**;当前前端代码**没有**为 type 11 写 label 或表单项。\n- 若字典未配 11,仍可能通过接口保存 `type: 11`,但下拉无友好选项,且会走「默认赠送卡片」UI(见下一节)。\n\n---\n\n## 3. 编辑表单按 type 如何分支渲染?\n\n**机制:`v-if` / `template v-if` + 少量 `computed`,不是动态组件,也不是 schema 驱动。**\n\n### 分支一览\n\n| 条件 | 额外 UI |\n|------|---------|\n| `type == 6` | `ext_config.gift_amount` 额外赠送 |\n| `type == 9` | 频道链接、每日生成/兑换次数、发送模板、兑换概率表 |\n| `type === 10` | `reg_end_h`、`ext_config.amount/balance/times`(提交时 amount/balance ×1000) |\n| `type !== 10` | 显示「添加赠送」按钮 |\n| `type == 9` | 兑换表;否则 `goods` 卡片列表 |\n| `onlyGift`(7/8/9) | 隐藏「商品类型」单选 |\n| `[1,4,6]` | 赠送金额/Bonus 百分比展示 |\n\n核心片段:\n\n```42:46:/Users/ray/Documents/project/www/slot/backend/slot_admin_vue/src/views/game/activity/edit.vue\n \n \n \n```\n\n```83:123:/Users/ray/Documents/project/www/slot/backend/slot_admin_vue/src/views/game/activity/edit.vue\n \n \n \n \n \n \n\n \n \n \n \n \n \n \n \n \n```\n\n```125:180:/Users/ray/Documents/project/www/slot/backend/slot_admin_vue/src/views/game/activity/edit.vue\n \n 添加赠送\n \n \n \n \n \n \n```\n\n```211:214:/Users/ray/Documents/project/www/slot/backend/slot_admin_vue/src/views/game/activity/edit.vue\nlet onlyGift = computed(() => {\n return formData.type == 7 || formData.type == 8 || formData.type == 9;\n})\n```\n\n**type 11**:无任何 `v-if`;若选中 11,会落到 `type !== 10` 的通用 `goods` 赠送 UI,与 Free Credits 的 `ext_config` 配置需求不匹配。\n\n---\n\n## 4. 详情/编辑接口与提交字段结构\n\n### API 文件与 URL\n\n`/Users/ray/Documents/project/www/slot/backend/slot_admin_vue/src/api/game/activity.js`:\n\n| 操作 | Method | URL |\n|------|--------|-----|\n| 列表(兼“详情”数据源) | GET | `/game/activity/index` |\n| 新增 | POST | `/game/activity/save` |\n| 更新 | POST | `/game/activity/update?id={id}`(body 带 `updateData: 1`) |\n| 删除 | DELETE | `/game/activity/destroy` |\n| 改状态 | POST | `/game/activity/update`(无 `updateData`) |\n\n```12:38:/Users/ray/Documents/project/www/slot/backend/slot_admin_vue/src/api/game/activity.js\n getPageList(params = {}) {\n return request({\n url: '/game/activity/index',\n method: 'get',\n params\n })\n },\n save(params = {}) {\n return request({\n url: '/game/activity/save',\n method: 'post',\n data: params\n })\n },\n update(id, params = {}) {\n params['updateData'] = 1;\n return request({\n url: '/game/activity/update?id=' + id,\n method: 'post',\n data: params\n })\n },\n```\n\n**无** `read` / `details` 方法;`activity.js` 里也没有单独详情 URL。\n\n### 提交 payload(前端 `submit`)\n\n```358:382:/Users/ray/Documents/project/www/slot/backend/slot_admin_vue/src/views/game/activity/edit.vue\nconst submit = async (done) => {\n const validate = await formRef.value?.validate()\n if (!validate) {\n loading.value = true\n let data = {...formData}\n let result = {}\n data.modelId = formData.model_id\n data.goods = formData.goods.map(item => {\n return {...item}\n })\n\n if(formData.type === 10){\n data.ext_config.balance = data.ext_config.balance*1000\n data.ext_config.amount = data.ext_config.amount*1000\n }\n\n if (mode.value === 'add') {\n data.id = undefined\n result = await api.save(data)\n } else {\n result = await api.update(data.id, data)\n }\n```\n\n`formData` 默认结构:\n\n```242:260:/Users/ray/Documents/project/www/slot/backend/slot_admin_vue/src/views/game/activity/edit.vue\nconst formData = reactive({\n id: null,\n model_id: '',\n source: '',\n type: 1,\n goods: [],\n goods_type: 1,\n show_time: [],\n status: 1,\n end_time_type: 1,\n is_count_down: 2,\n title: '',\n min_amount_qf:0,\n reg_end_h: 99999,\n sub_title: '',\n help: '',\n ext_config: {},\n sort_num: 0,\n})\n```\n\n后端 `ActivityConfigEntity::updateConfig` 会消费:`type`, `show_time[0/1]`, `goods_type`, `title`, `sub_title`, `help`, `reg_end_h`, `status`, `sort_num`, **`ext_config`**, **`goods`**, `source`, `modelId` 等(经 slot_admin → slot_console innerapi)。\n\n---\n\n## 5. config 在 form 里是平铺还是嵌套?后端表字段?\n\n### 前端\n\n- **没有名为 `config` 的字段**。\n- 类型相关扩展放在 **`ext_config` 对象**(嵌套),例如 `ext_config.gift_amount`、`ext_config.link`、`ext_config.exchangeList`。\n- 档位赠送在 **`goods` 数组**(与 `ext_config` 并列,非塞进单一 JSON 文本框)。\n\n### 后端表(供对照,不在 vue 工程内)\n\n活动主表为 **`s_recharge_gift_config`**,模型注释含 **`ext_config`(json)**、**`goods`(json)**,无单独 `config` / `extra` 列名:\n\n```21:36:/Users/ray/Documents/project/www/slot/slot_console/app/model/common/RechargeGiftConfigModel.php\n * @property integer $type 活动类型:1:首冲, 2:二充, 3: 三充,4:普通,5:红包拉新 6:每日优惠 7:注册 8:下载 9:兑换码 10:破产保护\n * ...\n * @property array|null $ext_config json格式\n```\n\ntype 11 的业务配置预期写在 **`ext_config`**(如 `win_threshold`、`recharge_unlock_amount`、`first_cash_amount`、`package_amount` 等,见 `FreeCreditsLogic`),玩家状态在 **`free_credits_player` / `free_credits_package`** 表,不在 activity 表单里。\n\n---\n\n## 6. type 11 在前端是否有残留/注释/占位?\n\n在 **`slot_admin_vue/src` 全目录检索结果**:\n\n- **无** `type === 11` / `type == 11`\n- **无** `free_credits`、`定格`、`分档释放`、`TYPE_FREE_CREDITS`\n- **无** type 11 专用表单项或注释\n\n唯一相关是 **通用活动编辑**;若 DB 字典加了 value=11,也只会显示为普通下拉项,**没有专属子表单**。\n\n需求文档 §17 规划了独立「首充前免费余额配置」后台模块,**当前 vue 工程未实现**。\n\n---\n\n## 总结结论\n\n1. **入口**:动态路由 `game/activity/index` → `views/game/activity/index.vue`;编辑为同目录 `edit.vue` 弹窗,非独立路由页。 \n2. **活动类型**:前端**不维护枚举**,用字典 `activity_type` + 字段 `type`;代码显式处理 **6、7、8、9、10** 及 **1/4/6 百分比**;**11 在前端零实现**,字典是否在 DB 配置未知。 \n3. **分支渲染**:纯 **`v-if` 模板分支**,非 schema/动态组件;type 11 会误用通用 `goods` 表单。 \n4. **接口**:列表 `GET /game/activity/index` 即编辑数据源;保存 `POST save/update`,无单独详情 API。 \n5. **配置形态**:`ext_config` + `goods` **嵌套在 formData**;表字段为 **`ext_config` JSON**,无 `config` 列。 \n6. **type 11**:后端 `TYPE_FREE_CREDITS = 11` 已有,**管理端 vue 无任何占位或专用 UI**,需新增字典项 + `edit.vue` 分支(及可能的新菜单页)才能按需求文档配置。\n\n[REDACTED]"}]}}