feat: 语言切换功能页面推进

This commit is contained in:
hzj 2026-04-24 10:11:46 +08:00
parent b3661292cb
commit e5e517a2a3
60 changed files with 5554 additions and 1580 deletions

View File

@ -1,4 +1,4 @@
# UI 橙色主题与交互顺序调整记录
# UI 橙色主题与交互顺序调整记录
## 1. 变更背景
@ -32,7 +32,8 @@
### 2.2 按钮与弹窗按钮视觉顺序
已统一主按钮视觉优先级,使确认类按钮更突出。
已统一主按钮的视觉优先级,使确认类按钮更突出。
其中弹窗/抽屉底部操作区通过样式顺序优化为主按钮优先展示,但未改动原始点击逻辑。
## 3. 暗夜模式取消
@ -98,7 +99,8 @@
## 6. 侧边栏折叠态图标居中修复
在橙色样式改造后,发现侧边栏收起状态下图标未居中。
在橙色样式改造后,发现侧边栏收起状态下图标未居中。
原因是旧实现依赖固定 `margin-left` 偏移,无法适配新的侧边栏宽度、内边距和圆角布局。
处理方式:
@ -123,7 +125,7 @@
/>
```
存在“弹窗本体在遮罩层下方、示不全且无法点击”的问题。
存在“弹窗本体在遮罩层下方、示不全且无法点击”的问题。
### 7.1 原因
@ -149,12 +151,13 @@
### 7.2.1 `bank-balance/table` 页面补充修复
后续排查发现 `views/user/bank-balance/table` 页面仍存在同类问题。
后续排查发现 `views/user/bank-balance/table` 页面仍存在同类问题。
该页面内不仅有列表页内联弹窗,还包含开户弹窗、流水弹窗、导入弹窗等多个 `el-dialog`,其中部分弹窗未挂载到 `body`,因此在页面容器和遮罩层共同作用下,依旧会出现弹窗被压在遮罩层下方、无法完整展示和点击的问题。
本次补充修复范围:
- 发/扣除弹窗
- 发/扣除弹窗
- 转账弹窗
- 导入数据弹窗
- 开户弹窗 `form-edit.vue`
@ -246,7 +249,7 @@ $env:NODE_OPTIONS='--openssl-legacy-provider'; npm run build:prod
### 11.3 复扫结论
批量修复后,已再次对仓库内 `el-dialog``el-drawer` 做缺失项扫描,当前未再发现这类“未挂载到 body 导致遮罩层级异常”的遗漏项。
批量修复后,已再次对仓库内 `el-dialog``el-drawer` 做缺失项扫描,当前未再发现这类“未挂载到 `body` 导致遮罩层级异常”的遗漏项。
### 11.4 验证情况
@ -261,6 +264,7 @@ $env:NODE_OPTIONS='--openssl-legacy-provider'; npm run build:prod
- 构建通过
- 仍存在项目原有 warning
- 本轮弹窗挂载层级修复未引入新的构建错误
## 12. 批量弹窗修复后的重复属性问题记录
### 12.1 问题现象
@ -278,16 +282,16 @@ duplicate attribute: :append-to-body
### 12.2 原因
上一轮批量补齐弹窗/抽屉挂载属性时,少数原本已经存在以下配置的组件被重复插入了一组属性
上一轮批量补齐弹窗/抽屉挂载属性时,少数组件原本已经存在以下配置,却又被重复插入了一组
- `:modal-append-to-body="true"`
- `:append-to-body="true"`
Vue 模板不允许同一个标签出现重复属性,因此构建阶段直接报错。
Vue 模板不允许同一个标签出现重复属性,因此构建阶段直接报错。
### 12.3 修复范围
全仓扫描后发现重复项存在于以下文件:
全仓扫描后发现重复项存在于以下文件:
- `src/views/dashboard/components/user-daily-currency-gold-top/index.vue`
- `src/layout/components/Navbar.vue`
@ -321,9 +325,24 @@ $env:NODE_OPTIONS='--openssl-legacy-provider'; npm run build:atu-prod
结果:
- 构建通过
- 未再出现 duplicate attribute 模板编译错误
- 仍存在项目原有 lint warning / eslint warning与本次重复属性修复无关
- 未再出现 `duplicate attribute` 模板编译错误
- 仍存在项目原有 `lint warning / eslint warning`,与本次重复属性修复无关
### 12.6 后续注意事项
后续如继续做批量模板属性处理,需要先解析单个 `el-dialog` / `el-drawer` 标签块内是否已经存在目标属性,避免按文件级别判断导致重复插入。
后续如继续做批量模板属性处理,需要先解析单个 `el-dialog` / `el-drawer` 标签块内是否已经存在目标属性,避免按文件级别判断导致重复插入。
## 13. `NavbarLight.vue` 后续收口记录
在后续排查 `NavbarLight.vue` 生成后的实际使用情况时,对 Navbar 和导航搜索做了一次一致性收口:
- 确认 `src/layout/index.vue` 真正使用的 `Navbar` 来自 `src/layout/components/index.js`,而其导出实际指向 `NavbarLight.vue`
- 原有 `src/layout/components/Navbar.vue` 已无实际引用,因此已删除,避免后续维护中出现同名导航栏文件的混淆
- 修改 `src/components/HeaderSearch/index.vue``generateRoutes()` 逻辑,让导航栏搜索更倾向于真正可进入的页面路由
- 新增对“仍有可见子菜单的容器路由”的过滤,避免搜索到一级/二级菜单壳路由后点击进入空白页面
验证情况:
- 已执行 `npm run build:prod`
- 构建通过
- 项目原有 warning 仍存在,但本次 `Navbar/Search` 收口未引入新的构建错误

File diff suppressed because it is too large Load Diff

View File

@ -33,10 +33,10 @@
- 已完成 `src/views` 页面私有组件复查任务
- 首页 `Dashboard`
- `docs/i18n/主要路由.md` 中列出的主要路由页面
- 其他路由页面
- 临时规则补充:
- 在“已完成 `src/views` 页面私有组件复查任务”完成前,暂不继续领取新的未完成普通页面
- 这里的“私有组件”仅指 `src/views/**` 目录下、随页面一同渲染或由页面局部引用的组件,不包含公共组件目录 `src/components/**`
- 自 2026-04-22 起,后续任务范围只保留“主要路由页面 + 这些页面直接涉及的页面内组件/私有组件”;非主要路由页面不再继续补翻
- 当首页或 `主要路由.md` 中仍有未完成页面时,不应优先领取其他普通路由页面
- `主要路由.md` 中具体先领取哪一个页面,仍可按原来的灵活方式分配,只要遵守“不重复认领、不与其他线程冲突”
- 每次正式认领任务时,必须先把认领结果写入 md再开始修改源码不要先改代码、后补文档
@ -47,6 +47,10 @@
- 每完成一轮修改后,必须同步更新:
- `docs/i18n/翻译进度.md`
- `docs/i18n/翻译清单.md`
- 判断“独立路由”还是“页面内功能文件”时,统一按以下顺序执行:
- 先看原始接口返回中该菜单是否是独立菜单项
- 再看入口 `index.vue` 是否通过 `import`/局部注册直接引用同目录文件
- 如果接口只返回 1 个菜单项、而入口页直接引用多个同目录 `.vue` 文件,则记为“一个主路由页面 + 多个页面内功能文件”,不要误记成多个独立路由
- 自 2026-04-20 起,`src/views` 页面标记“已完成”时,还必须单独记录该页面私有组件状态:
- `无私有组件`
- `已随页面同步完成`
@ -155,13 +159,814 @@
| 线程 | 当前状态 | 认领范围 | 说明 |
| --- | --- | --- | --- |
| 线程一 | 待分配 | 未认领 | 2026-04-20 已完成 `src/views/props/props-source-group/index.vue``src/views/sys/country-code/index.vue``src/views/user/app-manager/index.vue``src/views/sys/notice-message/index.vue``src/views/sys/start-page/index.vue` 国际化收口;当前无进行中任务 |
| 线程二 | 待分配 | 未认领 | 2026-04-17 已完成 `src/views/message/push/index.vue``src/views/message/push/new-push-form.vue``src/views/message/push/push-log.vue``src/views/message/push/push-task.vue` 的国际化收口、语言 key 补充、中文残留扫描、构建验证与 md 同步;`push-text-store.vue` 已登记为当前未纳入入口渲染链的排除项 |
| 线程三 | 待分配 | 未认领 | 2026-04-20 已完成 `src/views/tools/redis/index.vue``src/views/tools/upload/index.vue` 国际化收口、语言 key 补充、中文残留扫描、构建验证与 md 同步;当前无进行中任务,后续按临时规则优先处理“已完成 views 页面私有组件复查” |
| 线程一 | 进行中 | `src/views/family/config` | 2026-04-22 已认领 `家族配置 / Family Configuration` 模块:`index.vue` + `config-level/index.vue` + `config-create-role/index.vue` + `config-weekly-reward/index.vue` |
| 线程二 | 进行中 | `src/views/game/ludo/index.vue` | 2026-04-22 已按规则重新读取协作文档与语言文件,现认领 Ludo 对局页面国际化、中文残留扫描、构建验证与 md 同步 |
| 线程三 | 进行中 | `src/views/version/app/index.vue` | 2026-04-22 已按规则重新读取协作文档、主路由清单与语言文件,现认领 `版本管理 / Version Management` 模块:`index.vue` + `app-version-edit.vue` + `app-update-content.vue` |
| 线程四 | 待分配 | 未认领 | 避免和其他线程同时编辑同一语言模块 |
## 最新认领记录
- 2026-04-22
- 线程三已新认领:`src/views/version/app/index.vue`
- 同步纳入范围:
- `src/views/version/app/app-version-edit.vue`
- `src/views/version/app/app-update-content.vue`
- 领取前动作:
- 已重新读取 `docs/i18n/线程协作.md`
- 已重新读取 `docs/i18n/翻译进度.md`
- 已重新读取 `docs/i18n/翻译清单.md`
- 已重新读取 `docs/i18n/主要路由.md`
- 已重新读取最新 `src/lang/messages/pages/en.js``src/lang/messages/pages/zh.js`
- 已读取 `src/views/external/im.vue`,确认该主路由页面当前仅为纯 iframe无待翻静态展示文本
- 已读取 `src/views/version/app/index.vue`
- 已读取 `src/views/version/app/app-version-edit.vue`
- 已读取 `src/views/version/app/app-update-content.vue`
- 已核对 `src/views/version/app` 目录当前可见文件为 `index.vue``app-version-edit.vue``app-update-content.vue`
- 已确认线程一当前认领 `src/views/family/config`、线程二当前认领 `src/views/game/ludo/index.vue`,本轮不与其他线程冲突
- 已按规则先更新 md写完后需立刻复读 4 份 md 再开始源码改动
- 认领范围包括:
- `src/views/version/app/index.vue`
- `src/views/version/app/app-version-edit.vue`
- `src/views/version/app/app-update-content.vue`
- 筛选区、表格列头、状态展示、操作菜单、弹窗标题、表单标签、placeholder、按钮、校验提示、确认弹窗与本地提示国际化
- 新增或复用 `pages.appVersionManager` 页面级 key
- 目录级中文残留扫描
- 构建验证
- `docs/i18n/翻译进度.md``docs/i18n/翻译清单.md` 同步更新
- 不包括:
- 接口参数
- 路由路径
- 平台 / 渠道 / 语言 / appType 等提交值与接口依赖值
- 版本号、构建号、下载链接、描述内容等动态数据
- 新增、编辑、删除、服务强更与分页逻辑
- 2026-04-22
- 线程三已完成:`src/views/team/bd-lead/index.vue`
- 同步完成:
- `src/views/team/bd-lead/add-bd-lead.vue`
- `src/views/team/bd-lead/bind-bd.vue`
- 完成范围包括:
- 筛选区、表格列头、操作菜单、抽屉标题、表单标签、placeholder、按钮、校验提示、确认弹窗与本地提示国际化
- 新增 `pages.bdLead`
- 目录级中文残留扫描
- 构建验证
- `docs/i18n/翻译进度.md``docs/i18n/翻译清单.md``docs/i18n/主要路由.md` 同步更新
- 页面私有组件状态:
- `src/views/team/bd-lead/add-bd-lead.vue`:已随页面同步完成
- `src/views/team/bd-lead/bind-bd.vue`:已随页面同步完成
- 补充说明:
- 未改动接口参数、权限码、路由路径、动态区域/成员/用户资料数据与新增/编辑/删除/绑定/分页逻辑
- `npm run build:prod` 已通过,仍为项目既有 warning
- 线程三本轮完成后切回待分配
- 2026-04-22
- 线程二已新认领:`src/views/game/ludo/index.vue`
- 领取前动作:
- 已重新读取 `docs/i18n/线程协作.md`
- 已重新读取 `docs/i18n/翻译进度.md`
- 已重新读取 `docs/i18n/翻译清单.md`
- 已重新读取 `docs/i18n/主要路由.md`
- 已读取最新 `src/lang/messages/pages/en.js``src/lang/messages/pages/zh.js`
- 已读取并扫描 `src/views/game/ludo/index.vue`
- 已核对 `src/views/game/ludo` 目录仅包含当前 `index.vue`
- 已确认线程一当前认领 `src/views/family/config`、线程三当前认领 `src/views/team/bd-lead/index.vue`,本轮不与其他线程冲突
- 已按规则先更新 md写完后需立即复读 4 份 md 再开始源码改动
- 认领范围包括:
- `src/views/game/ludo/index.vue`
- 筛选区 placeholder、搜索按钮、表格列头、玩家弹窗标题、按钮、分页提示、确认弹窗与本地状态/事件展示映射国际化
- 新增或复用 `pages.gameLudo` 页面级 key
- 中文残留扫描
- 构建验证
- `docs/i18n/翻译进度.md``docs/i18n/翻译清单.md` 同步更新
- 不包括:
- 接口参数
- `RUN``HISTORY``WAIT``PROGRESS``END``BACK_DISBAND` 等状态值
- `permissionsSysOriginPlatforms``SearchRoomInput` 与接口返回的动态数据
- 解散对局、加载更多与查询逻辑
- 2026-04-22
- 线程一已新认领:`src/views/family/config/index.vue`
- 同步纳入范围:
- `src/views/family/config/config-level/index.vue`
- `src/views/family/config/config-create-role/index.vue`
- `src/views/family/config/config-weekly-reward/index.vue`
- 领取前动作:
- 已重新读取 `docs/i18n/线程协作.md`
- 已重新读取 `docs/i18n/翻译进度.md`
- 已重新读取 `docs/i18n/翻译清单.md`
- 已重新读取 `docs/i18n/主要路由.md`
- 已重新读取最新 `src/lang/messages/pages/en.js``src/lang/messages/pages/zh.js`
- 已读取 `src/views/family/config/index.vue`,并确认其通过 tab 直接渲染 3 个子页
- 已核对 `src/views/family/config` 目录文件列表,仅包含入口页与 3 个可见子页
- 已对上述 4 个文件执行中文扫描,确认当前未被其他线程认领
- 已按规则先更新 md写完后需立刻复读 4 份 md 再开始源码改动
- 认领范围包括:
- 家族配置入口页 tab 标题国际化
- 家族等级配置、创建家族规则、创建每周奖励规则 3 个可见子页的静态展示文案国际化
- 新增或复用 `pages.familyConfig` 页面级 key
- 目录级中文残留扫描
- 构建验证
- `docs/i18n/翻译进度.md``docs/i18n/翻译清单.md` 同步更新
- 不包括:
- 接口参数
- 平台枚举值
- 等级 key / 类型 value / 资源类型 value
- 接口返回的资源名称、徽章名称、礼物名称等动态数据
- 新增、修改、查询逻辑
- 2026-04-22
- 线程三已新认领:`src/views/team/bd-lead/index.vue`
- 同步纳入范围:
- `src/views/team/bd-lead/add-bd-lead.vue`
- `src/views/team/bd-lead/bind-bd.vue`
- 领取前动作:
- 已重新读取 `docs/i18n/线程协作.md`
- 已重新读取 `docs/i18n/翻译进度.md`
- 已重新读取 `docs/i18n/翻译清单.md`
- 已重新读取 `docs/i18n/主要路由.md`
- 已重新读取最新 `src/lang/messages/pages/en.js``src/lang/messages/pages/zh.js`
- 已读取并扫描 `src/views/team/bd-lead/index.vue`
- 已读取并扫描 `src/views/team/bd-lead/add-bd-lead.vue`
- 已读取并扫描 `src/views/team/bd-lead/bind-bd.vue`
- 已核对 `src/views/team/bd-lead` 目录当前可见文件为 `index.vue``add-bd-lead.vue``bind-bd.vue`
- 已确认线程一、线程二当前无进行中任务,当前无其他线程认领该模块
- 已按规则先更新 md写完后需立刻复读 4 份 md 再开始源码改动
- 认领范围包括:
- `src/views/team/bd-lead/index.vue`
- `src/views/team/bd-lead/add-bd-lead.vue`
- `src/views/team/bd-lead/bind-bd.vue`
- 筛选区、表格列头、操作菜单、抽屉标题、表单标签、placeholder、按钮、校验提示、确认弹窗与本地提示国际化
- 新增或复用 `pages.bdLead` 页面级 key
- 中文残留扫描
- 构建验证
- `docs/i18n/翻译进度.md``docs/i18n/翻译清单.md` 同步更新
- 不包括:
- 接口参数
- 权限码
- 路由路径
- 区域、成员、用户资料等动态或接口返回内容
- 新增、编辑、删除、绑定与分页逻辑
- 2026-04-22
- 线程三已完成:`src/views/user/custom-gift/index.vue`
- 完成范围包括:
- 筛选区、状态/类型展示、表格列头、下载操作文案、成功与失败提示国际化
- 新增 `pages.userCustomGift`
- 中文残留扫描
- 构建验证
- `docs/i18n/翻译进度.md``docs/i18n/翻译清单.md``docs/i18n/主要路由.md` 同步更新
- 补充说明:
- 本轮不修改接口参数、状态与类型实际值、区域/用户/资源地址等动态或接口返回内容,以及审核切换、下载与分页逻辑
- `src/views/user/custom-gift/index.vue` 当前无中文残留
- 直接执行 `npm run build:prod` 构建通过,仍为项目既有 `22` 条 warning未新增阻塞错误
- 线程三本轮完成后切回待分配
- 2026-04-22
- 线程三已新认领:`src/views/user/custom-gift/index.vue`
- 领取前动作:
- 已重新读取 `docs/i18n/线程协作.md`
- 已重新读取 `docs/i18n/翻译进度.md`
- 已重新读取 `docs/i18n/翻译清单.md`
- 已重新读取 `docs/i18n/主要路由.md`
- 已重新读取最新 `src/lang/messages/pages/en.js``src/lang/messages/pages/zh.js`
- 已读取并扫描 `src/views/user/custom-gift/index.vue`
- 已核对 `src/views/user/custom-gift` 目录仅包含当前 `index.vue`
- 已确认线程一、线程二当前无进行中任务,当前无其他线程认领 `src/views/user/custom-gift/index.vue`
- 已按规则先更新 md写完后需立刻复读 4 份 md 再开始源码改动
- 认领范围包括:
- `src/views/user/custom-gift/index.vue`
- 筛选区、状态/类型展示、表格列头、下载操作文案、成功与失败提示国际化
- 新增或复用 `pages.userCustomGift` 页面级 key
- 中文残留扫描
- 构建验证
- `docs/i18n/翻译进度.md``docs/i18n/翻译清单.md` 同步更新
- 不包括:
- 接口参数
- 状态与类型实际值
- 区域、用户、资源地址等动态或接口返回内容
- 审核切换、下载与分页逻辑
- 2026-04-22
- 线程一已完成:`src/views/user/im-account-manager/index.vue`
- 完成范围包括:
- 筛选区、提示说明、表格列头、操作按钮、弹窗标题、表单标签、placeholder、校验提示与确认弹窗国际化
- 新增 `pages.imAccountManager`
- 中文残留扫描
- 构建验证
- `docs/i18n/翻译进度.md``docs/i18n/翻译清单.md` 同步更新
- 补充说明:
- 本轮未修改接口参数、默认密码值 `88888888`、动态昵称与业务逻辑
- `src/views/user/im-account-manager/index.vue` 当前无中文残留
- 直接执行 `npm run build:prod` 构建通过,仍为项目既有 `22` 条 warning未新增阻塞错误
- 线程一本轮完成后切回待分配
- 2026-04-22
- 线程二已完成:`src/views/tools/websocket/index.vue`
- 完成范围包括:
- 广播订阅与点对点卡片标题、步骤说明、表单标签、placeholder、按钮、状态提示与本地错误提示国际化
- 新增 `pages.websocketTool`
- 中文残留扫描
- 构建验证
- `docs/i18n/翻译进度.md``docs/i18n/翻译清单.md` 同步更新
- 补充说明:
- 本轮不修改 Socket 端点默认值、接口参数、订阅地址与发送地址实际值、Token/用户 ID/消息体字段和 WebSocket 连接发送逻辑
- `src/views/tools/websocket/index.vue` 当前无中文残留
- 直接执行 `npm run build:prod` 构建通过,仍为项目既有 `22` 条 warning未新增阻塞错误
- 线程二本轮完成后切回待分配
- 2026-04-22
- 线程一已新认领:`src/views/user/im-account-manager/index.vue`
- 领取前动作:
- 已重新读取 `docs/i18n/线程协作.md`
- 已重新读取 `docs/i18n/翻译进度.md`
- 已重新读取 `docs/i18n/翻译清单.md`
- 已重新读取 `docs/i18n/主要路由.md`
- 已重新读取最新 `src/lang/messages/pages/en.js``src/lang/messages/pages/zh.js`
- 已读取并扫描 `src/views/user/im-account-manager/index.vue`
- 已核对 `src/views/user/im-account-manager` 目录仅包含当前 `index.vue`
- 已确认当前无其他线程认领该文件
- 已按规则先更新 md写完后需立刻复读 4 份 md 再开始源码改动
- 认领范围包括:
- `src/views/user/im-account-manager/index.vue`
- 筛选区、提示说明、表格列头、操作按钮、弹窗标题、表单标签、placeholder、校验提示与确认弹窗国际化
- 新增或复用 `pages.imAccountManager` 页面级 key
- 中文残留扫描
- 构建验证
- `docs/i18n/翻译进度.md``docs/i18n/翻译清单.md` 同步更新
- 不包括:
- 接口参数
- 默认密码值 `88888888`
- 用户昵称等动态数据
- 删除、重置密码与新增逻辑
- 2026-04-22
- 线程一已完成:`src/views/sys/request-blacklist/index.vue`
- 完成范围包括:
- 请求黑名单页面新增按钮、确认弹窗、输入提示、提示文案与本地错误提示国际化
- 新增 `pages.requestBlacklist`
- 中文残留扫描
- 构建验证
- `docs/i18n/翻译进度.md``docs/i18n/翻译清单.md` 同步更新
- 补充说明:
- 本轮未修改接口参数、黑名单内容值本身、路由路径与新增删除逻辑
- `src/views/sys/request-blacklist/index.vue` 当前无中文残留
- 直接执行 `npm run build:prod` 构建通过,仍为项目既有 `22` 条 warning未新增阻塞错误
- 线程一本轮完成后切回待分配
- 2026-04-22
- 线程二已新认领:`src/views/tools/websocket/index.vue`
- 领取前动作:
- 已重新读取 `docs/i18n/线程协作.md`
- 已重新读取 `docs/i18n/翻译进度.md`
- 已重新读取 `docs/i18n/翻译清单.md`
- 已重新读取 `docs/i18n/主要路由.md`
- 已读取最新 `src/lang/messages/pages/en.js``src/lang/messages/pages/zh.js`
- 已读取并扫描 `src/views/tools/websocket/index.vue`
- 已核对 `src/views/tools/websocket` 目录仅包含当前 `index.vue`
- 已确认线程一当前认领 `src/views/sys/request-blacklist/index.vue`,线程三当前无进行中任务,本轮不与其他线程冲突
- 已按规则先更新 md写完后需立即复读 4 份 md 再开始源码改动
- 认领范围包括:
- `src/views/tools/websocket/index.vue`
- 广播订阅与点对点卡片标题、步骤说明、表单标签、placeholder、按钮、状态提示与本地错误提示国际化
- 新增或复用 `pages.websocketTool` 页面级 key
- 中文残留扫描
- 构建验证
- `docs/i18n/翻译进度.md``docs/i18n/翻译清单.md` 同步更新
- 不包括:
- Socket 端点默认值
- 接口参数
- 订阅地址与发送地址实际值
- Token、用户 ID 与消息体字段
- WebSocket 连接、订阅与发送逻辑
- 2026-04-22
- 线程一已新认领:`src/views/sys/request-blacklist/index.vue`
- 领取前动作:
- 已重新读取 `docs/i18n/线程协作.md`
- 已重新读取 `docs/i18n/翻译进度.md`
- 已重新读取 `docs/i18n/翻译清单.md`
- 已重新读取 `docs/i18n/主要路由.md`
- 已读取最新 `src/lang/messages/pages/en.js``src/lang/messages/pages/zh.js`
- 已读取并扫描 `src/views/sys/request-blacklist/index.vue`
- 已确认当前无其他线程认领该文件
- 已按规则先更新 md写完后需立刻复读 4 份 md 再开始源码改动
- 认领范围包括:
- `src/views/sys/request-blacklist/index.vue`
- 按钮、确认弹窗、输入提示、提示文案与本地错误提示国际化
- 新增或复用 `pages.requestBlacklist` 页面级 key
- 中文残留扫描
- 构建验证
- `docs/i18n/翻译进度.md``docs/i18n/翻译清单.md` 同步更新
- 不包括:
- 接口参数
- 黑名单内容值本身
- 路由路径
- 删除与新增逻辑
- 2026-04-22
- 线程二已完成:`src/views/tools/gold-analyze/index.vue`
- 完成范围包括:
- 筛选区 placeholder、日期区间文案、搜索按钮、图表标题、副标题与本地 warn 提示国际化
- 新增 `pages.goldAnalyze`
- 中文残留扫描
- 构建验证
- `docs/i18n/翻译进度.md``docs/i18n/翻译清单.md` 同步更新
- 补充说明:
- 本轮不修改接口参数、图表返回数据、`originName` 动态数据、图表配置结构与查询逻辑
- `src/views/tools/gold-analyze/index.vue` 当前无中文残留
- 直接执行 `npm run build:prod` 构建通过,仍为项目既有 `22` 条 warning未新增阻塞错误
- 线程二本轮完成后切回待分配
- 2026-04-22
- 线程二已新认领:`src/views/tools/gold-analyze/index.vue`
- 领取前动作:
- 已重新读取 `docs/i18n/线程协作.md`
- 已重新读取 `docs/i18n/翻译进度.md`
- 已重新读取 `docs/i18n/翻译清单.md`
- 已重新读取 `docs/i18n/主要路由.md`
- 已读取最新 `src/lang/messages/pages/en.js``src/lang/messages/pages/zh.js`
- 已读取并扫描 `src/views/tools/gold-analyze/index.vue`
- 已确认线程一、线程三当前均无进行中任务,本轮不与其他线程冲突
- 已按规则先更新 md写完后需立即复读 4 份 md 再开始源码改动
- 认领范围包括:
- `src/views/tools/gold-analyze/index.vue`
- 筛选区 placeholder、日期区间文案、搜索按钮、图表标题、副标题与本地 warn 提示国际化
- 新增或复用 `pages.goldAnalyze` 页面级 key
- 中文残留扫描
- 构建验证
- `docs/i18n/翻译进度.md``docs/i18n/翻译清单.md` 同步更新
- 不包括:
- 接口参数
- 图表返回数据
- `originName` 动态数据
- 图表配置结构与查询逻辑
- 2026-04-22
- 线程二已完成:`src/views/tools/compensate-order/index.vue`
- 完成范围包括:
- 订单补偿与糖果补偿表单标题、标签、placeholder、按钮、校验文案与确认弹窗国际化
- 新增 `pages.compensateOrder`
- 中文残留扫描
- 构建验证
- `docs/i18n/翻译进度.md``docs/i18n/翻译清单.md` 同步更新
- 补充说明:
- 本轮不修改接口参数、提交字段、平台枚举值、支付方式实际 value 或提交逻辑
- `src/views/tools/compensate-order/index.vue` 当前无中文残留
- 直接执行 `npm run build:prod` 构建通过,仍为项目既有 `22` 条 warning未新增阻塞错误
- 线程二本轮完成后切回待分配
- 2026-04-22
- 线程一已完成:`src/views/tools/other` Utilities 路由页
- 完成范围包括:
- `src/views/tools/other/index.vue`
- `src/views/tools/other/alias-method.vue`
- `src/views/tools/other/api-sign.vue`
- `src/views/tools/other/redis-ops.vue`
- `src/views/tools/other/token-parse.vue`
- `src/views/tools/other/user-cancel-and-restore.vue`
- `src/views/tools/other/user-room-region.vue`
- `src/views/tools/other/user-swap-user.vue`
- 入口页卡片标题、页面内功能文件的表单标签、placeholder、按钮、弹窗标题、校验提示、本地展示说明与本地检查项描述国际化
- 新增 `pages.toolsOther` 模块级语言 key便于后续语言继续复用同一键名结构
- 对 `src/views/tools/other/*.vue` 执行目录级中文残留扫描
- 构建验证
- 同步更新 `docs/i18n/翻译进度.md``docs/i18n/翻译清单.md``docs/i18n/主要路由.md`
- 补充说明:
- 按原始接口返回与 `src/views/tools/other/index.vue` 的直接引用关系,当前记为“一个主路由页面 + 多个页面内功能文件”,不记为多个独立路由
- 当前残留中文仅为 `alias-method.vue` 中会随表单提交到接口的快速填充样例值,以及 `user-swap-user.vue` 中注释掉的提示代码
- 上述样例值属于提交数据,不按纯展示文案翻译,避免影响接口入参
- 直接执行 `npm run build:prod` 已通过,仍为项目既有 `22` 条 warning未新增阻塞错误
- 线程一本轮完成后切回待分配
- 2026-04-22
- 线程二已新认领:`src/views/tools/compensate-order/index.vue`
- 领取前动作:
- 已重新读取 `docs/i18n/线程协作.md`
- 已重新读取 `docs/i18n/翻译进度.md`
- 已重新读取 `docs/i18n/翻译清单.md`
- 已重新读取 `docs/i18n/主要路由.md`
- 已读取最新 `src/lang/messages/pages/en.js``src/lang/messages/pages/zh.js`
- 已读取并扫描 `src/views/tools/compensate-order/index.vue`
- 已核对 `src/views/tools/compensate-order` 目录仅包含当前 `index.vue`
- 已确认线程一当前认领 `src/views/tools/other`,线程三当前无进行中任务,本轮不与其他线程冲突
- 已按规则先更新 md写完后需立即复读 4 份 md 再开始源码改动
- 认领范围包括:
- `src/views/tools/compensate-order/index.vue`
- 订单补偿与糖果补偿表单标题、标签、placeholder、按钮、校验文案与确认弹窗国际化
- 新增或复用 `pages.compensateOrder` 页面级 key
- 中文残留扫描
- 构建验证
- `docs/i18n/翻译进度.md``docs/i18n/翻译清单.md` 同步更新
- 不包括:
- 接口参数
- 提交字段
- 平台枚举值
- 支付方式实际 value
- 订单补偿与糖果补偿提交逻辑
- 2026-04-22
- 线程二已完成:`src/views/401.vue`
- 完成范围包括:
- 401 权限错误页标题、副标题、导航提示、链接文案与图片弹窗标题国际化
- 新增 `pages.page401`
- 中文残留扫描
- 构建验证
- `docs/i18n/翻译进度.md``docs/i18n/翻译清单.md` 同步更新
- 补充说明:
- 本轮不修改路由路径、返回上一页逻辑、首页跳转路径、图片资源或远程图片地址
- `src/views/401.vue` 当前无中文残留
- 本机当前直接执行 `npm run build:prod` 可通过,仍为项目既有 `22` 条 warning未新增阻塞错误
- 本机当前设置 `$env:NODE_OPTIONS='--openssl-legacy-provider'` 反而会报 `--openssl-legacy-provider is not allowed in NODE_OPTIONS`,新线程不要先默认加该变量
- 线程二本轮完成后切回待分配
- 2026-04-22
- 线程二已新认领:`src/views/401.vue`
- 领取前动作:
- 已重新读取 `docs/i18n/线程协作.md`
- 已重新读取 `docs/i18n/翻译进度.md`
- 已重新读取 `docs/i18n/翻译清单.md`
- 已重新读取 `docs/i18n/主要路由.md`
- 已读取最新 `src/lang/messages/pages/en.js``src/lang/messages/pages/zh.js`
- 已读取并扫描 `src/views/401.vue`
- 已确认线程一当前认领 `src/views/tools/other`,线程三当前无进行中任务,本轮不与其他线程冲突
- 已按规则先更新 md写完后需立即复读 4 份 md 再开始源码改动
- 认领范围包括:
- `src/views/401.vue`
- 401 权限错误页标题、副标题、导航提示、链接文案与图片弹窗标题国际化
- 新增或复用 `pages.page401` 页面级 key
- 中文残留扫描
- 构建验证
- `docs/i18n/翻译进度.md``docs/i18n/翻译清单.md` 同步更新
- 不包括:
- 路由路径
- 返回上一页逻辑
- 首页跳转路径
- 图片资源与远程图片地址
- 2026-04-22
- 线程一已新认领:`src/views/tools/other` Utilities 路由页
- 领取前动作:
- 已重新读取 `docs/i18n/线程协作.md`
- 已重新读取 `docs/i18n/翻译进度.md`
- 已重新读取 `docs/i18n/翻译清单.md`
- 已重新读取 `docs/i18n/主要路由.md`
- 已读取并扫描 `src/views/tools/other` 目录文件列表
- 已确认当前没有其他线程认领该模块
- 已按规则先更新 md写完后需立即复读 4 份 md 再开始源码改动
- 认领范围包括:
- `src/views/tools/other/index.vue`
- `src/views/tools/other/alias-method.vue`
- `src/views/tools/other/api-sign.vue`
- `src/views/tools/other/redis-ops.vue`
- `src/views/tools/other/token-parse.vue`
- `src/views/tools/other/user-cancel-and-restore.vue`
- `src/views/tools/other/user-room-region.vue`
- `src/views/tools/other/user-swap-user.vue`
- 按原始接口返回与入口页引用关系,一并作为同路由下的页面内功能文件处理
- 入口页卡片标题、表单标签、placeholder、按钮、弹窗标题、校验提示、本地展示说明与本地检查项描述国际化
- 优先新增 `pages.toolsOther` 模块级 key
- 中文残留扫描
- 构建验证
- `docs/i18n/翻译进度.md``docs/i18n/翻译清单.md` 与必要的路由进度同步更新
- 不包括:
- 接口参数
- 路由路径
- 权限码
- 后端依赖值
- 表单提交字段
- `CLEAR_KEYS``TOKEN_PARSE``GET_TOKEN` 等模式值
- 2026-04-22
- 线程三已完成:`src/views/activity/template-cnf` 页面私有组件复查
- 完成范围包括:
- `src/views/activity/template-cnf/activity-rank.vue`
- `src/views/activity/template-cnf/activity-rank-reward.vue`
- `src/views/activity/template-cnf/form-edit.vue`
- `src/views/activity/template-cnf/reward-edit.vue`
- `src/views/activity/template-cnf/template-editor/attr-editor.vue`
- `src/views/activity/template-cnf/template-editor/index.vue`
- `src/views/activity/template-cnf/template-editor/lang-text-editor.vue`
- `src/views/activity/template-cnf/template-editor/page-editor.vue`
- `src/views/activity/template-cnf/template-editor/props-card.vue`
- `src/views/activity/template-cnf/template-editor/reward-array-editor.vue`
- `src/views/activity/template-cnf/template-form/index.vue`
- 活动排名弹窗、奖品编辑、模板编辑器、模板表单与本地道具类型展示映射国际化
- 补充 `pages.activityTemplateManage` 语言 key
- 对 `src/views/activity/template-cnf` 执行目录级中文残留扫描
- 构建验证
- `docs/i18n/翻译进度.md``docs/i18n/翻译清单.md``docs/i18n/主要路由.md` 同步更新
- 补充说明:
- 本轮不修改接口参数、路由路径、权限码、提交字段或后端依赖值
- 当前 `src/views/activity/template-cnf` 目录残余中文均为 `index.vue` 注释或源码注释,不属于可见展示文案
- 直接执行 `npm run build:prod` 触发项目既有 `ERR_OSSL_EVP_UNSUPPORTED`
- 改用 `$env:NODE_OPTIONS='--openssl-legacy-provider'; npm run build:prod` 后构建通过,仍为项目既有 `22` 条 warning未新增阻塞错误
- 线程三本轮完成后切回待分配,暂不继续认领新任务
- 2026-04-22
- 线程二已完成:`src/views/team/components/TeamCreate/index.vue`
- 完成范围包括:
- 主播中心共享私有创建团队组件中的抽屉标题、表单标签、placeholder、单选展示文案、分组标题、按钮、校验提示与提交中提示国际化
- 新增 `pages.teamCreate` 独立 key避免继续扩散 `pages.businessDevelopment` 的页面语义
- 将表单校验规则改为 computed 中按当前语言取值,支持语言切换后重新取当前文案
- 中文残留扫描
- 语言文件随生产构建完成校验
- 构建验证
- `docs/i18n/翻译进度.md``docs/i18n/翻译清单.md` 同步更新
- 补充说明:
- 本轮不修改接口参数、路由路径、权限码、表单提交字段或后端依赖值
- `bindBd``yes/no` 绑定值、`bdAccountDefaultSelectType``LONG` 值与 `contactTypes` 的实际 value 均保持原样
- `src/views/team/components/TeamCreate/index.vue` 当前无中文残留
- `node --check` 不适合直接检查当前 `export default` 语言文件;本轮以生产构建结果作为语言文件校验依据
- 直接执行 `npm run build:prod` 触发项目既有 `ERR_OSSL_EVP_UNSUPPORTED`
- 改用 `$env:NODE_OPTIONS='--openssl-legacy-provider'; npm run build:prod` 后构建通过,仍为项目既有 `22` 条 warning未新增阻塞错误
- 线程二本轮完成后切回待分配,暂不继续认领新任务
- 2026-04-22
- 线程二已新认领:`src/views/team/components/TeamCreate/index.vue`
- 领取前动作:
- 已重新读取 `docs/i18n/线程协作.md`
- 已重新读取 `docs/i18n/翻译进度.md`
- 已重新读取 `docs/i18n/翻译清单.md`
- 已重新读取 `docs/i18n/主要路由.md`
- 已读取最新 `src/lang/messages/pages/en.js``src/lang/messages/pages/zh.js`
- 已读取共享私有组件 `src/views/team/components/TeamCreate/index.vue`
- 已核对该组件目录仅包含当前 `index.vue`
- 已核对引用位置:`src/views/team/business-development/index.vue``src/views/team/team-list/index.vue` 当前直接 import`src/views/team/bd-lead/index.vue` 仅出现同名状态/方法文本匹配,未在本次直接引用扫描中命中 import
- 已执行当前组件中文扫描确认待处理中文集中在抽屉标题、表单标签、placeholder、按钮、校验提示与提交中提示
- 已确认线程一当前待分配,线程三当前认领 `src/views/activity/template-cnf/**`,本轮不与其他线程冲突
- 已按规则先更新 md写完后需立即复读 4 份 md 再开始源码改动
- 认领范围包括:
- `src/views/team/components/TeamCreate/index.vue`
- 主播中心共享私有创建团队组件中的静态展示文本、抽屉标题、表单标签、placeholder、单选展示文案、分组标题、按钮、校验提示与提交中提示国际化
- 优先复用 `pages.businessDevelopment` 既有 key必要时补充 `pages.teamCreate` 独立 key
- 中文残留扫描
- 构建验证
- `docs/i18n/翻译进度.md``docs/i18n/翻译清单.md` 同步更新
- 不包括:
- 接口参数
- 路由路径
- 权限码
- 后端依赖值
- 表单提交字段
- `bindBd``yes/no` 绑定值与 `bdAccountDefaultSelectType``LONG`
- 2026-04-22
- 线程一已完成:`src/views/cnf/lottery/select-prize.vue`
- 完成范围包括:
- 转盘抽奖品配置页面奖品选择弹窗标题、表单标签、placeholder、按钮、校验提示、类型分组与类型选项展示国际化
- 保持 `value``type`、接口字段、提交字段、路由路径与后端依赖值原样
- 类型分组与选项改为 `labelKey` 展示映射,便于后续 `bn``ar``tr` 等语言文件复用同一组 key
- 补充 `pages.lotteryConfig.placeholder.select/quantity``pages.lotteryConfig.validation.requiredField``pages.lotteryConfig.selectPrize`
- 对 `src/views/cnf/lottery/*.vue` 执行目录级中文残留扫描
- 执行语言文件语法检查与构建验证
- 同步更新 `docs/i18n/翻译进度.md``docs/i18n/翻译清单.md``docs/i18n/主要路由.md`
- 补充说明:
- 当前 `src/views/cnf/lottery` 目录残余中文仅为 `index.vue` 中“扩展属性”注释,不属于可见展示文案
- 直接执行 `npm run build:prod` 触发项目既有 `ERR_OSSL_EVP_UNSUPPORTED`
- 改用 `$env:NODE_OPTIONS='--openssl-legacy-provider'; npm run build:prod` 后构建通过,仍为项目既有 `22` 条 warning未新增阻塞错误
- 线程一本轮完成后切回待分配,暂不继续认领新任务
- 2026-04-22
- 线程三已新认领:`src/views/activity/template-cnf` 页面私有组件复查
- 领取前动作:
- 已重新读取 `docs/i18n/线程协作.md`
- 已重新读取 `docs/i18n/翻译进度.md`
- 已重新读取 `docs/i18n/翻译清单.md`
- 已重新读取 `docs/i18n/主要路由.md`
- 已读取最新 `src/lang/messages/pages/en.js``src/lang/messages/pages/zh.js`
- 已读取入口页 `src/views/activity/template-cnf/index.vue`
- 已核对 `src/views/activity/template-cnf` 目录文件列表、入口页本地 import 与子目录文件
- 已执行 `src/views/activity/template-cnf` 目录级中文扫描
- 已确认线程一当前认领 `src/views/cnf/lottery/select-prize.vue`,线程二在 `翻译清单.md``翻译进度.md` 中当前无进行中任务,本轮不与其他线程冲突
- 已按规则先更新 md写完后需立即复读 4 份 md 再开始源码改动
- 认领范围包括:
- `src/views/activity/template-cnf/activity-rank.vue`
- `src/views/activity/template-cnf/activity-rank-reward.vue`
- `src/views/activity/template-cnf/form-edit.vue`
- `src/views/activity/template-cnf/reward-edit.vue`
- `src/views/activity/template-cnf/template-editor/attr-editor.vue`
- `src/views/activity/template-cnf/template-editor/index.vue`
- `src/views/activity/template-cnf/template-editor/lang-text-editor.vue`
- `src/views/activity/template-cnf/template-editor/page-editor.vue`
- `src/views/activity/template-cnf/template-editor/props-card.vue`
- `src/views/activity/template-cnf/template-editor/reward-array-editor.vue`
- `src/views/activity/template-cnf/template-form/index.vue`
- 活动管理页面同目录私有组件、活动排名弹窗、奖品编辑、模板编辑器与模板表单中的静态展示文本、按钮、placeholder、表格列头、提示、确认弹窗与本地展示映射国际化复核
- 优先复用并补充 `pages.activityTemplateManage` 既有 key
- 中文残留扫描
- 构建验证
- `docs/i18n/翻译进度.md``docs/i18n/翻译清单.md``docs/i18n/主要路由.md` 同步更新
- 不包括:
- 接口参数
- 路由路径
- 权限码
- 后端依赖值
- 2026-04-22
- 线程一已新认领:`src/views/cnf/lottery/select-prize.vue`
- 领取前动作:
- 已重新读取 `docs/i18n/线程协作.md`
- 已重新读取 `docs/i18n/翻译进度.md`
- 已重新读取 `docs/i18n/翻译清单.md`
- 已重新读取 `docs/i18n/主要路由.md`
- 已读取 `src/lang/messages/pages/en.js``src/lang/messages/pages/zh.js``pages.lotteryConfig` 相关 key
- 已读取 `src/views/cnf/lottery/index.vue`
- 已读取 `src/views/cnf/lottery/select-prize.vue`
- 已读取 `src/views/cnf/lottery` 目录文件列表,确认该目录仅包含入口页与当前私有组件
- 已对 `src/views/cnf/lottery/*.vue` 执行中文扫描,确认入口页残留中文仅为注释,当前私有组件仍有可见中文
- 已确认当前没有其他线程认领该私有组件
- 已按最新规则先更新 md再开始源码改动
- 认领范围包括:
- `src/views/cnf/lottery/select-prize.vue`
- 奖品选择弹窗标题、表单标签、placeholder、按钮、校验提示、类型分组与类型选项展示国际化
- 优先复用并补充 `pages.lotteryConfig` 既有 key
- 中文残留扫描
- 构建验证
- `docs/i18n/翻译进度.md``docs/i18n/翻译清单.md``docs/i18n/主要路由.md` 同步更新
- 不包括:
- 接口参数
- 路由路径
- 权限码
- 后端依赖值
- 2026-04-21
- 线程三已完成:`src/views/room/profile` 页面同目录私有组件复查
- 完成范围包括:
- `src/views/room/profile/room-active-charts.vue`
- `src/views/room/profile/room-operation-log.vue`
- `src/views/room/profile/room-visitor-log.vue`
- `src/views/room/profile/user-drawer.vue`
- 弹窗/抽屉标题、筛选 placeholder、按钮、表格列头、角色展示、图表标题与图表 series 名称国际化
- `src/lang/messages/pages/en.js``src/lang/messages/pages/zh.js``pages.roomProfile` key 补充
- 图表组件监听 `language`,支持运行时切换语言后重绘 legend 与 series 名称
- 中文残留扫描
- 构建验证
- `docs/i18n/翻译进度.md``docs/i18n/翻译清单.md``docs/i18n/主要路由.md` 同步更新
- 补充说明:
- 本轮不修改接口参数、路由路径、权限码、提交字段或后端依赖值
- `roomRoles` 常量值保持原样,仅在页面展示层按 `HOMEOWNER``ADMIN``MEMBER` 映射翻译
- 当前 `src/views/room/profile` 目录残余中文均在 `index.vue` 注释或注释掉的历史菜单代码中,不属于本轮可见展示文案
- 首次直接执行 `npm run build:prod` 触发项目既有 `ERR_OSSL_EVP_UNSUPPORTED`
- 改用 `$env:NODE_OPTIONS='--openssl-legacy-provider'; npm run build:prod` 后构建通过,仍为项目既有 `22` 条 warning未新增阻塞错误
- 线程三本轮完成后切回待分配,暂不继续认领新任务
- 2026-04-21
- 线程二已完成:`src/views/team/business-development` 页面同目录私有组件复查
- 完成范围包括:
- `src/views/team/business-development/add-bd.vue`
- `src/views/team/business-development/auth.vue`
- `src/views/team/business-development/bind-bd-leader.vue`
- `src/views/team/business-development/bind-team.vue`
- `src/views/team/business-development/team-member.vue`
- 弹窗/抽屉标题、表单标签、placeholder、按钮、校验提示、提交中提示、表格列头、复制/删除反馈与确认弹窗国际化
- `src/lang/messages/pages/en.js``src/lang/messages/pages/zh.js``pages.businessDevelopment` key 补充
- 中文残留扫描
- 构建验证
- `docs/i18n/翻译进度.md``docs/i18n/翻译清单.md` 同步更新
- 补充说明:
- 本轮不修改接口参数、路由路径、权限码、提交字段或后端依赖值
- `src/views/team/business-development/index.vue` 当前残留中文主要为注释,以及未传入本轮组件的历史 `textOptTitle` 赋值,不属于本轮私有组件可见文案
- `src/views/team/components/TeamCreate/index.vue``bd-lead``team-list``business-development` 多页面共用组件,已保留为后续单独认领或随主要归属页面统一复查
- 首次直接执行 `npm run build:prod` 触发项目既有 `ERR_OSSL_EVP_UNSUPPORTED`
- 改用 `$env:NODE_OPTIONS='--openssl-legacy-provider'; npm run build:prod` 后构建通过,仍为项目既有 `22` 条 warning未新增阻塞错误
- 线程二本轮完成后切回待分配,暂不继续认领新任务
- 2026-04-21
- 线程三已新认领:`src/views/room/profile` 页面私有组件复查
- 领取前动作:
- 已重新读取 `docs/i18n/线程协作.md`
- 已重新读取 `docs/i18n/翻译进度.md`
- 已重新读取 `docs/i18n/翻译清单.md`
- 已重新读取 `docs/i18n/主要路由.md`
- 已读取最新 `src/lang/messages/pages/en.js``src/lang/messages/pages/zh.js`
- 已读取入口页 `src/views/room/profile/index.vue`
- 已核对 `src/views/room/profile` 目录文件列表与入口页本地组件
- 已执行 `src/views/room/profile` 目录级中文扫描
- 已确认线程二当前认领 `src/views/team/business-development` 私有组件,线程一当前无进行中任务,本轮不与其他线程冲突
- 已按规则先更新 md写完后需立即复读 4 份 md 再开始源码改动
- 认领范围包括:
- `src/views/room/profile/room-active-charts.vue`
- `src/views/room/profile/room-operation-log.vue`
- `src/views/room/profile/room-visitor-log.vue`
- `src/views/room/profile/user-drawer.vue`
- 房间资料列表同目录私有组件的静态展示文本、弹窗标题、筛选 placeholder、按钮、表格列头、角色展示、图表标题与图表 series 名称国际化复核
- 优先复用并补充 `pages.roomProfile` 既有 key
- 中文残留扫描
- 构建验证
- `docs/i18n/翻译进度.md``docs/i18n/翻译清单.md``docs/i18n/主要路由.md` 同步更新
- 不包括:
- 接口参数
- 路由路径
- 权限码
- 后端依赖值
- 2026-04-21
- 线程一已完成:`src/views/team/diamond-policy/props-form-edit.vue`
- 完成范围包括:
- 代理钻石政策页面私有道具配置抽屉的静态展示文本复核
- 可触发表单校验提示改为复用 `pages.propsSourceGroupForm.validation.requiredField`
- 资源类型兜底展示名称去除中文字面量;实际资源类型展示仍优先复用 `pages.propsSourceGroupForm.resourceType`
- 对 `src/views/team/diamond-policy/*.vue` 执行目录级中文残留扫描
- 执行构建验证
- 同步更新 `docs/i18n/翻译进度.md``docs/i18n/翻译清单.md``docs/i18n/主要路由.md`
- 补充说明:
- `pItem.name``item.badgeName`、礼物/徽章/道具名称等接口返回或动态数据保持原样
- 当前 `src/views/team/diamond-policy` 目录残余中文均为注释中的中文,不属于可见展示文案
- 直接执行 `npm run build:prod` 触发项目既有 `ERR_OSSL_EVP_UNSUPPORTED`;随后使用 `$env:NODE_OPTIONS='--openssl-legacy-provider'; npm run build:prod` 构建通过,仍保留项目既有 `22` 条 warning未新增阻塞错误
- 2026-04-21
- 线程二已新认领:`src/views/team/business-development` 页面私有组件复查
- 领取前动作:
- 已重新读取 `docs/i18n/线程协作.md`
- 已重新读取 `docs/i18n/翻译进度.md`
- 已重新读取 `docs/i18n/翻译清单.md`
- 已重新读取 `docs/i18n/主要路由.md`
- 已读取最新 `src/lang/messages/pages/en.js``src/lang/messages/pages/zh.js`
- 已读取入口页 `src/views/team/business-development/index.vue`
- 已核对 `src/views/team/business-development` 目录文件列表与入口页本地 import
- 已执行 `src/views/team/business-development` 目录级中文扫描
- 已确认线程一当前认领 `src/views/team/diamond-policy/props-form-edit.vue`,线程三当前无进行中任务,本轮不与其他线程冲突
- 已按规则先更新 md写完后需立即复读 4 份 md 再开始源码改动
- 认领范围包括:
- `src/views/team/business-development/add-bd.vue`
- `src/views/team/business-development/auth.vue`
- `src/views/team/business-development/bind-bd-leader.vue`
- `src/views/team/business-development/bind-team.vue`
- `src/views/team/business-development/team-member.vue`
- 私有组件内静态展示文本、弹窗标题、表单标签、placeholder、按钮、校验提示、提交中提示、表格列头、复制/删除反馈与确认弹窗国际化复核
- 优先复用并补充 `pages.businessDevelopment` 既有 key
- 中文残留扫描
- 构建验证
- `docs/i18n/翻译进度.md``docs/i18n/翻译清单.md` 同步更新
- 暂不纳入:
- `src/views/team/components/TeamCreate/index.vue`
- 原因:该组件由 `bd-lead``team-list``business-development` 多个页面共用,虽然位于 `src/views` 下,但不是本轮 business-development 同目录私有组件;后续应作为共享私有组件单独认领或随其主要归属页面统一复查
- 不包括:
- 接口参数
- 路由路径
- 权限码
- 后端依赖值
- 2026-04-21
- 线程一已新认领:`src/views/team/diamond-policy/props-form-edit.vue`
- 领取前动作:
- 已重新读取 `docs/i18n/线程协作.md`
- 已重新读取 `docs/i18n/翻译进度.md`
- 已重新读取 `docs/i18n/翻译清单.md`
- 已重新读取 `docs/i18n/主要路由.md`
- 已读取 `src/lang/messages/pages/en.js``src/lang/messages/pages/zh.js` 中相关 key 位置
- 已读取 `src/views/team/diamond-policy/index.vue`
- 已读取 `src/views/team/diamond-policy/props-form-edit.vue`
- 已读取 `src/views/team/diamond-policy` 目录文件列表,确认该目录仅包含入口页与当前私有组件
- 已对 `src/views/team/diamond-policy/*.vue` 执行中文扫描
- 已确认当前没有其他线程认领该私有组件
- 已按最新规则先更新 md再开始源码改动
- 认领范围包括:
- `src/views/team/diamond-policy/props-form-edit.vue`
- 私有组件静态展示文本、按钮、placeholder、校验提示、失败提示与资源类型展示国际化复核
- 优先复用 `pages.teamDiamondPolicy``pages.propsSourceGroupForm` 既有 key
- 中文残留扫描
- 构建验证
- `docs/i18n/翻译进度.md``docs/i18n/翻译清单.md` 同步更新
- 不包含:
- 接口参数
- 路由路径
- 权限码
- 后端依赖值
- 2026-04-21
- 线程三已完成:`src/views/team/policy/props-form-edit.vue`
- 完成范围包括:
- 代理政策页面私有道具配置抽屉的静态展示文本国际化
- 奖励明细描述、添加配置表单、placeholder、校验提示、资源类型按钮、抽屉标题、保存/取消按钮与配置缺失提示国际化
- 复用并扩展 `pages.propsSourceGroupForm` 既有 key本轮仅新增 `pages.propsSourceGroupForm.action.view`
- 对 `src/views/team/policy/props-form-edit.vue` 执行中文残留扫描
- 对 `src/views/team/policy/*.vue` 执行目录级中文扫描
- 执行构建验证
- 同步更新 `docs/i18n/翻译进度.md``docs/i18n/翻译清单.md``docs/i18n/主要路由.md`
- 补充说明:
- `pItem.name``item.badgeName`、接口返回礼物/徽章/道具名称等动态数据保持原样
- 当前 `src/views/team/policy` 目录残余中文均为注释中的中文,不属于可见展示文案
- 直接执行 `npm run build:prod` 触发项目既有 `ERR_OSSL_EVP_UNSUPPORTED`;随后使用 `$env:NODE_OPTIONS='--openssl-legacy-provider'; npm run build:prod` 构建通过,仍保留项目既有 `22` 条 warning未新增阻塞错误
- 2026-04-21
- 线程三已新认领:`src/views/team/policy/props-form-edit.vue`
- 领取前动作:
- 已重新读取 `docs/i18n/线程协作.md`
- 已重新读取 `docs/i18n/翻译进度.md`
- 已重新读取 `docs/i18n/翻译清单.md`
- 已重新读取 `docs/i18n/主要路由.md`
- 已读取 `src/views/team/policy/index.vue`
- 已读取 `src/views/team/policy/props-form-edit.vue`
- 已读取 `src/views/team/policy` 目录文件列表,确认该目录仅包含入口页与当前私有组件
- 已确认当前没有其他线程认领该私有组件
- 已按最新规则先更新 md再开始源码改动
- 认领范围包括:
- `src/views/team/policy/props-form-edit.vue`
- 私有组件静态展示文本、按钮、placeholder、校验提示、失败提示与资源类型展示国际化
- 优先复用 `pages.teamPolicy``pages.propsSourceGroupForm` 既有 key必要时补充语义 key
- 中文残留扫描
- 构建验证
- `docs/i18n/翻译进度.md``docs/i18n/翻译清单.md` 同步更新
- 不包含:
- 接口参数
- 路由路径
- 权限码
- 后端依赖值
- 2026-04-20
- 全线程临时任务更新:
- 暂停继续领取新的未完成普通页面
@ -1579,6 +2384,7 @@
8. 修改完成后先本地构建验证
9. 把结果同步写入 `翻译进度.md``翻译清单.md`
10. 在线程认领表里把状态改为“已完成”或“阻塞”
11. 后续不要再新认领非主要路由页面;已经处理过的非主要路由保持现状即可
## 线程之间如何“保持联系”

View File

@ -14,9 +14,9 @@
### 翻译范围
- 扫描项目中的中文展示文本
- 只要不是涉及接口参数的内容,都要做翻译
- 路由、导航、页面标题、选项、按钮、表单标签、表格列名、提示文案、空态文案等都属于处理范围
- 后续任务范围只限 `docs/i18n/主要路由.md` 中列出的主要路由页面,以及这些页面直接涉及到的页面内组件 / 私有组件
- 已经处理过的其他非主要路由页面保持现状,不回滚,也不再继续扩展翻译
- 主要路由范围内,路由、导航、页面标题、选项、按钮、表单标签、表格列名、提示文案、空态文案等都属于处理范围
### 禁止改动项
@ -51,7 +51,7 @@
## 领取优先级
- 适用于所有线程,不只线程三
- 统一领取优先级:`已完成 src/views 页面私有组件复查 > 首页 > docs/i18n/主要路由.md > 其他路由`
- 统一领取优先级:`已完成 src/views 页面私有组件复查 > 首页 > docs/i18n/主要路由.md`
- 在“已完成 `src/views` 页面私有组件复查”完成前,暂不继续领取新的未完成普通页面
- 只要首页或 `主要路由.md` 中页面仍未完成,后续线程都应优先从这两部分中选择任务
- `主要路由.md` 中具体优先领取哪个页面,仍可按原来的协作方式灵活分配,只要先读 md、先写认领、避免冲突
@ -62,37 +62,51 @@
- 详细父子级结构、路由别名、路径与入口文件统一见 `docs/i18n/主要路由.md`
- 以下索引已按 `docs/i18n/主要路由.md` 当前父子级顺序同步更新
- 系统管理 / System Management
- App系统 / App System
- 请求黑名单 / Request Blacklist
- 用户管理 / User Management
- 用户列表 / User List
- 用户专属礼物制作 / User Custom Gift Creation
- 运营管理 / Operations Management
- 数据配置 / Data Configuration
- 转盘抽奖品配置 / Spin Wheel Prize Configuration
- 国家管理 / Country Management
- IM账号管理 / IM Account Management
- 活动配置 / Activity Configuration
- 活动管理 / Activity Management
- 活动模版 / Activity Template
- 消息推送 / Message Push
- 运营日志 / Operations Logs
- 砸金蛋记录 / Golden Egg Smash Records
- APP管理员 / App Administrator
- App启动页 / App Launch Page
- 系统公告管理 / System Notice Management
- 金币代理 / Gold Agent
- 区域配置 / Region Configuration
- 语音房间 / Voice Rooms
- 房间资料列表 / Room Profile List
- 道具管理 / Prop Management
- 资源组配置 / Resource Group Configuration
- 系统工具 / System Tools
- Redis缓存管理 / Redis Cache Management
- 文件上传 / File Upload
- 主播中心 / Broadcaster Center
- 代理钻石政策 / Agent Diamond Policy
- BD Lead / BD Lead
- 代理政策 / Agent Policy
- BD列表 / BD List
- 补充页面
- Redis缓存管理 / Redis Cache Management
- 文件上传 / File Upload
- 小工具 / Utilities
- 金币分析 / Gold Analysis
- 订单补偿 / Order Compensation
- WebSocket调试 / WebSocket Tool
- 抽奖概率配置 / Lottery Probability Settings
### 已完成文件平铺清单
- `src/views/login/index.vue`
- `src/views/401.vue`
- `src/views/tools/compensate-order/index.vue`
- `src/views/tools/gold-analyze/index.vue`
- `src/views/tools/websocket/index.vue`
- `src/views/sys/request-blacklist/index.vue`
- `src/components/Breadcrumb/index.vue`
- `src/components/HeaderSearch/index.vue`
- `src/components/SizeSelect/index.vue`
@ -135,14 +149,42 @@
- `src/views/message/push/push-log.vue`
- `src/views/message/push/push-task.vue`
- `src/views/user/user-table/index.vue`
- `src/views/user/custom-gift/index.vue`
- `src/components/data/UserInfo/BaseInfo/index.vue`
- `src/views/room/profile/index.vue`
- `src/views/room/profile/room-active-charts.vue`
- `src/views/room/profile/room-operation-log.vue`
- `src/views/room/profile/room-visitor-log.vue`
- `src/views/room/profile/user-drawer.vue`
- `src/views/team/policy/index.vue`
- `src/views/team/policy/props-form-edit.vue`
- `src/views/props/props-source-group/form-edit.vue`
- `src/views/cnf/lottery/index.vue`
- `src/views/cnf/lottery/select-prize.vue`
- `src/views/activity/template-cnf/index.vue`
- `src/views/activity/template-cnf/activity-rank.vue`
- `src/views/activity/template-cnf/activity-rank-reward.vue`
- `src/views/activity/template-cnf/form-edit.vue`
- `src/views/activity/template-cnf/reward-edit.vue`
- `src/views/activity/template-cnf/template-editor/attr-editor.vue`
- `src/views/activity/template-cnf/template-editor/index.vue`
- `src/views/activity/template-cnf/template-editor/lang-text-editor.vue`
- `src/views/activity/template-cnf/template-editor/page-editor.vue`
- `src/views/activity/template-cnf/template-editor/props-card.vue`
- `src/views/activity/template-cnf/template-editor/reward-array-editor.vue`
- `src/views/activity/template-cnf/template-form/index.vue`
- `src/views/team/business-development/index.vue`
- `src/views/team/business-development/add-bd.vue`
- `src/views/team/business-development/auth.vue`
- `src/views/team/business-development/bind-bd-leader.vue`
- `src/views/team/business-development/bind-team.vue`
- `src/views/team/business-development/team-member.vue`
- `src/views/team/bd-lead/index.vue`
- `src/views/team/bd-lead/add-bd-lead.vue`
- `src/views/team/bd-lead/bind-bd.vue`
- `src/views/team/components/TeamCreate/index.vue`
- `src/views/team/diamond-policy/index.vue`
- `src/views/team/diamond-policy/props-form-edit.vue`
- `src/components/data/AccountHanle/index.vue`
- `src/views/sys/region-config/region/index.vue`
- `src/components/data/RoomInfo/index.vue`
@ -192,17 +234,40 @@
- `src/views/user/app-manager/administrator/index.vue`
- `src/views/user/app-manager/administrator/auth.vue`
- `src/views/user/app-manager/administrator-auth-resource/index.vue`
- `src/views/user/im-account-manager/index.vue`
- `src/views/sys/notice-message/index.vue`
- `src/views/sys/start-page/index.vue`
- `src/views/sys/start-page/form-edit.vue`
- `src/views/tools/redis/index.vue`
- `src/views/tools/upload/index.vue`
- `src/views/tools/other/index.vue`
- `src/views/tools/other/alias-method.vue`
- `src/views/tools/other/api-sign.vue`
- `src/views/tools/other/redis-ops.vue`
- `src/views/tools/other/token-parse.vue`
- `src/views/tools/other/user-cancel-and-restore.vue`
- `src/views/tools/other/user-room-region.vue`
- `src/views/tools/other/user-swap-user.vue`
- 表格展示适配:表头自动换行 + 默认最小宽度补齐
- 表格展示适配:表头 `.cell` 高度自适应 + 纵向居中修正
- 表单展示适配:标题纵向居中
## 进行中
- `src/views/game/ludo/index.vue`
- 状态:进行中(线程二)
- 范围:筛选区 placeholder、搜索按钮、表格列头、玩家弹窗标题、按钮、分页提示、确认弹窗与本地状态/事件展示映射国际化;不改接口参数、状态值、动态数据与解散查询逻辑
- `src/views/family/config/index.vue`
- 状态:进行中(线程一)
- 同步范围:`src/views/family/config/config-level/index.vue``src/views/family/config/config-create-role/index.vue``src/views/family/config/config-weekly-reward/index.vue`
- 范围:入口页 tab 标题、3 个可见子页的按钮、表格列头、表单标签、placeholder、弹窗标题、提醒文案与校验提示国际化不改接口参数、平台 value、等级 key、类型 value、动态资源名称与业务逻辑
- `src/views/version/app/index.vue`
- 状态:进行中(线程三)
- 同步范围:`src/views/version/app/app-version-edit.vue``src/views/version/app/app-update-content.vue`
- 范围筛选区、表格列头、状态展示、操作菜单、弹窗标题、表单标签、placeholder、按钮、校验提示、确认弹窗与本地提示国际化不改接口参数、路由路径、平台/渠道/语言/appType 等提交值、动态版本数据与业务逻辑
- 临时任务:已完成 `src/views` 页面私有组件复查
- 状态:进行中
- 范围说明:
@ -210,36 +275,9 @@
- 只复查页面私有组件,不包含公共组件目录 `src/components/**`
- 后续页面完成记录需要额外写明“私有组件状态”
- 当前优先复核目录:
- `src/views/team/policy`
- `src/views/team/diamond-policy`
- `src/views/team/business-development`
- `src/views/room/profile`
- `src/views/activity/template-cnf`
- `src/views/cnf/lottery`
- 暂无明确剩余目录;领取前需重新读取本文件与 `docs/i18n/主要路由.md` 后确认
- 当前已发现但尚未补齐到已完成记录的私有组件:
- `src/views/team/policy/props-form-edit.vue`
- `src/views/team/diamond-policy/props-form-edit.vue`
- `src/views/team/business-development/add-bd.vue`
- `src/views/team/business-development/auth.vue`
- `src/views/team/business-development/bind-bd-leader.vue`
- `src/views/team/business-development/bind-team.vue`
- `src/views/team/business-development/team-member.vue`
- `src/views/room/profile/room-active-charts.vue`
- `src/views/room/profile/room-operation-log.vue`
- `src/views/room/profile/room-visitor-log.vue`
- `src/views/room/profile/user-drawer.vue`
- `src/views/activity/template-cnf/activity-rank.vue`
- `src/views/activity/template-cnf/activity-rank-reward.vue`
- `src/views/activity/template-cnf/form-edit.vue`
- `src/views/activity/template-cnf/reward-edit.vue`
- `src/views/activity/template-cnf/template-editor/attr-editor.vue`
- `src/views/activity/template-cnf/template-editor/index.vue`
- `src/views/activity/template-cnf/template-editor/lang-text-editor.vue`
- `src/views/activity/template-cnf/template-editor/page-editor.vue`
- `src/views/activity/template-cnf/template-editor/props-card.vue`
- `src/views/activity/template-cnf/template-editor/reward-array-editor.vue`
- `src/views/activity/template-cnf/template-form/index.vue`
- `src/views/cnf/lottery/select-prize.vue`
- 当前无
- 当前明确排除:
- `src/views/message/push/push-text-store.vue`
- 原因:当前未纳入入口渲染链,继续按既有排除项处理
@ -252,7 +290,7 @@
- 主要路由中已部分完成、但仍需继续收口的入口页
- 主要路由中的其他页面
- 统一参考 `docs/i18n/主要路由.md`
- 当首页与本文件中的“优先待处理”完成后,再继续处理 `主要路由.md` 中剩余未完成页面
- 后续只继续处理主要路由,不再继续补非主要路由页面
## 第一批高优先级页面
@ -286,6 +324,9 @@
- `src/components/data/UserInfo/AccountStatusLatestLog/index.vue`:已完成(线程二)
## 本轮保留项
- `src/views/cnf/lottery/index.vue`
- 当前保留注释“扩展属性”原样
- 原因:该内容仅为代码注释,不属于可见展示文案;`select-prize.vue` 已完成奖品选择弹窗私有组件复查
- `src/components/data/AccountHanle/index.vue`
- 默认“警告”描述文案保留中文
- 原因:该内容会直接写入提交表单并发给后端,不按纯展示文案处理
@ -304,6 +345,12 @@
- `src/views/sys/notice-message/index.vue`
- 当前保留字段行尾注释与“删除”注释原样
- 原因:均不属于可见展示文案;`permissionsSysOriginPlatforms``item.label` 作为公共系统来源数据,本轮未改其值
- `src/views/tools/other/alias-method.vue`
- 当前保留快速填充样例值中的中文名称
- 原因:`fills` 中的 `name` 会随表单一起提交到接口,属于提交数据,不按纯展示文案处理
- `src/views/tools/other/user-swap-user.vue`
- 当前保留注释掉的“用户id不可相同”原样
- 原因:该内容仅存在于注释中,不属于可见展示文案
## 需持续关注

View File

@ -9,13 +9,14 @@
## 当前目标
- 将项目页面中出现的中文展示文本逐步接入国际化
- 后续只对 `docs/i18n/主要路由.md` 中的主要路由页面及其直接涉及组件继续做国际化
- 已经处理过的其他非主要路由页面保持现状,不回滚,也不再继续扩展
- 当前先支持 `en``zh`
- 后续如果增加 `bn``ar``tr` 等语言,优先复用现有键名体系
## 任务优先级
- 全线程统一优先级:`已完成 src/views 页面私有组件复查 > 首页 > docs/i18n/主要路由.md > 其他路由`
- 全线程统一优先级:`已完成 src/views 页面私有组件复查 > 首页 > docs/i18n/主要路由.md`
- 在“已完成 `src/views` 页面私有组件复查”完成前,暂不继续领取新的未完成普通页面
- 只要首页 `Dashboard` 模块或 `主要路由.md` 中页面仍未完成,后续线程领取新任务时都应优先从这两部分中选择
- `主要路由.md` 中具体先领哪个页面仍可灵活分配,但不得跳过首页整体优先级
@ -46,32 +47,40 @@
- 详细父子级结构、路由别名、路径与验收入口统一见 `docs/i18n/主要路由.md`
- 以下摘要已按 `docs/i18n/主要路由.md` 当前父子级顺序同步更新
- 已完成主路由摘要:
- 系统管理 / System Management
- App系统 / App System
- 请求黑名单 / Request Blacklist
- 用户管理 / User Management
- 用户列表 / User List
- 运营管理 / Operations Management
- 数据配置 / Data Configuration
- 转盘抽奖品配置 / Spin Wheel Prize Configuration
- 国家管理 / Country Management
- IM账号管理 / IM Account Management
- 活动配置 / Activity Configuration
- 活动管理 / Activity Management
- 活动模版 / Activity Template
- 消息推送 / Message Push
- 运营日志 / Operations Logs
- 砸金蛋记录 / Golden Egg Smash Records
- APP管理员 / App Administrator
- App启动页 / App Launch Page
- 系统公告管理 / System Notice Management
- 金币代理 / Gold Agent
- 区域配置 / Region Configuration
- 语音房间 / Voice Rooms
- 房间资料列表 / Room Profile List
- 道具管理 / Prop Management
- 资源组配置 / Resource Group Configuration
- 系统工具 / System Tools
- Redis缓存管理 / Redis Cache Management
- 文件上传 / File Upload
- 主播中心 / Broadcaster Center
- 代理钻石政策 / Agent Diamond Policy
- BD Lead / BD Lead
- 代理政策 / Agent Policy
- BD列表 / BD List
- 已完成但未在当前主要路由菜单返回中命中的补充页面:
- Redis缓存管理 / Redis Cache Management
- 文件上传 / File Upload
- 小工具 / Utilities
- 金币分析 / Gold Analysis
- 订单补偿 / Order Compensation
- WebSocket调试 / WebSocket Tool
- 抽奖概率配置 / Lottery Probability Settings
### 基础能力
@ -99,6 +108,296 @@
## 本轮新增进展
### 2026-04-22
- 已完成页面:
- `src/views/team/bd-lead/index.vue`
- 说明已完成筛选区、表格列头、操作菜单、抽屉标题、表单标签、placeholder、按钮、校验提示、确认弹窗与本地提示国际化接口参数、权限码、路由路径、区域/成员/用户资料等动态或接口返回内容,以及新增、编辑、删除、绑定与分页逻辑保持原样
- 页面私有组件状态:
- `src/views/team/bd-lead/add-bd-lead.vue`:已随页面同步完成
- `src/views/team/bd-lead/bind-bd.vue`:已随页面同步完成
- 已补充页面级语言 key
- `src/lang/messages/pages/en.js`
- `src/lang/messages/pages/zh.js`
- 新增范围:`pages.bdLead`
- 已完成构建验证:
- `npm run build:prod` 已通过
- 仍有项目既有 22 个 warning无新增阻塞错误
- 已完成页面:
- `src/views/user/custom-gift/index.vue`
- 说明:已完成筛选区、状态/类型展示、表格列头、下载操作文案、成功与失败提示国际化;接口参数、状态与类型实际值、区域/用户/资源地址等动态或接口返回内容,以及审核切换、下载与分页逻辑保持原样
- 页面私有组件状态:
- `src/views/user/custom-gift/index.vue`:无私有组件,已完成
- 已补充页面级语言 key
- `src/lang/messages/pages/en.js`
- `src/lang/messages/pages/zh.js`
- 新增范围:`pages.userCustomGift`
- 已完成模块级复核:
- `src/views/user/custom-gift/index.vue`
- 说明:已执行中文残留扫描;当前未发现剩余中文可见文本
- 已完成验证:
- 直接执行 `npm run build:prod` 构建通过
- 说明:当前仍保留项目既有 `22` 条 warning未因本轮 `user/custom-gift` 国际化改动新增阻塞错误
- 已完成页面:
- `src/views/user/im-account-manager/index.vue`
- 说明已完成筛选区、提示说明、表格列头、操作按钮、弹窗标题、表单标签、placeholder、校验提示与确认弹窗国际化接口参数、默认密码值 `88888888`、动态昵称与业务逻辑保持原样
- 页面私有组件状态:
- `src/views/user/im-account-manager/index.vue`:无私有组件,已完成
- 已补充页面级语言 key
- `src/lang/messages/pages/en.js`
- `src/lang/messages/pages/zh.js`
- 新增范围:`pages.imAccountManager`
- 已完成模块级复核:
- `src/views/user/im-account-manager/index.vue`
- 说明:已执行中文残留扫描;当前未发现剩余中文可见文本
- 已完成验证:
- 直接执行 `npm run build:prod` 构建通过
- 说明:当前仍保留项目既有 `22` 条 warning未因本轮 `im-account-manager` 国际化改动新增阻塞错误
- 已完成页面:
- `src/views/tools/websocket/index.vue`
- 说明已完成广播订阅与点对点卡片标题、步骤说明、表单标签、placeholder、按钮、状态提示与本地错误提示国际化Socket 端点默认值、接口参数、订阅地址与发送地址实际值、Token/用户 ID/消息体字段和 WebSocket 连接发送逻辑保持原样
- 页面私有组件状态:
- `src/views/tools/websocket/index.vue`:无私有组件,已完成
- 已补充页面级语言 key
- `src/lang/messages/pages/en.js`
- `src/lang/messages/pages/zh.js`
- 新增范围:`pages.websocketTool`
- 已完成模块级复核:
- `src/views/tools/websocket/index.vue`
- 说明:已执行中文残留扫描;当前未发现剩余中文可见文本
- 已完成验证:
- 直接执行 `npm run build:prod` 构建通过
- 说明:当前仍保留项目既有 `22` 条 warning未因本轮 `websocket` 国际化改动新增阻塞错误
- 已完成页面:
- `src/views/sys/request-blacklist/index.vue`
- 说明:已完成新增按钮、确认弹窗、输入提示、提示文案与本地错误提示国际化;接口参数、黑名单内容值本身、路由路径与新增删除逻辑保持原样
- 页面私有组件状态:
- `src/views/sys/request-blacklist/index.vue`:无私有组件,已完成
- 已补充页面级语言 key
- `src/lang/messages/pages/en.js`
- `src/lang/messages/pages/zh.js`
- 新增范围:`pages.requestBlacklist`
- 已完成模块级复核:
- `src/views/sys/request-blacklist/index.vue`
- 说明:已执行中文残留扫描;当前未发现剩余中文可见文本
- 已完成验证:
- 直接执行 `npm run build:prod` 构建通过
- 说明:当前仍保留项目既有 `22` 条 warning未因本轮 `request-blacklist` 国际化改动新增阻塞错误
- 已完成页面:
- `src/views/tools/gold-analyze/index.vue`
- 说明:已完成筛选区 placeholder、日期区间文案、搜索按钮、图表标题、副标题与本地 warn 提示国际化;接口参数、图表返回数据、`originName` 动态数据、图表配置结构与查询逻辑保持原样
- 页面私有组件状态:
- `src/views/tools/gold-analyze/index.vue`:无私有组件,已完成
- 已补充页面级语言 key
- `src/lang/messages/pages/en.js`
- `src/lang/messages/pages/zh.js`
- 新增范围:`pages.goldAnalyze`
- 已完成模块级复核:
- `src/views/tools/gold-analyze/index.vue`
- 说明:已执行中文残留扫描;当前未发现剩余中文可见文本
- 已完成验证:
- 直接执行 `npm run build:prod` 构建通过
- 说明:当前仍保留项目既有 `22` 条 warning未因本轮 `gold-analyze` 国际化改动新增阻塞错误
- 已完成页面:
- `src/views/tools/compensate-order/index.vue`
- 说明已完成订单补偿与糖果补偿表单标题、标签、placeholder、按钮、校验文案与确认弹窗国际化接口参数、提交字段、平台枚举值、支付方式实际 value 与提交逻辑保持原样
- 页面私有组件状态:
- `src/views/tools/compensate-order/index.vue`:无私有组件,已完成
- 已补充页面级语言 key
- `src/lang/messages/pages/en.js`
- `src/lang/messages/pages/zh.js`
- 新增范围:`pages.compensateOrder`
- 已完成模块级复核:
- `src/views/tools/compensate-order/index.vue`
- 说明:已执行中文残留扫描;当前未发现剩余中文可见文本
- 已完成验证:
- 直接执行 `npm run build:prod` 构建通过
- 说明:当前仍保留项目既有 `22` 条 warning未因本轮 `compensate-order` 国际化改动新增阻塞错误
- 已完成页面:
- `src/views/tools/other/index.vue`
- `src/views/tools/other/alias-method.vue`
- `src/views/tools/other/api-sign.vue`
- `src/views/tools/other/redis-ops.vue`
- `src/views/tools/other/token-parse.vue`
- `src/views/tools/other/user-cancel-and-restore.vue`
- `src/views/tools/other/user-room-region.vue`
- `src/views/tools/other/user-swap-user.vue`
- 说明:已完成 Utilities 路由页入口卡片标题、页面内功能文件的表单标签、placeholder、按钮、弹窗标题、校验提示、本地说明文案与本地检查项提示国际化`CLEAR_KEYS``TOKEN_PARSE``GET_TOKEN` 等模式值、接口参数、提交字段、路由路径与后端依赖值保持原样
- 页面私有组件状态:
- `src/views/tools/other/index.vue`:已随页面同步完成;按原始接口返回与入口页引用关系,`alias-method.vue``api-sign.vue``redis-ops.vue``token-parse.vue``user-cancel-and-restore.vue``user-room-region.vue``user-swap-user.vue` 记为页面内功能文件,均已复核通过
- 已补充页面级语言 key
- `src/lang/messages/pages/en.js`
- `src/lang/messages/pages/zh.js`
- 新增范围:`pages.toolsOther`
- 已完成模块级复核:
- `src/views/tools/other/*.vue`
- 说明:已执行目录级中文残留扫描;当前残留中文仅为 `alias-method.vue` 中会随表单提交到接口的快速填充样例值,以及 `user-swap-user.vue` 中注释掉的提示代码,不属于可继续按纯展示文案翻译的范围
- 已完成验证:
- 直接执行 `npm run build:prod` 构建通过
- 说明:当前仍保留项目既有 `22` 条 warning未因本轮 `tools/other` 国际化改动新增阻塞错误
- 已完成页面:
- `src/views/401.vue`
- 说明:已完成 401 权限错误页标题、副标题、导航提示、链接文案与图片弹窗标题国际化;未改动首页跳转路径、返回上一页逻辑、图片资源与远程图片地址
- 页面私有组件状态:
- `src/views/401.vue`:无私有组件,已完成
- 已补充页面级语言 key
- `src/lang/messages/pages/en.js`
- `src/lang/messages/pages/zh.js`
- 新增范围:`pages.page401`
- 已完成模块级复核:
- `src/views/401.vue`
- 说明:已执行中文残留扫描;当前未发现剩余中文可见文本
- 已完成验证:
- 直接执行 `npm run build:prod` 构建通过
- 说明:当前仍保留项目既有 `22` 条 warning未因本轮 `401.vue` 国际化改动新增阻塞错误
- 环境补充:本机当前设置 `$env:NODE_OPTIONS='--openssl-legacy-provider'` 会报 `--openssl-legacy-provider is not allowed in NODE_OPTIONS`,后续新线程不要默认附加该变量
- 已完成历史页面私有组件复查:
- `src/views/activity/template-cnf/activity-rank.vue`
- `src/views/activity/template-cnf/activity-rank-reward.vue`
- `src/views/activity/template-cnf/form-edit.vue`
- `src/views/activity/template-cnf/reward-edit.vue`
- `src/views/activity/template-cnf/template-editor/attr-editor.vue`
- `src/views/activity/template-cnf/template-editor/index.vue`
- `src/views/activity/template-cnf/template-editor/lang-text-editor.vue`
- `src/views/activity/template-cnf/template-editor/page-editor.vue`
- `src/views/activity/template-cnf/template-editor/props-card.vue`
- `src/views/activity/template-cnf/template-editor/reward-array-editor.vue`
- `src/views/activity/template-cnf/template-form/index.vue`
- 归属页面:`src/views/activity/template-cnf/index.vue`
- 说明:已完成活动管理页面同目录私有组件、活动排名弹窗、奖品编辑、模板编辑器、模板表单与本地道具类型展示映射国际化;接口参数、路由路径、权限码、提交字段与后端依赖值保持原样
- 页面私有组件状态:
- `src/views/activity/template-cnf/index.vue`:历史已完成,同目录私有组件 `activity-rank.vue``activity-rank-reward.vue``form-edit.vue``reward-edit.vue``template-editor/*.vue``template-form/index.vue` 已复核通过
- 已补充页面级语言 key
- `src/lang/messages/pages/en.js`
- `src/lang/messages/pages/zh.js`
- 扩展范围:`pages.activityTemplateManage` 的模板编辑器、排名奖品、表单、提示、渐变方向、预览昵称与道具类型展示 key
- 已完成模块级复核:
- `src/views/activity/template-cnf/**/*.vue`
- 说明:已执行目录级中文残留扫描;当前残余中文均为 `index.vue` 注释或源码注释,不属于可见展示文案
- 已完成验证:
- 直接执行 `npm run build:prod` 触发项目既有 `ERR_OSSL_EVP_UNSUPPORTED`
- 改用 `$env:NODE_OPTIONS='--openssl-legacy-provider'; npm run build:prod` 后构建通过
- 说明:当前仍保留项目既有 `22` 条 warning未因本轮 `activity/template-cnf` 私有组件复查新增阻塞错误
- 已完成历史页面私有组件复查:
- `src/views/cnf/lottery/select-prize.vue`
- 归属页面:`src/views/cnf/lottery/index.vue`
- 说明已完成转盘抽奖品配置页面奖品选择弹窗标题、表单标签、placeholder、按钮、校验提示、类型分组与类型选项展示国际化接口字段、提交字段、`value``type`、路由路径与后端依赖值保持原样
- 页面私有组件状态:
- `src/views/cnf/lottery/index.vue`:历史已完成,私有组件 `src/views/cnf/lottery/select-prize.vue` 已复核通过
- 已补充页面级语言 key
- `src/lang/messages/pages/en.js`
- `src/lang/messages/pages/zh.js`
- 扩展范围:`pages.lotteryConfig.placeholder.select/quantity``pages.lotteryConfig.validation.requiredField``pages.lotteryConfig.selectPrize`
- 已完成模块级复核:
- `src/views/cnf/lottery/*.vue`
- 说明:已执行目录级中文残留扫描;当前残余中文仅为 `index.vue` 中“扩展属性”注释,不属于可见展示文案
- 已完成验证:
- `node -c src/lang/messages/pages/en.js`
- `node -c src/lang/messages/pages/zh.js`
- 直接执行 `npm run build:prod` 触发项目既有 `ERR_OSSL_EVP_UNSUPPORTED`
- 改用 `$env:NODE_OPTIONS='--openssl-legacy-provider'; npm run build:prod` 后构建通过
- 说明:当前仍保留项目既有 `22` 条 warning未因本轮 `cnf/lottery/select-prize.vue` 私有组件复查新增阻塞错误
- 已完成历史页面共享私有组件复查:
- `src/views/team/components/TeamCreate/index.vue`
- 归属页面:主播中心共享私有组件,当前已知由 `src/views/team/business-development/index.vue``src/views/team/team-list/index.vue` 直接引用
- 说明:已完成创建团队/代理抽屉标题、表单标签、placeholder、绑定 BD 单选展示文案、设置/联系方式分组、按钮、校验提示与提交中提示国际化;接口参数、提交字段、`bindBd``yes/no` 绑定值、`bdAccountDefaultSelectType``LONG` 值、`contactTypes` 实际 value 与后端依赖值保持原样
- 页面私有组件状态:
- `src/views/team/components/TeamCreate/index.vue`:共享私有组件已单独复核通过
- 已补充页面级语言 key
- `src/lang/messages/pages/en.js`
- `src/lang/messages/pages/zh.js`
- 新增范围:`pages.teamCreate`
- 已完成模块级复核:
- `src/views/team/components/TeamCreate/index.vue`
- 说明:已执行中文残留扫描;当前源码未发现剩余中文可见文案
- 已完成验证:
- `node --check` 不适合直接检查当前 `export default` 语言文件,本轮以生产构建结果作为语言文件校验依据
- 直接执行 `npm run build:prod` 触发项目既有 `ERR_OSSL_EVP_UNSUPPORTED`
- 改用 `$env:NODE_OPTIONS='--openssl-legacy-provider'; npm run build:prod` 后构建通过
- 说明:当前仍保留项目既有 `22` 条 warning未因本轮 `TeamCreate` 共享私有组件复查新增阻塞错误
### 2026-04-21
- 已完成历史页面私有组件复查:
- `src/views/room/profile/room-active-charts.vue`
- `src/views/room/profile/room-operation-log.vue`
- `src/views/room/profile/room-visitor-log.vue`
- `src/views/room/profile/user-drawer.vue`
- 归属页面:`src/views/room/profile/index.vue`
- 说明:已完成房间资料列表页面同目录私有组件的弹窗/抽屉标题、筛选 placeholder、按钮、表格列头、角色展示、图表标题与图表 series 名称国际化;接口参数、角色提交值、路由路径与后端依赖值保持原样
- 页面私有组件状态:
- `src/views/room/profile/index.vue`:历史已完成,同目录私有组件 `room-active-charts.vue``room-operation-log.vue``room-visitor-log.vue``user-drawer.vue` 已复核通过
- 已补充页面级语言 key
- `src/lang/messages/pages/en.js`
- `src/lang/messages/pages/zh.js`
- 扩展范围:`pages.roomProfile.filter``pages.roomProfile.table``pages.roomProfile.dialog``pages.roomProfile.chart`
- 已完成模块级复核:
- `src/views/room/profile/*.vue`
- 说明:已执行目录级中文残留扫描;当前残余中文均在 `index.vue` 注释或注释掉的历史菜单代码中,不属于可见展示文案
- 已完成构建验证:
- 直接执行 `npm run build:prod` 触发项目既有 `ERR_OSSL_EVP_UNSUPPORTED`
- 改用 `$env:NODE_OPTIONS='--openssl-legacy-provider'; npm run build:prod` 后构建通过
- 说明:当前仍保留项目既有 `22` 条 warning未因本轮 `room/profile` 私有组件复查新增阻塞错误
- 已完成历史页面私有组件复查:
- `src/views/team/business-development/add-bd.vue`
- `src/views/team/business-development/auth.vue`
- `src/views/team/business-development/bind-bd-leader.vue`
- `src/views/team/business-development/bind-team.vue`
- `src/views/team/business-development/team-member.vue`
- 归属页面:`src/views/team/business-development/index.vue`
- 说明:已完成 BD 列表页面同目录私有弹窗/抽屉组件的标题、表单标签、placeholder、按钮、校验提示、提交中提示、表格列头、复制/删除反馈与确认弹窗国际化;接口参数、提交字段、权限码、路由路径与后端依赖值保持原样
- 页面私有组件状态:
- `src/views/team/business-development/index.vue`:历史已完成,同目录私有组件 `add-bd.vue``auth.vue``bind-bd-leader.vue``bind-team.vue``team-member.vue` 已复核通过
- `src/views/team/components/TeamCreate/index.vue`:多页面共用组件,未纳入本轮同目录私有组件范围;后续已于 2026-04-22 单独复核通过
- 已补充页面级语言 key
- `src/lang/messages/pages/en.js`
- `src/lang/messages/pages/zh.js`
- 扩展范围:`pages.businessDevelopment.table``pages.businessDevelopment.action``pages.businessDevelopment.confirm``pages.businessDevelopment.dialog``pages.businessDevelopment.form``pages.businessDevelopment.placeholder``pages.businessDevelopment.validation``pages.businessDevelopment.message`
- 已完成模块级复核:
- `src/views/team/business-development/*.vue`
- 说明:已执行目录级中文残留扫描;本轮认领的 5 个私有组件中,剩余中文仅为注释,`index.vue` 中残留中文主要为历史注释与未传入本轮组件的本地标题赋值,不属于本轮私有组件可见文案
- 已完成构建验证:
- 直接执行 `npm run build:prod` 触发项目既有 `ERR_OSSL_EVP_UNSUPPORTED`
- 改用 `$env:NODE_OPTIONS='--openssl-legacy-provider'; npm run build:prod` 后构建通过
- 说明:当前仍保留项目既有 `22` 条 warning未因本轮 `team/business-development` 私有组件复查新增阻塞错误
- 已完成历史页面私有组件复查:
- `src/views/team/diamond-policy/props-form-edit.vue`
- 归属页面:`src/views/team/diamond-policy/index.vue`
- 说明:已完成代理钻石政策页面私有道具配置抽屉复核;可触发表单校验提示改为复用 `pages.propsSourceGroupForm.validation.requiredField`,资源类型兜底展示名称去除中文字面量;接口参数、提交字段、资源类型值与后端依赖值保持原样
- 页面私有组件状态:
- `src/views/team/diamond-policy/index.vue`:历史已完成,私有组件 `src/views/team/diamond-policy/props-form-edit.vue` 已复核通过
- 语言 key 处理:
- 本轮复用 `pages.propsSourceGroupForm.validation.requiredField`
- 本轮复用 `pages.propsSourceGroupForm.resourceType`
- 无需新增页面级语言 key
- 已完成模块级复核:
- `src/views/team/diamond-policy/*.vue`
- 说明:已执行目录级中文残留扫描;当前残余中文均为注释中的中文,不属于可见展示文案
- 已完成构建验证:
- 直接执行 `npm run build:prod` 触发项目既有 `ERR_OSSL_EVP_UNSUPPORTED`
- 改用 `$env:NODE_OPTIONS='--openssl-legacy-provider'; npm run build:prod` 后构建通过
- 说明:当前仍保留项目原有 `22` 条 warning未因本轮 `team/diamond-policy` 私有组件复查改动新增阻塞错误
- 已完成历史页面私有组件复查:
- `src/views/team/policy/props-form-edit.vue`
- 归属页面:`src/views/team/policy/index.vue`
- 说明已完成代理政策页面私有道具配置抽屉的奖励明细描述、添加配置表单、placeholder、校验提示、资源类型按钮、抽屉标题、保存/取消按钮与配置缺失提示国际化;接口参数、提交字段、资源类型值与后端依赖值保持原样
- 页面私有组件状态:
- `src/views/team/policy/index.vue`:历史已完成,私有组件 `src/views/team/policy/props-form-edit.vue` 已复核通过
- 已补充页面级语言 key
- `src/lang/messages/pages/en.js`
- `src/lang/messages/pages/zh.js`
- 扩展范围:`pages.propsSourceGroupForm.action.view`
- 复用范围:`pages.propsSourceGroupForm.dialog``pages.propsSourceGroupForm.form``pages.propsSourceGroupForm.summary``pages.propsSourceGroupForm.placeholder``pages.propsSourceGroupForm.resourceType``pages.propsSourceGroupForm.validation``pages.propsSourceGroupForm.word`
- 已完成模块级复核:
- `src/views/team/policy/*.vue`
- 说明:已执行目录级中文残留扫描;当前残余中文均为注释中的中文,不属于可见展示文案
- 已完成构建验证:
- 直接执行 `npm run build:prod` 触发项目既有 `ERR_OSSL_EVP_UNSUPPORTED`
- 改用 `$env:NODE_OPTIONS='--openssl-legacy-provider'; npm run build:prod` 后构建通过
- 说明:当前仍保留项目原有 `22` 条 warning未因本轮 `team/policy` 私有组件国际化改动新增阻塞错误
### 2026-04-20
- 已完成页面:
@ -756,11 +1055,12 @@
- 历史已完成 `src/views` 页面私有组件复查任务当前优先于新页面认领
- 下一步目标:
- 先补查已标记完成页面目录中的私有组件,并把组件状态补写入进度文档
- 完成该临时任务后,再继续按 `docs/i18n/主要路由.md` 领取下一个未完成主路由页面
- 完成该临时任务后,只继续按 `docs/i18n/主要路由.md` 领取下一个未完成主路由页面
- 非主要路由页面后续不再继续扩展翻译
- 当前认领线程:
- 线程一:当前无进行中任务;已完成 `src/views/sys/start-page/index.vue``src/views/sys/start-page/form-edit.vue`,并已完成构建验证与 md 同步
- 线程二:当前无进行中任务;已完成 `src/views/message/push/index.vue``src/views/message/push/new-push-form.vue``src/views/message/push/push-log.vue``src/views/message/push/push-task.vue`,并已完成构建验证与 md 同步
- 线程三:当前无进行中任务;已完成 `src/views/tools/upload/index.vue`,后续与其他线程一样优先处理历史已完成页面私有组件复查
- 线程一:进行中 `src/views/family/config/index.vue`;已完成认领前文档复读、语言文件复读、入口 import 核对、目录文件核对与模块中文扫描,准备进入页面国际化改动
- 线程二:进行中 `src/views/game/ludo/index.vue`;已完成认领前文档复读、语言文件复读、目录核对与页面中文扫描,准备进入页面国际化改动
- 线程三:进行中 `src/views/version/app/index.vue`;已完成认领前文档复读、语言文件复读、目录核对与模块中文扫描,准备进入页面国际化改动
## 扫描概况

View File

@ -6,10 +6,12 @@
## 背景
本次排查涉及个独立但连续出现的问题:
本次排查涉及个独立但连续出现的问题:
1. 页面初始化时,`this.permissionsSysOriginPlatforms[0]` 偶发拿不到,导致默认平台没有赋值,后续 `renderData(true)` 不执行。
2. 本地执行 `npm run dev` 后,控制台一直显示 `Network: unavailable`,但同机器的其他项目可以正常显示类似 `http://192.168.0.115:5173/` 的局域网访问地址。
3. `src/views/user/user-table/index.vue` 页面刷新后,搜索栏中的系统默认选项为空,列表也没有自动刷出;只有再次手动点击搜索后,默认系统和列表才恢复正常。
4. 除首页外,进入其他页面后刷新浏览器会出现页面空白。
---
@ -90,6 +92,126 @@ await store.dispatch('user/buttonPermissions')
---
## 三、`user-table` 刷新后系统默认项为空
### 现象
`src/views/user/user-table/index.vue` 页面中:
- 首次进入时有概率搜索栏中的“系统”默认值为空。
- 刷新页面后更容易复现。
- 首屏列表没有自动加载。
- 用户手动点击一次搜索后,系统默认选项和列表又会恢复正常。
### 根因
这个问题本质上还是权限平台数据的异步时序问题,但触发点更具体:
1. 页面在 `created()` 中直接读取 `this.permissionsSysOriginPlatforms[0]`
2. 如果刷新时权限平台数据尚未回填,`querySystem` 就为空。
3. 原逻辑在判空后会直接 `return`,导致:
- `listQuery.sysOrigins` 没有默认值。
- `renderData(true)` 不执行。
- 页面其余初始化流程也被提前中断。
4. 等到权限数据后续回填后,页面没有额外 watcher 继续补初始化,所以只能依赖用户再次手动搜索。
### 已做处理
本次针对这个页面做了两层修复:
1. 在 `src/permission.js` 中继续保留并确认:
```js
await store.dispatch('user/buttonPermissions')
```
确保路由放行前优先等待按钮权限加载完成。
2. 在 `src/views/user/user-table/index.vue` 中增加页面级兜底:
- 新增 `hasInitializedSysOrigin` 标记,避免重复初始化。
- 监听 `permissionsSysOriginPlatforms`
- 当权限平台列表第一次变为有值时:
- 自动取第一项写入 `listQuery.sysOrigins`
- 自动执行 `renderData(true)`
3. 去掉 `created()` 中“拿不到系统平台就直接 return”的初始化方式避免首屏逻辑因为一次空值被永久跳过。
### 处理结果
调整后,即使刷新页面时 `permissionsSysOriginPlatforms` 比页面创建更晚回填:
- 搜索栏系统默认值也会在权限到达后自动补上。
- 首屏列表会自动触发加载。
- 不再需要用户手动点击搜索来恢复页面状态。
### 涉及文件
- `src/permission.js`
- `src/views/user/user-table/index.vue`
---
## 四、非首页刷新后页面白屏
### 现象
- 首页通常可以正常刷新。
- 进入其他菜单页后,直接刷新浏览器会出现页面空白。
- 这类问题在二级、三级动态路由页面上更容易复现。
### 根因
问题出在刷新时的动态路由注入时序:
1. 刷新页面后,应用会重新进入 `src/permission.js` 中的路由守卫。
2. 当前项目的很多页面并不是一开始就存在于静态路由中,而是依赖 `getMenus()` 返回菜单后再通过 `router.addRoutes()` 动态注入。
3. 原逻辑中虽然调用了 `getMenus()`,但没有等待菜单加载和动态路由注入真正完成,就提前执行了 `next()`
4. 当用户刷新的是非首页深层路由时,当前目标路由可能还没注册到 router 中,于是会出现匹配失败,最终表现为空白页。
首页之所以通常不受影响,是因为首页对应的是默认静态路由,不依赖后续动态注入。
### 已做处理
已将 `src/permission.js` 中刷新后的初始化流程改为严格串行:
```js
await store.dispatch('user/getInfo')
const grantRouters = await store.dispatch('user/getMenus')
if (grantRouters && grantRouters.length > 0) {
router.addRoutes(grantRouters)
}
store.dispatch('user/getUserDashboard')
await store.dispatch('user/buttonPermissions')
next({ ...to, replace: true })
```
### 修复要点
这次修复包含两个关键点:
1. 先等待 `getMenus()` 返回,再执行 `router.addRoutes(grantRouters)`
2. 动态路由注入完成后,不再直接 `next()`,而是使用:
```js
next({ ...to, replace: true })
```
这样会用当前目标地址重新进入一次匹配流程,确保刚注入的动态路由能够正确命中当前页面。
### 处理结果
调整后:
- 刷新非首页页面时,不会再因为动态路由尚未注册而白屏。
- 当前页面会在动态路由注入完成后重新匹配并正常进入。
### 涉及文件
- `src/permission.js`
---
## 二、开发服务 `Network: unavailable`
### 现象
@ -201,6 +323,7 @@ npm run dev -- --public 192.168.0.115:9528
## 本次涉及的改动文件
- `src/permission.js`
- `src/views/user/user-table/index.vue`
- `vue.config.js`
- `docs/问题排查记录-权限平台与开发服务地址.md`
@ -210,3 +333,5 @@ npm run dev -- --public 192.168.0.115:9528
2. `permissionsSysOriginPlatforms` 这类字段要区分“候选项来源”和“最终权限结果来源”。
3. 老版本 Vue CLI 对网卡选择并不稳定,机器上存在 VPN、TAP、虚拟网卡时`Network: unavailable` 往往是地址识别问题,不代表 dev server 本身不可用。
4. 对局域网访问有明确需求的项目,最好显式配置 `devServer.host``devServer.public`,不要完全依赖工具链自动推断。
5. 对首屏依赖异步权限数据的页面,不应只在 `created()` 中做一次性读取;更稳妥的方式是配合 watcher 或显式初始化状态,在权限到达后自动补齐默认值与首屏请求。
6. 涉及动态路由的后台项目,刷新页面时必须保证“菜单加载 -> 路由注入 -> 再放行当前路由”是串行完成的,否则深层页面极易出现刷新白屏。

View File

@ -111,6 +111,9 @@ export default {
}]
})
},
hasVisibleChildren(route) {
return Array.isArray(route.children) && route.children.some(child => !child.hidden)
},
// Filter out the routes that can be displayed in the sidebar
// And generate the internationalized title
generateRoutes(routes, basePath = '/', prefixTitle = []) {
@ -128,9 +131,9 @@ export default {
if (router.meta && router.meta.title) {
data.title = [...data.title, translateRouteTitle(router.meta.title)]
if (router.redirect !== 'noRedirect') {
if (router.redirect !== 'noRedirect' && !this.hasVisibleChildren(router)) {
// only push the routes with title
// special case: need to exclude parent router without redirect
// exclude menu container routes that only exist to hold children
res.push(data)
}
}

View File

@ -9,7 +9,7 @@ export default {
'APP运行日志': 'App Runtime Logs',
'IM通讯': 'IM Communication',
'请求黑名单': 'Request Blacklist',
'工具管理': 'Tools Management',
'工具管理': 'Tools',
'Redis缓存管理': 'Redis Cache Management',
'订单补偿': 'Order Compensation',
'金币分析': 'Gold Analysis',

View File

@ -1,5 +1,71 @@
export default {
pages: {
page401: {
title: 'You do not have permission to access this page',
subtitle: 'Please contact your administrator if you need access.',
suggestion: 'Or you can go to:',
backHome: 'Back to Home',
viewImage: 'View Image',
backPrevious: 'Go Back',
dialog: {
imageTitle: 'Image Preview'
}
},
compensateOrder: {
section: {
order: 'Order Compensation',
candy: 'Candy Compensation'
},
form: {
userId: 'User ID',
orderId: 'Order ID',
system: 'System',
paymentPlatform: 'Payment Method',
productCode: 'Product Code',
expireTime: 'Expiration Date',
candy: 'Candy'
},
placeholder: {
selectSystem: 'Please select a system',
selectPaymentPlatform: 'Please select a payment method'
},
action: {
submit: 'Submit'
},
validation: {
requiredField: 'Required field cannot be empty'
},
confirm: {
submit: 'Are you sure you want to submit?',
title: 'Prompt',
confirm: 'Confirm',
cancel: 'Cancel'
}
},
goldAnalyze: {
placeholder: {
system: 'System',
userId: 'User ID'
},
dateRange: {
separator: 'To',
start: 'Registration Start Date',
end: 'Registration End Date'
},
action: {
search: 'Search'
},
chart: {
incomeTitle: 'Gold Income',
expenditureTitle: 'Gold Expenditure',
userSubtext: 'User: Data Distribution Chart',
systemSubtext: 'System: Data Distribution Chart'
},
message: {
selectTime: 'Please select a time range',
withinOneMonth: 'The time range must be within one month'
}
},
messagePush: {
tabs: {
newPush: 'New Push',
@ -285,7 +351,9 @@ export default {
description: 'Description'
},
placeholder: {
system: 'System'
system: 'System',
select: 'Please select',
quantity: 'Quantity'
},
type: {
notification: 'Notification',
@ -1302,13 +1370,22 @@ export default {
userId: 'User ID',
search: 'Search',
removeOrBlacklist: 'Remove/Blacklist Member',
roleChange: 'Permission Change'
roleChange: 'Permission Change',
rangeSeparator: 'to',
createDateStart: 'Start Date',
createDateEnd: 'End Date',
businessCode: 'Business Code',
role: 'Role'
},
table: {
cover: 'Cover',
roomInfo: 'Room Info',
time: 'Time',
actions: 'Actions'
actions: 'Actions',
user: 'User',
visitTime: 'Visit Time',
roomRole: 'Room Role',
registeredAt: 'Registered At'
},
row: {
account: 'Account',
@ -1370,7 +1447,16 @@ export default {
roomPermissionChange: 'Room Permission Change',
roomNicknameApproval: 'Room Nickname Approval',
roomAvatarApproval: 'Room Avatar Approval',
roomNoticeApproval: 'Room Notice Approval'
roomNoticeApproval: 'Room Notice Approval',
activeMetrics: 'Room Activity Metrics',
operationLog: 'Room Operation Log',
visitorLog: 'Room Visitor Records',
memberDetails: 'Room Member Details'
},
chart: {
onlineUsers: 'Online Users',
giftUsers: 'Gift Senders',
giftValue: 'Gift Value'
},
validation: {
uploadCover: 'Please upload a cover',
@ -1855,7 +1941,12 @@ export default {
activityTime: 'Activity Time',
updatedCreatedBy: 'Updated / Created By',
updatedCreatedTime: 'Updated / Created Time',
actions: 'Actions'
actions: 'Actions',
name: 'Name',
createdAt: 'Created At',
updatedAt: 'Updated At',
createdBy: 'Created By',
updatedBy: 'Updated By'
},
label: {
start: 'Start',
@ -1879,7 +1970,16 @@ export default {
copyActivityLink: 'Copy Activity Link',
loadMore: 'Load More',
confirm: 'Confirm',
cancel: 'Cancel'
cancel: 'Cancel',
save: 'Save',
exit: 'Exit',
send: 'Send',
add: 'Add',
addLanguage: 'Add Language',
reward: 'Reward',
clickToChange: 'Click to Change',
editName: 'Edit Name',
copyTemplate: 'Copy Template'
},
word: {
notStarted: 'Not Started',
@ -1887,7 +1987,13 @@ export default {
ended: 'Ended',
onShelf: 'On Shelf',
offShelf: 'Off Shelf',
loadedAll: 'All Loaded'
loadedAll: 'All Loaded',
snapshot: 'Snapshot',
realtime: 'Real-Time',
sent: 'Sent',
notSent: 'Not Sent',
rangeSeparator: 'to',
permanent: 'Permanent'
},
help: {
title: 'Operation Manual',
@ -1909,6 +2015,112 @@ export default {
confirm: {
editTemplateMessage: 'Note: changing the template will synchronously affect all related references. Continue?',
title: 'Prompt'
},
dialog: {
rank: 'Activity Ranking',
rankReward: 'Activity Ranking Rewards - {mode}'
},
section: {
button1: 'Button 1',
button2: 'Button 2',
button1Top20: 'Button 1 TOP20',
button2Top20: 'Button 2 TOP20',
prizeButton1: 'Prize - Button 1',
prizeButton2: 'Prize - Button 2',
basicInfo: 'Basic Info',
activityGift: 'Activity Gifts',
rankingRewards: 'Activity Ranking Rewards',
attributes: 'Attributes',
style: 'Style',
background: 'Background',
font: 'Font',
border: 'Border'
},
form: {
system: 'System',
template: 'Template',
time: 'Time',
status: 'Status',
description: 'Description',
componentName: 'Component Name',
imageIndex: 'Image {index}',
rewardTier: 'Reward Tier',
backgroundImage: 'Background Image',
backgroundColor: 'Background Color',
backgroundRepeat: 'Background Repeat',
backgroundSize: 'Background Size',
color: 'Color',
size: 'Size',
weight: 'Weight',
borderWidth: 'Border Width',
borderStyle: 'Border Style',
borderColor: 'Border Color',
borderRadius: 'Border Radius'
},
placeholder: {
template: 'Template',
startDate: 'Start Date',
endDate: 'End Date',
status: 'Status',
select: 'Please select',
rankRange: 'Ranking Range'
},
message: {
rankRetention: 'Activity ranking data is retained for only 30 days. Rankings cannot be viewed after the activity ends.',
rankRewardRetention: 'After the activity ends, send rewards through the system activity module. The system keeps the winner list, and other data is gradually cleaned after 30 days.',
endedNotEditable: 'Activities cannot be edited after they end.',
expiredTimeWarning: 'Expired activities cannot be edited. Please check the activity time.',
endedCannotEdit: 'The activity has ended and cannot be edited here.',
giftQuantityInsufficient: 'At least 3 gifts are required.',
noButtonReward: '[{button}] has no associated activity ranking rewards.',
invalidButtonReward: '[{button}] has invalid records. The system will automatically correct and exclude them.',
continueConfirm: 'Click Confirm to continue. Continue?',
sysOriginRequired: 'Source system cannot be empty.',
rewardRuleTop20: '1. The ranking shows only the top 20, and backend reward processing supports only TOP20.',
rewardRuleSort: '2. Rankings are displayed in the order entered. The system will not sort them automatically.',
rewardRuleRange: '3. Enter a number such as 1, 2, 3, or connect two numbers with ~, such as 1~10 or 11~20.',
pcRecommended: 'We recommend editing on a PC.',
noPaginationTip: 'Pagination is not implemented. If there are more than 20 templates, please contact an administrator to add pagination.'
},
validation: {
requiredField: 'Required field',
templateNameMax: 'Template name must be no more than 200 characters.'
},
prompt: {
templateName: 'Please enter the template name',
newTemplateName: 'Please enter the new template name'
},
gradient: {
none: 'None',
toRight: 'Gradient Left to Right',
toLeft: 'Gradient Right to Left',
toTop: 'Gradient Bottom to Top',
toBottom: 'Gradient Top to Bottom',
toBottomRight: 'Diagonal Gradient from Left',
toBottomLeft: 'Diagonal Gradient from Right'
},
preview: {
nickname: 'User Nickname',
nicknameRank: 'User Nickname-1'
},
propsType: {
GIFT: 'Gift',
DIAMOND: 'Diamond',
GOLD: 'Gold',
ROOM_BADGE: 'Room Badge',
BADGE: 'Personal Badge',
SPECIAL_ID: 'Special ID',
NOBLE_VIP: 'Noble VIP',
AVATAR_FRAME: 'Avatar Frame',
RIDE: 'Ride',
THEME: 'Room Background',
EMOJI: 'Emoji',
CHAT_BUBBLE: 'Chat Bubble',
GAME_COUPON: 'Game Coupon',
FRAGMENTS: 'Fragment',
FLOAT_PICTURE: 'Floating Window',
LAYOUT: 'Room Decoration',
CUSTOMIZE: 'Custom'
}
},
businessDevelopment: {
@ -1921,9 +2133,11 @@ export default {
backendMember: 'Backend Member'
},
table: {
system: 'System',
user: 'User',
region: 'Region',
teamCount: 'Team Count',
anchorCount: 'Anchor Count',
contact: 'Contact',
createdBy: 'Created By',
updatedBy: 'Updated By',
@ -1940,12 +2154,42 @@ export default {
changeBdLeader: 'Rebind BD Leader',
assignPermission: 'Assign Permission',
confirm: 'Confirm',
cancel: 'Cancel'
cancel: 'Cancel',
close: 'Close',
save: 'Save',
submit: 'Submit'
},
confirm: {
deleteMessage: 'Are you sure you want to delete this item? This action cannot be undone and will disband the current team members.',
deleteMemberMessage: 'Are you sure you want to delete this member?',
title: 'Friendly Reminder'
},
dialog: {
addBd: 'Add BD',
editBd: 'Edit BD',
permissionSettings: 'Permission Settings',
bindBdLeader: 'Bind BD Leader',
bindAgent: 'Bind Agent',
teamMembers: 'BD Team Agent Members'
},
form: {
bd: 'BD',
region: 'Region',
contact: 'Contact',
agent: 'Agent',
bdLeader: 'BD Leader'
},
placeholder: {
bdUserId: 'BD User ID',
selectRegion: 'Select a region',
contact: 'Contact',
accountOrSpecialId: 'Enter account or special ID',
agentAccount: 'Enter agent account',
userId: 'User ID'
},
validation: {
requiredField: 'Required fields cannot be empty'
},
permission: {
validating: 'Validating permissions...',
deniedPrefix: 'Sorry, you do not have permission to view this page. Please contact the administrator to grant access.',
@ -1953,7 +2197,222 @@ export default {
},
message: {
copySuccess: 'Copied successfully',
copyFail: 'Copy failed'
copyFail: 'Copy failed',
submitting: 'Submitting!',
success: 'Successful'
}
},
teamCreate: {
dialog: {
title: 'Create Agent'
},
section: {
settingInfo: 'Settings',
contactInfo: 'Contact Information'
},
form: {
region: 'Region',
agent: 'Agent',
bindBd: 'Bind BD',
bd: 'BD',
remark: 'Remark',
setting: 'Settings',
contactInfo: 'Contact Information',
memberQuantity: 'Member Quantity',
type: 'Type',
contact: 'Contact'
},
placeholder: {
region: 'Region',
agentId: 'Enter agent ID',
bdId: 'Enter BD ID',
remark: 'Enter remark',
maxMember: 'Maximum member quantity',
selectType: 'Select a type',
contact: 'Enter contact information'
},
action: {
cancel: 'Cancel',
save: 'Save'
},
validation: {
requiredField: 'Required fields cannot be empty'
},
message: {
submitting: 'Submitting!'
},
word: {
yes: 'Yes',
no: 'No'
}
},
bdLead: {
loading: 'Loading',
filter: {
system: 'System',
backendMember: 'Backend Member'
},
table: {
system: 'System',
user: 'User',
region: 'Region',
bdQuantity: 'BD Count',
contact: 'Contact',
createdBy: 'Created By',
createdAt: 'Created At',
actions: 'Actions'
},
form: {
leader: 'Leader',
bd: 'BD',
region: 'Region',
contact: 'Contact'
},
placeholder: {
bdLeadUserId: 'BD Lead User ID',
bdLeadUserIdWithSpace: 'BD Lead User ID',
selectRegion: 'Please select a region',
contact: 'Contact',
bdAccount: 'Please enter the BD account'
},
action: {
search: 'Search',
createBdLead: 'Add BD Lead',
addSubBd: 'Add Sub-BD',
edit: 'Edit',
delete: 'Delete',
confirm: 'Confirm',
cancel: 'Cancel',
close: 'Close',
save: 'Save'
},
dialog: {
create: 'Create BD Lead',
edit: 'Edit BD Lead',
bindSubBd: 'Bind Sub-BD'
},
validation: {
requiredField: 'Required field cannot be empty'
},
confirm: {
deleteMessage: 'Are you sure you want to delete it? This will unbind its BD members and will not affect other BD identities.',
title: 'Friendly Reminder'
},
message: {
copySuccess: 'Copied successfully',
copyFail: 'Copy failed',
submitting: 'Submitting!',
success: 'Successful'
}
},
appVersionManager: {
loading: 'Loading',
filter: {
system: 'System',
platform: 'Platform',
channel: 'Channel'
},
table: {
id: 'ID',
platform: 'Platform',
version: 'Version',
buildVersion: 'Build Version',
forceUpdate: 'Force Update',
review: 'Under Review',
appType: 'App Type',
patch: 'Patch',
updateDescribe: 'Version Description',
apkSize: 'Package Size',
downloadUrl: 'Download Link',
createTime: 'Created At',
actions: 'Actions',
language: 'Language',
description: 'Description',
worshipDescription: 'Worship Description'
},
form: {
platform: 'Platform',
channel: 'Channel',
version: 'Version',
buildVersion: 'Build Version',
forceUpdate: 'Force Update',
appType: 'App Type',
review: 'Under Review',
patch: 'Patch',
apkSize: 'Package Size',
downloadUrl: 'Download Link',
language: 'Language',
updateDescribe: 'Update Description',
updateWorshipDescribe: 'Worship Update Description'
},
placeholder: {
version: 'Please enter the version',
buildVersion: 'Please enter the build version',
apkSize: 'Please enter the package size',
downloadUrl: 'Please enter the download link',
language: 'Language',
updateDescribe: 'Please enter the update description',
updateWorshipDescribe: 'Please enter the worship update description'
},
action: {
search: 'Search',
add: 'Add',
copy: 'Copy',
edit: 'Edit',
delete: 'Delete',
serviceForceUpdate: 'Server Force Update',
updateDescription: 'Update Description',
addDescription: 'Add Description',
save: 'Save',
cancel: 'Cancel',
submit: 'Submit',
confirm: 'Confirm'
},
dialog: {
descriptionList: 'Description List',
addDescription: 'Add Description',
editDescription: 'Edit Description',
addVersion: 'Add ({sysOrigin})',
editVersion: 'Edit ({sysOrigin})'
},
confirm: {
title: 'Prompt',
deleteVersion: 'Are you sure you want to delete it?',
setServerBuildVersion: 'The server will block requests below the current build version. It will take effect within 10 minutes. Continue?',
deleteDescription: 'Are you sure you want to delete this description?'
},
validation: {
requiredField: 'Required field cannot be empty',
max500: 'Maximum 500 characters'
},
message: {
copySuccess: 'Copied successfully',
copyFail: 'Copy failed',
deleteSuccess: 'Deleted successfully'
},
word: {
yes: 'Yes',
no: 'No'
},
appType: {
chat: 'Voice Chat',
worship: 'Worship'
},
language: {
en: 'English',
ar: 'Arabic',
id: 'Indonesian',
tr: 'Turkish',
ru: 'Russian',
pt: 'Portuguese',
ko: 'Korean',
ja: 'Japanese',
hi: 'Hindi',
fr: 'French',
es: 'Spanish',
de: 'German',
zh_CN: 'Chinese',
zh_TW: 'Traditional Chinese'
}
},
lotteryConfig: {
@ -2052,7 +2511,33 @@ export default {
resetConsumeStock: 'Reset consumed stock?'
},
validation: {
positiveInteger: 'Please enter a positive integer'
positiveInteger: 'Please enter a positive integer',
requiredField: 'This field is required'
},
selectPrize: {
title: 'Select Prize',
form: {
type: 'Select Type',
quantity: 'Quantity'
},
group: {
common: 'Common',
props: 'Props',
badge: 'Badge',
currency: 'Currency'
},
typeOptions: {
gift: 'Gift',
avatarFrame: 'Avatar Frame',
ride: 'Ride',
nobleVip: 'Noble VIP',
roomBackground: 'Room Background',
emoji: 'Emoji',
userBadge: 'User Badge',
roomBadge: 'Room Badge',
gold: 'Gold',
diamond: 'Diamond'
}
},
defaultPrizeText: '{index} Yuan Red Packet'
},
@ -2199,6 +2684,304 @@ export default {
sizeExceeded: '{fileName} exceeds {maxSize} MB'
}
},
toolsOther: {
cards: {
userSwap: 'User Account Swap',
apiSign: 'API Parameter Signature',
aliasMethod: 'Alias Method Analysis',
tokenParse: 'User Token Parsing',
redisOps: 'Redis Operations',
userRoomRegion: 'User Room Region Update',
userCancelRestore: 'Restore Canceled User'
},
action: {
setAttributes: 'Set Attributes',
rerun: 'Run Again',
clearAttributes: 'Clear Attributes',
addAttribute: 'Add Attribute',
quickFill: 'Quick Fill',
confirm: 'Confirm'
},
form: {
mode: 'Mode',
userId: 'User ID',
longUserId: 'User Long ID',
country: 'Country'
},
placeholder: {
enterKey: 'Please enter the key information',
enterUserToken: 'Please enter the user token',
enterUserId: 'Please enter the user ID',
enterSourceLongUserId: 'Please enter the source user long ID',
enterTargetLongUserId: 'Please enter the target user long ID',
selectCountry: 'Please select a country'
},
validation: {
requiredField: 'Required field cannot be empty',
requiredParam: 'Required parameter cannot be empty'
},
aliasMethod: {
dialogTitle: 'Set Probability',
probabilityDescription: 'Probability Notes',
averageProbability: 'Average probability: 1.0 / prize count = {value}',
probabilityFill: 'Probability fill: probability >= average probability (goes to large), otherwise it goes to small.',
sampling: 'Sampling:',
samplingRuleOne: '1. If all configured probabilities are lower than the average probability, the current probability becomes 1.',
samplingRuleTwo: '2. If (current maximum probability + current minimum probability - average probability) > average probability, continue using large.',
samplingScenario: 'Example: if probabilities are set to 0.9, 0.8, and 0.3, then 0.3 may become a high probability because the configured probabilities are too high and the first sampled probability keeps getting selected.',
suggestion: 'Suggestion: use probability + inventory together for a safer setup.',
runCount: 'Run Count',
item: 'Item',
probability: 'Probability'
},
apiSign: {
platform: 'Signature Platform',
platformPlaceholder: 'Platform',
body: 'Payload',
signKey: 'Signature Key',
dialogTitle: 'API Content Attributes',
key: 'Key',
value: 'Value'
},
tokenParse: {
parseToken: 'Parse Token',
getToken: 'Get Token',
token: 'Token'
},
redisOps: {
clearKey: 'Clear Key'
},
userSwap: {
sourceId: 'Source ID',
targetId: 'Target ID',
checkCondition: 'Validate Swap Conditions',
submitExecute: 'Submit Execution',
tips: {
sourceUserNotFound: 'Source user not found',
targetUserNotFound: 'Target user not found',
differentSystem: 'Users come from different systems',
sourceAuthNotFound: 'Source user authentication info not found',
targetAuthNotFound: 'Target user authentication info not found',
openIdMismatch: 'Registration source OpenId does not match'
}
}
},
websocketTool: {
broadcast: {
title: 'Subscribe to Broadcast Messages'
},
direct: {
title: 'Point-to-Point'
},
step: {
flow: 'Create Connection -> Subscribe Topic -> Send Message'
},
form: {
socketEndpoint: 'Socket Endpoint',
subscribePath: 'Subscription Path',
sendEndpoint: 'Send Endpoint',
myToken: 'My Token',
content: 'Message Content',
repositoryEndpoint: 'Repository Endpoint',
repositoryData: 'Repository Data',
statusDescription: 'Status Description',
subscribePrefix: 'Subscription Prefix',
targetUserId: 'Recipient User ID'
},
placeholder: {
socketEndpoint: 'Socket endpoint',
subscribePath: 'Subscription path',
sendEndpoint: 'Send endpoint',
myToken: 'My token',
content: 'Message content',
repositoryEndpoint: 'Repository endpoint',
repositoryData: 'Insert repository data',
subscribePrefix: 'Subscription prefix',
targetUserId: 'Recipient user, long ID'
},
action: {
addRepository: 'Add Repository',
sendMessage: 'Send Message',
subscribeTopic: 'Subscribe Topic',
disconnect: 'Disconnect',
connect: 'Connect'
},
validation: {
requiredField: 'Required field'
},
message: {
connected: 'Connected successfully',
disconnected: 'Disconnected',
connectFirst: 'Please connect first.',
createFirst: 'Please create a connection first.',
enterMessageContent: 'Please enter the message content.'
}
},
gameLudo: {
placeholder: {
type: 'Type',
system: 'System'
},
action: {
search: 'Search',
dismiss: 'Dismiss',
loadMore: 'Click to Load More'
},
state: {
allLoaded: 'All data has been loaded'
},
dialog: {
players: 'Game Players'
},
table: {
system: 'System',
roomInfo: 'Room Info',
status: 'Status',
amount: 'Amount (Join/Bonus)',
situation: 'Situation',
playerCount: 'Players: {count}',
lockedSeat: 'Locked Seats: {count}',
victorySide: 'Winning Side',
createdAt: 'Created At',
updatedAt: 'Updated At',
actions: 'Actions',
avatar: 'Avatar',
nickname: 'Nickname',
board: 'Board',
ranking: 'Ranking',
roomOwner: 'Room Owner',
consume: 'Consumption'
},
type: {
run: 'Running',
history: 'History'
},
status: {
wait: 'Waiting to Start',
progress: 'In Progress',
end: 'Ended'
},
event: {
none: 'None',
timeoutDisband: 'Timed out before the game started and was dismissed',
disconnectedDisband: 'The game started and was dismissed after all players disconnected',
otherDisband: 'Other unexpected state',
normalSettlement: 'Settled Normally',
backDisband: 'Dismissed from Back Office'
},
confirm: {
dismiss: 'Dismissing the game from the back office will leave no winner. Are you sure you want to dismiss it?',
title: 'Prompt',
confirm: 'Confirm',
cancel: 'Cancel'
}
},
requestBlacklist: {
action: {
addIp: 'Add IP',
addPhoneModel: 'Add Phone Model'
},
confirm: {
delete: 'Are you sure you want to delete it?',
title: 'Prompt'
},
prompt: {
invalidType: 'Invalid parameter type',
ipTip:
'Please enter an IP. Example: 127.0.0.1 or 127.0.0. Each missing segment blocks one more network segment.',
phoneModelTip: 'Please enter the phone model. It will be stored in uppercase.',
title: 'Prompt',
confirm: 'Confirm',
cancel: 'Cancel'
},
message: {
paramTypeError: 'Parameter type error.'
}
},
imAccountManager: {
filter: {
userId: 'User ID'
},
alert: {
title: 'Prompt',
description:
'Use an in-app registered account to authorize IM login on the back-office PC side. The login account uses the short ID and the initial password is "88888888".'
},
table: {
avatar: 'Avatar',
nickname: 'Nickname',
imUsername: 'IM Username',
createdAt: 'Created At',
actions: 'Actions'
},
dialog: {
create: 'Add'
},
form: {
userId: 'User ID',
password: 'Login Password'
},
placeholder: {
userId: 'Please enter the user ID',
password: 'Please enter the login password'
},
action: {
search: 'Search',
add: 'Add',
delete: 'Delete',
resetPassword: 'Reset Password',
cancel: 'Cancel',
submit: 'Submit'
},
validation: {
requiredField: 'Required parameter cannot be empty'
},
confirm: {
title: 'Prompt',
delete: 'Are you sure you want to delete "{nickname}"?',
resetPassword: 'Are you sure you want to reset the login password for "{nickname}"?',
confirm: 'Confirm',
cancel: 'Cancel'
}
},
userCustomGift: {
loading: 'Loading',
filter: {
system: 'System',
userId: 'User ID',
status: 'Status',
type: 'Type',
region: 'Region'
},
status: {
pending: 'Pending',
processed: 'Processed'
},
type: {
uploadGift: 'Custom Gift',
increaseTime: 'Extend Duration'
},
table: {
system: 'System',
user: 'User',
region: 'Region',
type: 'Type',
giftCode: 'Gift Code',
days: 'Days',
approveStatus: 'Pending / Processed',
createdAt: 'Created At',
actions: 'Actions'
},
action: {
search: 'Search',
downloadImage: 'Download Image {index}',
downloadSong: 'Download Song'
},
message: {
success: 'Operation successful',
resourceNotUploaded: 'The resource has not been uploaded by the user'
}
},
countryCode: {
filter: {
countryName: 'English Country Name',
@ -2298,6 +3081,7 @@ export default {
action: {
add: 'Add',
edit: 'Edit',
view: 'View',
cancel: 'Cancel',
save: 'Save'
},

View File

@ -1,5 +1,71 @@
export default {
pages: {
page401: {
title: '你没有权限去该页面',
subtitle: '如需访问权限,请联系管理员',
suggestion: '或者你可以去:',
backHome: '回首页',
viewImage: '点我看图',
backPrevious: '返回上一级',
dialog: {
imageTitle: '随便看'
}
},
compensateOrder: {
section: {
order: '补偿订单',
candy: '补偿糖果'
},
form: {
userId: '用户ID',
orderId: '订单ID',
system: '平台',
paymentPlatform: '支付方式',
productCode: '产品CODE',
expireTime: '过期时间',
candy: '糖果'
},
placeholder: {
selectSystem: '请选择平台',
selectPaymentPlatform: '请选择平台'
},
action: {
submit: '提交'
},
validation: {
requiredField: '必填字段不可为空'
},
confirm: {
submit: '是否确认提交?',
title: '提示',
confirm: '确定',
cancel: '取消'
}
},
goldAnalyze: {
placeholder: {
system: '系统',
userId: '用户ID'
},
dateRange: {
separator: '至',
start: '注册开始日期',
end: '注册结束日期'
},
action: {
search: '搜索'
},
chart: {
incomeTitle: '金币收入',
expenditureTitle: '金币支出',
userSubtext: '用户:数据分配图',
systemSubtext: '系统:数据分配图'
},
message: {
selectTime: '请选择时间',
withinOneMonth: '时间必须在1个月以内'
}
},
messagePush: {
tabs: {
newPush: '新建Push',
@ -285,7 +351,9 @@ export default {
description: '描述'
},
placeholder: {
system: '系统'
system: '系统',
select: '请选择',
quantity: '数量'
},
type: {
notification: '通知',
@ -1301,13 +1369,22 @@ export default {
userId: '用户ID',
search: '搜索',
removeOrBlacklist: '移除/拉黑成员',
roleChange: '权限变更'
roleChange: '权限变更',
rangeSeparator: '至',
createDateStart: '创建日期开始',
createDateEnd: '创建日期结束',
businessCode: '业务CODE',
role: '角色'
},
table: {
cover: '封面',
roomInfo: '房间信息',
time: '时间',
actions: '操作'
actions: '操作',
user: '用户',
visitTime: '浏览时间',
roomRole: '房间角色',
registeredAt: '注册时间'
},
row: {
account: '账号',
@ -1369,7 +1446,16 @@ export default {
roomPermissionChange: '房间内权限变更',
roomNicknameApproval: '房间昵称审批',
roomAvatarApproval: '房间头像审批',
roomNoticeApproval: '房间公告审批'
roomNoticeApproval: '房间公告审批',
activeMetrics: '房间活跃指标',
operationLog: '房间操作日志',
visitorLog: '房间访客记录',
memberDetails: '房间成员详情'
},
chart: {
onlineUsers: '在线用户',
giftUsers: '送礼用户',
giftValue: '礼物价值'
},
validation: {
uploadCover: '请上传封面',
@ -1854,7 +1940,12 @@ export default {
activityTime: '活动时间',
updatedCreatedBy: '修改/创建人',
updatedCreatedTime: '修改/创建时间',
actions: '操作'
actions: '操作',
name: '名称',
createdAt: '创建时间',
updatedAt: '修改时间',
createdBy: '创建人',
updatedBy: '修改人'
},
label: {
start: '开始',
@ -1878,7 +1969,16 @@ export default {
copyActivityLink: '复制活动链接',
loadMore: '加载更多',
confirm: '确定',
cancel: '取消'
cancel: '取消',
save: '保存',
exit: '退出',
send: '发送',
add: '添加',
addLanguage: '添加语言',
reward: '奖品',
clickToChange: '点击更换',
editName: '编辑名称',
copyTemplate: '复制模版'
},
word: {
notStarted: '未开始',
@ -1886,7 +1986,13 @@ export default {
ended: '已结束',
onShelf: '上架',
offShelf: '下架',
loadedAll: '已加载全部'
loadedAll: '已加载全部',
snapshot: '快照',
realtime: '实时',
sent: '已发送',
notSent: '未发送',
rangeSeparator: '至',
permanent: '永久'
},
help: {
title: '操作手册',
@ -1908,6 +2014,112 @@ export default {
confirm: {
editTemplateMessage: '注意: 模版发生改变后相关引用都会同步发生变化, 是否继续?',
title: '提示'
},
dialog: {
rank: '活动排名',
rankReward: '活动排名奖品-{mode}'
},
section: {
button1: '按钮1',
button2: '按钮2',
button1Top20: '按钮1 TOP20',
button2Top20: '按钮2 TOP20',
prizeButton1: '奖品-按钮1',
prizeButton2: '奖品-按钮2',
basicInfo: '基础信息',
activityGift: '活动礼物',
rankingRewards: '活动排名奖品',
attributes: '属性',
style: '样式',
background: '背景',
font: '字体',
border: '边框'
},
form: {
system: '系统',
template: '模版',
time: '时间',
status: '状态',
description: '描述',
componentName: '组件名称',
imageIndex: '图片{index}',
rewardTier: '奖励阶梯',
backgroundImage: '背景图',
backgroundColor: '背景颜色',
backgroundRepeat: '背景图重复',
backgroundSize: '背景图大小',
color: '颜色',
size: '大小',
weight: '粗细',
borderWidth: '边框宽',
borderStyle: '边框样式',
borderColor: '边框颜色',
borderRadius: '边框圆角'
},
placeholder: {
template: '模版',
startDate: '开始日期',
endDate: '结束日期',
status: '状态',
select: '请选择',
rankRange: '排名范围'
},
message: {
rankRetention: '活动排行榜数据只会保留30天活动结束后排行榜不可查看。',
rankRewardRetention: '结束后通过系统活动模块发送奖品系统会保留获奖名单数据其他数据将在30天后逐渐清理。',
endedNotEditable: '活动结束后不可再编辑。',
expiredTimeWarning: '过期活动将不可编辑,请检查活动时间。',
endedCannotEdit: '活动已结束,不可在此编辑!',
giftQuantityInsufficient: '礼物数量不足3个。',
noButtonReward: '[{button}] 没有关联活动排名奖品。',
invalidButtonReward: '[{button}] 存在错误记录,系统将自动矫正排除。',
continueConfirm: '如果继续请点击确认按钮,是否继续?',
sysOriginRequired: '归属系统不可为空!',
rewardRuleTop20: '1. 排行榜只会展示前20名后台处理奖品也只支持TOP20。',
rewardRuleSort: '2. 排名展示在输入的时候自行排好顺序,系统不会整理排序。',
rewardRuleRange: '3. 直接输入指定数字 1、2、3... 或使用 ~ 将两个数字连接1~10、11~20...',
pcRecommended: '建议您使用PC电脑进行编辑',
noPaginationTip: '没有做分页,模版数量 > 20 个后请联系管理员添加分页功能!'
},
validation: {
requiredField: '必填字段',
templateNameMax: '模版名称必须 <= 200 个字符串。'
},
prompt: {
templateName: '请输入模版名称',
newTemplateName: '请输入新的模版名称'
},
gradient: {
none: '无',
toRight: '从左到右渐变',
toLeft: '从右到左渐变',
toTop: '从下到上渐变',
toBottom: '从上到下渐变',
toBottomRight: '从左对角渐变',
toBottomLeft: '从右对角渐变'
},
preview: {
nickname: '用户昵称',
nicknameRank: '用户昵称-1'
},
propsType: {
GIFT: '礼物',
DIAMOND: '钻石',
GOLD: '金币',
ROOM_BADGE: '房间徽章',
BADGE: '个人徽章',
SPECIAL_ID: '靓号',
NOBLE_VIP: '贵族VIP',
AVATAR_FRAME: '头像框',
RIDE: '座驾',
THEME: '房间背景',
EMOJI: '表情包',
CHAT_BUBBLE: '聊天气泡',
GAME_COUPON: '游戏券',
FRAGMENTS: '碎片',
FLOAT_PICTURE: '个人飘窗',
LAYOUT: '房间装扮',
CUSTOMIZE: '自定义'
}
},
businessDevelopment: {
@ -1920,9 +2132,11 @@ export default {
backendMember: '后台成员'
},
table: {
system: '系统',
user: '用户',
region: '区域',
teamCount: '团队数量',
anchorCount: '主播数量',
contact: '联系方式',
createdBy: '创建人',
updatedBy: '修改人',
@ -1939,12 +2153,42 @@ export default {
changeBdLeader: '换绑BD Leader',
assignPermission: '权限分配',
confirm: '确定',
cancel: '取消'
cancel: '取消',
close: '关闭',
save: '保存',
submit: '提交'
},
confirm: {
deleteMessage: '您确定要删除吗(将无法恢复, 解散当前团队成员)?',
deleteMemberMessage: '您确定要删除吗',
title: '温馨提示'
},
dialog: {
addBd: '添加BD',
editBd: '修改BD',
permissionSettings: '权限设置',
bindBdLeader: '绑定BDLeader',
bindAgent: '绑定代理',
teamMembers: 'BD团队代理成员'
},
form: {
bd: 'BD',
region: '区域',
contact: '联系方式',
agent: '代理',
bdLeader: 'BD Leader'
},
placeholder: {
bdUserId: 'BD用户ID',
selectRegion: '请选择区域',
contact: '联系方式',
accountOrSpecialId: '请输入账号或靓号',
agentAccount: '请输入代理账号',
userId: '用户ID'
},
validation: {
requiredField: '必填字段不可为空'
},
permission: {
validating: '权限验证中...',
deniedPrefix: '抱歉您无权查看,请联系管理员开通查看权限',
@ -1952,7 +2196,222 @@ export default {
},
message: {
copySuccess: '复制成功',
copyFail: '复制失败'
copyFail: '复制失败',
submitting: '正在提交!',
success: '成功'
}
},
teamCreate: {
dialog: {
title: '创建代理'
},
section: {
settingInfo: '设置信息',
contactInfo: '联系方式'
},
form: {
region: '区域',
agent: '代理',
bindBd: '绑定BD',
bd: 'BD',
remark: '备注',
setting: '设置',
contactInfo: '联系方式',
memberQuantity: '成员数量',
type: '类型',
contact: '联系'
},
placeholder: {
region: '区域',
agentId: '请输入代理ID',
bdId: '请输入BD ID',
remark: '请输入备注',
maxMember: '最大成员数量',
selectType: '选择类型',
contact: '请输入联系方式'
},
action: {
cancel: '取消',
save: '保存'
},
validation: {
requiredField: '必填字段不可为空'
},
message: {
submitting: '正在提交!'
},
word: {
yes: '是',
no: '否'
}
},
bdLead: {
loading: '加载中',
filter: {
system: '系统',
backendMember: '后台成员'
},
table: {
system: '系统',
user: '用户',
region: '区域',
bdQuantity: 'BD数量',
contact: '联系方式',
createdBy: '创建人',
createdAt: '创建时间',
actions: '操作'
},
form: {
leader: 'Leader',
bd: 'BD',
region: '区域',
contact: '联系方式'
},
placeholder: {
bdLeadUserId: 'BD Leader用户ID',
bdLeadUserIdWithSpace: 'BD Leader 用户ID',
selectRegion: '请选择区域',
contact: '联系方式',
bdAccount: '请输入BD账号'
},
action: {
search: '搜索',
createBdLead: '新增BD Lead',
addSubBd: '增加子级BD',
edit: '编辑',
delete: '删除',
confirm: '确定',
cancel: '取消',
close: '关闭',
save: '保存'
},
dialog: {
create: '创建BD Leader',
edit: '编辑BD Leader',
bindSubBd: '绑定子级BD'
},
validation: {
requiredField: '必填字段不可为空'
},
confirm: {
deleteMessage: '您确定要删除吗(将与名下BD解除绑定, 不影响其他BD身份)?',
title: '温馨提示'
},
message: {
copySuccess: '复制成功',
copyFail: '复制失败',
submitting: '正在提交!',
success: '成功'
}
},
appVersionManager: {
loading: '加载中',
filter: {
system: '系统',
platform: '平台',
channel: '渠道'
},
table: {
id: 'ID',
platform: '平台',
version: '版本',
buildVersion: '编译版本',
forceUpdate: '强更',
review: '审核中',
appType: 'App类型',
patch: '补丁',
updateDescribe: '版本描述',
apkSize: '安装包大小',
downloadUrl: '下载链接',
createTime: '创建时间',
actions: '操作',
language: '语言',
description: '描述',
worshipDescription: '朝拜描述'
},
form: {
platform: '平台',
channel: '渠道',
version: '版本号',
buildVersion: '编译版本号',
forceUpdate: '强更',
appType: 'App类型',
review: '审核中',
patch: '补丁',
apkSize: '安装包大小',
downloadUrl: '下载链接',
language: '语言',
updateDescribe: '更新描述',
updateWorshipDescribe: '更新朝拜描述'
},
placeholder: {
version: '请输入版本号',
buildVersion: '请输入编译版本号',
apkSize: '请输入安装包大小',
downloadUrl: '请输入下载链接',
language: '语言',
updateDescribe: '请输入更新描述',
updateWorshipDescribe: '请输入更新朝拜描述'
},
action: {
search: '搜索',
add: '添加',
copy: '点击复制',
edit: '编辑',
delete: '删除',
serviceForceUpdate: '服务强更',
updateDescription: '更新描述',
addDescription: '新增描述',
save: '保存',
cancel: '取消',
submit: '提交',
confirm: '确定'
},
dialog: {
descriptionList: '描述列表',
addDescription: '新增描述',
editDescription: '修改描述',
addVersion: '添加({sysOrigin})',
editVersion: '修改({sysOrigin})'
},
confirm: {
title: '温馨提示',
deleteVersion: '是否确认删除',
setServerBuildVersion: '服务器将拦截小于当前buildVersion请求10分钟内生效是否继续',
deleteDescription: '确定删除描述吗?'
},
validation: {
requiredField: '必填项不可为空',
max500: '最大 500 字符'
},
message: {
copySuccess: '复制成功',
copyFail: '复制失败',
deleteSuccess: '删除成功'
},
word: {
yes: '是',
no: '否'
},
appType: {
chat: '语聊',
worship: '朝拜'
},
language: {
en: '英语',
ar: '阿拉伯语',
id: '印尼语',
tr: '土耳其语',
ru: '俄语',
pt: '葡萄牙语',
ko: '韩语',
ja: '日语',
hi: '印地语',
fr: '法语',
es: '西班牙语',
de: '德语',
zh_CN: '中文',
zh_TW: '繁体'
}
},
lotteryConfig: {
@ -2051,7 +2510,33 @@ export default {
resetConsumeStock: '是否重置消费库存'
},
validation: {
positiveInteger: '请输入正整数'
positiveInteger: '请输入正整数',
requiredField: '必填字段不可为空'
},
selectPrize: {
title: '选择奖品',
form: {
type: '选择类型',
quantity: '数量'
},
group: {
common: '常用',
props: '道具',
badge: '徽章',
currency: '货币'
},
typeOptions: {
gift: '礼物',
avatarFrame: '头像框',
ride: '座驾',
nobleVip: '贵族',
roomBackground: '房间背景',
emoji: '表情包',
userBadge: '用户徽章',
roomBadge: '房间徽章',
gold: '金币',
diamond: '钻石'
}
},
defaultPrizeText: '{index} 元红包'
},
@ -2198,6 +2683,302 @@ export default {
sizeExceeded: '{fileName} 超出 {maxSize}MB'
}
},
toolsOther: {
cards: {
userSwap: '用户账户调换',
apiSign: 'API参数签名',
aliasMethod: 'Alias Method分析',
tokenParse: '用户令牌解析',
redisOps: 'Redis操作',
userRoomRegion: '用户房间区域变动',
userCancelRestore: '注销用户恢复'
},
action: {
setAttributes: '设置属性',
rerun: '重新运行',
clearAttributes: '清空属性',
addAttribute: '新增属性',
quickFill: '快速填充',
confirm: '确定'
},
form: {
mode: '模式',
userId: '用户ID',
longUserId: '用户长ID',
country: '国家'
},
placeholder: {
enterKey: '请输入Key信息',
enterUserToken: '请输入用户令牌',
enterUserId: '请输入用户ID',
enterSourceLongUserId: '请输入原始用户长ID',
enterTargetLongUserId: '请输入目标用户长ID',
selectCountry: '请选择国家'
},
validation: {
requiredField: '必填字段不可为空',
requiredParam: '必填参数不可为空'
},
aliasMethod: {
dialogTitle: '设置概率',
probabilityDescription: '概率说明',
averageProbability: '平均概率: 1.0 / 奖品数量 = {value}',
probabilityFill: '概率填充: 概率 >= 平均概率(进入 large), 否则(进入 small)',
sampling: '采样:',
samplingRuleOne: '1. 如果设置概率全部小于平均概率, 当前概率 = 1',
samplingRuleTwo: '2. 如果设置概率 (当前最大概率 + 当前最小概率 - 平均概率) > 平均概率, 继续采用 large。',
samplingScenario: '出现情况: 我设置了概率 0.9、0.8、0.3, 但是 0.3 变成了大概率, 由于概率设置过高导致不够平均, 取到的第一个概率被不断采样。',
suggestion: '建议: 概率 + 库存这样比较保险安全',
runCount: '运行次数',
item: '商品',
probability: '概率'
},
apiSign: {
platform: '签名平台',
platformPlaceholder: '平台',
body: '数据内容',
signKey: '签名KEY',
dialogTitle: 'API内容属性',
key: '键',
value: '值'
},
tokenParse: {
parseToken: '令牌解析',
getToken: '获取令牌',
token: '令牌'
},
redisOps: {
clearKey: '清理Key'
},
userSwap: {
sourceId: '原始ID',
targetId: '目标ID',
checkCondition: '验证转换条件',
submitExecute: '提交执行',
tips: {
sourceUserNotFound: '原始用户没有找到',
targetUserNotFound: '目标用户没有找到',
differentSystem: '用户来自不同系统',
sourceAuthNotFound: '原始用户认证信息没找到',
targetAuthNotFound: '目标用户认证信息没找到',
openIdMismatch: '注册来源OpenId不一致'
}
}
},
websocketTool: {
broadcast: {
title: '订阅广播消息'
},
direct: {
title: '点对点'
},
step: {
flow: '创建连接 -> 订阅主题 -> 发送消息'
},
form: {
socketEndpoint: '连接端点',
subscribePath: '订阅地址',
sendEndpoint: '发送接口',
myToken: '我的token',
content: '消息内容',
repositoryEndpoint: '仓库端点',
repositoryData: '仓库数据',
statusDescription: '状态描述',
subscribePrefix: '订阅前缀',
targetUserId: '接收用户ID'
},
placeholder: {
socketEndpoint: '连接端点',
subscribePath: '订阅地址',
sendEndpoint: '发送接口',
myToken: '我的token',
content: '消息内容',
repositoryEndpoint: '仓库端点',
repositoryData: '插入仓库数据',
subscribePrefix: '订阅前缀',
targetUserId: '接收用户长ID'
},
action: {
addRepository: '添加仓库',
sendMessage: '发送消息',
subscribeTopic: '订阅主题',
disconnect: '断开连接',
connect: '进行连接'
},
validation: {
requiredField: '必填字段'
},
message: {
connected: '连接成功',
disconnected: '断开连接',
connectFirst: '请先进行连接.',
createFirst: '请先创建连接.',
enterMessageContent: '请输入消息内容.'
}
},
gameLudo: {
placeholder: {
type: '类型',
system: '系统'
},
action: {
search: '搜索',
dismiss: '解散',
loadMore: '点击加载更多'
},
state: {
allLoaded: '已经加载全部'
},
dialog: {
players: '游戏玩家'
},
table: {
system: '系统',
roomInfo: '房间信息',
status: '状态',
amount: '金额(加入/奖金)',
situation: '情况',
playerCount: '玩家:{count}',
lockedSeat: '锁定座位:{count}',
victorySide: '胜利方',
createdAt: '创建时间',
updatedAt: '修改时间',
actions: '操作',
avatar: '头像',
nickname: '昵称',
board: '棋盘',
ranking: '名次',
roomOwner: '房主',
consume: '消费'
},
type: {
run: '运行',
history: '历史'
},
status: {
wait: '等待开始',
progress: '进行中',
end: '已结束'
},
event: {
none: '无',
timeoutDisband: '游戏未开始超时,解散',
disconnectedDisband: '游戏已开始,全部掉线后,解散',
otherDisband: '其他,不在预计状态内',
normalSettlement: '正常结算',
backDisband: '后台解散'
},
confirm: {
dismiss: '后台解散游戏将没有胜利者, 是否确定解散?',
title: '提示',
confirm: '确定',
cancel: '取消'
}
},
requestBlacklist: {
action: {
addIp: '新增IP',
addPhoneModel: '新增手机型号'
},
confirm: {
delete: '确认删除吗?',
title: '提示'
},
prompt: {
invalidType: '错误的参数类型',
ipTip: '请输入IP, 格式如:127.0.0.1 或 127.0.0 每少一位表示多屏蔽一个网络段',
phoneModelTip: '请输入手机型号, 最终系统保存为大写',
title: '提示',
confirm: '确定',
cancel: '取消'
},
message: {
paramTypeError: '参数类型错误'
}
},
imAccountManager: {
filter: {
userId: '用户ID'
},
alert: {
title: '提示',
description: '使用站内注册账号关联后台PC端IM登录授权登录账号“短ID”初始密码“88888888”'
},
table: {
avatar: '头像',
nickname: '昵称',
imUsername: 'IM用户名',
createdAt: '创建时间',
actions: '操作'
},
dialog: {
create: '添加'
},
form: {
userId: '用户ID',
password: '登录密码'
},
placeholder: {
userId: '请输入用户ID',
password: '请输入登录密码'
},
action: {
search: '搜索',
add: '添加',
delete: '删除',
resetPassword: '重置密码',
cancel: '取消',
submit: '提交'
},
validation: {
requiredField: '必填参数不可为空'
},
confirm: {
title: '提示',
delete: '是否确认删除 "{nickname}" ?',
resetPassword: '是否确认重置 "{nickname}" 登录密码?',
confirm: '确定',
cancel: '取消'
}
},
userCustomGift: {
loading: '加载中',
filter: {
system: '系统',
userId: '用户ID',
status: '状态',
type: '类型',
region: '区域'
},
status: {
pending: '待处理',
processed: '已处理'
},
type: {
uploadGift: '专属定制',
increaseTime: '增加时长'
},
table: {
system: '归属系统',
user: '用户',
region: '区域',
type: '类型',
giftCode: '礼物Code',
days: '天数',
approveStatus: '待处理/已完成',
createdAt: '创建时间',
actions: '操作'
},
action: {
search: '搜索',
downloadImage: '下载图片{index}',
downloadSong: '下载歌曲'
},
message: {
success: '操作成功',
resourceNotUploaded: '资源用户未上传'
}
},
countryCode: {
filter: {
countryName: '英文国家名称',
@ -2297,6 +3078,7 @@ export default {
action: {
add: '添加',
edit: '修改',
view: '查看',
cancel: '取消',
save: '保存'
},

View File

@ -11,13 +11,14 @@
<script>
export default {
name: 'AppMain',
name: "AppMain",
computed: {
key() {
return this.$route.path
return this.$route.path;
// return `${this.$route.path}-${this.$store.getters.language}`
}
}
}
};
</script>
<style scoped>
@ -28,7 +29,7 @@ export default {
position: relative;
overflow: hidden;
}
.fixed-header+.app-main {
.fixed-header + .app-main {
padding-top: 50px;
}
</style>

View File

@ -1,235 +0,0 @@
<template>
<div class="navbar">
<hamburger :is-active="sidebar.opened" class="hamburger-container" @toggleClick="toggleSideBar" />
<breadcrumb class="breadcrumb-container" />
<div class="right-menu flex-r">
<!-- <div class="sys-origin right-menu-item">
<el-dropdown>
<span type="info" class="cursor-pointer">
{{ sysOriginActive }}<i class="el-icon-arrow-down el-icon--right" />
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item
v-for="item in sysOriginPlatforms"
:key="item.value"
@click.native="clickSysOrigin(item)"
>{{ item.label }}</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</div> -->
<template v-if="device!=='mobile'">
<search id="header-search" class="right-menu-item" />
<screenfull id="screenfull" class="right-menu-item hover-effect" />
<el-tooltip content="Global Size" effect="dark" placement="bottom">
<size-select id="size-select" class="right-menu-item hover-effect" />
</el-tooltip>
</template>
<el-dropdown class="avatar-container" trigger="click">
<div class="avatar-wrapper">
<div class="user-avatar flex-c" :style="userAvatarColor">{{ firstNameStr }}</div>
<i class="el-icon-caret-bottom" />
</div>
<el-dropdown-menu slot="dropdown" class="user-dropdown">
<el-dropdown-item @click.native="clickSwitchTheme">切换模式</el-dropdown-item>
<el-dropdown-item @click.native="resetPwd">修改密码</el-dropdown-item>
<el-dropdown-item @click.native="logout">退出登录</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</div>
<rest-password v-if="resetPasswordVisible" @success="resetPasswordVisible=false" @close="resetPasswordVisible=false" />
<el-dialog
title="选择主题"
:visible.sync="themeSwitchVisible"
:modal-append-to-body="true"
:append-to-body="true"
:close-on-press-escape="false"
:close-on-click-modal="false"
:before-close="()=>themeSwitchVisible=false"
>
<div class="theme-list ">
<el-radio-group v-model="themeVal" @change="themeChange">
<el-radio label="normal">正常模式</el-radio>
<el-radio label="dark">暗黑模式</el-radio>
<el-radio label="auto">自动切换</el-radio>
</el-radio-group>
<div v-if="themeVal === 'auto'" style="margin-top: 20px;">
07:00~18:00 正常模式19:00~06:00 进入暗黑模式
</div>
</div>
</el-dialog>
</div>
</template>
<script>
import { mapGetters } from 'vuex'
import Breadcrumb from '@/components/Breadcrumb'
import Hamburger from '@/components/Hamburger'
import RestPassword from '@/components/data/RestPassword'
import Screenfull from '@/components/Screenfull'
import SizeSelect from '@/components/SizeSelect'
import Search from '@/components/HeaderSearch'
import { sysOriginPlatforms } from '@/constant/origin'
import { getTheme, setTheme, checkSwitchTheme } from '@/utils/theme'
import { updateTheme } from '@/api/ops-system'
import { extractColorByText } from '@/utils'
export default {
components: {
Breadcrumb,
Hamburger,
RestPassword,
Screenfull,
SizeSelect,
Search
},
data() {
return {
themeVal: getTheme() || 'auto',
themeSwitchVisible: false,
sysOriginActive: 'RICOM',
sysOriginPlatforms,
resetPasswordVisible: false
}
},
computed: {
...mapGetters([
'sidebar',
'avatar',
'device',
'dashboard',
'name'
]),
userAvatarColor() {
return 'background-color:' + extractColorByText(this.name) + ';'
},
firstNameStr() {
return this.name ? this.name.trim().charAt(0) : '?'
}
},
watch: {
dashbaord(newVal) {
this.switchTheme(newVal || 'auto')
}
},
methods: {
switchTheme(val) {
setTheme(val)
checkSwitchTheme()
},
themeChange(val) {
this.switchTheme(val)
updateTheme(val).then(res => {}).catch(er => {})
},
clickSysOrigin(item) {
this.sysOriginActive = item.label
},
toggleSideBar() {
this.$store.dispatch('app/toggleSideBar')
},
clickSwitchTheme() {
this.themeSwitchVisible = true
},
resetPwd() {
this.resetPasswordVisible = true
},
async logout() {
this.$store.dispatch('user/resetToken').then(() => {
// window.clearVuexAlong(true, true)
location.reload()
})
// await this.$store.dispatch('user/logout')
// this.$router.push(`/login?redirect=${this.$route.fullPath}`)
}
}
}
</script>
<style lang="scss" scoped>
.navbar {
height: 50px;
overflow: hidden;
position: relative;
background: linear-gradient(90deg, rgba(255, 250, 243, 0.96), rgba(255, 244, 228, 0.94));
border-bottom: 1px solid rgba(255, 147, 38, 0.12);
box-shadow: 0 10px 30px rgba(111, 67, 25, 0.08);
backdrop-filter: blur(12px);
.hamburger-container {
line-height: 46px;
height: 100%;
float: left;
cursor: pointer;
transition: background .3s;
-webkit-tap-highlight-color:transparent;
&:hover {
background: rgba(255, 147, 38, 0.08)
}
}
.breadcrumb-container {
float: left;
}
.right-menu {
float: right;
height: 100%;
line-height: 50px;
&:focus {
outline: none;
}
.right-menu-item {
display: inline-block;
padding: 0 8px;
height: 100%;
font-size: 18px;
color: #7A5C3F;
vertical-align: text-bottom;
&.hover-effect {
cursor: pointer;
transition: background .3s;
&:hover {
background: rgba(255, 147, 38, 0.08);
color: #FF9326;
}
}
}
.avatar-container {
margin-right: 30px;
.avatar-wrapper {
position: relative;
.user-avatar {
cursor: pointer;
width: 35px;
height: 35px;
border-radius: 100%;
color: #FFFFFF;
font-weight: bold;
box-shadow: 0 10px 24px rgba(255, 147, 38, 0.24);
}
.el-icon-caret-bottom {
cursor: pointer;
position: absolute;
right: -20px;
top: 20px;
font-size: 12px;
color: #A77441;
}
}
}
}
}
</style>

View File

@ -41,21 +41,15 @@ router.beforeEach(async(to, from, next) => {
} else {
try {
// get user info
await store.dispatch('user/getInfo').then(res => {
store.dispatch('user/getMenus').then(res => {
// store.getters.routers
// router.addRoutes(store.getters.routers)
if (res && res.length > 0) {
router.addRoutes(res)
}
})
// load dashboard
store.dispatch('user/getUserDashboard')
}).catch(err => {
console.error(err)
})
await store.dispatch('user/getInfo')
const grantRouters = await store.dispatch('user/getMenus')
if (grantRouters && grantRouters.length > 0) {
router.addRoutes(grantRouters)
}
// load dashboard
store.dispatch('user/getUserDashboard')
await store.dispatch('user/buttonPermissions')
next()
next({ ...to, replace: true })
} catch (error) {
// remove token and go to login page to re-login
await store.dispatch('user/resetToken')

View File

@ -5,19 +5,19 @@
<h1 class="text-jumbo text-ginormous">
Oops!
</h1>
<h2>你没有权限去该页面</h2>
<h6>如有不满请联系你领导</h6>
<h2>{{ $t('pages.page401.title') }}</h2>
<h6>{{ $t('pages.page401.subtitle') }}</h6>
<ul class="list-unstyled">
<li>或者你可以去:</li>
<li>{{ $t('pages.page401.suggestion') }}</li>
<li class="link-type">
<router-link to="/dashboard">
回首页
{{ $t('pages.page401.backHome') }}
</router-link>
</li>
<li><a href="#" @click.prevent="dialogVisible=true">点我看图</a></li>
<li><a href="#" @click.prevent="dialogVisible=true">{{ $t('pages.page401.viewImage') }}</a></li>
<li @click="back">
<a href="#">返回上一级</a>
<a href="#">{{ $t('pages.page401.backPrevious') }}</a>
</li>
</ul>
</el-col>
@ -25,7 +25,7 @@
<img :src="errGif" width="313" height="428" alt="Girl has dropped her ice cream.">
</el-col>
</el-row>
<el-dialog top="20px" :visible.sync="dialogVisible" title="随便看" :modal-append-to-body="true" :append-to-body="true">
<el-dialog top="20px" :visible.sync="dialogVisible" :title="$t('pages.page401.dialog.imageTitle')" :modal-append-to-body="true" :append-to-body="true">
<img :src="ewizardClap" class="pan-img">
</el-dialog>
</div>

View File

@ -1,7 +1,7 @@
<template>
<div class="activity-rank">
<el-drawer
:title="'活动排名奖品-'+ (rewardRank.awardStatus === true? '快照': '实时')"
:title="$t('pages.activityTemplateManage.dialog.rankReward', { mode: rewardRank.awardStatus === true ? $t('pages.activityTemplateManage.word.snapshot') : $t('pages.activityTemplateManage.word.realtime') })"
:visible="true"
:before-close="handleClose"
:close-on-press-escape="false"
@ -11,10 +11,10 @@
custom-class="drawer-auto-layout"
>
<div v-loading="rewardRankLoading" class="activity-rank-content">
<div class="blockquote blockquote-mini blockquote-info">按钮1</div>
<div class="blockquote blockquote-mini blockquote-info">{{ $t('pages.activityTemplateManage.section.button1') }}</div>
<div v-for="(item, index) in rewardRank.butOneRanks" :key="'but1_'+index" class="reward">
<div class="blockquote blockquote-mini">TOP{{ item.reward.rankRange }}
<el-tag v-if="item.rankUsers && item.rankUsers.length > 0" size="mini" :type="item.status === true? 'success' : 'danger'">{{ item.status === true ? '已发送' : '未发送' }}</el-tag>
<el-tag v-if="item.rankUsers && item.rankUsers.length > 0" size="mini" :type="item.status === true? 'success' : 'danger'">{{ item.status === true ? $t('pages.activityTemplateManage.word.sent') : $t('pages.activityTemplateManage.word.notSent') }}</el-tag>
</div>
<div v-for="(userItem,userIndex) in item.rankUsers" :key="userIndex">
<el-row>
@ -29,10 +29,10 @@
<props-row :list="item.reward.rewards" />
</div>
<div class="blockquote blockquote-mini blockquote-info">按钮2</div>
<div class="blockquote blockquote-mini blockquote-info">{{ $t('pages.activityTemplateManage.section.button2') }}</div>
<div v-for="(item, index) in rewardRank.butTwoRanks" :key="'but2_'+index" class="reward">
<div class="blockquote blockquote-mini">TOP{{ item.reward.rankRange }}
<el-tag v-if="item.rankUsers && item.rankUsers.length > 0" size="mini" :type="item.status === true? 'success' : 'danger'">{{ item.status === true ? '已发送' : '未发送' }}</el-tag>
<el-tag v-if="item.rankUsers && item.rankUsers.length > 0" size="mini" :type="item.status === true? 'success' : 'danger'">{{ item.status === true ? $t('pages.activityTemplateManage.word.sent') : $t('pages.activityTemplateManage.word.notSent') }}</el-tag>
</div>
<div v-for="(userItem,userIndex) in item.rankUsers" :key="userIndex">
<el-row>
@ -47,7 +47,7 @@
<props-row :list="item.reward.rewards" />
</div>
<div v-if="!rewardRank.awardStatus" class="drawer-footer">
<el-button :disabled="sendLoading" :loading="sendLoading" type="primary" @click="clickSend">发送</el-button>
<el-button :disabled="sendLoading" :loading="sendLoading" type="primary" @click="clickSend">{{ $t('pages.activityTemplateManage.action.send') }}</el-button>
</div>
</div>
</el-drawer>

View File

@ -1,7 +1,7 @@
<template>
<div class="activity-rank">
<el-drawer
title="活动排名"
:title="$t('pages.activityTemplateManage.dialog.rank')"
:visible="true"
:before-close="handleClose"
:close-on-press-escape="false"
@ -17,10 +17,10 @@
style="margin-bottom: 10px;"
:closable="false"
>
<p>活动排行榜数据只会保留30天, 活动结束后排行榜不可查看</p>
<p>结束后: 通过系统活动模块发送奖品,系统会保留获奖名单数据, 其他数据将在30天后逐渐清理</p>
<p>{{ $t('pages.activityTemplateManage.message.rankRetention') }}</p>
<p>{{ $t('pages.activityTemplateManage.message.rankRewardRetention') }}</p>
</el-alert>
<div class="blockquote blockquote-mini">按钮1 TOP20</div>
<div class="blockquote blockquote-mini">{{ $t('pages.activityTemplateManage.section.button1Top20') }}</div>
<div v-for="(item, index) in rank.butOneRanks" :key="'but1_'+index" class="reward">
<el-row>
<el-col :span="12">
@ -31,7 +31,7 @@
</el-col>
</el-row>
</div>
<div class="blockquote blockquote-mini">按钮2 TOP20</div>
<div class="blockquote blockquote-mini">{{ $t('pages.activityTemplateManage.section.button2Top20') }}</div>
<div v-for="(item, index) in rank.butTwoRanks" :key="'but1_'+index" class="reward">
<el-row>
<el-col :span="12">

View File

@ -12,21 +12,21 @@
>
<div v-loading="submitLoading">
<div class="drawer-form">
<div class="blockquote blockquote-mini">基础信息</div>
<div class="blockquote blockquote-mini">{{ $t('pages.activityTemplateManage.section.basicInfo') }}</div>
<el-alert
v-if="isClose"
:closable="false"
type="info"
show-icon
style="margin-bottom: 10px;"
title="活动结束后不可用在编辑"
:title="$t('pages.activityTemplateManage.message.endedNotEditable')"
/>
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
<el-form-item label="系统"> {{ sysOrigin }} </el-form-item>
<el-form-item v-loading="loadingTemplateValueLoading" label="模版" prop="templateId">
<el-form-item :label="$t('pages.activityTemplateManage.form.system')"> {{ sysOrigin }} </el-form-item>
<el-form-item v-loading="loadingTemplateValueLoading" :label="$t('pages.activityTemplateManage.form.template')" prop="templateId">
<el-select
v-model="form.templateId"
placeholder="模版"
:placeholder="$t('pages.activityTemplateManage.placeholder.template')"
style="width:100%;"
class="filter-item"
filterable
@ -34,15 +34,15 @@
<el-option v-for="(item, index) in templateValues" :key="index" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
<el-form-item label="时间" prop="startTime">
<el-form-item :label="$t('pages.activityTemplateManage.form.time')" prop="startTime">
<el-date-picker
v-model="rangeDate"
type="datetimerange"
value-format="timestamp"
style="width:100%;"
range-separator=""
start-placeholder="开始日期"
end-placeholder="结束日期"
:range-separator="$t('pages.activityTemplateManage.word.rangeSeparator')"
:start-placeholder="$t('pages.activityTemplateManage.placeholder.startDate')"
:end-placeholder="$t('pages.activityTemplateManage.placeholder.endDate')"
clearable
/>
<el-alert
@ -52,29 +52,29 @@
show-icon
size="mini"
style="margin-bottom: 10px;"
title="过期活动将不可编辑, 请检查活动时间"
:title="$t('pages.activityTemplateManage.message.expiredTimeWarning')"
/>
</el-form-item>
<el-form-item label="状态" prop="showcase">
<el-form-item :label="$t('pages.activityTemplateManage.form.status')" prop="showcase">
<el-select
v-model="form.showcase"
placeholder="状态"
:placeholder="$t('pages.activityTemplateManage.placeholder.status')"
style="width:100%;"
class="filter-item"
>
<el-option label="下架" :value="false" />
<el-option label="上架" :value="true" />
<el-option :label="$t('pages.activityTemplateManage.word.offShelf')" :value="false" />
<el-option :label="$t('pages.activityTemplateManage.word.onShelf')" :value="true" />
</el-select>
</el-form-item>
<el-form-item label="描述" prop="remark">
<el-form-item :label="$t('pages.activityTemplateManage.form.description')" prop="remark">
<el-input v-model.trim="form.remark" resize="none" show-word-limit type="textarea" :rows="6" maxlength="200" />
</el-form-item>
</el-form>
<div class="blockquote blockquote-mini">活动礼物</div>
<div class="blockquote blockquote-mini">{{ $t('pages.activityTemplateManage.section.activityGift') }}</div>
<div class="activity-gift flex-c">
<div v-for="(item, index) in form.gifts" :key="index" class="gift-item">
<div class="gift" @click="clickChangeGift(index)">
<div class="tips">点击更换</div>
<div class="tips">{{ $t('pages.activityTemplateManage.action.clickToChange') }}</div>
<img v-if="item.id" :src="item.giftPhoto">
<img v-else src="@/assets/default_img.png">
</div>
@ -83,22 +83,22 @@
<img v-if="item.type === 'DIAMOND'" width="20" height="20" src="@/assets/diamond_icon.png">
{{ item.giftCandy }}
</div>
<div v-else class="gift-amount">请选择</div>
<div v-else class="gift-amount">{{ $t('pages.activityTemplateManage.placeholder.select') }}</div>
</div>
</div>
<div class="blockquote blockquote-mini">活动排名奖品</div>
<div class="blockquote blockquote-mini">{{ $t('pages.activityTemplateManage.section.rankingRewards') }}</div>
<el-tabs v-model="activeName">
<el-tab-pane label="奖品-按钮1" name="but1">
<el-tab-pane :label="$t('pages.activityTemplateManage.section.prizeButton1')" name="but1">
<reward-edit :sys-origin="sysOrigin" :rewards-group="form.butOneRewards" @change="rewards=> form.butOneRewards = rewards" />
</el-tab-pane>
<el-tab-pane label="奖品-按钮2" name="but2">
<el-tab-pane :label="$t('pages.activityTemplateManage.section.prizeButton2')" name="but2">
<reward-edit :sys-origin="sysOrigin" :rewards-group="form.butTwoRewards" @change="rewards=> form.butTwoRewards = rewards" />
</el-tab-pane>
</el-tabs>
</div>
<div class="drawer-footer">
<el-button @click="handleClose()">取消</el-button>
<el-button v-if="!isClose" type="primary" @click="submitForm()">保存</el-button>
<el-button @click="handleClose()">{{ $t('pages.activityTemplateManage.action.cancel') }}</el-button>
<el-button v-if="!isClose" type="primary" @click="submitForm()">{{ $t('pages.activityTemplateManage.action.save') }}</el-button>
</div>
</div>
</el-drawer>
@ -131,7 +131,7 @@ export default {
}
},
data() {
const commonRules = [{ required: true, message: '必填字段', trigger: 'blur' }]
const commonRules = [{ required: true, message: this.$t('pages.activityTemplateManage.validation.requiredField'), trigger: 'blur' }]
return {
activeName: 'but1',
giftSelectVisible: false,
@ -168,7 +168,7 @@ export default {
return this.row && this.row.status === 2
},
textOptTitle() {
return this.row && this.row.id ? '修改' : '新增'
return this.row && this.row.id ? this.$t('pages.activityTemplateManage.action.edit') : this.$t('pages.activityTemplateManage.action.create')
},
...mapGetters(['permissionsSysOriginPlatforms'])
},
@ -285,7 +285,7 @@ export default {
submitForm() {
const that = this
if (this.isClose) {
that.$opsMessage.fail('活动已结束,不可在此编辑!')
that.$opsMessage.fail(that.$t('pages.activityTemplateManage.message.endedCannotEdit'))
return
}
that.$refs.form.validate(valid => {
@ -298,32 +298,32 @@ export default {
submitForm.gifts = null
const tips = []
if (!submitForm.giftIds || submitForm.giftIds.length < 3) {
tips.push('礼物数量不足3个')
tips.push(that.$t('pages.activityTemplateManage.message.giftQuantityInsufficient'))
}
if (!submitForm.butOneRewards || submitForm.butOneRewards.length <= 0) {
tips.push('[奖品-按钮1] 没有关联活动排名奖品')
tips.push(that.$t('pages.activityTemplateManage.message.noButtonReward', { button: that.$t('pages.activityTemplateManage.section.prizeButton1') }))
} else {
submitForm.butOneRewards = that.filterConvertRewards(submitForm.butOneRewards)
}
if (submitForm.butOneRewards.length !== that.form.butOneRewards.length) {
tips.push('[奖品-按钮1] 存在错误记录,系统将自动矫正排除')
tips.push(that.$t('pages.activityTemplateManage.message.invalidButtonReward', { button: that.$t('pages.activityTemplateManage.section.prizeButton1') }))
}
if (!submitForm.butTwoRewards || submitForm.butTwoRewards.length <= 0) {
tips.push('[奖品-按钮2] 没有关联活动排名奖品')
tips.push(that.$t('pages.activityTemplateManage.message.noButtonReward', { button: that.$t('pages.activityTemplateManage.section.prizeButton2') }))
} else {
submitForm.butTwoRewards = that.filterConvertRewards(submitForm.butTwoRewards)
}
if (submitForm.butTwoRewards.length !== that.form.butTwoRewards.length) {
tips.push('[奖品-按钮2] 存在错误记录,系统将自动矫正排除')
tips.push(that.$t('pages.activityTemplateManage.message.invalidButtonReward', { button: that.$t('pages.activityTemplateManage.section.prizeButton2') }))
}
if (tips.length > 0) {
tips.push('如果继续请点击确认按钮, 是否继续?')
that.$confirm(tips.join('<br>'), '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
tips.push(that.$t('pages.activityTemplateManage.message.continueConfirm'))
that.$confirm(tips.join('<br>'), that.$t('pages.activityTemplateManage.confirm.title'), {
confirmButtonText: that.$t('pages.activityTemplateManage.action.confirm'),
cancelButtonText: that.$t('pages.activityTemplateManage.action.cancel'),
type: 'warning',
dangerouslyUseHTMLString: true
}).then(() => {
@ -338,7 +338,7 @@ export default {
submit(submitForm) {
const that = this
if (!that.sysOrigin) {
that.$opsMessage.fail('归属系统不可为空!')
that.$opsMessage.fail(that.$t('pages.activityTemplateManage.message.sysOriginRequired'))
return
}
submitForm.sysOrigin = that.sysOrigin

View File

@ -6,17 +6,17 @@
style="margin-bottom: 10px;"
:closable="false"
>
<p>1.排行榜只会展示前20名, 后台处理奖品也只支持道TOP20</p>
<p>2.排名展示在输入的时候自行排好顺序, 系统不会整理排序</p>
<p>3.直接输入指定数字 123... 使用~将两个数字连接: 1~1011~20...</p>
<p>{{ $t('pages.activityTemplateManage.message.rewardRuleTop20') }}</p>
<p>{{ $t('pages.activityTemplateManage.message.rewardRuleSort') }}</p>
<p>{{ $t('pages.activityTemplateManage.message.rewardRuleRange') }}</p>
</el-alert>
<el-row v-for="(rootItem,rootIndex) in formRewards" :key="rootIndex">
<el-col :span="18">
<el-input v-model.trim="rootItem.rankRange" placeholder="排名范围" show-word-limit maxlength="100" @input="rankRangeInput" />
<el-input v-model.trim="rootItem.rankRange" :placeholder="$t('pages.activityTemplateManage.placeholder.rankRange')" show-word-limit maxlength="100" @input="rankRangeInput" />
</el-col>
<el-col :span="4" class="flex-c">
<el-button type="text" @click="clickAddAssociationReward(rootItem, rootIndex)">
<i class="el-icon-circle-plus" />奖品
<i class="el-icon-circle-plus" />{{ $t('pages.activityTemplateManage.action.reward') }}
</el-button>
</el-col>
<el-col :span="2" class="flex-c">
@ -26,7 +26,7 @@
<props-row :list="rootItem.rewards" />
</el-col>
</el-row>
<div class="add" @click="clickAdd"><i class="el-icon-circle-plus" />添加</div>
<div class="add" @click="clickAdd"><i class="el-icon-circle-plus" />{{ $t('pages.activityTemplateManage.action.add') }}</div>
<associated-resources
v-if="associatedResourcesVisable"

View File

@ -1,30 +1,30 @@
<template>
<div class="attr-editor">
<el-tabs v-model="activeName" size="mini">
<el-tab-pane v-if="attrForm" label="属性" name="attr">
<el-tab-pane v-if="attrForm" :label="$t('pages.activityTemplateManage.section.attributes')" name="attr">
<el-form ref="form" class="form" :model="attrForm" label-width="90px" size="mini">
<el-form-item v-if="attrForm.id" label="ID">
{{ attrForm.id }}
</el-form-item>
<el-form-item v-if="attrForm.label" label="组件名称">
<el-form-item v-if="attrForm.label" :label="$t('pages.activityTemplateManage.form.componentName')">
{{ attrForm.label }}
</el-form-item>
<el-form-item v-if="hasOwnProperty(attrForm,'copywriting')" :label="attrForm.copywriting.label">
<lang-text-editor :lang-array="attrForm.copywriting.i18n" :type="attrForm.copywriting.type" @text-change="langChange" />
</el-form-item>
<el-form-item v-if="hasOwnProperty(attrForm,'imgUrls')" label-width="0">
<el-form-item v-for="(item, index) in attrForm.imgUrls" :key="index" :label="item.label ? item.label : '图片'+index">
<el-form-item v-for="(item, index) in attrForm.imgUrls" :key="index" :label="item.label ? item.label : $t('pages.activityTemplateManage.form.imageIndex', { index })">
<el-input v-model="item.url" clearable @input="changeNotice" />
</el-form-item>
</el-form-item>
<!-- <el-form-item v-if="hasOwnProperty(attrForm,'rewards')" label-width="0">
<el-form-item label="奖励阶梯">
<el-form-item :label="$t('pages.activityTemplateManage.form.rewardTier')">
<reward-array-editor :rewards="attrForm.rewards" @reward-change="rewardChange" />
</el-form-item>
</el-form-item> -->
</el-form>
</el-tab-pane>
<el-tab-pane v-if="styleForm && !styleForm.hide" label="样式" name="style">
<el-tab-pane v-if="styleForm && !styleForm.hide" :label="$t('pages.activityTemplateManage.section.style')" name="style">
<el-alert
v-if="styleForm.tips"
:title="styleForm.tips"
@ -36,11 +36,11 @@
show-icon
/>
<el-form ref="form" class="form" :model="styleForm" label-width="90px" size="mini">
<div class="blockquote blockquote-mini">背景</div>
<el-form-item v-if="hasOwnProperty(styleForm,'background.img.url')" label="背景图">
<div class="blockquote blockquote-mini">{{ $t('pages.activityTemplateManage.section.background') }}</div>
<el-form-item v-if="hasOwnProperty(styleForm,'background.img.url')" :label="$t('pages.activityTemplateManage.form.backgroundImage')">
<el-input v-model="styleForm.background.img.url" clearable @input="changeNotice" />
</el-form-item>
<el-form-item v-if="hasOwnProperty(styleForm,'background.color')" label="背景颜色">
<el-form-item v-if="hasOwnProperty(styleForm,'background.color')" :label="$t('pages.activityTemplateManage.form.backgroundColor')">
<el-col :span="4" class="flex-c">
<el-color-picker v-model="styleForm.background.color.start" @change="changeNotice" />
</el-col>
@ -50,14 +50,14 @@
style="width: 100%;"
@change="changeNotice"
>
<el-option v-for="item in backgroundColorDirections" :key="item.value" :label="item.name" :value="item.value" />
<el-option v-for="item in translatedBackgroundColorDirections" :key="item.value" :label="item.name" :value="item.value" />
</el-select>
</el-col>
<el-col v-if="styleForm.background.color.direction !== 'NONE'" :span="4" class="flex-c">
<el-color-picker v-model="styleForm.background.color.end" @change="changeNotice" />
</el-col>
</el-form-item>
<el-form-item v-if="hasOwnProperty(styleForm,'background.img.repeat')" label="背景图重复">
<el-form-item v-if="hasOwnProperty(styleForm,'background.img.repeat')" :label="$t('pages.activityTemplateManage.form.backgroundRepeat')">
<el-select
v-model="styleForm.background.img.repeat"
style="width: 100%;"
@ -68,32 +68,32 @@
</el-select>
</el-form-item>
<el-form-item v-if="hasOwnProperty(styleForm,'background.img.size')" label="背景图大小">
<el-form-item v-if="hasOwnProperty(styleForm,'background.img.size')" :label="$t('pages.activityTemplateManage.form.backgroundSize')">
<el-input v-model="styleForm.background.img.size" clearable @input="changeNotice" />
</el-form-item>
<div class="blockquote blockquote-mini">字体</div>
<el-form-item v-if="hasOwnProperty(styleForm,'font.color')" label="颜色">
<div class="blockquote blockquote-mini">{{ $t('pages.activityTemplateManage.section.font') }}</div>
<el-form-item v-if="hasOwnProperty(styleForm,'font.color')" :label="$t('pages.activityTemplateManage.form.color')">
<el-color-picker v-model="styleForm.font.color" @change="changeNotice" />
</el-form-item>
<el-form-item v-if="hasOwnProperty(styleForm,'font.size')" label="大小">
<el-form-item v-if="hasOwnProperty(styleForm,'font.size')" :label="$t('pages.activityTemplateManage.form.size')">
<el-input v-model="styleForm.font.size" clearable @input="changeNotice" />
</el-form-item>
<el-form-item v-if="hasOwnProperty(styleForm,'font.weight')" label="粗细">
<el-form-item v-if="hasOwnProperty(styleForm,'font.weight')" :label="$t('pages.activityTemplateManage.form.weight')">
<el-input v-model="styleForm.font.weight" clearable @input="changeNotice" />
</el-form-item>
<div class="blockquote blockquote-mini">边框</div>
<el-form-item v-if="hasOwnProperty(styleForm,'border.width')" label="边框宽">
<div class="blockquote blockquote-mini">{{ $t('pages.activityTemplateManage.section.border') }}</div>
<el-form-item v-if="hasOwnProperty(styleForm,'border.width')" :label="$t('pages.activityTemplateManage.form.borderWidth')">
<el-input v-model="styleForm.border.width" clearable @input="changeNotice" />
</el-form-item>
<el-form-item v-if="hasOwnProperty(styleForm,'border.style')" label="边框样式">
<el-form-item v-if="hasOwnProperty(styleForm,'border.style')" :label="$t('pages.activityTemplateManage.form.borderStyle')">
<el-input v-model="styleForm.border.style" clearable @input="changeNotice" />
</el-form-item>
<el-form-item v-if="hasOwnProperty(styleForm,'border.color')" label="边框颜色">
<el-form-item v-if="hasOwnProperty(styleForm,'border.color')" :label="$t('pages.activityTemplateManage.form.borderColor')">
<el-color-picker v-model="styleForm.border.color" @change="changeNotice" />
</el-form-item>
<el-form-item v-if="hasOwnProperty(styleForm,'border.radius')" label="边框圆角">
<el-form-item v-if="hasOwnProperty(styleForm,'border.radius')" :label="$t('pages.activityTemplateManage.form.borderRadius')">
<el-input v-model="styleForm.border.radius" clearable @input="changeNotice" />
</el-form-item>
</el-form>
@ -121,13 +121,13 @@ export default {
return {
activeName: 'attr',
backgroundColorDirections: [
{ name: '无', value: 'NONE' },
{ name: '从左到右渐变', value: 'to right' },
{ name: '从右到左渐变', value: 'to left' },
{ name: '从下到上渐变', value: 'to top' },
{ name: '从上到下渐变', value: 'to bottom' },
{ name: '从左对角渐变', value: 'to bottom right' },
{ name: '从右对角渐变', value: 'to bottom left' }
{ nameKey: 'none', value: 'NONE' },
{ nameKey: 'toRight', value: 'to right' },
{ nameKey: 'toLeft', value: 'to left' },
{ nameKey: 'toTop', value: 'to top' },
{ nameKey: 'toBottom', value: 'to bottom' },
{ nameKey: 'toBottomRight', value: 'to bottom right' },
{ nameKey: 'toBottomLeft', value: 'to bottom left' }
],
backgroundImgRepeats: [
{ name: 'repeat', value: 'repeat' },
@ -153,6 +153,14 @@ export default {
styleForm: {}
}
},
computed: {
translatedBackgroundColorDirections() {
return this.backgroundColorDirections.map(item => ({
...item,
name: this.$t(`pages.activityTemplateManage.gradient.${item.nameKey}`)
}))
}
},
watch: {
form: {
handler(newVal) {

View File

@ -8,8 +8,8 @@
>
<el-option v-for="item in langs" :key="item.value" :label="item.name" :value="item.value" />
</el-select>
<el-button size="mini" type="primary" :loading="formSubmitLoading" :disabled="formSubmitLoading" @click="clickSubmit">保存</el-button>
<el-button size="mini" type="danger" @click="clickExit">退出</el-button>
<el-button size="mini" type="primary" :loading="formSubmitLoading" :disabled="formSubmitLoading" @click="clickSubmit">{{ $t('pages.activityTemplateManage.action.save') }}</el-button>
<el-button size="mini" type="danger" @click="clickExit">{{ $t('pages.activityTemplateManage.action.exit') }}</el-button>
</div>
<el-row style="height: 100%;">
<el-col class="activity-layout flex-c" :md="14" :xs="24">
@ -84,7 +84,7 @@ export default {
created() {
const that = this
if (that.mobile) {
that.$message('建议您使用PC电脑进行编辑!')
that.$message(that.$t('pages.activityTemplateManage.message.pcRecommended'))
}
},
mounted() {

View File

@ -18,7 +18,7 @@
<div v-if="langList.length !== langs.length" class="add-but">
<el-dropdown size="mini">
<span class="el-dropdown-link">
添加语言<i class="el-icon-arrow-down el-icon--right" />
{{ $t('pages.activityTemplateManage.action.addLanguage') }}<i class="el-icon-arrow-down el-icon--right" />
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item v-for="(item, index) in langs" :key="index" :disabled="existsLang(item.value)" @click.native="clickAddLang(item)">{{ item.name }}</el-dropdown-item>

View File

@ -72,7 +72,7 @@
<span v-else>{{ index }}</span>
</div>
<img ref="ACTIVITY_BODY_RING_AVATAR" class="avatar" src="@/assets/default_avatar.png">
<div ref="ACTIVITY_BODY_RING_NICKNAME" :style="renderStyle('ACTIVITY_BODY_RING_NICKNAME')" class="nickanme nowrap-ellipsis">用户昵称-1</div>
<div ref="ACTIVITY_BODY_RING_NICKNAME" :style="renderStyle('ACTIVITY_BODY_RING_NICKNAME')" class="nickanme nowrap-ellipsis">{{ $t('pages.activityTemplateManage.preview.nicknameRank') }}</div>
<div ref="ACTIVITY_BODY_RING_AMOUNT" :style="renderStyle('ACTIVITY_BODY_RING_AMOUNT')" class="golds nowrap-ellipsis">100.1K</div>
<img v-for="(imgItem, imgIndex) in getAttrImgUrls('ACTIVITY_BODY_RING_ICON')" ref="ACTIVITY_BODY_RING_ICON" :key="imgIndex" class="gift_pack" :src="imgItem.url">
</div>
@ -97,7 +97,7 @@
</div>
<div ref="ACTIVITY_MY" :style="renderStyle('ACTIVITY_MY')" class="flex-user flex-l">
<img ref="ACTIVITY_MY_AVATAR" class="avatar" src="@/assets/default_avatar.png">
<div ref="ACTIVITY_MY_NICKNAME" :style="renderStyle('ACTIVITY_MY_NICKNAME')" class="nickname nowrap-ellipsis">用户昵称</div>
<div ref="ACTIVITY_MY_NICKNAME" :style="renderStyle('ACTIVITY_MY_NICKNAME')" class="nickname nowrap-ellipsis">{{ $t('pages.activityTemplateManage.preview.nickname') }}</div>
<div ref="ACTIVITY_MY_TEXT" :style="renderStyle('ACTIVITY_MY_TEXT')" class="golds">
<div class="nowrap-ellipsis">{{ getLangText('ACTIVITY_BUT_1') }}: 100K</div>
<div class="nowrap-ellipsis">{{ getLangText('ACTIVITY_BUT_2') }}: 100k</div>

View File

@ -80,23 +80,23 @@ export default {
return {
propsText: {
'zh': {
'GIFT': '礼物',
'DIAMOND': '钻石',
'GOLD': '金币',
'ROOM_BADGE': '房间徽章',
'BADGE': '个人徽章',
'SPECIAL_ID': '靓号',
'NOBLE_VIP': '贵族VIP',
'AVATAR_FRAME': '头像框',
'RIDE': '座驾',
'THEME': '房间背景',
'EMOJI': '表情包',
'CHAT_BUBBLE': '聊天气泡',
'GAME_COUPON': '游戏券',
'FRAGMENTS': '碎片',
'FLOAT_PICTURE': '个人飘窗',
'LAYOUT': '房间装扮',
'CUSTOMIZE': '自定义'
'GIFT': 'pages.activityTemplateManage.propsType.GIFT',
'DIAMOND': 'pages.activityTemplateManage.propsType.DIAMOND',
'GOLD': 'pages.activityTemplateManage.propsType.GOLD',
'ROOM_BADGE': 'pages.activityTemplateManage.propsType.ROOM_BADGE',
'BADGE': 'pages.activityTemplateManage.propsType.BADGE',
'SPECIAL_ID': 'pages.activityTemplateManage.propsType.SPECIAL_ID',
'NOBLE_VIP': 'pages.activityTemplateManage.propsType.NOBLE_VIP',
'AVATAR_FRAME': 'pages.activityTemplateManage.propsType.AVATAR_FRAME',
'RIDE': 'pages.activityTemplateManage.propsType.RIDE',
'THEME': 'pages.activityTemplateManage.propsType.THEME',
'EMOJI': 'pages.activityTemplateManage.propsType.EMOJI',
'CHAT_BUBBLE': 'pages.activityTemplateManage.propsType.CHAT_BUBBLE',
'GAME_COUPON': 'pages.activityTemplateManage.propsType.GAME_COUPON',
'FRAGMENTS': 'pages.activityTemplateManage.propsType.FRAGMENTS',
'FLOAT_PICTURE': 'pages.activityTemplateManage.propsType.FLOAT_PICTURE',
'LAYOUT': 'pages.activityTemplateManage.propsType.LAYOUT',
'CUSTOMIZE': 'pages.activityTemplateManage.propsType.CUSTOMIZE'
},
en: {
'GIFT': 'Gift',
@ -275,9 +275,9 @@ export default {
return item.remark.indexOf('#') >= 0 ? item.remark.trim().split('#')[0] : item.remark
}
const text = this.propsText[this.lang]
const name = text[item.detailType]
const name = this.getPropsTypeText(text, item.detailType)
if (!name) {
const nameRoot = text[item.type]
const nameRoot = this.getPropsTypeText(text, item.type)
return nameRoot || '?'
}
return name
@ -295,10 +295,20 @@ export default {
const val = item[format.field]
if (format.forever === true && val <= 0) {
return '永久'
return this.lang === 'zh' ? this.$t('pages.activityTemplateManage.word.permanent', 'zh') : this.$t('pages.activityTemplateManage.word.permanent')
}
return `${format.prefix}${val}${format.suffix}`
},
getPropsTypeText(text, type) {
if (!text || !type || !text[type]) {
return ''
}
const value = text[type]
if (String(value).startsWith('pages.')) {
return this.$t(value, this.lang)
}
return value
}
}

View File

@ -12,7 +12,7 @@
</div>
<div v-if="list.length < 10" class="add-but">
<el-button type="text" size="mini" @click.native="clickAdd(item)">
添加<i class="el-icon-circle-plus" />
{{ $t('pages.activityTemplateManage.action.add') }}<i class="el-icon-circle-plus" />
</el-button>
</div>
</div>

View File

@ -6,7 +6,7 @@
v-show="!templateEditorVisible"
v-model.trim="listQuery.id"
v-number
placeholder="模版ID"
:placeholder="$t('pages.activityTemplateManage.filter.templateId')"
style="width: 200px;"
class="filter-item"
/>
@ -16,7 +16,7 @@
icon="el-icon-search"
@click="handleSearch"
>
搜索
{{ $t('pages.activityTemplateManage.action.search') }}
</el-button>
<el-button
class="filter-item"
@ -24,7 +24,7 @@
icon="el-icon-edit"
@click="clickCreateTemplate"
>
新增
{{ $t('pages.activityTemplateManage.action.create') }}
</el-button>
</div>
<el-table
@ -35,35 +35,35 @@
highlight-current-row
>
<el-table-column label="ID" prop="template.id" align="center" min-width="200" />
<el-table-column label="名称" prop="template.name" align="center" min-width="200" />
<el-table-column label="创建时间" prop="template.createTime" align="center" min-width="200">
<el-table-column :label="$t('pages.activityTemplateManage.table.name')" prop="template.name" align="center" min-width="200" />
<el-table-column :label="$t('pages.activityTemplateManage.table.createdAt')" prop="template.createTime" align="center" min-width="200">
<template slot-scope="scope">
{{ scope.row.template.createTime | dateFormat }}
</template>
</el-table-column>
<el-table-column label="修改时间" prop="template.updateTime" align="center" min-width="200">
<el-table-column :label="$t('pages.activityTemplateManage.table.updatedAt')" prop="template.updateTime" align="center" min-width="200">
<template slot-scope="scope">
{{ scope.row.template.updateTime | dateFormat }}
</template>
</el-table-column>
<el-table-column label="创建人" prop="createUserName" align="center" min-width="150" />
<el-table-column label="修改人" prop="updateUserName" align="center" min-width="150" />
<el-table-column fixed="right" label="操作" align="center" width="80">
<el-table-column :label="$t('pages.activityTemplateManage.table.createdBy')" prop="createUserName" align="center" min-width="150" />
<el-table-column :label="$t('pages.activityTemplateManage.table.updatedBy')" prop="updateUserName" align="center" min-width="150" />
<el-table-column fixed="right" :label="$t('pages.activityTemplateManage.table.actions')" align="center" width="80">
<template slot-scope="scope">
<el-dropdown>
<span class="el-dropdown-link">
<i class="el-icon-more" />
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item @click.native="clickEditName(scope.row)">编辑名称</el-dropdown-item>
<el-dropdown-item @click.native="clickEditTemplate(scope.row)">编辑模版</el-dropdown-item>
<el-dropdown-item @click.native="clickCopyTemplate(scope.row)">复制模版</el-dropdown-item>
<el-dropdown-item @click.native="clickEditName(scope.row)">{{ $t('pages.activityTemplateManage.action.editName') }}</el-dropdown-item>
<el-dropdown-item @click.native="clickEditTemplate(scope.row)">{{ $t('pages.activityTemplateManage.action.editTemplate') }}</el-dropdown-item>
<el-dropdown-item @click.native="clickCopyTemplate(scope.row)">{{ $t('pages.activityTemplateManage.action.copyTemplate') }}</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</template>
</el-table-column>
</el-table>
<div v-if="list.length >= 20" style="text-align: center; margin: 20px;">没有做分页,模版数量>20个后请联系管理员添加分页功能!!!</div>
<div v-if="list.length >= 20" style="text-align: center; margin: 20px;">{{ $t('pages.activityTemplateManage.message.noPaginationTip') }}</div>
</div>
<template-editor
@ -132,9 +132,9 @@ export default {
this.renderData(true)
},
clickCreateTemplate() {
this.$prompt('请输入模版名称', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
this.$prompt(this.$t('pages.activityTemplateManage.prompt.templateName'), this.$t('pages.activityTemplateManage.confirm.title'), {
confirmButtonText: this.$t('pages.activityTemplateManage.action.confirm'),
cancelButtonText: this.$t('pages.activityTemplateManage.action.cancel'),
closeOnClickModal: false,
inputValidator: (val) => {
if (!val) {
@ -142,7 +142,7 @@ export default {
}
return val.length <= 200
},
inputErrorMessage: '模版名称必须<=200个字符串'
inputErrorMessage: this.$t('pages.activityTemplateManage.validation.templateNameMax')
}).then(({ value }) => {
const that = this
addTemplate({
@ -157,9 +157,9 @@ export default {
})
},
clickEditName(row) {
this.$prompt('请输入新的模版名称', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
this.$prompt(this.$t('pages.activityTemplateManage.prompt.newTemplateName'), this.$t('pages.activityTemplateManage.confirm.title'), {
confirmButtonText: this.$t('pages.activityTemplateManage.action.confirm'),
cancelButtonText: this.$t('pages.activityTemplateManage.action.cancel'),
closeOnClickModal: false,
inputValidator: (val) => {
if (!val) {
@ -167,7 +167,7 @@ export default {
}
return val.length <= 200
},
inputErrorMessage: '模版名称必须<=200个字符串'
inputErrorMessage: this.$t('pages.activityTemplateManage.validation.templateNameMax')
}).then(({ value }) => {
const that = this
updateTemplateName(row.template.id, value).then(res => {
@ -184,9 +184,9 @@ export default {
this.templateEditorVisible = true
},
clickCopyTemplate(row) {
this.$prompt('请输入模版名称', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
this.$prompt(this.$t('pages.activityTemplateManage.prompt.templateName'), this.$t('pages.activityTemplateManage.confirm.title'), {
confirmButtonText: this.$t('pages.activityTemplateManage.action.confirm'),
cancelButtonText: this.$t('pages.activityTemplateManage.action.cancel'),
closeOnClickModal: false,
inputValidator: (val) => {
if (!val) {
@ -194,7 +194,7 @@ export default {
}
return val.length <= 200
},
inputErrorMessage: '模版名称必须<=200个字符串'
inputErrorMessage: this.$t('pages.activityTemplateManage.validation.templateNameMax')
}).then(({ value }) => {
const that = this
addTemplate({

View File

@ -1,7 +1,7 @@
<template>
<div class="select-prize">
<el-dialog
title="选择奖品"
:title="$t('pages.lotteryConfig.selectPrize.title')"
:visible="true"
:modal-append-to-body="true"
:append-to-body="true"
@ -11,25 +11,25 @@
top="20px"
>
<div class="form-edit">
<el-form ref="form" :model="form" :rules="formRules" label-width="110px" style="margin-right:50px;">
<el-form-item label="选择类型" prop="propsType">
<el-select v-model="form.propsType" :disabled="listLoading" placeholder="请选择" style="width:100%;" @change="changePropsType">
<el-form ref="form" class="i18n-form" :model="form" :rules="formRules" label-width="120px" style="margin-right:50px;">
<el-form-item :label="$t('pages.lotteryConfig.selectPrize.form.type')" prop="propsType">
<el-select v-model="form.propsType" :disabled="listLoading" :placeholder="$t('pages.lotteryConfig.placeholder.select')" style="width:100%;" @change="changePropsType">
<el-option-group
v-for="group in propsTypeSelectGroup"
:key="group.label"
:label="group.label"
:key="group.labelKey"
:label="$t('pages.lotteryConfig.selectPrize.group.' + group.labelKey)"
>
<el-option
v-for="item in group.options"
:key="item.value"
:label="item.label"
:label="$t('pages.lotteryConfig.selectPrize.typeOptions.' + item.labelKey)"
:value="item.value"
/>
</el-option-group>
</el-select>
</el-form-item>
<el-form-item v-if="!isInput(form.propsType)" label="选择类型" prop="content">
<el-form-item v-if="!isInput(form.propsType)" :label="$t('pages.lotteryConfig.selectPrize.form.type')" prop="content">
<div v-if="selectProps.cover" class="payer-source">
<div class="paler-icon">
<svgaplayer
@ -48,15 +48,15 @@
/>
</el-form-item>
<el-form-item label="数量" prop="quantity">
<el-input v-model="form.quantity" v-number placeholder="数量" />
<el-form-item :label="$t('pages.lotteryConfig.selectPrize.form.quantity')" prop="quantity">
<el-input v-model="form.quantity" v-number :placeholder="$t('pages.lotteryConfig.placeholder.quantity')" />
</el-form-item>
</el-form>
</div>
<div slot="footer">
<el-button @click="handleClose()">取消</el-button>
<el-button type="primary" @click="submitForm()">确定</el-button>
<el-button @click="handleClose()">{{ $t('pages.lotteryConfig.action.cancel') }}</el-button>
<el-button type="primary" @click="submitForm()">{{ $t('pages.lotteryConfig.action.confirm') }}</el-button>
</div>
</el-dialog>
@ -80,9 +80,6 @@ export default {
}
},
data() {
const commonRules = [
{ required: true, message: '必填字段不可为空', trigger: 'blur' }
]
return {
visiblePopover: false,
selectPopoverProperty: {
@ -97,46 +94,41 @@ export default {
quantity: '',
cover: ''
},
formRules: {
propsType: commonRules,
content: commonRules,
quantity: commonRules
},
list: [],
listLoading: false,
selectProps: {},
propsTypeSelectGroup: [
{
label: '常用',
labelKey: 'common',
options: [
{ value: 'GIFT', label: '礼物', type: 'GIFT' }
{ value: 'GIFT', labelKey: 'gift', type: 'GIFT' }
]
},
{
label: '道具',
labelKey: 'props',
options: [
{ value: 'AVATAR_FRAME', label: '头像框', type: 'PROPS' },
{ value: 'RIDE', label: '座驾', type: 'PROPS' },
{ value: 'NOBLE_VIP', label: '贵族', type: 'PROPS' },
{ value: 'THEME', label: '房间背景', type: 'PROPS' },
{ value: 'EMOJI', label: '表情包', type: 'EMOJI' }
{ value: 'AVATAR_FRAME', labelKey: 'avatarFrame', type: 'PROPS' },
{ value: 'RIDE', labelKey: 'ride', type: 'PROPS' },
{ value: 'NOBLE_VIP', labelKey: 'nobleVip', type: 'PROPS' },
{ value: 'THEME', labelKey: 'roomBackground', type: 'PROPS' },
{ value: 'EMOJI', labelKey: 'emoji', type: 'EMOJI' }
]
},
{
label: '徽章',
labelKey: 'badge',
options: [
{ value: 'BADGE', label: '用户徽章', type: 'BADGE' },
{ value: 'ROOM_BADGE', label: '房间徽章', type: 'BADGE' }
{ value: 'BADGE', labelKey: 'userBadge', type: 'BADGE' },
{ value: 'ROOM_BADGE', labelKey: 'roomBadge', type: 'BADGE' }
]
},
{
label: '货币',
labelKey: 'currency',
options: [
{ value: 'GOLD', label: '金币', type: 'GOLD' },
{ value: 'DIAMOND', label: '钻石', type: 'DIAMOND' }
{ value: 'GOLD', labelKey: 'gold', type: 'GOLD' },
{ value: 'DIAMOND', labelKey: 'diamond', type: 'DIAMOND' }
]
}
],
@ -177,6 +169,18 @@ export default {
selectPropsTypeVal: {}
}
},
computed: {
formRules() {
const commonRules = [
{ required: true, message: this.$t('pages.lotteryConfig.validation.requiredField'), trigger: 'blur' }
]
return {
propsType: commonRules,
content: commonRules,
quantity: commonRules
}
}
},
methods: {
selectPropsSourcePopover(item) {
this.selectProps = item

View File

@ -3,7 +3,7 @@
<div class="filter-container">
<el-select
v-model="listQuery.type"
placeholder="类型"
:placeholder="$t('pages.gameLudo.placeholder.type')"
style="width: 120px"
class="filter-item"
@change="changeTypes"
@ -17,7 +17,7 @@
</el-select>
<el-select
v-model="listQuery.sysOrigin"
placeholder="系统"
:placeholder="$t('pages.gameLudo.placeholder.system')"
style="width: 120px"
class="filter-item"
@change="handleSearch"
@ -50,7 +50,7 @@
:disabled="searchDisabled"
@click="handleSearch"
>
搜索
{{ $t('pages.gameLudo.action.search') }}
</el-button>
</div>
<el-table
@ -60,7 +60,7 @@
fit
highlight-current-row
>
<el-table-column prop="sysOrigin" label="系统" align="center" width="100">
<el-table-column prop="sysOrigin" :label="$t('pages.gameLudo.table.system')" align="center" width="100">
<template slot-scope="scope">
<sys-origin-icon
:icon="scope.row.history.sysOrigin"
@ -68,7 +68,7 @@
/>
</template>
</el-table-column>
<el-table-column label="房间信息" align="center">
<el-table-column :label="$t('pages.gameLudo.table.roomInfo')" align="center">
<template slot-scope="scope">
<div v-if="scope.row.roomProfile">
<avatar :url="scope.row.roomProfile.roomCover" />
@ -86,32 +86,32 @@
<div v-else>{{ scope.row.history.roomId }}</div>
</template>
</el-table-column>
<el-table-column label="状态" align="center" min-width="200">
<el-table-column :label="$t('pages.gameLudo.table.status')" align="center" min-width="200">
<template slot-scope="scope">
{{ gameStatusName[scope.row.history.gameStatus] || "-" }}
{{ gameEventName[scope.row.history.gameStatusEvent] || "-" }}
</template>
</el-table-column>
<el-table-column label="金额(加入/奖金)" align="center" min-width="150">
<el-table-column :label="$t('pages.gameLudo.table.amount')" align="center" min-width="150">
<template slot-scope="scope">
{{ scope.row.history.joinAmount || "-" }}/{{
scope.row.history.bonus || "-"
}}
</template>
</el-table-column>
<el-table-column label="情况" align="center" width="200">
<el-table-column :label="$t('pages.gameLudo.table.situation')" align="center" width="200">
<template slot-scope="scope">
<el-button
type="text"
style="cursor: pointer;"
@click="clickGamePlayer(scope.row)"
>玩家:{{ scope.row.history.playerSize || "-" }}</el-button>
锁定座位:{{ scope.row.history.lockSeat || "-" }}
>{{ $t('pages.gameLudo.table.playerCount', { count: scope.row.history.playerSize || "-" }) }}</el-button>
{{ $t('pages.gameLudo.table.lockedSeat', { count: scope.row.history.lockSeat || "-" }) }}
</template>
</el-table-column>
<el-table-column
prop="history.victoryColor"
label="胜利方"
:label="$t('pages.gameLudo.table.victorySide')"
width="200"
align="center"
>
@ -130,7 +130,7 @@
</el-table-column>
<el-table-column
prop="history.createTime"
label="创建时间"
:label="$t('pages.gameLudo.table.createdAt')"
width="200"
align="center"
>
@ -141,7 +141,7 @@
<el-table-column
v-if="listQuery.type === 'HISTORY'"
prop="history.updateTime"
label="修改时间"
:label="$t('pages.gameLudo.table.updatedAt')"
width="200"
align="center"
>
@ -151,7 +151,7 @@
</el-table-column>
<el-table-column
prop="history.victoryColor"
label="操作"
:label="$t('pages.gameLudo.table.actions')"
width="200"
align="center"
>
@ -160,7 +160,7 @@
type="text"
:disabled="scope.row.history.gameStatus === 'END'"
@click="clickDismiss(scope.row)"
>解散</el-button>
>{{ $t('pages.gameLudo.action.dismiss') }}</el-button>
</template>
</el-table-column>
</el-table>
@ -170,12 +170,12 @@
type="text"
:loading="listLoading"
@click="clickLoadMore"
>点击加载更多</el-button>
<div v-else-if="list.length > 0">已经加载全部</div>
>{{ $t('pages.gameLudo.action.loadMore') }}</el-button>
<div v-else-if="list.length > 0">{{ $t('pages.gameLudo.state.allLoaded') }}</div>
</div>
<el-dialog
title="游戏玩家"
:title="$t('pages.gameLudo.dialog.players')"
:visible="visibleGamePlayers"
:modal-append-to-body="true"
:append-to-body="true"
@ -191,7 +191,7 @@
highlight-current-row
max-height="350px"
>
<el-table-column label="头像" align="center" width="150">
<el-table-column :label="$t('pages.gameLudo.table.avatar')" align="center" width="150">
<template slot-scope="scope">
<avatar
:url="scope.row.userProfile.userAvatar"
@ -209,7 +209,7 @@
</div>
</template>
</el-table-column>
<el-table-column label="昵称" align="center">
<el-table-column :label="$t('pages.gameLudo.table.nickname')" align="center">
<template slot-scope="scope">
<div
style="width:100%;overflow: hidden;text-overflow:ellipsis;white-space: nowrap;"
@ -222,7 +222,7 @@
</div>
</template>
</el-table-column>
<el-table-column label="棋盘" align="center">
<el-table-column :label="$t('pages.gameLudo.table.board')" align="center">
<template slot-scope="scope">
<div
:style="{
@ -238,22 +238,22 @@
</el-table-column>
<el-table-column
prop="players.rankingIndex"
label="名次"
:label="$t('pages.gameLudo.table.ranking')"
align="center"
/>
<el-table-column prop="players.initiator" label="房主" align="center">
<el-table-column prop="players.initiator" :label="$t('pages.gameLudo.table.roomOwner')" align="center">
<template slot-scope="scope">
{{ scope.row.players.initiator ? "是" : "否" }}
{{ scope.row.players.initiator ? $t('pages.userBaseInfo.words.yes') : $t('pages.userBaseInfo.words.no') }}
</template>
</el-table-column>
<el-table-column
prop="players.consumeJoinAmount"
label="消费"
:label="$t('pages.gameLudo.table.consume')"
align="center"
/>
<el-table-column
prop="players.createTime"
label="创建时间"
:label="$t('pages.gameLudo.table.createdAt')"
align="center"
>
<template slot-scope="scope">
@ -287,10 +287,6 @@ export default {
data() {
return {
userDeatilsDrawerVisible: false,
gameDataTypes: [
{ label: '运行', value: 'RUN' },
{ label: '历史', value: 'HISTORY' }
],
searchDisabled: false,
list: [],
listQuery: {
@ -301,19 +297,6 @@ export default {
type: 'RUN'
},
listLoading: true,
gameStatusName: {
WAIT: '等待开始',
PROGRESS: '进行中',
END: '已结束'
},
gameEventName: {
NONE: '无',
TIMEOUT_DISBAND: '游戏未开始超时,解散',
DISCONNECTED_DISBAND: '游戏已开始,全部掉线后,解散',
OTHER__DISBAND: '其他,不在预计状态内',
NORMAL_SETTLEMENT: '正常结算',
BACK_DISBAND: '后台解散'
},
roomDeatilsDrawerVisible: false,
queryRoomId: '',
listNoteData: false,
@ -323,7 +306,30 @@ export default {
}
},
computed: {
...mapGetters(['permissionsSysOriginPlatforms'])
...mapGetters(['permissionsSysOriginPlatforms']),
gameDataTypes() {
return [
{ label: this.$t('pages.gameLudo.type.run'), value: 'RUN' },
{ label: this.$t('pages.gameLudo.type.history'), value: 'HISTORY' }
]
},
gameStatusName() {
return {
WAIT: this.$t('pages.gameLudo.status.wait'),
PROGRESS: this.$t('pages.gameLudo.status.progress'),
END: this.$t('pages.gameLudo.status.end')
}
},
gameEventName() {
return {
NONE: this.$t('pages.gameLudo.event.none'),
TIMEOUT_DISBAND: this.$t('pages.gameLudo.event.timeoutDisband'),
DISCONNECTED_DISBAND: this.$t('pages.gameLudo.event.disconnectedDisband'),
OTHER__DISBAND: this.$t('pages.gameLudo.event.otherDisband'),
NORMAL_SETTLEMENT: this.$t('pages.gameLudo.event.normalSettlement'),
BACK_DISBAND: this.$t('pages.gameLudo.event.backDisband')
}
}
},
created() {
const that = this
@ -386,9 +392,9 @@ export default {
return
}
that
.$confirm('后台解散游戏将没有胜利者, 是否确定解散?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
.$confirm(this.$t('pages.gameLudo.confirm.dismiss'), this.$t('pages.gameLudo.confirm.title'), {
confirmButtonText: this.$t('pages.gameLudo.confirm.confirm'),
cancelButtonText: this.$t('pages.gameLudo.confirm.cancel'),
type: 'warning'
})
.then(() => {

View File

@ -1,7 +1,7 @@
<template>
<div class="app-container">
<!-- 搜索栏 -->
<div class="filter-container">
<div :key="language" class="filter-container">
<!-- 归属系统 -->
<el-select
v-model="listQuery.sysOrigin"
@ -677,7 +677,7 @@ export default {
};
},
computed: {
...mapGetters(["permissionsSysOriginPlatforms", "buttonPermissions"]),
...mapGetters(["permissionsSysOriginPlatforms", "buttonPermissions", "language"]),
localizedRoomEvents() {
const roomEventNameMap = {
AVAILABLE: "normal",

View File

@ -1,6 +1,6 @@
<template>
<el-dialog
title="房间活跃指标"
:title="$t('pages.roomProfile.dialog.activeMetrics')"
:visible="true"
:modal-append-to-body="true"
:append-to-body="true"
@ -17,9 +17,9 @@
value-format="timestamp"
type="datetimerange"
:picker-options="pickerOptions"
range-separator=""
start-placeholder="创建日期开始"
end-placeholder="创建日期结束"
:range-separator="$t('pages.roomProfile.filter.rangeSeparator')"
:start-placeholder="$t('pages.roomProfile.filter.createDateStart')"
:end-placeholder="$t('pages.roomProfile.filter.createDateEnd')"
:clearable="false"
:editable="false"
@change="rangeDateChange"
@ -40,6 +40,7 @@ import { listRoomActiveIndex } from '@/api/statistics'
import { formatDate } from '@/utils'
import { pickerOptions } from '@/constant/el-const'
import { recentDate } from '@/utils'
import { mapGetters } from 'vuex'
export default {
props: {
@ -62,6 +63,9 @@ export default {
rangeDate: []
}
},
computed: {
...mapGetters(['language'])
},
watch: {
roomId: {
immediate: true,
@ -71,6 +75,9 @@ export default {
this.initRangeDate()
this.renderData()
}
},
language() {
this.renderCharts()
}
},
mounted() {
@ -120,7 +127,13 @@ export default {
},
renderCharts() {
const that = this
if (!that.charts) {
return
}
const thatDayData = that.processChartsData()
const onlineUsers = that.$t('pages.roomProfile.chart.onlineUsers')
const giftUsers = that.$t('pages.roomProfile.chart.giftUsers')
const giftValue = that.$t('pages.roomProfile.chart.giftValue')
that.charts.setOption({
color: ['#66b3ff', '#ce90e8', '#ff9c6e', '#5cdbd3'],
tooltip: {
@ -174,9 +187,9 @@ export default {
},
series: [
{
name: '在线用户',
name: onlineUsers,
type: 'line',
stack: '在线用户',
stack: onlineUsers,
smooth: 0.6,
symbol: 'none',
symbolSize: 10,
@ -184,9 +197,9 @@ export default {
areaStyle: {}
},
{
name: '送礼用户',
name: giftUsers,
type: 'line',
stack: '送礼用户',
stack: giftUsers,
smooth: 0.6,
symbol: 'none',
symbolSize: 10,
@ -194,9 +207,9 @@ export default {
areaStyle: {}
},
{
name: '礼物价值',
name: giftValue,
type: 'line',
stack: '礼物价值',
stack: giftValue,
smooth: 0.6,
symbol: 'none',
symbolSize: 10,

View File

@ -1,7 +1,7 @@
<template>
<div class="room-operation-log-drawer">
<el-drawer
title="房间操作日志"
:title="$t('pages.roomProfile.dialog.operationLog')"
:visible="true"
:append-to-body="true"
:before-close="handleClose"
@ -14,7 +14,7 @@
<div class="filter-container">
<el-select
v-model="listQuery.businessCode"
placeholder="业务CODE"
:placeholder="$t('pages.roomProfile.filter.businessCode')"
style="width: 100%;"
class="filter-item"
clearable

View File

@ -1,7 +1,7 @@
<template>
<el-dialog
title="房间访客记录"
:title="$t('pages.roomProfile.dialog.visitorLog')"
:visible="true"
:modal-append-to-body="true"
:append-to-body="true"
@ -12,7 +12,7 @@
>
<div class="filter-container">
<div class="filter-item">
<account-input v-model="listQuery.userId" placeholder="用户ID" :sys-origin="sysOrigin" />
<account-input v-model="listQuery.userId" :placeholder="$t('pages.roomProfile.filter.userId')" :sys-origin="sysOrigin" />
</div>
<el-button
class="filter-item"
@ -20,7 +20,7 @@
icon="el-icon-search"
@click="handleSearch"
>
搜索
{{ $t('pages.roomProfile.filter.search') }}
</el-button>
</div>
<el-table
@ -31,7 +31,7 @@
highlight-current-row
height="500"
>
<el-table-column label="用户" align="left" min-width="200">
<el-table-column :label="$t('pages.roomProfile.table.user')" align="left" min-width="200">
<template slot-scope="scope">
<user-table-exhibit
:user-profile="scope.row.userProfile"
@ -39,7 +39,7 @@
</template>
</el-table-column>
<el-table-column prop="createTime" label="浏览时间" align="center" width="200">
<el-table-column prop="createTime" :label="$t('pages.roomProfile.table.visitTime')" align="center" width="200">
<template slot-scope="scope">
{{ scope.row.createTime | dateFormat }}
</template>

View File

@ -1,7 +1,7 @@
<template>
<el-dialog
title="房间成员详情"
:title="$t('pages.roomProfile.dialog.memberDetails')"
:visible="true"
:modal-append-to-body="true"
:append-to-body="true"
@ -12,17 +12,17 @@
>
<div class="filter-container">
<div class="filter-item">
<account-input v-model="listQuery.userId" :sys-origin="sysOrigin" placeholder="用户ID" />
<account-input v-model="listQuery.userId" :sys-origin="sysOrigin" :placeholder="$t('pages.roomProfile.filter.userId')" />
</div>
<el-select
v-model="listQuery.roomRole"
placeholder="角色"
:placeholder="$t('pages.roomProfile.filter.role')"
style="width: 120px"
class="filter-item"
@change="handleSearch"
>
<el-option
v-for="(item, index) in roomRoles"
v-for="(item, index) in translatedRoomRoles"
:key="index"
:label="item.name"
:value="item.value"
@ -34,7 +34,7 @@
icon="el-icon-search"
@click="handleSearch"
>
搜索
{{ $t('pages.roomProfile.filter.search') }}
</el-button>
</div>
<el-table
@ -45,21 +45,19 @@
highlight-current-row
height="500"
>
<el-table-column label="用户" align="center" min-width="200">
<el-table-column :label="$t('pages.roomProfile.table.user')" align="center" min-width="200">
<template slot-scope="scope">
<user-table-exhibit :user-profile="scope.row.userProfile" :query-details="true" />
</template>
</el-table-column>
<el-table-column label="房间角色" align="center">
<el-table-column :label="$t('pages.roomProfile.table.roomRole')" align="center">
<template slot-scope="scope">
<span v-if="scope.row.roomRole === 'HOMEOWNER'"> 房主 </span>
<span v-if="scope.row.roomRole === 'ADMIN'"> 管理员 </span>
<span v-if="scope.row.roomRole === 'MEMBER'"> 成员 </span>
<span>{{ getRoomRoleLabel(scope.row.roomRole) }}</span>
</template>
</el-table-column>
<el-table-column prop="createTime" label="注册时间" align="center">
<el-table-column prop="createTime" :label="$t('pages.roomProfile.table.registeredAt')" align="center">
<template slot-scope="scope">
{{ scope.row.createTime | dateFormat }}
</template>
@ -120,6 +118,14 @@ export default {
searchDisabled: false
}
},
computed: {
translatedRoomRoles() {
return this.roomRoles.map(item => ({
...item,
name: this.getRoomRoleLabel(item.value)
}))
}
},
watch: {
roomId: {
immediate: true,
@ -131,6 +137,14 @@ export default {
}
},
methods: {
getRoomRoleLabel(role) {
const roleMap = {
HOMEOWNER: this.$t('pages.roomProfile.option.role.homeowner'),
ADMIN: this.$t('pages.roomProfile.option.role.admin'),
MEMBER: this.$t('pages.roomProfile.option.role.member')
}
return roleMap[role] || role || ''
},
renderData() {
const that = this
that.listLoading = true

View File

@ -7,7 +7,7 @@
icon="el-icon-edit"
@click="handleCreate('IP')"
>
新增IP
{{ $t('pages.requestBlacklist.action.addIp') }}
</el-button>
<el-button
class="filter-item"
@ -15,7 +15,7 @@
icon="el-icon-edit"
@click="handleCreate('PHONE_MODEL')"
>
新增手机型号
{{ $t('pages.requestBlacklist.action.addPhoneModel') }}
</el-button>
</div>
<div v-loading="listLoading" class="content">
@ -69,9 +69,13 @@ export default {
})
},
handlDel(content) {
this.$confirm('确认删除吗?', '提示', {
this.$confirm(
this.$t('pages.requestBlacklist.confirm.delete'),
this.$t('pages.requestBlacklist.confirm.title'),
{
type: 'warning'
})
}
)
.then(() => {
delBlackList(content).then(res => {
this.$opsMessage.success()
@ -82,18 +86,17 @@ export default {
},
handleCreate(type) {
const that = this
let tips = '错误的参数类型'
let tips = this.$t('pages.requestBlacklist.prompt.invalidType')
if (type === 'IP') {
tips =
'请输入IP, 格式如:127.0.0.1 或 127.0.0 每少一位表示多屏蔽一个网络段'
tips = this.$t('pages.requestBlacklist.prompt.ipTip')
}
if (type === 'PHONE_MODEL') {
tips = '请输入手机型号,最终系统保存为大写'
tips = this.$t('pages.requestBlacklist.prompt.phoneModelTip')
}
this.$prompt(tips, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消'
this.$prompt(tips, this.$t('pages.requestBlacklist.prompt.title'), {
confirmButtonText: this.$t('pages.requestBlacklist.prompt.confirm'),
cancelButtonText: this.$t('pages.requestBlacklist.prompt.cancel')
})
.then(({ value }) => {
if (type === 'IP') {
@ -115,7 +118,7 @@ export default {
.catch(er => {})
return
}
that.$opsMessage.fail('Param type Error.')
that.$opsMessage.fail(this.$t('pages.requestBlacklist.message.paramTypeError'))
})
.catch(() => {})
}

View File

@ -16,25 +16,25 @@
<div class="drawer-form">
<el-form ref="form" :model="form" :rules="formRules" label-width="80px">
<el-form-item v-if="!isUpdate" prop="userId" label="Leader">
<account-input v-model="form.userId" :sys-origin="form.sysOrigin" placeholder="BD Leader 用户ID" />
<el-form-item v-if="!isUpdate" prop="userId" :label="$t('pages.bdLead.form.leader')">
<account-input v-model="form.userId" :sys-origin="form.sysOrigin" :placeholder="$t('pages.bdLead.placeholder.bdLeadUserIdWithSpace')" />
</el-form-item>
<el-form-item prop="regionId" label="区域">
<el-form-item prop="regionId" :label="$t('pages.bdLead.form.region')">
<select-system-region
ref="regionSelectPolicy"
v-model="form.regionId"
:sys-origin="form.sysOrigin"
placeholder="请选择区域"
:placeholder="$t('pages.bdLead.placeholder.selectRegion')"
/>
</el-form-item>
<el-form-item prop="contact" label="联系方式">
<el-input v-model="form.contact" :sys-origin="form.sysOrigin" placeholder="联系方式" />
<el-form-item prop="contact" :label="$t('pages.bdLead.form.contact')">
<el-input v-model="form.contact" :sys-origin="form.sysOrigin" :placeholder="$t('pages.bdLead.placeholder.contact')" />
</el-form-item>
</el-form>
</div>
<div class="drawer-footer">
<el-button @click="handleClose()">关闭</el-button>
<el-button type="primary" :disabled="submitLoading" :loading="submitLoading" @click="submitForm">保存</el-button>
<el-button @click="handleClose()">{{ $t('pages.bdLead.action.close') }}</el-button>
<el-button type="primary" :disabled="submitLoading" :loading="submitLoading" @click="submitForm">{{ $t('pages.bdLead.action.save') }}</el-button>
</div>
</div>
</el-drawer>
@ -56,7 +56,7 @@ export default {
},
data() {
const commonRules = [
{ required: true, message: '必填字段不可为空', trigger: 'blur' }
{ required: true, message: this.$t('pages.bdLead.validation.requiredField'), trigger: 'blur' }
]
return {
submitLoading: false,
@ -75,7 +75,7 @@ export default {
},
computed: {
textOptTitle() {
return this.row && this.row.id ? '修改BD Leader' : '添加BD Leader'
return this.row && this.row.id ? this.$t('pages.bdLead.dialog.edit') : this.$t('pages.bdLead.dialog.create')
},
isUpdate() {
return this.row && this.row.id
@ -92,7 +92,7 @@ export default {
methods: {
handleClose() {
if (this.submitLoading) {
this.$opsMessage.warn('正在提交!')
this.$opsMessage.warn(this.$t('pages.bdLead.message.submitting'))
return
}
this.$emit('close')

View File

@ -1,7 +1,7 @@
<template>
<div class="bind-team-drawer">
<el-drawer
title="绑定子级BD"
:title="$t('pages.bdLead.dialog.bindSubBd')"
:visible="true"
:before-close="handleClose"
:close-on-press-escape="false"
@ -14,14 +14,14 @@
<div class="drawer-form">
<el-form ref="form" :model="form" :rules="formRules" label-width="80px">
<el-form-item prop="userId" label="BD">
<account-input v-model="form.userId" :sys-origin="sysOrigin" placeholder="请输入BD账号" />
<el-form-item prop="userId" :label="$t('pages.bdLead.form.bd')">
<account-input v-model="form.userId" :sys-origin="sysOrigin" :placeholder="$t('pages.bdLead.placeholder.bdAccount')" />
</el-form-item>
</el-form>
</div>
<div class="drawer-footer">
<el-button @click="handleClose()">关闭</el-button>
<el-button type="primary" :disabled="submitLoading" :loading="submitLoading" @click="submitForm">保存</el-button>
<el-button @click="handleClose()">{{ $t('pages.bdLead.action.close') }}</el-button>
<el-button type="primary" :disabled="submitLoading" :loading="submitLoading" @click="submitForm">{{ $t('pages.bdLead.action.save') }}</el-button>
</div>
</div>
</el-drawer>
@ -43,7 +43,7 @@ export default {
},
data() {
const commonRules = [
{ required: true, message: '必填字段不可为空', trigger: 'blur' }
{ required: true, message: this.$t('pages.bdLead.validation.requiredField'), trigger: 'blur' }
]
return {
submitLoading: false,
@ -59,7 +59,7 @@ export default {
},
computed: {
textOptTitle() {
return '绑定子级BD'
return this.$t('pages.bdLead.dialog.bindSubBd')
}
},
watch: {
@ -75,7 +75,7 @@ export default {
methods: {
handleClose() {
if (this.submitLoading) {
this.$opsMessage.warn('正在提交!')
this.$opsMessage.warn(this.$t('pages.bdLead.message.submitting'))
return
}
this.$emit('close')

View File

@ -5,7 +5,7 @@
<div class="filter-container">
<el-select
v-model="listQuery.sysOrigin"
placeholder="系统"
:placeholder="$t('pages.bdLead.filter.system')"
style="width: 120px"
class="filter-item"
@change="handleSearch"
@ -21,13 +21,17 @@
</el-option>
</el-select>
<div class="filter-item">
<account-input v-model="listQuery.userId" :sys-origin="listQuery.sysOrigin" placeholder="BD Leader用户ID" />
<account-input
v-model="listQuery.userId"
:sys-origin="listQuery.sysOrigin"
:placeholder="$t('pages.bdLead.placeholder.bdLeadUserId')"
/>
</div>
<el-select
v-if="backUserConditionVisible"
v-model="listQuery.createUser"
v-loading="listMembersLoading"
placeholder="后台成员"
:placeholder="$t('pages.bdLead.filter.backendMember')"
style="width: 120px"
class="filter-item"
clearable
@ -49,7 +53,7 @@
icon="el-icon-search"
@click="handleSearch"
>
搜索
{{ $t('pages.bdLead.action.search') }}
</el-button>
<el-button
@ -59,46 +63,46 @@
icon="el-icon-edit"
@click="handleCreate"
>
新增BD Lead
{{ $t('pages.bdLead.action.createBdLead') }}
</el-button>
</div>
<el-table
v-loading="listLoading"
:data="list"
element-loading-text="Loading"
:element-loading-text="$t('pages.bdLead.loading')"
fit
highlight-current-row
@cell-mouse-enter="handleMouseEnter"
>
<el-table-column prop="sysOrigin" label="系统" align="center" min-width="60">
<el-table-column prop="sysOrigin" :label="$t('pages.bdLead.table.system')" align="center" min-width="60">
<template slot-scope="scope">
<sys-origin-icon :icon="scope.row.sysOrigin" :desc="scope.row.sysOrigin" />
</template>
</el-table-column>
<el-table-column label="用户" align="center" min-width="200">
<el-table-column :label="$t('pages.bdLead.table.user')" align="center" min-width="200">
<template slot-scope="scope">
<user-table-exhibit :user-profile="scope.row.userProfile" :query-details="true" />
</template>
</el-table-column>
<el-table-column prop="regionName" label="区域" align="center" min-width="60" />
<el-table-column prop="bdQuantity" label="BD数量" align="center" min-width="100" />
<el-table-column v-if="buttonPermissions.includes('bd:lead:list:edit')" prop="contact" label="联系方式" align="center" min-width="100" />
<el-table-column prop="createUserName" label="创建人" align="center" min-width="100" />
<el-table-column prop="createTime" label="创建时间" align="center" width="160">
<el-table-column prop="regionName" :label="$t('pages.bdLead.table.region')" align="center" min-width="60" />
<el-table-column prop="bdQuantity" :label="$t('pages.bdLead.table.bdQuantity')" align="center" min-width="100" />
<el-table-column v-if="buttonPermissions.includes('bd:lead:list:edit')" prop="contact" :label="$t('pages.bdLead.table.contact')" align="center" min-width="100" />
<el-table-column prop="createUserName" :label="$t('pages.bdLead.table.createdBy')" align="center" min-width="100" />
<el-table-column prop="createTime" :label="$t('pages.bdLead.table.createdAt')" align="center" width="160">
<template slot-scope="scope">
{{ scope.row.createTime | dateFormat }}
</template>
</el-table-column>
<el-table-column v-if="showOperationCol" fixed="right" label="操作" align="center" width="80">
<el-table-column v-if="showOperationCol" fixed="right" :label="$t('pages.bdLead.table.actions')" align="center" width="80">
<template slot-scope="scope">
<el-dropdown>
<span class="el-dropdown-link">
<i class="el-icon-more" />
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item v-if="buttonPermissions.includes('bd:lead:list:add:member')" @click.native="bindBd(scope.row, scope.$index)">增加子级BD</el-dropdown-item>
<el-dropdown-item v-if="buttonPermissions.includes('bd:lead:list:edit')" @click.native="clickEdit(scope.row, scope.$index)">编辑</el-dropdown-item>
<el-dropdown-item v-if="buttonPermissions.includes('bd:lead:list:del')" @click.native="handlDel(scope.row.id, scope.$index)">删除</el-dropdown-item>
<el-dropdown-item v-if="buttonPermissions.includes('bd:lead:list:add:member')" @click.native="bindBd(scope.row, scope.$index)">{{ $t('pages.bdLead.action.addSubBd') }}</el-dropdown-item>
<el-dropdown-item v-if="buttonPermissions.includes('bd:lead:list:edit')" @click.native="clickEdit(scope.row, scope.$index)">{{ $t('pages.bdLead.action.edit') }}</el-dropdown-item>
<el-dropdown-item v-if="buttonPermissions.includes('bd:lead:list:del')" @click.native="handlDel(scope.row.id, scope.$index)">{{ $t('pages.bdLead.action.delete') }}</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</template>
@ -254,7 +258,7 @@ export default {
that.renderData(true)
},
handleCreate() {
this.textOptTitle = '创建BD Leader'
this.textOptTitle = this.$t('pages.bdLead.dialog.create')
this.thisRow = {}
this.thisRow.sysOrigin = this.listQuery.sysOrigin
this.addBdVisible = true
@ -288,12 +292,12 @@ export default {
const that = this
copyText(text).then(() => {
that.$message({
message: '复制成功',
message: that.$t('pages.bdLead.message.copySuccess'),
type: 'success'
})
}).catch(() => {
that.$message({
message: '复制失败',
message: that.$t('pages.bdLead.message.copyFail'),
type: 'error'
})
})
@ -321,14 +325,14 @@ export default {
that.thisRow = row
that.thisRowIndex = _index
this.thisRow.sysOrigin = this.listQuery.sysOrigin
that.textOptTitle = '编辑BD Leader'
that.textOptTitle = this.$t('pages.bdLead.dialog.edit')
that.addBdVisible = true
},
handlDel(id, _index) {
const that = this
this.$confirm('您确定要删除吗(将与名下BD解除绑定, 不影响其他BD身份)?', '温馨提示', {
confirmButtonText: '确定',
cancelButtonText: '取消'
this.$confirm(this.$t('pages.bdLead.confirm.deleteMessage'), this.$t('pages.bdLead.confirm.title'), {
confirmButtonText: this.$t('pages.bdLead.action.confirm'),
cancelButtonText: this.$t('pages.bdLead.action.cancel')
}).then(() => {
deleteBdLead(id).then(res => {
that.$opsMessage.success()
@ -340,7 +344,7 @@ export default {
},
renderDataSuccess() {
this.$message({
message: 'Successful',
message: this.$t('pages.bdLead.message.success'),
type: 'success'
})
this.renderData()

View File

@ -16,25 +16,25 @@
<div class="drawer-form">
<el-form ref="form" :model="form" :rules="formRules" label-width="80px">
<el-form-item v-if="!isUpdate" prop="userId" label="BD">
<account-input v-model="form.userId" :sys-origin="form.sysOrigin" placeholder="BD用户ID" />
<el-form-item v-if="!isUpdate" prop="userId" :label="$t('pages.businessDevelopment.form.bd')">
<account-input v-model="form.userId" :sys-origin="form.sysOrigin" :placeholder="$t('pages.businessDevelopment.placeholder.bdUserId')" />
</el-form-item>
<el-form-item prop="region" label="区域">
<el-form-item prop="region" :label="$t('pages.businessDevelopment.form.region')">
<select-system-region
ref="regionSelectPolicy"
v-model="form.region"
:sys-origin="form.sysOrigin"
placeholder="请选择区域"
:placeholder="$t('pages.businessDevelopment.placeholder.selectRegion')"
/>
</el-form-item>
<el-form-item prop="contact" label="联系方式">
<el-input v-model="form.contact" :sys-origin="form.sysOrigin" placeholder="联系方式" />
<el-form-item prop="contact" :label="$t('pages.businessDevelopment.form.contact')">
<el-input v-model="form.contact" :sys-origin="form.sysOrigin" :placeholder="$t('pages.businessDevelopment.placeholder.contact')" />
</el-form-item>
</el-form>
</div>
<div class="drawer-footer">
<el-button @click="handleClose()">关闭</el-button>
<el-button type="primary" :disabled="submitLoading" :loading="submitLoading" @click="submitForm">保存</el-button>
<el-button @click="handleClose()">{{ $t('pages.businessDevelopment.action.close') }}</el-button>
<el-button type="primary" :disabled="submitLoading" :loading="submitLoading" @click="submitForm">{{ $t('pages.businessDevelopment.action.save') }}</el-button>
</div>
</div>
</el-drawer>
@ -55,9 +55,6 @@ export default {
}
},
data() {
const commonRules = [
{ required: true, message: '必填字段不可为空', trigger: 'blur' }
]
return {
submitLoading: false,
form: {
@ -66,16 +63,23 @@ export default {
region: '',
sysOrigin: '',
contact: ''
},
formRules: {
userId: commonRules,
region: commonRules
}
}
},
computed: {
formRules() {
const commonRules = [
{ required: true, message: this.$t('pages.businessDevelopment.validation.requiredField'), trigger: 'blur' }
]
return {
userId: commonRules,
region: commonRules
}
},
textOptTitle() {
return this.row && this.row.id ? '修改BD' : '添加BD'
return this.row && this.row.id
? this.$t('pages.businessDevelopment.dialog.editBd')
: this.$t('pages.businessDevelopment.dialog.addBd')
},
isUpdate() {
return this.row && this.row.id
@ -103,7 +107,7 @@ export default {
methods: {
handleClose() {
if (this.submitLoading) {
this.$opsMessage.warn('正在提交!')
this.$opsMessage.warn(this.$t('pages.businessDevelopment.message.submitting'))
return
}
this.$emit('close')

View File

@ -1,6 +1,6 @@
<template>
<el-dialog
title="权限设置"
:title="$t('pages.businessDevelopment.dialog.permissionSettings')"
:visible="true"
:modal-append-to-body="true"
:append-to-body="true"
@ -24,7 +24,7 @@
</el-form>
<div slot="footer">
<el-button type="primary" @click="submit()">提交</el-button>
<el-button type="primary" @click="submit()">{{ $t('pages.businessDevelopment.action.submit') }}</el-button>
</div>
</el-dialog>
</template>
@ -119,7 +119,7 @@ export default {
},
accountHandleSuccess(data) {
this.$message({
message: "Successful",
message: this.$t("pages.businessDevelopment.message.success"),
type: "success"
});
}

View File

@ -1,7 +1,7 @@
<template>
<div class="bind-team-drawer">
<el-drawer
title="绑定BDLeader"
:title="$t('pages.businessDevelopment.dialog.bindBdLeader')"
:visible="true"
:before-close="handleClose"
:close-on-press-escape="false"
@ -24,7 +24,7 @@
<el-input
v-model="form.newAccount"
:sys-origin="sysOrigin"
placeholder="请输入账号或靓号"
:placeholder="$t('pages.businessDevelopment.placeholder.accountOrSpecialId')"
/>
</el-form-item>
</el-form>
@ -32,13 +32,13 @@
<!-- 尾部 -->
<div class="drawer-footer">
<el-button @click="handleClose()">关闭</el-button>
<el-button @click="handleClose()">{{ $t('pages.businessDevelopment.action.close') }}</el-button>
<el-button
type="primary"
:disabled="submitLoading"
:loading="submitLoading"
@click="submitForm"
>保存</el-button
>{{ $t('pages.businessDevelopment.action.save') }}</el-button
>
</div>
</div>
@ -60,24 +60,26 @@ export default {
}
},
data() {
const commonRules = [
{ required: true, message: "必填字段不可为空", trigger: "blur" }
];
return {
submitLoading: false,
sysOrigin: "",
form: {
newAccount: "",
bdId: ""
},
formRules: {
newAccount: commonRules
}
};
},
computed: {
formRules() {
const commonRules = [
{ required: true, message: this.$t("pages.businessDevelopment.validation.requiredField"), trigger: "blur" }
];
return {
newAccount: commonRules
};
},
textOptTitle() {
return "绑定代理";
return this.$t("pages.businessDevelopment.dialog.bindAgent");
}
},
watch: {
@ -93,7 +95,7 @@ export default {
methods: {
handleClose() {
if (this.submitLoading) {
this.$opsMessage.warn("正在提交!");
this.$opsMessage.warn(this.$t("pages.businessDevelopment.message.submitting"));
return;
}
this.$emit("close");

View File

@ -1,7 +1,7 @@
<template>
<div class="bind-team-drawer">
<el-drawer
title="绑定代理"
:title="$t('pages.businessDevelopment.dialog.bindAgent')"
:visible="true"
:before-close="handleClose"
:close-on-press-escape="false"
@ -14,14 +14,14 @@
<div class="drawer-form">
<el-form ref="form" :model="form" :rules="formRules" label-width="80px">
<el-form-item prop="ownUserId" label="代理">
<account-input v-model="form.ownUserId" :sys-origin="sysOrigin" placeholder="请输入代理账号" />
<el-form-item prop="ownUserId" :label="$t('pages.businessDevelopment.form.agent')">
<account-input v-model="form.ownUserId" :sys-origin="sysOrigin" :placeholder="$t('pages.businessDevelopment.placeholder.agentAccount')" />
</el-form-item>
</el-form>
</div>
<div class="drawer-footer">
<el-button @click="handleClose()">关闭</el-button>
<el-button type="primary" :disabled="submitLoading" :loading="submitLoading" @click="submitForm">保存</el-button>
<el-button @click="handleClose()">{{ $t('pages.businessDevelopment.action.close') }}</el-button>
<el-button type="primary" :disabled="submitLoading" :loading="submitLoading" @click="submitForm">{{ $t('pages.businessDevelopment.action.save') }}</el-button>
</div>
</div>
</el-drawer>
@ -42,24 +42,26 @@ export default {
}
},
data() {
const commonRules = [
{ required: true, message: '必填字段不可为空', trigger: 'blur' }
]
return {
submitLoading: false,
sysOrigin: '',
form: {
ownUserId: '',
bdUserId: ''
},
formRules: {
ownUserId: commonRules
}
}
},
computed: {
formRules() {
const commonRules = [
{ required: true, message: this.$t('pages.businessDevelopment.validation.requiredField'), trigger: 'blur' }
]
return {
ownUserId: commonRules
}
},
textOptTitle() {
return '绑定代理'
return this.$t('pages.businessDevelopment.dialog.bindAgent')
}
},
watch: {
@ -75,7 +77,7 @@ export default {
methods: {
handleClose() {
if (this.submitLoading) {
this.$opsMessage.warn('正在提交!')
this.$opsMessage.warn(this.$t('pages.businessDevelopment.message.submitting'))
return
}
this.$emit('close')

View File

@ -1,7 +1,7 @@
<template>
<el-dialog
title="BD团队代理成员"
:title="$t('pages.businessDevelopment.dialog.teamMembers')"
:visible="true"
:modal-append-to-body="true"
:append-to-body="true"
@ -12,7 +12,7 @@
>
<div class="filter-container">
<div class="filter-item">
<account-input v-model="listQuery.agentId" :sys-origin="listQuery.sysOrigin" placeholder="用户ID" />
<account-input v-model="listQuery.agentId" :sys-origin="listQuery.sysOrigin" :placeholder="$t('pages.businessDevelopment.placeholder.userId')" />
</div>
<el-button
:loading="searchDisabled"
@ -21,7 +21,7 @@
icon="el-icon-search"
@click="handleSearch"
>
搜索
{{ $t('pages.businessDevelopment.action.search') }}
</el-button>
</div>
<div v-loading="listLoading" class="content">
@ -33,26 +33,26 @@
highlight-current-row
@cell-mouse-enter="handleMouseEnter"
>
<el-table-column prop="sysOrigin" label="系统" align="center" min-width="60">
<el-table-column prop="sysOrigin" :label="$t('pages.businessDevelopment.table.system')" align="center" min-width="60">
<template slot-scope="scope">
<sys-origin-icon :icon="scope.row.sysOrigin" :desc="scope.row.sysOrigin" />
</template>
</el-table-column>
<el-table-column label="用户" align="center" min-width="200">
<el-table-column :label="$t('pages.businessDevelopment.table.user')" align="center" min-width="200">
<template slot-scope="scope">
<user-table-exhibit :user-profile="scope.row.userProfile" :query-details="true" />
</template>
</el-table-column>
<el-table-column prop="anchorQuantity" label="主播数量" align="center" min-width="100" />
<el-table-column prop="createTime" label="创建时间" align="center" width="160">
<el-table-column prop="anchorQuantity" :label="$t('pages.businessDevelopment.table.anchorCount')" align="center" min-width="100" />
<el-table-column prop="createTime" :label="$t('pages.businessDevelopment.table.createdAt')" align="center" width="160">
<template slot-scope="scope">
{{ scope.row.createTime | dateFormat }}
</template>
</el-table-column>
<el-table-column label="操作" align="center" width="100">
<el-table-column :label="$t('pages.businessDevelopment.table.actions')" align="center" width="100">
<template slot-scope="scope">
<div>
<el-button type="text" @click.native="handlBDMemberDel(scope.row.id)">删除</el-button>
<el-button type="text" @click.native="handlBDMemberDel(scope.row.id)">{{ $t('pages.businessDevelopment.action.delete') }}</el-button>
</div>
</template>
</el-table-column>
@ -140,12 +140,12 @@ export default {
const that = this
copyText(text).then(() => {
that.$message({
message: '复制成功',
message: that.$t('pages.businessDevelopment.message.copySuccess'),
type: 'success'
})
}).catch(() => {
that.$message({
message: '复制失败',
message: that.$t('pages.businessDevelopment.message.copyFail'),
type: 'error'
})
})
@ -163,9 +163,9 @@ export default {
},
handlBDMemberDel(id) {
const that = this
this.$confirm('您确定要删除吗', '温馨提示', {
confirmButtonText: '确定',
cancelButtonText: '取消'
this.$confirm(this.$t('pages.businessDevelopment.confirm.deleteMemberMessage'), this.$t('pages.businessDevelopment.confirm.title'), {
confirmButtonText: this.$t('pages.businessDevelopment.action.confirm'),
cancelButtonText: this.$t('pages.businessDevelopment.action.cancel')
}).then(() => {
deleteBDMember(id).then(res => {
that.renderData()

View File

@ -10,40 +10,40 @@
custom-class="drawer-auto-layout"
>
<div slot="title" class="flex-l">
<sys-origin-icon :icon="sysOrigin" size="18px" />&nbsp;创建代理
<sys-origin-icon :icon="sysOrigin" size="18px" />&nbsp;{{ $t('pages.teamCreate.dialog.title') }}
</div>
<div class="create-team ">
<div class="drawer-form">
<el-form ref="form" :model="form" :rules="formRules" label-width="80px">
<el-form-item v-if="bindBd==='no'" prop="region" label="区域">
<el-form-item v-if="bindBd==='no'" prop="region" :label="$t('pages.teamCreate.form.region')">
<select-system-region
ref="regionSelectPolicy"
v-model="form.region"
:sys-origin="sysOrigin"
clearable
placeholder="区域"
:placeholder="$t('pages.teamCreate.placeholder.region')"
/>
</el-form-item>
<el-form-item prop="ownUserId" label="代理">
<account-input v-model="form.ownUserId" :sys-origin="sysOrigin" placeholder="请输入代理ID" />
<el-form-item prop="ownUserId" :label="$t('pages.teamCreate.form.agent')">
<account-input v-model="form.ownUserId" :sys-origin="sysOrigin" :placeholder="$t('pages.teamCreate.placeholder.agentId')" />
</el-form-item>
<el-form-item label="绑定BD">
<el-form-item :label="$t('pages.teamCreate.form.bindBd')">
<el-radio-group v-model="bindBd" :disabled="bdOpsTrue">
<el-radio label="yes"></el-radio>
<el-radio label="no"></el-radio>
<el-radio label="yes">{{ $t('pages.teamCreate.word.yes') }}</el-radio>
<el-radio label="no">{{ $t('pages.teamCreate.word.no') }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item v-if="bindBd==='yes'" prop="bdUserId" label="BD">
<account-input v-model="form.bdUserId" :sys-origin="sysOrigin" :default-select-type="bdAccountDefaultSelectType" placeholder="请输入BD ID" />
<el-form-item v-if="bindBd==='yes'" prop="bdUserId" :label="$t('pages.teamCreate.form.bd')">
<account-input v-model="form.bdUserId" :sys-origin="sysOrigin" :default-select-type="bdAccountDefaultSelectType" :placeholder="$t('pages.teamCreate.placeholder.bdId')" />
</el-form-item>
<el-form-item prop="remarks" label="备注">
<el-form-item prop="remarks" :label="$t('pages.teamCreate.form.remark')">
<el-input
v-model="form.remarks"
type="textarea"
placeholder="请输入备注"
:placeholder="$t('pages.teamCreate.placeholder.remark')"
maxlength="100"
show-word-limit
resize="none"
@ -52,32 +52,32 @@
</el-form-item>
<el-form-item label="">
<el-checkbox v-model="formSettingDefault">设置 </el-checkbox>
<el-checkbox v-model="formContactAdd">联系方式 </el-checkbox>
<el-checkbox v-model="formSettingDefault">{{ $t('pages.teamCreate.form.setting') }}</el-checkbox>
<el-checkbox v-model="formContactAdd">{{ $t('pages.teamCreate.form.contactInfo') }}</el-checkbox>
</el-form-item>
<div v-if="formSettingDefault" style="padding: 10px 0px;">
<el-divider content-position="left">设置信息</el-divider>
<el-form-item prop="setting.maxMember" label="成员数量">
<el-input v-model="form.setting.maxMember" placeholder="最大成员数量" maxlength="100" show-word-limit />
<el-divider content-position="left">{{ $t('pages.teamCreate.section.settingInfo') }}</el-divider>
<el-form-item prop="setting.maxMember" :label="$t('pages.teamCreate.form.memberQuantity')">
<el-input v-model="form.setting.maxMember" :placeholder="$t('pages.teamCreate.placeholder.maxMember')" maxlength="100" show-word-limit />
</el-form-item>
</div>
<div v-if="formContactAdd" style="padding: 10px 0px;">
<el-divider content-position="left">联系方式</el-divider>
<el-form-item prop="contact.type" label="类型">
<el-select v-model="form.contact.type" placeholder="选择类型" style="width: 100%;">
<el-divider content-position="left">{{ $t('pages.teamCreate.section.contactInfo') }}</el-divider>
<el-form-item prop="contact.type" :label="$t('pages.teamCreate.form.type')">
<el-select v-model="form.contact.type" :placeholder="$t('pages.teamCreate.placeholder.selectType')" style="width: 100%;">
<el-option v-for="(item, index) in contactTypes" :key="index" :label="item.name" :value="item.value" />
</el-select>
</el-form-item>
<el-form-item prop="contact.contact" label="联系">
<el-input v-model="form.contact.contact" placeholder="请输入联系方式" maxlength="100" show-word-limit />
<el-form-item prop="contact.contact" :label="$t('pages.teamCreate.form.contact')">
<el-input v-model="form.contact.contact" :placeholder="$t('pages.teamCreate.placeholder.contact')" maxlength="100" show-word-limit />
</el-form-item>
<el-form-item prop="contact.remarks" label="备注">
<el-form-item prop="contact.remarks" :label="$t('pages.teamCreate.form.remark')">
<el-input
v-model="form.contact.remarks"
type="textarea"
placeholder="请输入备注"
:placeholder="$t('pages.teamCreate.placeholder.remark')"
maxlength="100"
show-word-limit
resize="none"
@ -88,8 +88,8 @@
</el-form>
</div>
<div v-if="sysOrigin" class="drawer-footer">
<el-button :disabled="submitLoading" @click="handleClose()">取消</el-button>
<el-button type="primary" :disabled="submitLoading" :loading="submitLoading" @click="submitForm()">保存</el-button>
<el-button :disabled="submitLoading" @click="handleClose()">{{ $t('pages.teamCreate.action.cancel') }}</el-button>
<el-button type="primary" :disabled="submitLoading" :loading="submitLoading" @click="submitForm()">{{ $t('pages.teamCreate.action.save') }}</el-button>
</div>
</div>
</el-drawer>
@ -116,9 +116,6 @@ export default {
}
},
data() {
const commonRules = [
{ required: true, message: '必填字段不可为空', trigger: 'blur' }
]
return {
bdAccountDefaultSelectType: '',
bindBd: 'no',
@ -143,7 +140,15 @@ export default {
},
bdUserId: ''
},
formRules: {
bdOpsTrue: false
}
},
computed: {
formRules() {
const commonRules = [
{ required: true, message: this.$t('pages.teamCreate.validation.requiredField'), trigger: 'blur' }
]
return {
sysOrigin: commonRules,
region: commonRules,
ownUserId: commonRules,
@ -151,8 +156,7 @@ export default {
'contact.type': commonRules,
'contact.contact': commonRules,
'setting.maxMember': commonRules
},
bdOpsTrue: false
}
}
},
watch: {
@ -172,7 +176,7 @@ export default {
methods: {
handleClose() {
if (this.submitLoading) {
this.$opsMessage.warn('正在提交!')
this.$opsMessage.warn(this.$t('pages.teamCreate.message.submitting'))
return
}
this.$emit('close')

View File

@ -354,7 +354,7 @@ export default {
},
data() {
const commonRules = [
{ required: true, message: '必填字段不可为空', trigger: 'blur' }
{ required: true, message: this.$t('pages.propsSourceGroupForm.validation.requiredField'), trigger: 'blur' }
]
return {
specialIdTypes,
@ -429,71 +429,71 @@ export default {
propsTypeMap: {
'EMOJI': {
value: 'EMOJI',
name: '用户-表情包'
name: 'User Emoji Pack'
},
'BADGE': {
value: 'BADGE',
name: '用户-徽章'
name: 'User Badge'
},
'ROOM_BADGE': {
value: 'BADGE',
name: '房间-徽章'
name: 'Room Badge'
},
'AVATAR_FRAME': {
value: 'PROPS',
name: '头像框'
name: 'Avatar Frame'
},
'DATA_CARD': {
value: 'PROPS',
name: '资料卡'
name: 'Profile Card'
},
'CHAT_BUBBLE': {
value: 'PROPS',
name: '聊天气泡'
name: 'Chat Bubble'
},
'RIDE': {
value: 'PROPS',
name: '座驾'
name: 'Ride'
},
'NOBLE_VIP': {
value: 'PROPS',
name: '贵族'
name: 'Noble'
},
'GIFT': {
value: 'GIFT',
name: '礼物'
name: 'Gift'
},
'GOLD': {
value: 'GOLD',
name: '金币'
name: 'Gold'
},
'DIAMOND': {
value: 'DIAMOND',
name: '钻石'
name: 'Diamond'
},
'SPECIAL_ID': {
value: 'SPECIAL_ID',
name: '靓号'
name: 'Special ID'
},
'THEME': {
value: 'PROPS',
name: '房间背景主题'
name: 'Room Background Theme'
},
'GAME_COUPON': {
value: 'GAME_COUPON',
name: '游戏券'
name: 'Game Coupon'
},
'CUSTOMIZE': {
value: 'CUSTOMIZE',
name: '自定义(只读)'
name: 'Custom (Read Only)'
},
'FRAGMENTS': {
value: 'FRAGMENTS',
name: '碎片'
name: 'Fragments'
},
'FLOAT_PICTURE': {
value: 'PROPS',
name: '飘窗'
name: 'Floating Window'
}
},
isAllowEdit: false,

View File

@ -51,13 +51,13 @@
</el-col>
<el-col :span="15">
<span style="font-weight:bold;"> {{ getTypeName(item) }}</span>
<span v-if="item.type === 'SPECIAL_ID'">类型{{ item.content }}数量{{ item.quantity }}</span>
<span v-else-if="item.type === 'GOLD' || item.type === 'DIAMOND' || isGameCoupon(item.type)">数量{{ item.content }}</span>
<span v-else-if="item.type === 'BADGE'">{{ item.badgeName }} 天数{{ item.quantity }} </span>
<span v-else-if="item.type === 'CUSTOMIZE'">描述{{ item.remark }}</span>
<span v-if="item.type === 'SPECIAL_ID'">{{ $t('pages.propsSourceGroupForm.summary.typeQuantity', { content: item.content, quantity: item.quantity }) }}</span>
<span v-else-if="item.type === 'GOLD' || item.type === 'DIAMOND' || isGameCoupon(item.type)">{{ $t('pages.propsSourceGroupForm.summary.quantityValue', { value: item.content }) }}</span>
<span v-else-if="item.type === 'BADGE'">{{ $t('pages.propsSourceGroupForm.summary.badgeDays', { name: item.badgeName, quantity: item.quantity }) }} </span>
<span v-else-if="item.type === 'CUSTOMIZE'">{{ $t('pages.propsSourceGroupForm.summary.descriptionValue', { value: item.remark }) }}</span>
<span v-else>
<span v-if="item.type === 'GIFT' || item.type === 'FRAGMENTS'">数量</span>
<span v-else>天数</span>
<span v-if="item.type === 'GIFT' || item.type === 'FRAGMENTS'">{{ $t('pages.propsSourceGroupForm.summary.quantityLabel') }}</span>
<span v-else>{{ $t('pages.propsSourceGroupForm.summary.daysLabel') }}</span>
{{ item.quantity }}</span>
</el-col>
<el-col v-if="isAllowEdit" :span="3" style="text-align: right;">
@ -66,22 +66,22 @@
</el-row>
</div>
</div>
<el-form-item v-if="isAllowEdit" label="添加配置">
<el-form-item v-if="isAllowEdit" :label="$t('pages.propsSourceGroupForm.form.addConfig')">
<div class="content-list">
<el-row v-for="(item, index) in form.tmpConfigList" :key="index">
<div class="content-box">
<div class="content-box-label">
<span>{{ item.ext.title }}</span>
<span>{{ getConfigTitle(item) }}</span>
</div>
<el-row v-if="item.clickType === 'SPECIAL_ID'" :gutter="10">
<el-col :span="13">
<el-form-item
:prop="'tmpConfigList.' + index + '.content'"
:rules="{required: true, message: '必填字段不可为空', trigger: 'blur'}"
:rules="{required: true, message: $t('pages.propsSourceGroupForm.validation.requiredField'), trigger: 'blur'}"
>
<el-input
v-model="item.content"
placeholder="请选择"
:placeholder="$t('pages.propsSourceGroupForm.placeholder.select')"
style="width:100%"
/>
</el-form-item>
@ -89,9 +89,9 @@
<el-col :span="6">
<el-form-item
:prop="'tmpConfigList.' + index + '.quantity'"
:rules="{ required: true, message: '不可为空', trigger: 'blur'}"
:rules="{ required: true, message: $t('pages.propsSourceGroupForm.validation.notEmpty'), trigger: 'blur'}"
>
<span><el-input v-model="item.quantity" v-number style="width: 100%;" placeholder="数量" /></span>
<span><el-input v-model="item.quantity" v-number style="width: 100%;" :placeholder="$t('pages.propsSourceGroupForm.placeholder.quantity')" /></span>
</el-form-item>
</el-col>
<el-col :span="5">
@ -103,9 +103,9 @@
<el-col :span="19">
<el-form-item
:prop="'tmpConfigList.' + index + '.content'"
:rules="{ required: true, message: '必填字段不可为空', trigger: 'blur'}"
:rules="{ required: true, message: $t('pages.propsSourceGroupForm.validation.requiredField'), trigger: 'blur'}"
>
<el-input v-model.trim="item.content" v-number type="text" placeholder="请输入内容" />
<el-input v-model.trim="item.content" v-number type="text" :placeholder="$t('pages.propsSourceGroupForm.placeholder.inputContent')" />
</el-form-item>
</el-col>
<el-col :span="5">
@ -117,9 +117,9 @@
<el-col :span="19">
<el-form-item
:prop="'tmpConfigList.' + index + '.content'"
:rules="{ required: true, message: '必填字段不可为空', trigger: 'blur'}"
:rules="{ required: true, message: $t('pages.propsSourceGroupForm.validation.requiredField'), trigger: 'blur'}"
>
<el-input v-model.trim="item.content" v-number type="text" placeholder="请输入内容" />
<el-input v-model.trim="item.content" v-number type="text" :placeholder="$t('pages.propsSourceGroupForm.placeholder.inputContent')" />
</el-form-item>
</el-col>
<el-col :span="5">
@ -131,11 +131,11 @@
<el-col :span="13">
<el-form-item
:prop="'tmpConfigList.' + index + '.content'"
:rules="{required: true, message: '必填字段不可为空', trigger: 'blur'}"
:rules="{required: true, message: $t('pages.propsSourceGroupForm.validation.requiredField'), trigger: 'blur'}"
>
<el-select
v-model="item.content"
placeholder="请选择"
:placeholder="$t('pages.propsSourceGroupForm.placeholder.select')"
style="width:100%"
filterable
@change="(val)=> changeContent(item, val)"
@ -154,7 +154,7 @@
<el-col :span="6">
<el-form-item
:prop="'tmpConfigList.' + index + '.quantity'"
:rules="{ required: true, message: '不可为空', trigger: 'blur'}"
:rules="{ required: true, message: $t('pages.propsSourceGroupForm.validation.notEmpty'), trigger: 'blur'}"
>
<!-- <span><el-input v-model="item.quantity" v-number style="width: 100%;" placeholder="天数" /></span> -->
@ -163,7 +163,7 @@
v-number
popper-class="my-autocomplete"
:fetch-suggestions="querySearchBadgeRestaurants"
placeholder="天数"
:placeholder="$t('pages.propsSourceGroupForm.placeholder.days')"
>
<template slot-scope="scope">
<div class="name">{{ scope.value }}</div>
@ -201,11 +201,11 @@
<el-col :span="10">
<el-form-item
:prop="'tmpConfigList.' + index + '.content'"
:rules="{required: true, message: '必填字段不可为空', trigger: 'blur'}"
:rules="{required: true, message: $t('pages.propsSourceGroupForm.validation.requiredField'), trigger: 'blur'}"
>
<el-select
v-model="item.content"
placeholder="请选择"
:placeholder="$t('pages.propsSourceGroupForm.placeholder.select')"
style="width:100%"
filterable
@change="(val)=> changeContent(item, val)"
@ -224,9 +224,9 @@
<el-col :span="6">
<el-form-item
:prop="'tmpConfigList.' + index + '.remark'"
:rules="{ required: true, message: '不可为空', trigger: 'blur'}"
:rules="{ required: true, message: $t('pages.propsSourceGroupForm.validation.notEmpty'), trigger: 'blur'}"
>
<div><el-input v-model="item.remark" style="width: 100%;" placeholder="描述" /></div>
<div><el-input v-model="item.remark" style="width: 100%;" :placeholder="$t('pages.propsSourceGroupForm.placeholder.description')" /></div>
</el-form-item>
</el-col>
<el-col :span="5">
@ -257,11 +257,11 @@
<el-col :span="10">
<el-form-item
:prop="'tmpConfigList.' + index + '.content'"
:rules="{required: true, message: '必填字段不可为空', trigger: 'blur'}"
:rules="{required: true, message: $t('pages.propsSourceGroupForm.validation.requiredField'), trigger: 'blur'}"
>
<el-select
v-model="item.content"
placeholder="请选择"
:placeholder="$t('pages.propsSourceGroupForm.placeholder.select')"
style="width:100%"
filterable
@change="(val)=> changeContent(item, val)"
@ -280,9 +280,9 @@
<el-col :span="6">
<el-form-item
:prop="'tmpConfigList.' + index + '.quantity'"
:rules="{ required: true, message: '不可为空', trigger: 'blur'}"
:rules="{ required: true, message: $t('pages.propsSourceGroupForm.validation.notEmpty'), trigger: 'blur'}"
>
<div><el-input v-model="item.quantity" v-number style="width: 100%;" placeholder="数量/天数" /></div>
<div><el-input v-model="item.quantity" v-number style="width: 100%;" :placeholder="$t('pages.propsSourceGroupForm.placeholder.quantityOrDays')" /></div>
</el-form-item>
</el-col>
<el-col :span="5">
@ -295,31 +295,31 @@
</div>
<div>
<el-button type="text" :disabled="disableAddContent" :loading="propsTypeData['AVATAR_FRAME'].loading" @click="addContent('AVATAR_FRAME')"><i class="el-icon-circle-plus" />头像框</el-button>
<el-button type="text" :disabled="disableAddContent" :loading="propsTypeData['RIDE'].loading" @click="addContent('RIDE')"><i class="el-icon-circle-plus" />座驾</el-button>
<el-button type="text" :disabled="disableAddContent" :loading="propsTypeData['NOBLE_VIP'].loading" @click="addContent('NOBLE_VIP')"><i class="el-icon-circle-plus" />贵族</el-button>
<el-button type="text" :disabled="disableAddContent" :loading="propsTypeData['THEME'].loading" @click="addContent('THEME')"><i class="el-icon-circle-plus" />房间背景主题</el-button>
<el-button type="text" :disabled="disableAddContent" :loading="propsTypeData['GIFT'].loading" @click="addContent('GIFT')"><i class="el-icon-circle-plus" />礼物</el-button>
<el-button type="text" :disabled="disableAddContent" @click="addContent('SPECIAL_ID')"><i class="el-icon-circle-plus" />靓号</el-button>
<el-button type="text" :disabled="disableAddContent" @click="addContent('GOLD')"><i class="el-icon-circle-plus" />金币</el-button>
<el-button type="text" :disabled="disableAddContent" @click="addContent('DIAMOND')"><i class="el-icon-circle-plus" />钻石</el-button>
<el-button type="text" :disabled="disableAddContent" @click="addContent('GAME_COUPON')"><i class="el-icon-circle-plus" />游戏券</el-button>
<el-button type="text" :disabled="disableAddContent" :loading="propsTypeData['BADGE'].loading" @click="addContent('BADGE')"><i class="el-icon-circle-plus" />用户徽章</el-button>
<el-button type="text" :disabled="disableAddContent" :loading="propsTypeData['ROOM_BADGE'].loading" @click="addContent('ROOM_BADGE')"><i class="el-icon-circle-plus" />房间徽章</el-button>
<el-button type="text" :disabled="disableAddContent" :loading="propsTypeData['EMOJI'].loading" @click="addContent('EMOJI')"><i class="el-icon-circle-plus" />表情包</el-button>
<el-button type="text" :disabled="disableAddContent" :loading="propsTypeData['CHAT_BUBBLE'].loading" @click="addContent('CHAT_BUBBLE')"><i class="el-icon-circle-plus" />聊天气泡</el-button>
<el-button type="text" :disabled="disableAddContent" :loading="propsTypeData['FLOAT_PICTURE'].loading" @click="addContent('FLOAT_PICTURE')"><i class="el-icon-circle-plus" />飘窗</el-button>
<el-button type="text" :disabled="disableAddContent" :loading="propsTypeData['CUSTOMIZE'].loading" @click="addContent('CUSTOMIZE')"><i class="el-icon-circle-plus" />自定义(只读)</el-button>
<el-button type="text" :disabled="disableAddContent" :loading="propsTypeData['FRAGMENTS'].loading" @click="addContent('FRAGMENTS')"><i class="el-icon-circle-plus" />碎片</el-button>
<el-button type="text" :disabled="disableAddContent" :loading="propsTypeData['DATA_CARD'].loading" @click="addContent('DATA_CARD')"><i class="el-icon-circle-plus" />资料卡</el-button>
<el-button type="text" :disabled="disableAddContent" :loading="propsTypeData['AVATAR_FRAME'].loading" @click="addContent('AVATAR_FRAME')"><i class="el-icon-circle-plus" />{{ $t('pages.propsSourceGroupForm.resourceType.avatarFrame') }}</el-button>
<el-button type="text" :disabled="disableAddContent" :loading="propsTypeData['RIDE'].loading" @click="addContent('RIDE')"><i class="el-icon-circle-plus" />{{ $t('pages.propsSourceGroupForm.resourceType.ride') }}</el-button>
<el-button type="text" :disabled="disableAddContent" :loading="propsTypeData['NOBLE_VIP'].loading" @click="addContent('NOBLE_VIP')"><i class="el-icon-circle-plus" />{{ $t('pages.propsSourceGroupForm.resourceType.nobleVip') }}</el-button>
<el-button type="text" :disabled="disableAddContent" :loading="propsTypeData['THEME'].loading" @click="addContent('THEME')"><i class="el-icon-circle-plus" />{{ $t('pages.propsSourceGroupForm.resourceType.theme') }}</el-button>
<el-button type="text" :disabled="disableAddContent" :loading="propsTypeData['GIFT'].loading" @click="addContent('GIFT')"><i class="el-icon-circle-plus" />{{ $t('pages.propsSourceGroupForm.resourceType.gift') }}</el-button>
<el-button type="text" :disabled="disableAddContent" @click="addContent('SPECIAL_ID')"><i class="el-icon-circle-plus" />{{ $t('pages.propsSourceGroupForm.resourceType.specialId') }}</el-button>
<el-button type="text" :disabled="disableAddContent" @click="addContent('GOLD')"><i class="el-icon-circle-plus" />{{ $t('pages.propsSourceGroupForm.resourceType.gold') }}</el-button>
<el-button type="text" :disabled="disableAddContent" @click="addContent('DIAMOND')"><i class="el-icon-circle-plus" />{{ $t('pages.propsSourceGroupForm.resourceType.diamond') }}</el-button>
<el-button type="text" :disabled="disableAddContent" @click="addContent('GAME_COUPON')"><i class="el-icon-circle-plus" />{{ $t('pages.propsSourceGroupForm.resourceType.gameCoupon') }}</el-button>
<el-button type="text" :disabled="disableAddContent" :loading="propsTypeData['BADGE'].loading" @click="addContent('BADGE')"><i class="el-icon-circle-plus" />{{ $t('pages.propsSourceGroupForm.resourceType.userBadge') }}</el-button>
<el-button type="text" :disabled="disableAddContent" :loading="propsTypeData['ROOM_BADGE'].loading" @click="addContent('ROOM_BADGE')"><i class="el-icon-circle-plus" />{{ $t('pages.propsSourceGroupForm.resourceType.roomBadge') }}</el-button>
<el-button type="text" :disabled="disableAddContent" :loading="propsTypeData['EMOJI'].loading" @click="addContent('EMOJI')"><i class="el-icon-circle-plus" />{{ $t('pages.propsSourceGroupForm.resourceType.emoji') }}</el-button>
<el-button type="text" :disabled="disableAddContent" :loading="propsTypeData['CHAT_BUBBLE'].loading" @click="addContent('CHAT_BUBBLE')"><i class="el-icon-circle-plus" />{{ $t('pages.propsSourceGroupForm.resourceType.chatBubble') }}</el-button>
<el-button type="text" :disabled="disableAddContent" :loading="propsTypeData['FLOAT_PICTURE'].loading" @click="addContent('FLOAT_PICTURE')"><i class="el-icon-circle-plus" />{{ $t('pages.propsSourceGroupForm.resourceType.floatPicture') }}</el-button>
<el-button type="text" :disabled="disableAddContent" :loading="propsTypeData['CUSTOMIZE'].loading" @click="addContent('CUSTOMIZE')"><i class="el-icon-circle-plus" />{{ $t('pages.propsSourceGroupForm.resourceType.customizeReadonly') }}</el-button>
<el-button type="text" :disabled="disableAddContent" :loading="propsTypeData['FRAGMENTS'].loading" @click="addContent('FRAGMENTS')"><i class="el-icon-circle-plus" />{{ $t('pages.propsSourceGroupForm.resourceType.fragments') }}</el-button>
<el-button type="text" :disabled="disableAddContent" :loading="propsTypeData['DATA_CARD'].loading" @click="addContent('DATA_CARD')"><i class="el-icon-circle-plus" />{{ $t('pages.propsSourceGroupForm.resourceType.dataCard') }}</el-button>
</div>
</el-form-item>
</el-form>
</div>
<div v-if="isAllowEdit" class="drawer-footer">
<el-button @click="handlePropsClose()">取消</el-button>
<el-button type="primary" :loading="submitLoading" @click="submitForm()">保存</el-button>
<el-button @click="handlePropsClose()">{{ $t('pages.propsSourceGroupForm.action.cancel') }}</el-button>
<el-button type="primary" :loading="submitLoading" @click="submitForm()">{{ $t('pages.propsSourceGroupForm.action.save') }}</el-button>
</div>
</el-drawer>
@ -354,7 +354,7 @@ export default {
},
data() {
const commonRules = [
{ required: true, message: '必填字段不可为空', trigger: 'blur' }
{ required: true, message: this.$t('pages.propsSourceGroupForm.validation.requiredField'), trigger: 'blur' }
]
return {
specialIdTypes,
@ -429,71 +429,71 @@ export default {
propsTypeMap: {
'EMOJI': {
value: 'EMOJI',
name: '用户-表情包'
labelKey: 'pages.propsSourceGroupForm.resourceType.emoji'
},
'BADGE': {
value: 'BADGE',
name: '用户-徽章'
labelKey: 'pages.propsSourceGroupForm.resourceType.userBadge'
},
'ROOM_BADGE': {
value: 'BADGE',
name: '房间-徽章'
labelKey: 'pages.propsSourceGroupForm.resourceType.roomBadge'
},
'AVATAR_FRAME': {
value: 'PROPS',
name: '头像框'
labelKey: 'pages.propsSourceGroupForm.resourceType.avatarFrame'
},
'DATA_CARD': {
value: 'PROPS',
name: '资料卡'
labelKey: 'pages.propsSourceGroupForm.resourceType.dataCard'
},
'CHAT_BUBBLE': {
value: 'PROPS',
name: '聊天气泡'
labelKey: 'pages.propsSourceGroupForm.resourceType.chatBubble'
},
'RIDE': {
value: 'PROPS',
name: '座驾'
labelKey: 'pages.propsSourceGroupForm.resourceType.ride'
},
'NOBLE_VIP': {
value: 'PROPS',
name: '贵族'
labelKey: 'pages.propsSourceGroupForm.resourceType.nobleVip'
},
'GIFT': {
value: 'GIFT',
name: '礼物'
labelKey: 'pages.propsSourceGroupForm.resourceType.gift'
},
'GOLD': {
value: 'GOLD',
name: '金币'
labelKey: 'pages.propsSourceGroupForm.resourceType.gold'
},
'DIAMOND': {
value: 'DIAMOND',
name: '钻石'
labelKey: 'pages.propsSourceGroupForm.resourceType.diamond'
},
'SPECIAL_ID': {
value: 'SPECIAL_ID',
name: '靓号'
labelKey: 'pages.propsSourceGroupForm.resourceType.specialId'
},
'THEME': {
value: 'PROPS',
name: '房间背景主题'
labelKey: 'pages.propsSourceGroupForm.resourceType.theme'
},
'GAME_COUPON': {
value: 'GAME_COUPON',
name: '游戏券'
labelKey: 'pages.propsSourceGroupForm.resourceType.gameCoupon'
},
'CUSTOMIZE': {
value: 'CUSTOMIZE',
name: '自定义(只读)'
labelKey: 'pages.propsSourceGroupForm.resourceType.customizeReadonly'
},
'FRAGMENTS': {
value: 'FRAGMENTS',
name: '碎片'
labelKey: 'pages.propsSourceGroupForm.resourceType.fragments'
},
'FLOAT_PICTURE': {
value: 'PROPS',
name: '飘窗'
labelKey: 'pages.propsSourceGroupForm.resourceType.floatPicture'
}
},
isAllowEdit: false,
@ -520,12 +520,15 @@ export default {
selectType: {},
isLoadSourceTypeList: false,
listTypeLoading: false,
badgeRestaurants: [{ 'value': '0', 'label': '永久' }]
badgeRestaurants: [{ 'value': '0', 'label': this.$t('pages.propsSourceGroupForm.word.permanent') }]
}
},
computed: {
textOptTitle() {
return (this.isAllowEdit ? '编辑' : '查看') + '(' + this.sysOrigin + ')'
const action = this.isAllowEdit
? this.$t('pages.propsSourceGroupForm.action.edit')
: this.$t('pages.propsSourceGroupForm.action.view')
return this.$t('pages.propsSourceGroupForm.dialog.title', { action, sysOrigin: this.sysOrigin })
}
},
watch: {
@ -635,10 +638,16 @@ export default {
getTypeName(item) {
const typeData = this.propsTypeMap[item.detailType]
if (typeData) {
return typeData.name
return this.$t(typeData.labelKey)
}
return ''
},
getConfigTitle(item) {
if (!item || !item.ext) {
return ''
}
return item.ext.titleKey ? this.$t(item.ext.titleKey) : item.ext.title || ''
},
deleteUpdateItem(index) {
this.form.rewardConfigList.splice(index, 1)
},
@ -802,7 +811,7 @@ export default {
quantity: '',
remark: '',
ext: {
title: that.propsTypeMap[type].name,
titleKey: that.propsTypeMap[type].labelKey,
selectVal: { id: '', cover: '', sourceUrl: '' }
}
})
@ -862,7 +871,7 @@ export default {
}
if (!that.form.rewardConfigList || that.form.rewardConfigList.length < 1) {
that.$opsMessage.fail('请资源组配置内容.')
that.$opsMessage.fail(this.$t('pages.propsSourceGroupForm.validation.configRequired'))
return
}
that.submitLoading = true

View File

@ -2,20 +2,20 @@
<div class="app-container">
<div v-loading="loading" class="compensate-order">
<el-alert
title="补偿订单"
:title="$t('pages.compensateOrder.section.order')"
type="info"
:closable="false"
style="margin-bottom: 20px;"
/>
<el-form ref="form" :model="form" :rules="rules" label-width="100px">
<el-form-item prop="userId" label="用户ID">
<el-form-item prop="userId" :label="$t('pages.compensateOrder.form.userId')">
<el-input v-model="form.userId" v-number />
</el-form-item>
<el-form-item prop="orderId" label="订单id">
<el-form-item prop="orderId" :label="$t('pages.compensateOrder.form.orderId')">
<el-input v-model.trim="form.orderId" />
</el-form-item>
<el-form-item label="平台" prop="sysOrigin">
<el-select v-model="form.sysOrigin" placeholder="请选择平台">
<el-form-item :label="$t('pages.compensateOrder.form.system')" prop="sysOrigin">
<el-select v-model="form.sysOrigin" :placeholder="$t('pages.compensateOrder.placeholder.selectSystem')">
<el-option
v-for="item in permissionsSysOriginPlatforms"
:key="item.value"
@ -24,8 +24,8 @@
/>
</el-select>
</el-form-item>
<el-form-item label="支付方式" prop="platform">
<el-select v-model="form.platform" placeholder="请选择平台">
<el-form-item :label="$t('pages.compensateOrder.form.paymentPlatform')" prop="platform">
<el-select v-model="form.platform" :placeholder="$t('pages.compensateOrder.placeholder.selectPaymentPlatform')">
<el-option
v-for="item in payPlatforms"
:key="item.value"
@ -35,10 +35,10 @@
/>
</el-select>
</el-form-item>
<el-form-item label="产品CODE" prop="productCode">
<el-form-item :label="$t('pages.compensateOrder.form.productCode')" prop="productCode">
<el-input v-model="form.productCode" />
</el-form-item>
<el-form-item label="过期时间" prop="vipExpireTime">
<el-form-item :label="$t('pages.compensateOrder.form.expireTime')" prop="vipExpireTime">
<el-date-picker
v-model="form.vipExpireTime"
value-format="timestamp"
@ -46,25 +46,25 @@
/>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="onSubmit">提交</el-button>
<el-button type="primary" @click="onSubmit">{{ $t('pages.compensateOrder.action.submit') }}</el-button>
</el-form-item>
</el-form>
<el-alert
title="补偿糖果"
:title="$t('pages.compensateOrder.section.candy')"
type="info"
:closable="false"
style="margin-bottom: 20px;"
/>
<el-form ref="form_candy" :model="formCandy" :rules="rulesCnady" label-width="100px">
<el-form-item prop="userId" label="用户ID">
<el-form-item prop="userId" :label="$t('pages.compensateOrder.form.userId')">
<el-input v-model="formCandy.userId" v-number />
</el-form-item>
<el-form-item prop="candy" label="糖果">
<el-form-item prop="candy" :label="$t('pages.compensateOrder.form.candy')">
<el-input v-model="formCandy.candy" v-number />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="onSubmitCandy">提交</el-button>
<el-button type="primary" @click="onSubmitCandy">{{ $t('pages.compensateOrder.action.submit') }}</el-button>
</el-form-item>
</el-form>
@ -95,29 +95,34 @@ function newFormCandy() {
export default {
name: 'CompensateOrder',
data() {
const commonValid = [{ required: true, message: '必填字段不可为空', trigger: 'blur' }]
return {
loading: false,
payPlatforms,
form: newFrom(),
formCandy: newFormCandy(),
rules: {
inputFixedNumber: 0
}
},
computed: {
...mapGetters(['permissionsSysOriginPlatforms']),
rules() {
const commonValid = [{ required: true, message: this.$t('pages.compensateOrder.validation.requiredField'), trigger: 'blur' }]
return {
userId: commonValid,
orderId: commonValid,
platform: commonValid,
productCode: commonValid,
sysOrigin: commonValid
},
rulesCnady: {
}
},
rulesCnady() {
const commonValid = [{ required: true, message: this.$t('pages.compensateOrder.validation.requiredField'), trigger: 'blur' }]
return {
userId: commonValid,
candy: commonValid
},
inputFixedNumber: 0
}
}
},
computed: {
...mapGetters(['permissionsSysOriginPlatforms'])
},
methods: {
onSubmit() {
const that = this
@ -126,9 +131,9 @@ export default {
console.error('error submit!!')
return false
}
that.$confirm('是否确认提交?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
that.$confirm(that.$t('pages.compensateOrder.confirm.submit'), that.$t('pages.compensateOrder.confirm.title'), {
confirmButtonText: that.$t('pages.compensateOrder.confirm.confirm'),
cancelButtonText: that.$t('pages.compensateOrder.confirm.cancel'),
type: 'warning'
}).then(() => {
that.loading = true
@ -150,9 +155,9 @@ export default {
console.error('error submit!!')
return false
}
that.$confirm('是否确认提交?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
that.$confirm(that.$t('pages.compensateOrder.confirm.submit'), that.$t('pages.compensateOrder.confirm.title'), {
confirmButtonText: that.$t('pages.compensateOrder.confirm.confirm'),
cancelButtonText: that.$t('pages.compensateOrder.confirm.cancel'),
type: 'warning'
}).then(() => {
that.loading = true

View File

@ -3,7 +3,7 @@
<div class="filter-container">
<el-select
v-model="listQuery.sysOrigin"
placeholder="系统"
:placeholder="$t('pages.goldAnalyze.placeholder.system')"
style="width: 120px"
class="filter-item"
@change="handleSearch"
@ -19,7 +19,7 @@
</el-option>
</el-select>
<div class="filter-item">
<account-input v-model="listQuery.userId" placeholder="用户ID" />
<account-input v-model="listQuery.userId" :placeholder="$t('pages.goldAnalyze.placeholder.userId')" />
</div>
<div class="filter-item">
<el-date-picker
@ -27,9 +27,9 @@
value-format="timestamp"
type="datetimerange"
:picker-options="pickerOptions"
range-separator=""
start-placeholder="注册开始日期"
end-placeholder="注册结束日期"
:range-separator="$t('pages.goldAnalyze.dateRange.separator')"
:start-placeholder="$t('pages.goldAnalyze.dateRange.start')"
:end-placeholder="$t('pages.goldAnalyze.dateRange.end')"
/>
</div>
<el-button
@ -39,7 +39,7 @@
:disabled="searchDisabled"
@click="handleSearch"
>
搜索
{{ $t('pages.goldAnalyze.action.search') }}
</el-button>
</div>
<el-card v-loading="goldAnalyzeIncomePieChartLoading" style="margin-bottom: 10px;">
@ -103,7 +103,9 @@ export default {
computed: {
...mapGetters(['permissionsSysOriginPlatforms']),
subtext() {
return this.listQuery.userId ? '用户:数据分配图' : '系统:数据分配图'
return this.listQuery.userId
? this.$t('pages.goldAnalyze.chart.userSubtext')
: this.$t('pages.goldAnalyze.chart.systemSubtext')
}
},
watch: {
@ -152,7 +154,7 @@ export default {
that.goldAnalyzeIncomePieChart.setOption({
color: that.color,
title: {
text: '金币收入',
text: that.$t('pages.goldAnalyze.chart.incomeTitle'),
subtext: that.subtext,
left: 'center'
},
@ -195,7 +197,7 @@ export default {
that.goldAnalyzeExpenditureieChart.setOption({
color: that.color,
title: {
text: '金币支出',
text: that.$t('pages.goldAnalyze.chart.expenditureTitle'),
subtext: that.subtext,
left: 'center'
},
@ -229,12 +231,12 @@ export default {
},
handleSearch() {
if (!this.rangeDate) {
this.$opsMessage.warn('请选择时间')
this.$opsMessage.warn(this.$t('pages.goldAnalyze.message.selectTime'))
return
}
const time = this.listQuery.endTime - this.listQuery.startTime
if (time > 2592000000) {
this.$opsMessage.warn('时间必须在1个月以内')
this.$opsMessage.warn(this.$t('pages.goldAnalyze.message.withinOneMonth'))
return
}
this.loadGoldIncome()

View File

@ -1,10 +1,10 @@
<template>
<div class="alias-method">
<el-button :loading="submitFormLoading" :disabled="submitFormLoading" @click.prevent="settingRequest()">设置属性</el-button>
<el-button v-if="attributeForm.bets.length > 0 && attributeForm.bets[0].name" :loading="submitFormLoading" :disabled="submitFormLoading" @click.prevent="submitForm()">重新运行</el-button>
<el-button :loading="submitFormLoading" :disabled="submitFormLoading" @click.prevent="settingRequest()">{{ $t('pages.toolsOther.action.setAttributes') }}</el-button>
<el-button v-if="attributeForm.bets.length > 0 && attributeForm.bets[0].name" :loading="submitFormLoading" :disabled="submitFormLoading" @click.prevent="submitForm()">{{ $t('pages.toolsOther.action.rerun') }}</el-button>
<div id="aliasMethodColumnarChart" ref="aliasMethodColumnarChart" style="width: 100%;height: 300px;" />
<el-dialog
title="设置概率"
:title="$t('pages.toolsOther.aliasMethod.dialogTitle')"
:visible.sync="paramContentVisible"
:modal-append-to-body="true"
:append-to-body="true"
@ -13,28 +13,28 @@
>
<div class="form-dialog">
<el-form ref="attributeForm" label-width="80px" :model="attributeForm" style="margin-right: 20px;">
<el-form-item label="概率说明">
<div>平均概率:1.0/奖品数量={{ 1.0 / attributeForm.bets.length }}</div>
<div>概率填充: 概率>=平均概率(进入large), 否则(进入small) </div>
<div>采样:
<div>1.如果设置概率全部小于平均概率,当前概率=1</div>
<div>2.如果设置概率 (当前最大概率+当前最小概率 - 平均概率) > 平均概率 继续采用large
出现情况: 我设置了概率0.9,0.8,0.3 但是0.3变成了大概率,由于概率设置过高导致不够平均取到的第一个概率被不断的采样;
<el-form-item :label="$t('pages.toolsOther.aliasMethod.probabilityDescription')">
<div>{{ $t('pages.toolsOther.aliasMethod.averageProbability', { value: 1.0 / attributeForm.bets.length }) }}</div>
<div>{{ $t('pages.toolsOther.aliasMethod.probabilityFill') }}</div>
<div>{{ $t('pages.toolsOther.aliasMethod.sampling') }}
<div>{{ $t('pages.toolsOther.aliasMethod.samplingRuleOne') }}</div>
<div>{{ $t('pages.toolsOther.aliasMethod.samplingRuleTwo') }}
{{ $t('pages.toolsOther.aliasMethod.samplingScenario') }}
</div>
<div>建议: 概率 + 库存这样比较保险安全</div>
<div>{{ $t('pages.toolsOther.aliasMethod.suggestion') }}</div>
</div>
</el-form-item>
<el-form-item label="运行次数">
<el-form-item :label="$t('pages.toolsOther.aliasMethod.runCount')">
<el-input v-model="attributeForm.runSize" v-number />
</el-form-item>
<el-row v-for="(item, index) in attributeForm.bets" :key="index" :gutter="10">
<el-col :span="12">
<el-form-item label="商品">
<el-form-item :label="$t('pages.toolsOther.aliasMethod.item')">
<el-input v-model="item.name" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="概率">
<el-form-item :label="$t('pages.toolsOther.aliasMethod.probability')">
<el-input v-model="item.probability" minlength="1" maxlength="10" />
</el-form-item>
</el-col>
@ -42,10 +42,10 @@
</el-form>
</div>
<span slot="footer" class="dialog-footer">
<el-button @click="cleanAttribute">清空属性</el-button>
<el-button @click="addAttribute">新增属性</el-button>
<el-button @click="addAttributeFill">快速填充</el-button>
<el-button type="primary" @click="submitAttribute"> </el-button>
<el-button @click="cleanAttribute">{{ $t('pages.toolsOther.action.clearAttributes') }}</el-button>
<el-button @click="addAttribute">{{ $t('pages.toolsOther.action.addAttribute') }}</el-button>
<el-button @click="addAttributeFill">{{ $t('pages.toolsOther.action.quickFill') }}</el-button>
<el-button type="primary" @click="submitAttribute">{{ $t('pages.toolsOther.action.confirm') }}</el-button>
</span>
</el-dialog>
</div>

View File

@ -1,10 +1,10 @@
<template>
<div class="api-sign">
<el-form ref="apiSingFrom" :model="apiSingFrom" :rules="rules" label-width="80px">
<el-form-item label="签名平台" prop="platform">
<el-form-item :label="$t('pages.toolsOther.apiSign.platform')" prop="platform">
<el-select
v-model="apiSingFrom.platform"
placeholder="平台"
:placeholder="$t('pages.toolsOther.apiSign.platformPlaceholder')"
style="width: 100%"
class="filter-item"
>
@ -16,22 +16,22 @@
/>
</el-select>
</el-form-item>
<el-form-item label="数据内容" prop="body">
<el-form-item :label="$t('pages.toolsOther.apiSign.body')" prop="body">
<el-input v-model="apiSingFrom.body" />
</el-form-item>
<el-form-item label="签名KEY" prop="key">
<el-form-item :label="$t('pages.toolsOther.apiSign.signKey')" prop="key">
<el-input v-model="apiSingFrom.key" />
</el-form-item>
<el-form-item>
<el-button type="primary" :loading="submitApiSignFormLoading" @click="submitApiSignForm()">提交</el-button>
<el-button @click.prevent="settingRequestApiSing()">设置属性</el-button>
<el-button type="primary" :loading="submitApiSignFormLoading" @click="submitApiSignForm()">{{ $t('common.submit') }}</el-button>
<el-button @click.prevent="settingRequestApiSing()">{{ $t('pages.toolsOther.action.setAttributes') }}</el-button>
</el-form-item>
</el-form>
<div v-if="apiSingParamContent" class="sgin-content">
{{ apiSingParamContent }}
</div>
<el-dialog
title="API内容属性"
:title="$t('pages.toolsOther.apiSign.dialogTitle')"
:visible.sync="apiSingParamContentVisible"
:modal-append-to-body="true"
:append-to-body="true"
@ -42,12 +42,12 @@
<el-row v-for="(item, index) in addSingAttributeForm.attributes" :key="index" :gutter="10">
<el-col :span="12">
<el-form-item label="键">
<el-form-item :label="$t('pages.toolsOther.apiSign.key')">
<el-input v-model="item.key" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="值">
<el-form-item :label="$t('pages.toolsOther.apiSign.value')">
<el-input v-model="item.value" />
</el-form-item>
</el-col>
@ -55,9 +55,9 @@
</el-form>
</div>
<span slot="footer" class="dialog-footer">
<el-button @click="cleanSingAttribute">清空属性</el-button>
<el-button @click="addSingAttribute">新增属性</el-button>
<el-button type="primary" @click="submitAddSingAttribute"> </el-button>
<el-button @click="cleanSingAttribute">{{ $t('pages.toolsOther.action.clearAttributes') }}</el-button>
<el-button @click="addSingAttribute">{{ $t('pages.toolsOther.action.addAttribute') }}</el-button>
<el-button type="primary" @click="submitAddSingAttribute">{{ $t('pages.toolsOther.action.confirm') }}</el-button>
</span>
</el-dialog>
</div>
@ -84,10 +84,14 @@ export default {
},
submitApiSignFormLoading: false,
apiSingParamContent: null,
rules: {
platform: [{ required: true, message: '必填参数不可为空', trigger: 'blur' }],
body: [{ required: true, message: '必填参数不可为空', trigger: 'blur' }],
key: [{ required: true, message: '必填参数不可为空', trigger: 'blur' }]
}
},
computed: {
rules() {
return {
platform: [{ required: true, message: this.$t('pages.toolsOther.validation.requiredParam'), trigger: 'blur' }],
body: [{ required: true, message: this.$t('pages.toolsOther.validation.requiredParam'), trigger: 'blur' }],
key: [{ required: true, message: this.$t('pages.toolsOther.validation.requiredParam'), trigger: 'blur' }]
}
}
},

View File

@ -2,7 +2,7 @@
<div class="app-container">
<el-card class="box-card" style="margin-top: 10px">
<div slot="header" class="clearfix">
<span>用户账户调换</span>
<span>{{ $t('pages.toolsOther.cards.userSwap') }}</span>
</div>
<div class="content">
<user-swap-user />
@ -10,7 +10,7 @@
</el-card>
<el-card class="box-card" style="margin-top: 10px">
<div slot="header" class="clearfix">
<span>API参数签名</span>
<span>{{ $t('pages.toolsOther.cards.apiSign') }}</span>
</div>
<div class="content">
<api-sign />
@ -18,7 +18,7 @@
</el-card>
<el-card class="box-card" style="margin-top: 10px">
<div slot="header" class="clearfix">
<span>Alias Method分析</span>
<span>{{ $t('pages.toolsOther.cards.aliasMethod') }}</span>
</div>
<div class="content">
<alias-method />
@ -27,7 +27,7 @@
<el-card class="box-card" style="margin-top: 10px">
<div slot="header" class="clearfix">
<span>用户令牌解析</span>
<span>{{ $t('pages.toolsOther.cards.tokenParse') }}</span>
</div>
<div class="content">
<token-parse />
@ -36,7 +36,7 @@
<el-card class="box-card" style="margin-top: 10px">
<div slot="header" class="clearfix">
<span>Redis操作</span>
<span>{{ $t('pages.toolsOther.cards.redisOps') }}</span>
</div>
<div class="content">
<redis-ops />
@ -45,7 +45,7 @@
<el-card class="box-card" style="margin-top: 10px">
<div slot="header" class="clearfix">
<span>用户房间区域变动</span>
<span>{{ $t('pages.toolsOther.cards.userRoomRegion') }}</span>
</div>
<div class="content">
<user-room-region />
@ -54,7 +54,7 @@
<el-card class="box-card" style="margin-top: 10px">
<div slot="header" class="clearfix">
<span>注销用户恢复</span>
<span>{{ $t('pages.toolsOther.cards.userCancelRestore') }}</span>
</div>
<div class="content">
<user-cancel-and-restore />

View File

@ -1,14 +1,14 @@
<template>
<div class="redis-ops">
<el-form ref="form" label-width="80px" :model="form" :rules="rules" style="margin-right: 20px;">
<el-form-item label="模式">
<el-radio v-model="model" label="CLEAR_KEYS">清理key</el-radio>
<el-form-item :label="$t('pages.toolsOther.form.mode')">
<el-radio v-model="model" label="CLEAR_KEYS">{{ $t('pages.toolsOther.redisOps.clearKey') }}</el-radio>
</el-form-item>
<el-form-item v-if="model === 'CLEAR_KEYS'" label="Key" prop="userId">
<el-input v-model="form.userId" placeholder="请输入Key信息" />
<el-input v-model="form.userId" :placeholder="$t('pages.toolsOther.placeholder.enterKey')" />
</el-form-item>
<el-form-item>
<el-button type="primary" :disabled="formLoading" :loading="formLoading" @click="submit"> </el-button>
<el-button type="primary" :disabled="formLoading" :loading="formLoading" @click="submit">{{ $t('pages.toolsOther.action.confirm') }}</el-button>
</el-form-item>
</el-form>
</div>
@ -24,17 +24,21 @@ export default {
form: {
key: ''
},
rules: {
key: [{
required: true,
message: '必填字段不可为空',
trigger: 'blur'
}]
},
formLoading: false,
formResult: {}
}
},
computed: {
rules() {
return {
key: [{
required: true,
message: this.$t('pages.toolsOther.validation.requiredField'),
trigger: 'blur'
}]
}
}
},
mounted() {
},
methods: {

View File

@ -1,19 +1,19 @@
<template>
<div class="token-parse">
<el-form ref="form" label-width="80px" :model="form" :rules="rules" style="margin-right: 20px;">
<el-form-item label="模式">
<el-radio v-model="model" label="TOKEN_PARSE">令牌解析</el-radio>
<el-radio v-model="model" label="GET_TOKEN">获取令牌</el-radio>
<el-form-item :label="$t('pages.toolsOther.form.mode')">
<el-radio v-model="model" label="TOKEN_PARSE">{{ $t('pages.toolsOther.tokenParse.parseToken') }}</el-radio>
<el-radio v-model="model" label="GET_TOKEN">{{ $t('pages.toolsOther.tokenParse.getToken') }}</el-radio>
</el-form-item>
<el-form-item v-if="model === 'TOKEN_PARSE'" label="令牌" prop="token">
<el-input v-model="form.token" placeholder="请输入用户令牌" />
<el-form-item v-if="model === 'TOKEN_PARSE'" :label="$t('pages.toolsOther.tokenParse.token')" prop="token">
<el-input v-model="form.token" :placeholder="$t('pages.toolsOther.placeholder.enterUserToken')" />
</el-form-item>
<el-form-item v-if="model === 'GET_TOKEN'" label="用户ID" prop="userId">
<el-input v-model="form.userId" placeholder="请输入用户ID" />
<el-form-item v-if="model === 'GET_TOKEN'" :label="$t('pages.toolsOther.form.userId')" prop="userId">
<el-input v-model="form.userId" :placeholder="$t('pages.toolsOther.placeholder.enterUserId')" />
</el-form-item>
<el-form-item>
<el-button type="primary" :disabled="formLoading" :loading="formLoading" @click="submit"> </el-button>
<el-button type="primary" :disabled="formLoading" :loading="formLoading" @click="submit">{{ $t('pages.toolsOther.action.confirm') }}</el-button>
</el-form-item>
<div v-if="formDataShow">
@ -34,23 +34,27 @@ export default {
token: '',
userId: ''
},
rules: {
formLoading: false,
formResult: {},
formDataShow: false
}
},
computed: {
rules() {
return {
token: [{
required: true,
message: '必填字段不可为空',
message: this.$t('pages.toolsOther.validation.requiredField'),
trigger: 'blur'
}],
userId: [
{
required: true,
message: '必填字段不可为空',
message: this.$t('pages.toolsOther.validation.requiredField'),
trigger: 'blur'
}
]
},
formLoading: false,
formResult: {},
formDataShow: false
}
}
},
mounted() {

View File

@ -1,12 +1,12 @@
<template>
<div class="user-swap-user">
<el-form ref="userSwapUser" :model="form" :rules="rules" label-width="80px">
<el-form-item label="用户长ID" prop="sourceUserId">
<el-input v-model="form.userId" v-number placeholder="请输入原始用户长ID" />
<el-form-item :label="$t('pages.toolsOther.form.longUserId')" prop="sourceUserId">
<el-input v-model="form.userId" v-number :placeholder="$t('pages.toolsOther.placeholder.enterSourceLongUserId')" />
</el-form-item>
<el-form-item>
<el-button type="primary" :loading="checkCondtionLoading" @click="checkCondition()">确定</el-button>
<el-button type="primary" :loading="checkCondtionLoading" @click="checkCondition()">{{ $t('pages.toolsOther.action.confirm') }}</el-button>
</el-form-item>
</el-form>
</div>
@ -23,8 +23,12 @@ export default {
userId: '',
countryId: '11'
},
rules: {
userId: [{ required: true, message: '必填参数不可为空', trigger: 'blur' }]
}
},
computed: {
rules() {
return {
userId: [{ required: true, message: this.$t('pages.toolsOther.validation.requiredParam'), trigger: 'blur' }]
}
}
},

View File

@ -1,14 +1,14 @@
<template>
<div class="user-swap-user">
<el-form ref="userSwapUser" :model="form" :rules="rules" label-width="80px">
<el-form-item label="用户长ID" prop="userId">
<el-input v-model="form.userId" v-number placeholder="请输入原始用户长ID" />
<el-form-item :label="$t('pages.toolsOther.form.longUserId')" prop="userId">
<el-input v-model="form.userId" v-number :placeholder="$t('pages.toolsOther.placeholder.enterSourceLongUserId')" />
</el-form-item>
<el-form-item label="国家" prop="countryId">
<el-form-item :label="$t('pages.toolsOther.form.country')" prop="countryId">
<el-select
v-model="form.countryId"
placeholder="请选择国家"
:placeholder="$t('pages.toolsOther.placeholder.selectCountry')"
filterable
:filter-method="userFilter"
clearabl
@ -22,7 +22,7 @@
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" :loading="checkCondtionLoading" @click="checkCondition()">确定</el-button>
<el-button type="primary" :loading="checkCondtionLoading" @click="checkCondition()">{{ $t('pages.toolsOther.action.confirm') }}</el-button>
</el-form-item>
</el-form>
</div>
@ -46,13 +46,17 @@ export default {
userId: '',
countryId: ''
},
rules: {
userId: [{ required: true, message: '必填参数不可为空', trigger: 'blur' }],
countryId: [{ required: true, message: '必填参数不可为空', trigger: 'blur' }]
},
submitExecuteLoading: false
}
},
computed: {
rules() {
return {
userId: [{ required: true, message: this.$t('pages.toolsOther.validation.requiredParam'), trigger: 'blur' }],
countryId: [{ required: true, message: this.$t('pages.toolsOther.validation.requiredParam'), trigger: 'blur' }]
}
}
},
created() {
this.getUserWhiteList();
},

View File

@ -1,14 +1,14 @@
<template>
<div class="user-swap-user">
<el-form ref="userSwapUser" :model="form" :rules="rules" label-width="80px">
<el-form-item label="原始ID" prop="sourceUserId">
<el-input v-model="form.sourceUserId" v-number placeholder="请输入原始用户长ID" />
<el-form-item :label="$t('pages.toolsOther.userSwap.sourceId')" prop="sourceUserId">
<el-input v-model="form.sourceUserId" v-number :placeholder="$t('pages.toolsOther.placeholder.enterSourceLongUserId')" />
</el-form-item>
<el-form-item label="目标ID" prop="targetUserId">
<el-input v-model="form.targetUserId" v-number placeholder="请输入目标用户长ID" />
<el-form-item :label="$t('pages.toolsOther.userSwap.targetId')" prop="targetUserId">
<el-input v-model="form.targetUserId" v-number :placeholder="$t('pages.toolsOther.placeholder.enterTargetLongUserId')" />
</el-form-item>
<el-form-item>
<el-button type="primary" :loading="checkCondtionLoading" @click="checkCondition()">验证转换条件</el-button>
<el-button type="primary" :loading="checkCondtionLoading" @click="checkCondition()">{{ $t('pages.toolsOther.userSwap.checkCondition') }}</el-button>
</el-form-item>
</el-form>
@ -16,10 +16,10 @@
<div class="check-tips">
<div v-for="(item, index) in checkResults" :key="index" class="tips" :class="item.type">
<i :class="{'el-icon-error': item.type === 'error','el-icon-warning': item.type === 'warn'}" />
{{ item.desc }} {{ checkTips }}
{{ $t(item.descKey) }} {{ checkTips }}
</div>
</div>
<el-button v-if="!notAollwSubmit" type="primary" @click="submitExecute()">提交执行</el-button>
<el-button v-if="!notAollwSubmit" type="primary" @click="submitExecute()">{{ $t('pages.toolsOther.userSwap.submitExecute') }}</el-button>
</div>
</div>
</template>
@ -38,39 +38,43 @@ export default {
sourceUserId: '',
targetUserId: ''
},
rules: {
sourceUserId: [{ required: true, message: '必填参数不可为空', trigger: 'blur' }],
targetUserId: [{ required: true, message: '必填参数不可为空', trigger: 'blur' }]
},
tipsMap: {
'SOURCE_USER_IS_NULL': {
type: 'error',
desc: '原始用户没有找到'
descKey: 'pages.toolsOther.userSwap.tips.sourceUserNotFound'
},
'TARGET_USER_IS_NULL': {
type: 'error',
desc: '目标用户没有找到'
descKey: 'pages.toolsOther.userSwap.tips.targetUserNotFound'
},
'ORIGIN_NOT_EQUALS': {
type: 'warn',
desc: '用户来自不同系统'
descKey: 'pages.toolsOther.userSwap.tips.differentSystem'
},
'SOURCE_USER_AUTH_IS_NULL': {
type: 'error',
desc: '原始用户认证信息没找到'
descKey: 'pages.toolsOther.userSwap.tips.sourceAuthNotFound'
},
'TARGET_USER_AUTH_IS_NULL': {
type: 'error',
desc: '目标用户认证信息没找到'
descKey: 'pages.toolsOther.userSwap.tips.targetAuthNotFound'
},
'AUTH_OPENID_NOT_EQUALS': {
type: 'warn',
desc: '注册来源OpenId不一致'
descKey: 'pages.toolsOther.userSwap.tips.openIdMismatch'
}
},
submitExecuteLoading: false
}
},
computed: {
rules() {
return {
sourceUserId: [{ required: true, message: this.$t('pages.toolsOther.validation.requiredParam'), trigger: 'blur' }],
targetUserId: [{ required: true, message: this.$t('pages.toolsOther.validation.requiredParam'), trigger: 'blur' }]
}
}
},
methods: {
submitExecute() {
const that = this

View File

@ -4,57 +4,57 @@
<el-col :span="12">
<el-card class="box-card">
<div slot="header" class="clearfix">
<span>订阅广播消息</span>
<el-button style="float: right; padding: 3px; margin: 0 3px" type="success" @click="addRepository(socketSubForm)">添加仓库</el-button>
<el-button style="float: right; padding: 3px; margin: 0 3px" type="success" @click="sendMessage(socketSubForm)">发送消息</el-button>
<el-button style="float: right; padding: 3px; margin: 0 3px" type="warning" @click="subscribeSocket(socketSubForm)">订阅主题</el-button>
<el-button style="float: right; padding: 3px; margin: 0 3px" type="danger" @click="disconnect(socketSubForm)">断开连接</el-button>
<el-button style="float: right; padding: 3px; margin: 0 3px" type="primary" @click="createConnect(socketSubForm)">进行连接</el-button>
<span>{{ $t('pages.websocketTool.broadcast.title') }}</span>
<el-button style="float: right; padding: 3px; margin: 0 3px" type="success" @click="addRepository(socketSubForm)">{{ $t('pages.websocketTool.action.addRepository') }}</el-button>
<el-button style="float: right; padding: 3px; margin: 0 3px" type="success" @click="sendMessage(socketSubForm)">{{ $t('pages.websocketTool.action.sendMessage') }}</el-button>
<el-button style="float: right; padding: 3px; margin: 0 3px" type="warning" @click="subscribeSocket(socketSubForm)">{{ $t('pages.websocketTool.action.subscribeTopic') }}</el-button>
<el-button style="float: right; padding: 3px; margin: 0 3px" type="danger" @click="disconnect(socketSubForm)">{{ $t('pages.websocketTool.action.disconnect') }}</el-button>
<el-button style="float: right; padding: 3px; margin: 0 3px" type="primary" @click="createConnect(socketSubForm)">{{ $t('pages.websocketTool.action.connect') }}</el-button>
</div>
<div class="card-content">
<div style="padding: 10px 80px;">
创建连接 -> 订阅主题 -> 发送消息
{{ $t('pages.websocketTool.step.flow') }}
</div>
<el-form ref="socketSubForm" :model="socketSubForm" :rules="socketSubFormRules" label-width="120px">
<el-form-item label="连接端点" prop="socketEndpoint">
<el-input v-model="socketSubForm.socketEndpoint" placeholder="连接端点" />
<el-form-item :label="$t('pages.websocketTool.form.socketEndpoint')" prop="socketEndpoint">
<el-input v-model="socketSubForm.socketEndpoint" :placeholder="$t('pages.websocketTool.placeholder.socketEndpoint')" />
</el-form-item>
<el-form-item label="订阅地址" prop="subscribePatch">
<el-input v-model="socketSubForm.subscribePatch" placeholder="订阅地址" />
<el-form-item :label="$t('pages.websocketTool.form.subscribePath')" prop="subscribePatch">
<el-input v-model="socketSubForm.subscribePatch" :placeholder="$t('pages.websocketTool.placeholder.subscribePath')" />
</el-form-item>
<el-form-item label="发送接口" prop="sendEndpoint">
<el-input v-model="socketSubForm.sendEndpoint" placeholder="发送接口" />
<el-form-item :label="$t('pages.websocketTool.form.sendEndpoint')" prop="sendEndpoint">
<el-input v-model="socketSubForm.sendEndpoint" :placeholder="$t('pages.websocketTool.placeholder.sendEndpoint')" />
</el-form-item>
<el-form-item label="我的token" prop="meUserId">
<el-input v-model="socketSubForm.meUserId" placeholder="我的token" />
<el-form-item :label="$t('pages.websocketTool.form.myToken')" prop="meUserId">
<el-input v-model="socketSubForm.meUserId" :placeholder="$t('pages.websocketTool.placeholder.myToken')" />
</el-form-item>
<el-form-item label="消息内容" prop="content">
<el-form-item :label="$t('pages.websocketTool.form.content')" prop="content">
<el-input
v-model="socketSubForm.content"
type="textarea"
placeholder="消息内容"
:placeholder="$t('pages.websocketTool.placeholder.content')"
maxlength="300"
resize="none"
show-word-limit
/>
</el-form-item>
<el-form-item label="仓库端点" prop="repositoryEndpoint">
<el-input v-model="socketSubForm.repositoryEndpoint" placeholder="仓库端点" />
<el-form-item :label="$t('pages.websocketTool.form.repositoryEndpoint')" prop="repositoryEndpoint">
<el-input v-model="socketSubForm.repositoryEndpoint" :placeholder="$t('pages.websocketTool.placeholder.repositoryEndpoint')" />
</el-form-item>
<el-form-item label="仓库数据" prop="repositoryData">
<el-form-item :label="$t('pages.websocketTool.form.repositoryData')" prop="repositoryData">
<el-input
v-model="socketSubForm.repositoryData"
type="textarea"
placeholder="插入仓库数据"
:placeholder="$t('pages.websocketTool.placeholder.repositoryData')"
maxlength="300"
resize="none"
show-word-limit
/>
</el-form-item>
<el-form-item label="状态描述">
<el-form-item :label="$t('pages.websocketTool.form.statusDescription')">
{{ socketSubForm.message }}
</el-form-item>
<el-form-item label="消息内容">
<el-form-item :label="$t('pages.websocketTool.form.content')">
<div class="message-content">
<div v-for="(item, index) in socketSubForm.msgContents" :key="index">{{ item }}</div>
</div>
@ -66,49 +66,49 @@
<el-col :span="12">
<el-card class="box-card">
<div slot="header" class="clearfix">
<span>点对点</span>
<el-button style="float: right; padding: 3px; margin: 0 3px" type="success" @click="sendMessage(socketToUserForm)">发送消息</el-button>
<el-button style="float: right; padding: 3px; margin: 0 3px" type="warning" @click="subscribeSocket(socketToUserForm)">订阅主题</el-button>
<el-button style="float: right; padding: 3px; margin: 0 3px" type="danger" @click="disconnect(socketToUserForm)">断开连接</el-button>
<el-button style="float: right; padding: 3px; margin: 0 3px" type="primary" @click="createConnect(socketToUserForm)">进行连接</el-button>
<span>{{ $t('pages.websocketTool.direct.title') }}</span>
<el-button style="float: right; padding: 3px; margin: 0 3px" type="success" @click="sendMessage(socketToUserForm)">{{ $t('pages.websocketTool.action.sendMessage') }}</el-button>
<el-button style="float: right; padding: 3px; margin: 0 3px" type="warning" @click="subscribeSocket(socketToUserForm)">{{ $t('pages.websocketTool.action.subscribeTopic') }}</el-button>
<el-button style="float: right; padding: 3px; margin: 0 3px" type="danger" @click="disconnect(socketToUserForm)">{{ $t('pages.websocketTool.action.disconnect') }}</el-button>
<el-button style="float: right; padding: 3px; margin: 0 3px" type="primary" @click="createConnect(socketToUserForm)">{{ $t('pages.websocketTool.action.connect') }}</el-button>
</div>
<div class="card-content">
<div style="padding: 10px 80px;">
创建连接 -> 订阅主题 -> 发送消息
{{ $t('pages.websocketTool.step.flow') }}
</div>
<el-form ref="socketToUserForm" :model="socketToUserForm" :rules="socketToUserFormules" label-width="120px">
<el-form-item label="连接端点" prop="socketEndpoint">
<el-input v-model="socketToUserForm.socketEndpoint" placeholder="连接端点" />
<el-form-item :label="$t('pages.websocketTool.form.socketEndpoint')" prop="socketEndpoint">
<el-input v-model="socketToUserForm.socketEndpoint" :placeholder="$t('pages.websocketTool.placeholder.socketEndpoint')" />
</el-form-item>
<el-form-item label="订阅前缀" prop="subscribeUserPreffix">
<el-input v-model="socketToUserForm.subscribeUserPreffix" placeholder="订阅前缀" />
<el-form-item :label="$t('pages.websocketTool.form.subscribePrefix')" prop="subscribeUserPreffix">
<el-input v-model="socketToUserForm.subscribeUserPreffix" :placeholder="$t('pages.websocketTool.placeholder.subscribePrefix')" />
</el-form-item>
<el-form-item label="订阅地址" prop="subscribePatch">
<el-input v-model="socketToUserForm.subscribePatch" placeholder="订阅地址" />
<el-form-item :label="$t('pages.websocketTool.form.subscribePath')" prop="subscribePatch">
<el-input v-model="socketToUserForm.subscribePatch" :placeholder="$t('pages.websocketTool.placeholder.subscribePath')" />
</el-form-item>
<el-form-item label="发送接口" prop="sendEndpoint">
<el-input v-model="socketToUserForm.sendEndpoint" placeholder="发送接口" />
<el-form-item :label="$t('pages.websocketTool.form.sendEndpoint')" prop="sendEndpoint">
<el-input v-model="socketToUserForm.sendEndpoint" :placeholder="$t('pages.websocketTool.placeholder.sendEndpoint')" />
</el-form-item>
<el-form-item label="我的token" prop="meUserId">
<el-input v-model="socketToUserForm.meUserId" placeholder="我的token" />
<el-form-item :label="$t('pages.websocketTool.form.myToken')" prop="meUserId">
<el-input v-model="socketToUserForm.meUserId" :placeholder="$t('pages.websocketTool.placeholder.myToken')" />
</el-form-item>
<el-form-item label="接收用户ID" prop="targetUser">
<el-input v-model="socketToUserForm.targetUser" placeholder="接收用户长ID" />
<el-form-item :label="$t('pages.websocketTool.form.targetUserId')" prop="targetUser">
<el-input v-model="socketToUserForm.targetUser" :placeholder="$t('pages.websocketTool.placeholder.targetUserId')" />
</el-form-item>
<el-form-item label="消息内容" prop="content">
<el-form-item :label="$t('pages.websocketTool.form.content')" prop="content">
<el-input
v-model="socketToUserForm.content"
type="textarea"
placeholder="消息内容"
:placeholder="$t('pages.websocketTool.placeholder.content')"
maxlength="300"
resize="none"
show-word-limit
/>
</el-form-item>
<el-form-item label="状态描述">
<el-form-item :label="$t('pages.websocketTool.form.statusDescription')">
{{ socketToUserForm.message }}
</el-form-item>
<el-form-item label="消息内容">
<el-form-item :label="$t('pages.websocketTool.form.content')">
<div class="message-content">
<div v-for="(item, index) in socketToUserForm.msgContents" :key="index">{{ item }}</div>
</div>
@ -127,7 +127,6 @@ import Stomp from 'stompjs'
export default {
name: 'Dashboard',
data() {
const commonRules = [{ required: true, message: '必填字段', trigger: 'blur' }]
return {
socketSubForm: {
// socketEndpoint: 'https://web.sugartimeapp.com/game_api/socket_stomp/game',
@ -144,12 +143,6 @@ export default {
msgContents: [],
ruleFormName: 'socketSubForm'
},
socketSubFormRules: {
socketEndpoint: commonRules,
subscribePatch: commonRules,
sendEndpoint: commonRules,
meUserId: commonRules
},
socketToUserForm: {
socketEndpoint: 'http://192.168.31.179:9000/socket_stomp/game',
@ -164,18 +157,31 @@ export default {
message: '',
msgContents: [],
ruleFormName: 'socketToUserForm'
},
socketToUserFormules: {
socketEndpoint: commonRules,
subscribePatch: commonRules,
sendEndpoint: commonRules,
targetUser: commonRules,
meUserId: commonRules,
subscribeUserPreffix: commonRules
}
}
},
computed: {
commonRules() {
return [{ required: true, message: this.$t('pages.websocketTool.validation.requiredField'), trigger: 'blur' }]
},
socketSubFormRules() {
return {
socketEndpoint: this.commonRules,
subscribePatch: this.commonRules,
sendEndpoint: this.commonRules,
meUserId: this.commonRules
}
},
socketToUserFormules() {
return {
socketEndpoint: this.commonRules,
subscribePatch: this.commonRules,
sendEndpoint: this.commonRules,
targetUser: this.commonRules,
meUserId: this.commonRules,
subscribeUserPreffix: this.commonRules
}
}
},
methods: {
createConnect(form) {
@ -185,25 +191,25 @@ export default {
console.log('error submit!!')
return
}
// SOCKET
// Configure the socket connection.
const socket = new SockJS(form.socketEndpoint + `?token=${form.meUserId}&destination=${form.subscribePatch}`)
// STOMP
// Configure the STOMP client.
form.stompClient = Stomp.over(socket)
// STOMP
// Connect the STOMP client.
form.stompClient.connect({}, frame => {
form.message = '连接成功'
form.message = this.$t('pages.websocketTool.message.connected')
})
})
},
subscribeSocket(form) {
if (!form.stompClient) {
this.$opsMessage.fail('请先进行连接.')
this.$opsMessage.fail(this.$t('pages.websocketTool.message.connectFirst'))
return
}
form.message = form.subscribePatch
//
// Subscribe to the message channel.
form.stompClient.subscribe(form.subscribeUserPreffix ? form.subscribeUserPreffix + form.subscribePatch : form.subscribePatch, (responseBody) => {
console.log('接收:', responseBody)
console.log('Received:', responseBody)
var receiveMessage = JSON.parse(responseBody.body)
form.msgContents.push(receiveMessage.content)
})
@ -212,45 +218,45 @@ export default {
if (!form.stompClient) {
return
}
form.stompClient.disconnect(function() {
form.message = '断开连接'
form.stompClient.disconnect(() => {
form.message = this.$t('pages.websocketTool.message.disconnected')
})
},
sendMessage(form) {
if (!form.stompClient) {
this.$opsMessage.fail('请先创建连接.')
this.$opsMessage.fail(this.$t('pages.websocketTool.message.createFirst'))
return
}
if (!form.content) {
this.$opsMessage.fail('请输入消息内容.')
this.$opsMessage.fail(this.$t('pages.websocketTool.message.enterMessageContent'))
return
}
//
// Prepare the message payload to send.
var message = {
destination: form.subscribePatch,
content: form.content,
targetUser: form.targetUser
}
console.log('向端点:', form.sendEndpoint, '发送参数:', message)
//
console.log('Sending to endpoint:', form.sendEndpoint, 'Payload:', message)
// Send the message.
form.stompClient.send(form.sendEndpoint, {}, JSON.stringify(message))
},
addRepository(form) {
if (!form.stompClient) {
this.$opsMessage.fail('请先创建连接.')
this.$opsMessage.fail(this.$t('pages.websocketTool.message.createFirst'))
return
}
if (!form.repositoryData) {
this.$opsMessage.fail('请输入消息内容.')
this.$opsMessage.fail(this.$t('pages.websocketTool.message.enterMessageContent'))
return
}
//
// Prepare the repository payload to send.
var message = {
key: 'TEST',
body: form.repositoryData
}
console.log('向端点:', form.repositoryEndpoint, '发送参数:', message)
//
console.log('Sending to endpoint:', form.repositoryEndpoint, 'Payload:', message)
// Send the message.
form.stompClient.send(form.repositoryEndpoint, {}, JSON.stringify(message))
}
}

View File

@ -4,7 +4,7 @@
<div class="filter-container">
<el-select
v-model="listQuery.sysOrigin"
placeholder="系统"
:placeholder="$t('pages.userCustomGift.filter.system')"
style="width:120px;"
class="filter-item"
@change="changeSysOrigin"
@ -20,34 +20,38 @@
</el-option>
</el-select>
<div class="filter-item">
<account-input v-model="listQuery.userId" :sys-origin="listQuery.sysOrigin" placeholder="用户ID" />
<account-input
v-model="listQuery.userId"
:sys-origin="listQuery.sysOrigin"
:placeholder="$t('pages.userCustomGift.filter.userId')"
/>
</div>
<el-select
v-model="listQuery.approveStatus"
placeholder="状态"
:placeholder="$t('pages.userCustomGift.filter.status')"
style="width: 120px"
class="filter-item"
clearable
@change="handleSearch"
>
<el-option label="待处理" :value="0" />
<el-option label="已处理" :value="1" />
<el-option :label="$t('pages.userCustomGift.status.pending')" :value="0" />
<el-option :label="$t('pages.userCustomGift.status.processed')" :value="1" />
</el-select>
<el-select
v-model="listQuery.customType"
placeholder="类型"
:placeholder="$t('pages.userCustomGift.filter.type')"
style="width: 120px"
class="filter-item"
clearable
@change="handleSearch"
>
<el-option label="专属定制" :value="'UPLOAD_GIFT'" />
<el-option label="增加时长" :value="'INCREASE_TIME'" />
<el-option :label="$t('pages.userCustomGift.type.uploadGift')" :value="'UPLOAD_GIFT'" />
<el-option :label="$t('pages.userCustomGift.type.increaseTime')" :value="'INCREASE_TIME'" />
</el-select>
<el-select
v-model="listQuery.region"
v-loading="loading"
placeholder="区域"
:placeholder="$t('pages.userCustomGift.filter.region')"
style="width:120px;"
class="filter-item"
clearable
@ -66,39 +70,39 @@
icon="el-icon-search"
@click="handleSearch"
>
搜索
{{ $t('pages.userCustomGift.action.search') }}
</el-button>
</div>
<el-table
v-loading="listLoading"
:data="list"
element-loading-text="Loading"
:element-loading-text="$t('pages.userCustomGift.loading')"
fit
highlight-current-row
@cell-mouse-enter="handleMouseEnter"
>
<el-table-column label="归属系统" align="center">
<el-table-column :label="$t('pages.userCustomGift.table.system')" align="center">
<template slot-scope="scope">
<sys-origin-icon :icon="scope.row.sysOrigin" :desc="scope.row.sysOrigin" />
</template>
</el-table-column>
<el-table-column label="用户" align="center" min-width="150">
<el-table-column :label="$t('pages.userCustomGift.table.user')" align="center" min-width="150">
<template slot-scope="scope">
<user-table-exhibit :user-profile="scope.row.userBaseInfo" :query-details="true" />
</template>
</el-table-column>
<el-table-column prop="regionNameStr" label="区域" align="center" />
<el-table-column label="类型" align="center">
<el-table-column prop="regionNameStr" :label="$t('pages.userCustomGift.table.region')" align="center" />
<el-table-column :label="$t('pages.userCustomGift.table.type')" align="center">
<template slot-scope="scope">
<div>
<el-tag v-if="scope.row.customType === 'UPLOAD_GIFT'">专属定制</el-tag>
<el-tag v-else-if="scope.row.customType === 'INCREASE_TIME'">增加时长</el-tag>
<el-tag v-if="scope.row.customType === 'UPLOAD_GIFT'">{{ $t('pages.userCustomGift.type.uploadGift') }}</el-tag>
<el-tag v-else-if="scope.row.customType === 'INCREASE_TIME'">{{ $t('pages.userCustomGift.type.increaseTime') }}</el-tag>
</div>
</template>
</el-table-column>
<el-table-column prop="giftCode" label="礼物Code" align="center" />
<el-table-column prop="quantity" label="天数" align="center" />
<el-table-column width="200" label="待处理/已完成" align="center" min-width="100">
<el-table-column prop="giftCode" :label="$t('pages.userCustomGift.table.giftCode')" align="center" />
<el-table-column prop="quantity" :label="$t('pages.userCustomGift.table.days')" align="center" />
<el-table-column width="200" :label="$t('pages.userCustomGift.table.approveStatus')" align="center" min-width="100">
<template slot-scope="scope">
<el-switch
v-model="scope.row.approveStatus"
@ -108,17 +112,17 @@
/>
</template>
</el-table-column>
<el-table-column prop="createTime" label="创建时间" align="center" min-width="100">
<el-table-column prop="createTime" :label="$t('pages.userCustomGift.table.createdAt')" align="center" min-width="100">
<template slot-scope="scope">
{{ scope.row.createTime | dateFormat }}
</template>
</el-table-column>
<el-table-column fixed="right" label="操作" align="center" width="400">
<el-table-column fixed="right" :label="$t('pages.userCustomGift.table.actions')" align="center" width="400">
<template slot-scope="scope">
<a class="resourceUrl" @click="downloadFile(scope.row.coverOneUrl)">下载图片1</a>
<a class="resourceUrl" @click="downloadFile(scope.row.coverTwoUrl)">下载图片2</a>
<a class="resourceUrl" @click="downloadFile(scope.row.coverThreeUrl)">下载图片3</a>
<a class="resourceUrl" @click="downloadFile(scope.row.songSourceUrl)">下载歌曲</a>
<a class="resourceUrl" @click="downloadFile(scope.row.coverOneUrl)">{{ $t('pages.userCustomGift.action.downloadImage', { index: 1 }) }}</a>
<a class="resourceUrl" @click="downloadFile(scope.row.coverTwoUrl)">{{ $t('pages.userCustomGift.action.downloadImage', { index: 2 }) }}</a>
<a class="resourceUrl" @click="downloadFile(scope.row.coverThreeUrl)">{{ $t('pages.userCustomGift.action.downloadImage', { index: 3 }) }}</a>
<a class="resourceUrl" @click="downloadFile(scope.row.songSourceUrl)">{{ $t('pages.userCustomGift.action.downloadSong') }}</a>
</template>
</el-table-column>
</el-table>
@ -206,7 +210,7 @@ export default {
},
renderDataSuccess() {
this.$message({
message: '操作成功',
message: this.$t('pages.userCustomGift.message.success'),
type: 'success'
})
this.renderData()
@ -234,7 +238,7 @@ export default {
downloadFile(resource) {
if (!resource) {
this.$message({
message: '资源用户未上传',
message: this.$t('pages.userCustomGift.message.resourceNotUploaded'),
type: 'error'
})
return

View File

@ -2,8 +2,7 @@
<div class="app-container">
<div class="filter-container">
<div class="filter-item">
<account-input v-model="listQuery.userId" :sys-origin="listQuery.sysOrigin" placeholder="用户ID" />
<account-input v-model="listQuery.userId" :sys-origin="listQuery.sysOrigin" :placeholder="$t('pages.imAccountManager.filter.userId')" />
</div>
<el-button
class="filter-item"
@ -12,7 +11,7 @@
:disabled="searchDisabled"
@click="handleSearch"
>
搜索
{{ $t('pages.imAccountManager.action.search') }}
</el-button>
<el-button
class="filter-item"
@ -21,15 +20,15 @@
icon="el-icon-edit"
@click="handleCreate"
>
添加
{{ $t('pages.imAccountManager.action.add') }}
</el-button>
</div>
<el-alert
title="提示"
:title="$t('pages.imAccountManager.alert.title')"
type="warning"
:closable="false"
>
使用站内注册账号关联后台PC端IM登录授权登录账号 短id 初始密码 88888888
{{ $t('pages.imAccountManager.alert.description') }}
</el-alert>
<el-table
v-loading="listLoading"
@ -38,14 +37,14 @@
fit
highlight-current-row
>
<el-table-column label="头像" align="center" width="150">
<el-table-column :label="$t('pages.imAccountManager.table.avatar')" align="center" width="150">
<template slot-scope="scope">
<div v-if="scope.row.userBaseInfo">
<avatar :url="scope.row.userBaseInfo.userAvatar" :gender="scope.row.userBaseInfo.userSex" />
</div>
</template>
</el-table-column>
<el-table-column label="昵称" align="center">
<el-table-column :label="$t('pages.imAccountManager.table.nickname')" align="center">
<template slot-scope="scope">
<div
v-if="scope.row.userBaseInfo"
@ -55,7 +54,7 @@
</div>
</template>
</el-table-column>
<el-table-column prop="email" label="IM用户名" width="110" align="center">
<el-table-column prop="email" :label="$t('pages.imAccountManager.table.imUsername')" width="110" align="center">
<template slot-scope="scope">
<div v-if="scope.row.userBaseInfo">
<div
@ -66,15 +65,15 @@
</div>
</template>
</el-table-column>
<el-table-column prop="createTime" label="创建时间" width="200" align="center">
<el-table-column prop="createTime" :label="$t('pages.imAccountManager.table.createdAt')" width="200" align="center">
<template slot-scope="scope">
{{ scope.row.createTime | dateFormat }}
</template>
</el-table-column>
<el-table-column fixed="right" label="操作" align="center" width="150">
<el-table-column fixed="right" :label="$t('pages.imAccountManager.table.actions')" align="center" width="150">
<template slot-scope="scope">
<el-button type="text" size="small" @click="handleDelete(scope.row)">删除</el-button>
<el-button type="text" size="small" @click="handleRestPwd(scope.row)">重置密码</el-button>
<el-button type="text" size="small" @click="handleDelete(scope.row)">{{ $t('pages.imAccountManager.action.delete') }}</el-button>
<el-button type="text" size="small" @click="handleRestPwd(scope.row)">{{ $t('pages.imAccountManager.action.resetPassword') }}</el-button>
</template>
</el-table-column>
</el-table>
@ -89,7 +88,7 @@
<el-dialog
v-loading="submitLoading"
title="添加"
:title="$t('pages.imAccountManager.dialog.create')"
width="400px"
:visible.sync="dialogFormVisible"
:modal-append-to-body="true"
@ -105,30 +104,30 @@
label-position="right"
label-width="80px"
>
<el-form-item label="用户ID" prop="userId">
<el-form-item :label="$t('pages.imAccountManager.form.userId')" prop="userId">
<el-input
v-model="formData.userId"
v-number
placeholder="请输入用户ID"
:placeholder="$t('pages.imAccountManager.placeholder.userId')"
/>
</el-form-item>
<el-form-item label="登录密码" prop="password">
<el-form-item :label="$t('pages.imAccountManager.form.password')" prop="password">
<el-input
v-model="formData.password"
type="password"
placeholder="请输入登录密码"
:placeholder="$t('pages.imAccountManager.placeholder.password')"
/>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="handleClose()">
取消
{{ $t('pages.imAccountManager.action.cancel') }}
</el-button>
<el-button
type="primary"
@click="addSubmit()"
>
提交
{{ $t('pages.imAccountManager.action.submit') }}
</el-button>
</div>
</el-dialog>
@ -185,9 +184,14 @@ export default {
userId: '',
password: '88888888'
},
rules: {
userId: [{ required: true, message: '必填参数不可为空', trigger: 'blur' }],
password: [{ required: true, message: '必填参数不可为空', trigger: 'blur' }]
}
},
computed: {
rules() {
const requiredField = this.$t('pages.imAccountManager.validation.requiredField')
return {
userId: [{ required: true, message: requiredField, trigger: 'blur' }],
password: [{ required: true, message: requiredField, trigger: 'blur' }]
}
}
},
@ -215,9 +219,9 @@ export default {
},
handleDelete(row) {
const that = this
that.$confirm(`是否确认删除 "${row.userBaseInfo.userNickname}" ?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
that.$confirm(this.$t('pages.imAccountManager.confirm.delete', { nickname: row.userBaseInfo.userNickname }), this.$t('pages.imAccountManager.confirm.title'), {
confirmButtonText: this.$t('pages.imAccountManager.confirm.confirm'),
cancelButtonText: this.$t('pages.imAccountManager.confirm.cancel'),
type: 'warning'
}).then(() => {
delSysImAccountPassword({ userId: row.userBaseInfo.id }).then(res => {
@ -231,9 +235,9 @@ export default {
},
handleRestPwd(row) {
const that = this
that.$confirm(`是否确认重置 "${row.userBaseInfo.userNickname}" 登陆密码?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
that.$confirm(this.$t('pages.imAccountManager.confirm.resetPassword', { nickname: row.userBaseInfo.userNickname }), this.$t('pages.imAccountManager.confirm.title'), {
confirmButtonText: this.$t('pages.imAccountManager.confirm.confirm'),
cancelButtonText: this.$t('pages.imAccountManager.confirm.cancel'),
type: 'warning'
}).then(() => {
resetSysImAccountPassword(row.userBaseInfo.id).then(res => {

View File

@ -1,6 +1,6 @@
<template>
<el-dialog
title="描述列表"
:title="$t('pages.appVersionManager.dialog.descriptionList')"
:visible="true"
:modal-append-to-body="true"
:append-to-body="true"
@ -17,45 +17,45 @@
icon="el-icon-edit"
@click="handleCreate"
>
新增描述
{{ $t('pages.appVersionManager.action.addDescription') }}
</el-button>
</div>
<el-table
v-loading="listLoading"
:data="list"
:before-close="handleClose"
element-loading-text="Loading"
:element-loading-text="$t('pages.appVersionManager.loading')"
fit
highlight-current-row
max-height="350px"
>
<el-table-column label="ID" prop="id" align="center" />
<el-table-column label="语言" prop="language" align="center">
<el-table-column :label="$t('pages.appVersionManager.table.id')" prop="id" align="center" />
<el-table-column :label="$t('pages.appVersionManager.table.language')" prop="language" align="center">
<template slot-scope="scope">
<span v-for="item in language" :key="item.value">
<span v-for="item in languageOptions" :key="item.value">
<span v-if="item.value === scope.row.language">{{
item.name
}}</span>
</span>
</template>
</el-table-column>
<el-table-column label="描述" prop="updateDescribe" align="center" />
<el-table-column label="朝拜描述" prop="updateWorshipDescribe" align="center" />
<el-table-column prop="createTime" label="创建时间" align="center">
<el-table-column :label="$t('pages.appVersionManager.table.description')" prop="updateDescribe" align="center" />
<el-table-column :label="$t('pages.appVersionManager.table.worshipDescription')" prop="updateWorshipDescribe" align="center" />
<el-table-column prop="createTime" :label="$t('pages.appVersionManager.table.createTime')" align="center">
<template slot-scope="scope">
{{ scope.row.createTime | dateFormat }}
</template>
</el-table-column>
<el-table-column fixed="right" label="操作" align="center" width="120">
<el-table-column fixed="right" :label="$t('pages.appVersionManager.table.actions')" align="center" width="120">
<template slot-scope="scope">
<el-button
type="text"
@click.native="handleUpdate(scope.row)"
>修改</el-button>
>{{ $t('pages.appVersionManager.action.edit') }}</el-button>
<el-button
type="text"
@click.native="removeDescription(scope.row)"
>删除</el-button>
>{{ $t('pages.appVersionManager.action.delete') }}</el-button>
</template>
</el-table-column>
</el-table>
@ -87,42 +87,42 @@
label-width="100px"
style="margin-left: 15px;"
>
<el-form-item label="语言" prop="language">
<el-form-item :label="$t('pages.appVersionManager.form.language')" prop="language">
<el-select
v-model="form.language"
placeholder="语言"
:placeholder="$t('pages.appVersionManager.placeholder.language')"
clearable
style="width: 100%"
class="filter-item"
:disabled="form.id !== ''"
>
<el-option
v-for="item in language"
v-for="item in languageOptions"
:key="item.value"
:label="item.name"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item label="更新描述" prop="updateDescribe">
<el-form-item :label="$t('pages.appVersionManager.form.updateDescribe')" prop="updateDescribe">
<el-input
v-model.trim="form.updateDescribe"
:autosize="{ minRows: 5, maxRows: 8 }"
type="textarea"
placeholder="请输入更新描述"
:placeholder="$t('pages.appVersionManager.placeholder.updateDescribe')"
/>
</el-form-item>
<el-form-item label="更新朝拜描述" prop="updateWorshipDescribe">
<el-form-item :label="$t('pages.appVersionManager.form.updateWorshipDescribe')" prop="updateWorshipDescribe">
<el-input
v-model.trim="form.updateWorshipDescribe"
:autosize="{ minRows: 5, maxRows: 8 }"
type="textarea"
placeholder="请输入更新朝拜描述"
:placeholder="$t('pages.appVersionManager.placeholder.updateWorshipDescribe')"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submitForm()">保存</el-button>
<el-button @click="handleDescriptionClose()">取消</el-button>
<el-button type="primary" @click="submitForm()">{{ $t('pages.appVersionManager.action.save') }}</el-button>
<el-button @click="handleDescriptionClose()">{{ $t('pages.appVersionManager.action.cancel') }}</el-button>
</el-form-item>
</el-form>
</div>
@ -161,9 +161,9 @@ export default {
data() {
const validateUpdateDescribe = (rule, value, callback) => {
if (!value || value.trim().length === 0) {
callback(new Error('必填项不可为空'))
callback(new Error(this.$t('pages.appVersionManager.validation.requiredField')))
} else if (value && value.length > 500) {
callback(new Error('最大 500 字符'))
callback(new Error(this.$t('pages.appVersionManager.validation.max500')))
} else {
callback()
}
@ -176,7 +176,6 @@ export default {
limit: 20,
versionId: ''
},
language,
submitLoading: false,
listLoading: false,
searchDisabled: false,
@ -185,7 +184,7 @@ export default {
form: getFormData(),
rules: {
language: [
{ required: true, message: '必填项不可为空', trigger: 'blur' }
{ required: true, message: this.$t('pages.appVersionManager.validation.requiredField'), trigger: 'blur' }
],
updateDescribe: [
{ required: true, trigger: 'blur', validator: validateUpdateDescribe }
@ -193,6 +192,14 @@ export default {
}
}
},
computed: {
languageOptions() {
return language.map(item => ({
...item,
name: this.$t(`pages.appVersionManager.language.${item.value}`)
}))
}
},
watch: {
versionId: {
handler(newVal) {
@ -217,12 +224,12 @@ export default {
})
},
handleCreate() {
this.textOptTitle = '新增描述'
this.textOptTitle = this.$t('pages.appVersionManager.dialog.addDescription')
this.formVisible = true
this.form = getFormData()
},
handleUpdate(row) {
this.textOptTitle = '修改描述'
this.textOptTitle = this.$t('pages.appVersionManager.dialog.editDescription')
this.formVisible = true
this.form.id = row.id
this.form.versionId = row.versionId
@ -300,7 +307,7 @@ export default {
removeDescription(item) {
const that = this
that
.$confirm('确定删除描述吗?', '提示', {
.$confirm(this.$t('pages.appVersionManager.confirm.deleteDescription'), this.$t('pages.appVersionManager.confirm.title'), {
type: 'warning'
})
.then(() => {
@ -313,7 +320,7 @@ export default {
.then(res => {
that.listLoading = false
that.$message({
message: '删除成功',
message: this.$t('pages.appVersionManager.message.deleteSuccess'),
type: 'success'
})
that.renderData()

View File

@ -18,7 +18,7 @@
:model="formData"
style="width: 400px; margin-left:50px;"
>
<el-form-item label="平台" prop="platform">
<el-form-item :label="$t('pages.appVersionManager.form.platform')" prop="platform">
<el-select
v-model="formData.platform"
:disabled="!isAdd"
@ -35,7 +35,7 @@
/>
</el-select>
</el-form-item>
<el-form-item label="渠道" prop="channel">
<el-form-item :label="$t('pages.appVersionManager.form.channel')" prop="channel">
<el-select
v-model="formData.channel"
:disabled="!isAdd"
@ -51,54 +51,54 @@
/>
</el-select>
</el-form-item>
<el-form-item label="版本号" prop="version">
<el-input v-model.trim="formData.version" placeholder="请输入版本号" />
<el-form-item :label="$t('pages.appVersionManager.form.version')" prop="version">
<el-input v-model.trim="formData.version" :placeholder="$t('pages.appVersionManager.placeholder.version')" />
</el-form-item>
<el-form-item label="编译版本号" prop="buildVersion">
<el-input v-model="formData.buildVersion" v-number placeholder="请输入编译版本号" />
<el-form-item :label="$t('pages.appVersionManager.form.buildVersion')" prop="buildVersion">
<el-input v-model="formData.buildVersion" v-number :placeholder="$t('pages.appVersionManager.placeholder.buildVersion')" />
</el-form-item>
<el-form-item label="强更" prop="forceUpdate">
<el-form-item :label="$t('pages.appVersionManager.form.forceUpdate')" prop="forceUpdate">
<el-select
v-model="formData.forceUpdate"
clearable
style="width: 100%"
class="filter-item"
>
<el-option label="否" :value="false" />
<el-option label="是" :value="true" />
<el-option :label="$t('pages.appVersionManager.word.no')" :value="false" />
<el-option :label="$t('pages.appVersionManager.word.yes')" :value="true" />
</el-select>
</el-form-item>
<el-form-item label="app类型" prop="appType">
<el-form-item :label="$t('pages.appVersionManager.form.appType')" prop="appType">
<el-select
v-model="formData.appType"
clearable
style="width: 100%"
class="filter-item"
>
<el-option label="语聊" :value="'CHAT'" />
<el-option label="朝拜" :value="'WORSHIP'" />
<el-option :label="$t('pages.appVersionManager.appType.chat')" :value="'CHAT'" />
<el-option :label="$t('pages.appVersionManager.appType.worship')" :value="'WORSHIP'" />
</el-select>
</el-form-item>
<el-form-item label="审核中" prop="review">
<el-form-item :label="$t('pages.appVersionManager.form.review')" prop="review">
<el-select
v-model="formData.review"
clearable
style="width: 100%"
class="filter-item"
>
<el-option label="否" :value="false" />
<el-option label="是" :value="true" />
<el-option :label="$t('pages.appVersionManager.word.no')" :value="false" />
<el-option :label="$t('pages.appVersionManager.word.yes')" :value="true" />
</el-select>
</el-form-item>
<el-form-item label="补丁" prop="patch">
<el-form-item :label="$t('pages.appVersionManager.form.patch')" prop="patch">
<el-select
v-model="formData.patch"
clearable
style="width: 100%"
class="filter-item"
>
<el-option label="否" :value="false" />
<el-option label="是" :value="true" />
<el-option :label="$t('pages.appVersionManager.word.no')" :value="false" />
<el-option :label="$t('pages.appVersionManager.word.yes')" :value="true" />
</el-select>
</el-form-item>
<!-- <el-form-item label="更新描述" prop="updateDescribe">
@ -109,24 +109,24 @@
placeholder="请输入更新描述"
/>
</el-form-item> -->
<el-form-item label="安装包大小" prop="apkSize">
<el-input v-model.trim="formData.apkSize" placeholder="请输入安装包大小" />
<el-form-item :label="$t('pages.appVersionManager.form.apkSize')" prop="apkSize">
<el-input v-model.trim="formData.apkSize" :placeholder="$t('pages.appVersionManager.placeholder.apkSize')" />
</el-form-item>
<el-form-item label="下载链接" prop="downloadUrl">
<el-input v-model.trim="formData.downloadUrl" placeholder="请输入下载链接" />
<el-form-item :label="$t('pages.appVersionManager.form.downloadUrl')" prop="downloadUrl">
<el-input v-model.trim="formData.downloadUrl" :placeholder="$t('pages.appVersionManager.placeholder.downloadUrl')" />
</el-form-item>
</el-form>
</div>
<div slot="footer" class="dialog-footer">
<el-button @click="handleClose">
取消
{{ $t('pages.appVersionManager.action.cancel') }}
</el-button>
<el-button
:loading="submitLoading"
type="primary"
@click="handleSubmit"
>
提交
{{ $t('pages.appVersionManager.action.submit') }}
</el-button>
</div>
</el-dialog>
@ -178,19 +178,19 @@ export default {
},
rules: {
platform: [
{ required: true, message: '必填项不可为空', trigger: 'blur' }
{ required: true, message: this.$t('pages.appVersionManager.validation.requiredField'), trigger: 'blur' }
],
channel: [
{ required: true, message: '必填项不可为空', trigger: 'blur' }
{ required: true, message: this.$t('pages.appVersionManager.validation.requiredField'), trigger: 'blur' }
],
version: [{ required: true, message: '必填项不可为空', trigger: 'blur' }],
buildVersion: [{ required: true, message: '必填项不可为空', trigger: 'blur' }],
forceUpdate: [{ required: true, message: '必填项不可为空', trigger: 'blur' }],
review: [{ required: true, message: '必填项不可为空', trigger: 'blur' }],
version: [{ required: true, message: this.$t('pages.appVersionManager.validation.requiredField'), trigger: 'blur' }],
buildVersion: [{ required: true, message: this.$t('pages.appVersionManager.validation.requiredField'), trigger: 'blur' }],
forceUpdate: [{ required: true, message: this.$t('pages.appVersionManager.validation.requiredField'), trigger: 'blur' }],
review: [{ required: true, message: this.$t('pages.appVersionManager.validation.requiredField'), trigger: 'blur' }],
// updateDescribe: [{ required: true, trigger: 'blur', validator: validateUpdateDescribe }],
apkSize: [{ required: true, message: '必填项不可为空', trigger: 'blur' }],
downloadUrl: [{ required: true, message: '必填项不可为空', trigger: 'blur' }],
patch: [{ required: true, message: '必填项不可为空', trigger: 'blur' }]
apkSize: [{ required: true, message: this.$t('pages.appVersionManager.validation.requiredField'), trigger: 'blur' }],
downloadUrl: [{ required: true, message: this.$t('pages.appVersionManager.validation.requiredField'), trigger: 'blur' }],
patch: [{ required: true, message: this.$t('pages.appVersionManager.validation.requiredField'), trigger: 'blur' }]
},
submitLoading: false
}
@ -200,7 +200,9 @@ export default {
return this.updateData === null
},
title() {
return this.isAdd ? `添加(${this.sysOrigin})` : `修改(${this.updateData.sysOrigin})`
return this.isAdd
? this.$t('pages.appVersionManager.dialog.addVersion', { sysOrigin: this.sysOrigin })
: this.$t('pages.appVersionManager.dialog.editVersion', { sysOrigin: this.updateData.sysOrigin })
}
},
watch: {

View File

@ -4,7 +4,7 @@
<div class="filter-container">
<el-select
v-model="listQuery.sysOrigin"
placeholder="系统"
:placeholder="$t('pages.appVersionManager.filter.system')"
style="width: 120px"
class="filter-item filter-item--system"
@change="handleSearch"
@ -23,7 +23,7 @@
v-model="listQuery.platform"
style="width: 120px"
class="filter-item filter-item--platform"
placeholder="平台"
:placeholder="$t('pages.appVersionManager.filter.platform')"
@change="changeSelectPlatform"
>
<el-option
@ -36,7 +36,7 @@
<el-select
v-if="selectedPlatform && selectedPlatform.channels && selectedPlatform.channels .length > 0"
v-model="listQuery.channel"
placeholder="渠道"
:placeholder="$t('pages.appVersionManager.filter.channel')"
style="width: 200px"
class="filter-item filter-item--channel"
@change="handleSearch"
@ -55,7 +55,7 @@
icon="el-icon-search"
@click="handleSearch"
>
搜索
{{ $t('pages.appVersionManager.action.search') }}
</el-button>
<el-button
class="filter-item filter-item--create"
@ -64,7 +64,7 @@
icon="el-icon-edit"
@click="handleAdd"
>
添加
{{ $t('pages.appVersionManager.action.add') }}
</el-button>
<el-input
@ -88,39 +88,39 @@
<el-table
v-loading="listLoading"
:data="list"
element-loading-text="Loading"
:element-loading-text="$t('pages.appVersionManager.loading')"
fit
highlight-current-row
@cell-mouse-enter="handleMouseEnter"
>
<el-table-column prop="platform" label="平台" align="center" min-width="100px">
<el-table-column prop="platform" :label="$t('pages.appVersionManager.table.platform')" align="center" min-width="100px">
<template slot-scope="scope">
{{ scope.row.platform }}<span v-if="scope.row.channel">/{{ scope.row.channel }}</span>
</template>
</el-table-column>
<el-table-column prop="version" label="版本" align="center" min-width="100px" />
<el-table-column prop="buildVersion" label="编译版本" align="center" min-width="80px" />
<el-table-column label="强更" align="center">
<el-table-column prop="version" :label="$t('pages.appVersionManager.table.version')" align="center" min-width="100px" />
<el-table-column prop="buildVersion" :label="$t('pages.appVersionManager.table.buildVersion')" align="center" min-width="80px" />
<el-table-column :label="$t('pages.appVersionManager.table.forceUpdate')" align="center">
<template slot-scope="scope">
{{ scope.row.forceUpdate === true ? '是' : '否' }}
{{ getYesNoText(scope.row.forceUpdate) }}
</template>
</el-table-column>
<el-table-column label="审核中" align="center">
<el-table-column :label="$t('pages.appVersionManager.table.review')" align="center">
<template slot-scope="scope">
{{ scope.row.review === true ? '是' : '否' }}
{{ getYesNoText(scope.row.review) }}
</template>
</el-table-column>
<el-table-column label="app类型" align="center">
<el-table-column :label="$t('pages.appVersionManager.table.appType')" align="center">
<template slot-scope="scope">
{{ scope.row.appType === 'CHAT' ? '语聊' : '朝拜' }}
{{ getAppTypeText(scope.row.appType) }}
</template>
</el-table-column>
<el-table-column label="补丁" align="center">
<el-table-column :label="$t('pages.appVersionManager.table.patch')" align="center">
<template slot-scope="scope">
{{ scope.row.patch === true ? '是' : '否' }}
{{ getYesNoText(scope.row.patch) }}
</template>
</el-table-column>
<el-table-column prop="updateDescribe" label="版本描述" align="center">
<el-table-column prop="updateDescribe" :label="$t('pages.appVersionManager.table.updateDescribe')" align="center">
<template slot-scope="scope">
<div @click="copyTextContent(scope.row.updateDescribe)">
<el-tooltip class="item" effect="dark" placement="top-start">
@ -130,28 +130,28 @@
</div>
</template>
</el-table-column>
<el-table-column prop="apkSize" label="安装包大小" align="center" />
<el-table-column label="下载链接" align="center" width="100">
<el-table-column prop="apkSize" :label="$t('pages.appVersionManager.table.apkSize')" align="center" />
<el-table-column :label="$t('pages.appVersionManager.table.downloadUrl')" align="center" width="100">
<template slot-scope="scope">
<el-button type="text" @click="copyTextContent(scope.row.downloadUrl)">点击复制</el-button>
<el-button type="text" @click="copyTextContent(scope.row.downloadUrl)">{{ $t('pages.appVersionManager.action.copy') }}</el-button>
</template>
</el-table-column>
<el-table-column prop="createTime" label="创建时间" align="center" width="200">
<el-table-column prop="createTime" :label="$t('pages.appVersionManager.table.createTime')" align="center" width="200">
<template slot-scope="scope">
{{ scope.row.createTime | dateFormat }}
</template>
</el-table-column>
<el-table-column fixed="right" label="操作" align="center" width="100">
<el-table-column fixed="right" :label="$t('pages.appVersionManager.table.actions')" align="center" width="100">
<template slot-scope="scope">
<el-dropdown>
<span class="el-dropdown-link">
<i class="el-icon-more" />
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item @click.native="hanldeEidt(scope.row)">编辑</el-dropdown-item>
<el-dropdown-item @click.native="removeAppVersion(scope.row.id)">删除</el-dropdown-item>
<el-dropdown-item @click.native="clickSetServerBuildVersion(scope.row)">服务强更</el-dropdown-item>
<el-dropdown-item @click.native="handleUpdateDescribeList(scope.row)">更新描述</el-dropdown-item>
<el-dropdown-item @click.native="hanldeEidt(scope.row)">{{ $t('pages.appVersionManager.action.edit') }}</el-dropdown-item>
<el-dropdown-item @click.native="removeAppVersion(scope.row.id)">{{ $t('pages.appVersionManager.action.delete') }}</el-dropdown-item>
<el-dropdown-item @click.native="clickSetServerBuildVersion(scope.row)">{{ $t('pages.appVersionManager.action.serviceForceUpdate') }}</el-dropdown-item>
<el-dropdown-item @click.native="handleUpdateDescribeList(scope.row)">{{ $t('pages.appVersionManager.action.updateDescription') }}</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</template>
@ -290,12 +290,12 @@ export default {
const that = this
copyText(text).then(() => {
that.$message({
message: '复制成功',
message: that.$t('pages.appVersionManager.message.copySuccess'),
type: 'success'
})
}).catch(() => {
that.$message({
message: '复制失败',
message: that.$t('pages.appVersionManager.message.copyFail'),
type: 'error'
})
})
@ -317,12 +317,26 @@ export default {
this.appVersionEditVisible = false
this.renderData(event === 'create')
},
getYesNoText(value) {
return value === true
? this.$t('pages.appVersionManager.word.yes')
: this.$t('pages.appVersionManager.word.no')
},
getAppTypeText(value) {
return value === 'CHAT'
? this.$t('pages.appVersionManager.appType.chat')
: this.$t('pages.appVersionManager.appType.worship')
},
removeAppVersion(id) {
const that = this
this.$confirm('是否确认删除', '温馨提示', {
confirmButtonText: '确定',
cancelButtonText: '取消'
}).then(() => {
this.$confirm(
this.$t('pages.appVersionManager.confirm.deleteVersion'),
this.$t('pages.appVersionManager.confirm.title'),
{
confirmButtonText: this.$t('pages.appVersionManager.action.confirm'),
cancelButtonText: this.$t('pages.appVersionManager.action.cancel')
}
).then(() => {
delAppVersion(id).then(res => {
that.renderData()
})
@ -348,10 +362,14 @@ export default {
setServerBuildVersion(params) {
const that = this
that.buildVersionInputVsiible = false
this.$confirm('服务器将拦截小于当前buildVersion请求10分钟内生效是否继续', '温馨提示', {
confirmButtonText: '确定',
cancelButtonText: '取消'
}).then(() => {
this.$confirm(
this.$t('pages.appVersionManager.confirm.setServerBuildVersion'),
this.$t('pages.appVersionManager.confirm.title'),
{
confirmButtonText: this.$t('pages.appVersionManager.action.confirm'),
cancelButtonText: this.$t('pages.appVersionManager.action.cancel')
}
).then(() => {
addServerBuildVersion(params).then(res => {
that.buildVersion = params.buildVersion
that.$opsMessage.success()