feat: 语言切换功能

This commit is contained in:
hzj 2026-04-20 18:08:00 +08:00
parent ca86f075dd
commit e7a2073e00
134 changed files with 15194 additions and 4012 deletions

View File

@ -5,7 +5,9 @@ VUE_APP_BASE_API = '/console'
VUE_CLI_BABEL_TRANSPILE_MODULES = true
# application base url
VUE_APP_BASE_URL ='https://consoletest.azizichat.com'
# VUE_APP_BASE_URL ='https://consoletest.azizichat.com'
VUE_APP_BASE_URL ='https://console.atuchat.com'
# VUE_APP_BASE_URL = 'https://console.likeichat.com'
# oss
VUE_APP_OSS_BUCKET = 'tkm-likei'

BIN
build-egg-thread1.log Normal file

Binary file not shown.

View File

@ -0,0 +1,190 @@
# UI 橙色主题与交互顺序调整记录
## 1. 变更背景
本次调整目标:
- 将项目整体视觉风格从原有偏冷色风格调整为偏橙色风格,主色以 `#FF9326``#FEB219` 为核心。
- 取消暗夜模式,仅保留常规亮色展示。
- 允许调整按钮样式、圆角、阴影、表单项顺序、搜索项顺序、静态多选/下拉选项顺序。
- 明确不调整权限点、业务逻辑、接口行为、路由配置。
本次所有改动均以 UI 层和静态展示层为边界。
## 2. 整体视觉风格调整
### 2.1 主色与基础风格
已将项目主视觉切换为橙色系,重点调整包括:
- 主按钮、默认按钮、文本按钮颜色统一为橙色主题。
- 输入框、选择器、日期组件的聚焦态边框和阴影改为橙色高亮。
- 弹窗、抽屉、卡片、表格等容器统一增加暖色边框、圆角和阴影。
- 顶栏、侧边栏、Logo 区域统一为暖色风格。
重点样式文件:
- `src/styles/element-ui.scss`
- `src/styles/variables.scss`
- `src/styles/index.scss`
- `src/styles/sidebar.scss`
- `src/styles/element-variables.scss`
### 2.2 按钮与弹窗按钮视觉顺序
已统一主按钮视觉优先级,使确认类按钮更突出。
其中弹窗/抽屉底部操作区通过样式顺序优化为主按钮优先展示,但未改动原始点击逻辑。
## 3. 暗夜模式取消
已取消暗夜模式入口及暗夜切换能力,处理方式如下:
- `src/utils/theme.js`
- `getTheme()` 固定返回 `normal`
- `setTheme()` 固定写入 `normal`
- `checkSwitchTheme()` 固定清除根节点暗色类名
- `src/layout/components/index.js`
- 导航栏导出切换为 `NavbarLight.vue`
- `src/layout/components/NavbarLight.vue`
- 新增简化版导航栏
- 移除主题切换相关入口,仅保留常用功能
说明:
- 原有权限逻辑、导航逻辑未修改。
- 该处理方式属于 UI 层收口,不影响业务行为。
## 4. 搜索区与表单项顺序调整
针对页面内搜索区、过滤区、表单项的展示顺序做了纯 UI 层重排,主要通过模板顺序和样式 `order` 控制完成,不改查询参数结构和业务逻辑。
已调整的典型页面:
- `src/views/user/user-table/index.vue`
- `src/views/user/user-table-role/index.vue`
- `src/views/user/freight/index.vue`
- `src/views/version/app/index.vue`
调整方向包括:
- 系统、国家、账号、设备、状态、性别、注册来源、来源平台、日期区间等筛选项顺序重新整理。
- 搜索按钮、概览按钮、条件展开开关等操作项顺序重新排列。
- 保证搜索区主流程更靠前,次级条件靠后。
## 5. 静态选项顺序调整
仅对前端文件内写死的选项顺序进行了整理;接口返回的列表未做改动。
### 5.1 公共输入组件
- `src/components/data/AccountInput/index.vue`
- 类型顺序由原来的“长 ID / 短 ID”调整为“短 ID / 长 ID”
- `src/components/data/SearchRoomInput/index.vue`
- 类型顺序由原来的“长 ID / 短 ID”调整为“短 ID / 长 ID”
### 5.2 静态常量
- `src/constant/origin.js`
- 注册来源顺序调整
- 来源平台顺序调整
- App 平台顺序调整
- `src/constant/type.js`
- `platformOrigins` 平台顺序调整为 Android 优先
说明:
- 仅变更静态数组的前端展示顺序。
- 未变更任何接口字段含义或后端约定。
## 6. 侧边栏折叠态图标居中修复
在橙色样式改造后,发现侧边栏收起状态下图标未居中。
原因是旧实现依赖固定 `margin-left` 偏移,无法适配新的侧边栏宽度、内边距和圆角布局。
处理方式:
- 修改 `src/styles/sidebar.scss`
- 将折叠菜单项和子菜单标题改为 `flex` 居中
- 清除折叠态图标的额外左边距
修复结果:
- 侧边栏收起后图标可在中轴线上正常居中显示
- 不影响菜单高亮、路由跳转和交互逻辑
## 7. 弹窗被遮罩层覆盖问题修复
发现类似以下统计类弹窗:
```vue
<user-registration-overview-charts-dialog
v-if="UserRegistrationOverviewChartsDialogVisible"
@close="UserRegistrationOverviewChartsDialogVisible = false"
/>
```
存在“弹窗本体在遮罩层下方、展示不全且无法点击”的问题。
### 7.1 原因
- 遮罩层默认挂载到 `body`
- 弹窗本体未挂载到 `body`
- 页面内容容器存在局部层叠上下文和 `overflow: hidden`
- 最终导致遮罩层在外层,而弹窗仍在页面容器内部,被裁切或压住
### 7.2 修复方式
已为以下同类弹窗补充:
- `:modal-append-to-body="true"`
- `:append-to-body="true"`
已处理文件:
- `src/components/data/UserRegistrationOverviewCharts/dialog.vue`
- `src/components/data/PropsSalesOverviewCharts/dialog.vue`
### 7.3 修复结果
- 弹窗与遮罩层处于同一挂载层级
- 弹窗可完整显示并可正常交互
- 不影响弹窗内部业务逻辑
## 8. 验证情况
本次样式主改造完成后,已执行生产构建校验:
```powershell
$env:NODE_OPTIONS='--openssl-legacy-provider'; npm run build:prod
```
结果:
- 构建通过
- 存在若干项目原有 warning
- 这些 warning 主要为旧代码中的导出提示、样式顺序提示、Sass 旧 API 提示,不属于本次 UI 改造新增问题
说明:
- 后续对侧边栏折叠图标、统计弹窗挂载层级的修复属于局部样式修正,本记录未逐次重复构建
## 9. 变更边界说明
本次调整明确未涉及以下内容:
- 权限点
- 功能逻辑
- 接口行为
- 路由结构
- 后端字段定义
本次属于前端 UI 视觉统一、静态交互顺序整理与样式问题修复。
## 10. 建议后续项
如后续继续做 UI 收口,可优先补充以下内容:
- 继续逐页排查页面内写死的下拉、多选、单选顺序
- 统一所有统计类、详情类弹窗的 `append-to-body` 规范
- 继续检查折叠侧边栏下的图标、文字、tooltip 对齐一致性
- 对全站弹窗、抽屉、表格筛选区形成统一样式规范文档

533
docs/i18n/主要路由.md Normal file
View File

@ -0,0 +1,533 @@
## 路由摘要
### 文档定位
- 本文件是“首页 > 主要路由 > 其他路由”中的主要路由唯一清单
- 本文件用于把 `src/permission.js``store.dispatch('user/getMenus')` 的接口返回结果整理成便于国际化领取、验收和运行检查的父子级路由清单
- 需要查看主要路由的中文名、英文名、路由别名、路径、入口文件、完成状态时,统一以本文件为准
- 本文件中的菜单层级不默认限制为“父级 > 子级”两层,若实际菜单存在三级结构,则按真实父链整理
- 后续所有线程领取新任务前,除了读取 `线程协作.md``翻译进度.md``翻译清单.md`,还应先读取本文件
- 其他文档不再重复维护完整主路由树,最多保留摘要并引用本文件
- 下方保留原始接口返回,顶部摘要用于实际执行
### 全线程统一优先级
- 适用于所有线程,不只线程三
- 统一领取优先级:`已完成 src/views 页面私有组件复查 > 首页 > 本文件中的主要路由页面 > 其他路由页面`
- 在“已完成 `src/views` 页面私有组件复查”完成前,暂不继续领取新的未完成普通页面
- 只要首页或本文件中的主要路由页面还有未完成项,后续线程都应优先从这两部分中选择
- 本文件中的主要路由页面之间,具体先领哪一个页面仍可按原来的方式灵活分配,只要先读 md、先写认领、避免冲突
- 只有当首页和本文件中的主要路由页面全部处理完成后,才进入其他路由页面
### 首页
- 路由名:`Dashboard`
- 路径:`/dashboard`
- 路由来源:`src/router/index.js`
- 入口文件:`src/views/dashboard/index.vue`
- 当前状态:`已完成`
首页模块建议按同一轮检查的文件范围:
- `src/views/dashboard/index.vue`
- `src/views/dashboard/dashboard.vue`
- `src/views/dashboard/dashboard-charts/index.vue`
- `src/views/dashboard/quick-link/index.vue`
- `src/views/dashboard/introduction/index.vue`
- `src/views/dashboard/introduction/user-account-info/index.vue`
- `src/views/dashboard/introduction/user-navigation/index.vue`
- `src/views/dashboard/introduction/waiting-processing/index.vue`
- `src/views/dashboard/components/active-index/index.vue`
- `src/views/dashboard/components/daily-currency/index.vue`
- `src/views/dashboard/components/daily-currency-gold/index.vue`
- `src/views/dashboard/components/daily-purchase/index.vue`
- `src/views/dashboard/components/daily-register-user/index.vue`
- `src/views/dashboard/components/monthly-purchase/index.vue`
- `src/views/dashboard/components/user-daily-currency-gold-top/index.vue`
- `src/views/dashboard/components/user-daily-currency-recharge-top/index.vue`
### 当前与国际化进度直接相关的主要路由
以下顺序按父级/子级路由整理,方便运行项目时逐项检查。
### 当前进行中的主要路由
- 当前无
### 已完成主要路由总览
- 用户管理 / User Management
- 路由别名:`AppUserManager`
- 用户列表 / User List
- 路由别名:`AppUserList`
- 路径:`user/user-table/index`
- 文件:`src/views/user/user-table/index.vue`
- 状态:已完成
- 运营管理 / Operations Management
- 路由别名:`OperationManager`
- 数据配置 / Data Configuration
- 路由别名:`data:conf`
- 转盘抽奖品配置 / Spin Wheel Prize Configuration
- 路由别名:`cnf:lottery`
- 路径:`cnf/lottery/index`
- 文件:`src/views/cnf/lottery/index.vue`
- 状态:已完成
- 国家管理 / Country Management
- 路由别名:`SysCountryCode`
- 路径:`sys/country-code/index`
- 文件:`src/views/sys/country-code/index.vue`
- 状态:已完成
- 活动配置 / Activity Configuration
- 路由别名:`Activity:Config`
- 活动管理 / Activity Management
- 路由别名:`Activity:Manager`
- 路径:`activity/template-cnf/index`
- 文件:`src/views/activity/template-cnf/index.vue`
- 状态:已完成
- 消息推送 / Message Push (需要重新检查)
- 路由别名:`MessageManagerPush`
- 路径:`message/push/index`
- 已完成文件:
- `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`
- 状态:已完成
- 运营日志 / Operations Logs
- 路由别名:`operation:log`
- 砸金蛋记录 / Golden Egg Smash Records
- 路由别名:`GameEgg`
- 路径:`game/egg/index`
- 已完成文件:
- `src/views/game/egg/index.vue`
- `src/views/game/egg/egg-exchange-record/index.vue`
- `src/views/game/egg/egg-lottery-record/index.vue`
- `src/views/game/egg/egg-lottery-cnf/index.vue`
- 状态:已完成
- APP管理员 / App Administrator
- 路由别名:`AdministratorTable`
- 路径:`user/app-manager/index`
- 已完成文件:
- `src/views/user/app-manager/index.vue`
- `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`
- 状态:已完成
- App启动页 / App Launch Page
- 路由别名:`StartPage`
- 路径:`sys/start/page`
- 已完成文件:
- `src/views/sys/start-page/index.vue`
- `src/views/sys/start-page/form-edit.vue`
- 状态:已完成
- 系统公告管理 / System Notice Management
- 路由别名:`NoticeMessage`
- 路径:`sys/notice-message/index`
- 文件:`src/views/sys/notice-message/index.vue`
- 状态:已完成
- 语音房间 / Voice Rooms
- 路由别名:`VoiceRoom`
- 房间资料列表 / Room Profile List
- 路由别名:`ProfileRoom`
- 路径:`room/profile/index`
- 文件:`src/views/room/profile/index.vue`
- 状态:已完成
- 道具管理 / Prop Management
- 路由别名:`PropsManager`
- 资源组配置 / Resource Group Configuration
- 路由别名:`PropsActivitySourceGroup`
- 路径:`props/props-source-group/index`
- 已完成文件:
- `src/views/props/props-source-group/index.vue`
- `src/views/props/props-source-group/form-edit.vue`
- 状态:已完成
- 系统工具 / System Tools
- 路由别名:`ToolsManager`
- Redis缓存管理 / Redis Cache Management
- 路由别名:`RedisManager`
- 路径:`/redis`
- 文件:`src/views/tools/redis/index.vue`
- 状态:已完成
- 文件上传 / File Upload
- 路由别名:`upload:file`
- 路径:`/upload/file`
- 文件:`src/views/tools/upload/index.vue`
- 状态:已完成
- 主播中心 / Broadcaster Center
- 路由别名:`team:manaber`
- 代理钻石政策 / Agent Diamond Policy
- 路由别名:`TeamDiamondPolicyManager`
- 路径:`team/diamond-policy/index`
- 文件:`src/views/team/diamond-policy/index.vue`
- 状态:已完成
- 代理政策 / Agent Policy
- 路由别名:`TeamPolicyManager`
- 路径:`team/policy/index`
- 文件:`src/views/team/policy/index.vue`
- 状态:已完成
- BD列表 / BD List
- 路由别名:`BusinessDevelopment`
- 路径:`team/business-development/index`
- 文件:`src/views/team/business-development/index.vue`
- 状态:已完成
### 已完成但未在当前主要路由菜单返回中命中的补充页面
- 抽奖概率配置 / Lottery Probability Settings
- 路由别名:`GameLottery`
- 路由来源:`src/router/index.js` 静态路由
- 路径:`game/lottery-config/index`
- 已完成文件:
- `src/views/game/lottery-config/index.vue`
- `src/views/game/lottery-config/form-edit.vue`
- `src/views/game/lottery-config/select-propsr-popover.vue`
- 状态:已完成
### 已部分完成但入口页仍需复核的主要路由
- 当前无
### 当前建议检查顺序
1. 先处理“已完成 `src/views` 页面私有组件复查”任务
2. 首页 `Dashboard` 模块
3. 本文件中“当前进行中的主要路由”:
- 当前无
4. 本文件中“模块部分完成,入口页仍需复核”的页面:
- 当前无
5. 本文件之外的其他路由页面
## 原始接口返回(保留)
{
"time": "1776417483715",
"status": true,
"errorCode": 0,
"body": {
"id": "160",
"theme": "normal",
"recentPreviews": [
{
"reqTraceId": "GL-99fc25be00b9e5b70891628f3615fe89",
"reqUserId": "160",
"reqLanguage": "en",
"reqZoneId": "UTC",
"reqIp": "10.0.20.94",
"reqClient": "OPS",
"reqApi": "/console/dashboard/previews",
"reqApiVersion": "v2",
"reqTime": 1776417455831,
"id": "sys:role:list",
"type": "SYSTEM",
"name": "角色管理",
"link": "/system/manager/role",
"createTime": 1776417455831
},
{
"reqTraceId": "GL-4bdfb064bb5ba9936cc50ae6871d8094",
"reqUserId": "160",
"reqLanguage": "en",
"reqZoneId": "UTC",
"reqIp": "10.0.12.49",
"reqClient": "OPS",
"reqApi": "/console/dashboard/previews",
"reqApiVersion": "v2",
"reqTime": 1776417451877,
"id": "sys:user:list",
"type": "SYSTEM",
"name": "用户管理",
"link": "/system/manager/user",
"createTime": 1776417451878
},
{
"reqTraceId": "GL-5eea9989c975401d54c22eefce0def90",
"reqUserId": "160",
"reqLanguage": "en",
"reqZoneId": "UTC",
"reqIp": "10.0.20.94",
"reqClient": "OPS",
"reqApi": "/console/dashboard/previews",
"reqApiVersion": "v2",
"reqTime": 1776333417531,
"id": "RegionConfig",
"type": "SYSTEM",
"name": "区域配置",
"link": "/operation/manager/region/config",
"createTime": 1776333417531
},
{
"reqTraceId": "GL-0517a95d554779799e8899bffb75347b",
"reqUserId": "160",
"reqLanguage": "en",
"reqZoneId": "UTC",
"reqIp": "10.0.20.94",
"reqClient": "OPS",
"reqApi": "/console/dashboard/previews",
"reqApiVersion": "v2",
"reqTime": 1776332452114,
"id": "GameEgg",
"type": "SYSTEM",
"name": "砸金蛋记录",
"link": "/operation/manager/log/running_water/game/egg",
"createTime": 1776332452114
},
{
"reqTraceId": "GL-4c500d97fe3f6aa2e777c170e880bc31",
"reqUserId": "160",
"reqLanguage": "en",
"reqZoneId": "UTC",
"reqIp": "10.0.12.49",
"reqClient": "OPS",
"reqApi": "/console/dashboard/previews",
"reqApiVersion": "v2",
"reqTime": 1776313451145,
"id": "AppUserList",
"type": "SYSTEM",
"name": "用户列表",
"link": "/app/user/manager/user/table",
"createTime": 1776313451145
},
{
"reqTraceId": "GL-9eff89dc13f92cee8e476013e475b232",
"reqUserId": "160",
"reqLanguage": "en",
"reqZoneId": "UTC",
"reqIp": "10.0.20.94",
"reqClient": "OPS",
"reqApi": "/console/dashboard/previews",
"reqApiVersion": "v2",
"reqTime": 1776245058763,
"id": "TeamPolicyManager",
"type": "SYSTEM",
"name": "代理政策",
"link": "/team/team-policy",
"createTime": 1776245058764
},
{
"reqTraceId": "GL-dd55810a0797b1bd4288ebc6b7916f75",
"reqUserId": "160",
"reqLanguage": "en",
"reqZoneId": "UTC",
"reqIp": "10.0.12.49",
"reqClient": "OPS",
"reqApi": "/console/dashboard/previews",
"reqApiVersion": "v2",
"reqTime": 1776243303511,
"id": "team:member",
"type": "SYSTEM",
"name": "主播成员",
"link": "/team/team/member",
"createTime": 1776243303511
},
{
"reqTraceId": "GL-e41e534ef606191c784532ecefa38008",
"reqUserId": "160",
"reqLanguage": "en",
"reqZoneId": "UTC",
"reqIp": "10.0.12.49",
"reqClient": "OPS",
"reqApi": "/console/dashboard/previews",
"reqApiVersion": "v2",
"reqTime": 1776243294447,
"id": "BusinessDevelopment",
"type": "SYSTEM",
"name": "BD列表",
"link": "/team/business-development",
"createTime": 1776243294447
},
{
"reqTraceId": "GL-bae2f36d15f77bdb3e738dc58e1bc800",
"reqUserId": "160",
"reqLanguage": "en",
"reqZoneId": "UTC",
"reqIp": "10.0.20.94",
"reqClient": "OPS",
"reqApi": "/console/dashboard/previews",
"reqApiVersion": "v2",
"reqTime": 1776242391890,
"id": "game:config",
"type": "SYSTEM",
"name": "游戏列表",
"link": "/operation/manager/data_cnf/game-config",
"createTime": 1776242391890
},
{
"reqTraceId": "GL-022908aeb3ee33038d10981b0940fc26",
"reqUserId": "160",
"reqLanguage": "en",
"reqZoneId": "UTC",
"reqIp": "10.0.12.49",
"reqClient": "OPS",
"reqApi": "/console/dashboard/previews",
"reqApiVersion": "v2",
"reqTime": 1776158503061,
"id": "team:bdleader:work",
"type": "SYSTEM",
"name": "BDLeader工作",
"link": "/team/bd-leader-work",
"createTime": 1776158503061
},
{
"reqTraceId": "GL-7759554caea3846c5fca82c1478c28b3",
"reqUserId": "160",
"reqLanguage": "en",
"reqZoneId": "UTC",
"reqIp": "10.0.12.49",
"reqClient": "OPS",
"reqApi": "/console/dashboard/previews",
"reqApiVersion": "v2",
"reqTime": 1776074669162,
"id": "UserCustomGift",
"type": "SYSTEM",
"name": "用户专属礼物制作",
"link": "/app/user/manager/user_custom_gift",
"createTime": 1776074669162
},
{
"reqTraceId": "GL-9b16acbbd3ad6ac088740c1a30c564d1",
"reqUserId": "160",
"reqLanguage": "en",
"reqZoneId": "UTC",
"reqIp": "10.0.20.94",
"reqClient": "OPS",
"reqApi": "/console/dashboard/previews",
"reqApiVersion": "v2",
"reqTime": 1776074206583,
"id": "sys:resource:list",
"type": "SYSTEM",
"name": "资源管理",
"link": "/system/manager/resource",
"createTime": 1776074206583
},
{
"reqTraceId": "GL-d5e278dc379b183c02d52378775b1ad8",
"reqUserId": "160",
"reqLanguage": "en",
"reqZoneId": "UTC",
"reqIp": "10.0.20.94",
"reqClient": "OPS",
"reqApi": "/console/dashboard/previews",
"reqApiVersion": "v2",
"reqTime": 1776074206154,
"id": "sys:menu:list",
"type": "SYSTEM",
"name": "菜单管理",
"link": "/system/manager/menu",
"createTime": 1776074206154
},
{
"reqTraceId": "GL-f010314eafd3755510be60829bd7f5f8",
"reqUserId": "160",
"reqLanguage": "en",
"reqZoneId": "UTC",
"reqIp": "10.0.20.94",
"reqClient": "OPS",
"reqApi": "/console/dashboard/previews",
"reqApiVersion": "v2",
"reqTime": 1776070694764,
"id": "upload:file",
"type": "SYSTEM",
"name": "文件上传",
"link": "/system/manager/tools/manager/upload/file",
"createTime": 1776070694764
},
{
"reqTraceId": "GL-d305c7808e7665680e6f420c51f85be8",
"reqUserId": "160",
"reqLanguage": "en",
"reqZoneId": "UTC",
"reqIp": "10.0.20.94",
"reqClient": "OPS",
"reqApi": "/console/dashboard/previews",
"reqApiVersion": "v2",
"reqTime": 1776070692971,
"id": "tools:other",
"type": "SYSTEM",
"name": "小工具",
"link": "/system/manager/tools/manager/tools",
"createTime": 1776070692971
},
{
"reqTraceId": "GL-bfba1c4b6dddf7ca0e8afeb0567be6cb",
"reqUserId": "160",
"reqLanguage": "en",
"reqZoneId": "UTC",
"reqIp": "10.0.12.49",
"reqClient": "OPS",
"reqApi": "/console/dashboard/previews",
"reqApiVersion": "v2",
"reqTime": 1776070689919,
"id": "toot:gold:analyze",
"type": "SYSTEM",
"name": "金币分析",
"link": "/system/manager/tools/manager/gold-analyze",
"createTime": 1776070689919
},
{
"reqTraceId": "GL-9c6d5a4d2d0e240cdfa4f729d43b9fa8",
"reqUserId": "160",
"reqLanguage": "en",
"reqZoneId": "UTC",
"reqIp": "10.0.20.94",
"reqClient": "OPS",
"reqApi": "/console/dashboard/previews",
"reqApiVersion": "v2",
"reqTime": 1776070685881,
"id": "OrderCompensate",
"type": "SYSTEM",
"name": "订单补偿",
"link": "/system/manager/tools/manager/order/compensate",
"createTime": 1776070685882
},
{
"reqTraceId": "GL-f579fc5751d9d6867e1a703e7529ce18",
"reqUserId": "160",
"reqLanguage": "en",
"reqZoneId": "UTC",
"reqIp": "10.0.20.94",
"reqClient": "OPS",
"reqApi": "/console/dashboard/previews",
"reqApiVersion": "v2",
"reqTime": 1776070684510,
"id": "RedisManager",
"type": "SYSTEM",
"name": "Redis缓存管理",
"link": "/system/manager/tools/manager/redis",
"createTime": 1776070684511
},
{
"reqTraceId": "GL-dd1b84facd843153dd62aefea9fc01d4",
"reqUserId": "160",
"reqLanguage": "en",
"reqZoneId": "UTC",
"reqIp": "10.0.20.94",
"reqClient": "OPS",
"reqApi": "/console/dashboard/previews",
"reqApiVersion": "v2",
"reqTime": 1776070681380,
"id": "request:blacklist",
"type": "SYSTEM",
"name": "请求黑名单",
"link": "/system/manager/app/sys/request-blacklist",
"createTime": 1776070681380
},
{
"reqTraceId": "GL-2765d45daac6092185f983e42cc5c156",
"reqUserId": "160",
"reqLanguage": "en",
"reqZoneId": "UTC",
"reqIp": "10.0.12.49",
"reqClient": "OPS",
"reqApi": "/console/dashboard/previews",
"reqApiVersion": "v2",
"reqTime": 1776070679538,
"id": "ExternalIm",
"type": "SYSTEM",
"name": "IM通讯",
"link": "/system/manager/app/sys/external/im",
"createTime": 1776070679539
}
],
"customNavigations": []
}
}

1622
docs/i18n/线程协作.md Normal file

File diff suppressed because it is too large Load Diff

78
docs/i18n/线程模板.md Normal file
View File

@ -0,0 +1,78 @@
# 线程模板
## 使用方式
- 新开一个并行线程时,把下面内容复制到线程首条消息里,并把“线程名”和“认领文件”替换掉
- 新线程开始前,先读取:
- `docs/i18n/线程协作.md`
- `docs/i18n/翻译进度.md`
- `docs/i18n/翻译清单.md`
- `docs/i18n/主要路由.md`
- 每次准备领取新任务前,也要重新读取以上 3 份协作文档 + `docs/i18n/主要路由.md`,确认其他线程的认领状态已经写入文档,避免重复领取
- 每次领取任务时,先更新 md 认领记录,再开始改源码;不要先改代码再补文档
- 写入认领记录前和写完后,都要再检查一次以上 3 份协作文档 + `docs/i18n/主要路由.md`,确认没有线程冲突
## 文档定位
- 本文件只负责给新线程提供启动清单与首条消息模板
- 协作规则本体以 `docs/i18n/线程协作.md` 为准
- 主要路由结构与优先核查页面以 `docs/i18n/主要路由.md` 为准
- 进度时间线与文件级状态分别以 `docs/i18n/翻译进度.md``docs/i18n/翻译清单.md` 为准
## 启动前补充检查
- 读取 md 或源码时使用:`Get-Content -Raw -Encoding UTF8 <path>`
- 领取任务前先重读 3 份协作文档 + `docs/i18n/主要路由.md`,再决定是否写入新的认领记录
- 领取任务的推荐顺序:重读 3 份协作文档 + `docs/i18n/主要路由.md` -> 确认无人认领 -> 先写 md -> 再读同样的文档集合复查 -> 开始改代码
- 所有线程统一领取优先级:`首页 > docs/i18n/主要路由.md > 其他路由`
- 只要首页或 `docs/i18n/主要路由.md` 中页面仍未完成,就不要跳到其他普通路由
- 读取最新语言文件,避免覆盖别人新增的 key
- `src/lang/messages/pages/en.js`
- `src/lang/messages/pages/zh.js`
- 扫描认领页面是否存在同目录私有子组件、抽屉、弹窗表单;如果有,一并检查中文展示文案
- 构建验证使用:`npm run build:prod`
- 单文件快速扫中文可用:`Select-String -Path <file> -Pattern '[\u4e00-\u9fa5]' -Encoding UTF8`
## 推荐首条消息模板
你是国际化翻译协作中的【线程X】。
请先读取以下文件并基于其内容继续工作:
- `docs/i18n/线程协作.md`
- `docs/i18n/翻译进度.md`
- `docs/i18n/翻译清单.md`
- `docs/i18n/主要路由.md`
本线程当前认领文件:
- `待填写文件路径`
执行要求:
- 只翻译展示文本
- 不改功能逻辑
- 不改接口参数
- 不改路由路径
- 不改权限码
- 不改后端依赖值
- 认领优先级遵守:`首页 > docs/i18n/主要路由.md > 其他路由`
- 如果文本依赖接口返回且当前无法推断,先跳过,并把阻塞写入 md
- 领取任务后,先把认领信息写入 md确认无冲突后再开始改源码
- 完成后同步更新:
- `docs/i18n/翻译进度.md`
- `docs/i18n/翻译清单.md`
完成后输出:
- 已完成文件
- 新增语言 key 范围
- 未处理阻塞项
- 是否已更新 md
## 已沉淀的复用经验
- 页面内资源组/奖励配置类抽屉,优先查看是否可复用 `pages.propsSourceGroupForm`
- 如果 PowerShell 直接读取出现乱码,优先怀疑读取编码,不要急着判断文件损坏
- 如果 `npm run build` 失败,先检查 `package.json`,本项目应改用 `npm run build:prod`
- 多线程认领时,写 md 比改源码优先级更高;认领前后都要复查 3 份协作文档 + `docs/i18n/主要路由.md`,确认没有撞任务

312
docs/i18n/翻译清单.md Normal file
View File

@ -0,0 +1,312 @@
# 国际化翻译清单
## 文档定位
- 本文件只负责维护文件级待办、进行中、已完成与当前认领状态
- 需要看主要路由的完整父子级结构、路由别名、路径和入口文件时,统一查看 `docs/i18n/主要路由.md`
- 需要看阶段成果、构建验证与时间线时,统一查看 `docs/i18n/翻译进度.md`
- 状态说明:
- `已完成`
- `进行中`
- `待处理`
## 你的要求
### 翻译范围
- 扫描项目中的中文展示文本
- 只要不是涉及接口参数的内容,都要做翻译
- 路由、导航、页面标题、选项、按钮、表单标签、表格列名、提示文案、空态文案等都属于处理范围
### 禁止改动项
- 不要改功能逻辑
- 不要改接口参数
- 不要改路由路径
- 不要改权限码
- 不要改接口依赖的状态值、枚举值、提交值
- 不要把后端依赖的数据值翻译掉
### 文档维护要求
- 进度和清单文档统一放在 `docs/i18n/`
- 文档统一使用中文
- 后续每一轮翻译都要实时更新这些文档,避免上下文丢失
- 每轮源码翻译完成后,至少同步更新以下两份核心文档:
- `docs/i18n/翻译进度.md`
- `docs/i18n/翻译清单.md`
- 协作规则或主路由优先级发生变化时,再同步维护:
- `docs/i18n/线程协作.md`
- `docs/i18n/主要路由.md`
### 展示适配要求
- 英文等较长文案要兼顾展示宽度,不只做字面翻译
- 表单标签宽度不足时允许自动换行,必要时补充更宽的公共样式
- 表单标签需要保持纵向居中,避免宽度调整后标题贴边
- 表格列标题宽度不足时允许自动换行,避免英文单词被截断
- 没有设置 `min-width` 的普通表格列,需要补全局默认最小宽度
- 个别列仍不足时,再按页面单独调整
## 领取优先级
- 适用于所有线程,不只线程三
- 统一领取优先级:`已完成 src/views 页面私有组件复查 > 首页 > docs/i18n/主要路由.md > 其他路由`
- 在“已完成 `src/views` 页面私有组件复查”完成前,暂不继续领取新的未完成普通页面
- 只要首页或 `主要路由.md` 中页面仍未完成,后续线程都应优先从这两部分中选择任务
- `主要路由.md` 中具体优先领取哪个页面,仍可按原来的协作方式灵活分配,只要先读 md、先写认领、避免冲突
## 已完成
### 已完成主要路由索引
- 详细父子级结构、路由别名、路径与入口文件统一见 `docs/i18n/主要路由.md`
- 以下索引已按 `docs/i18n/主要路由.md` 当前父子级顺序同步更新
- 用户管理 / User Management
- 用户列表 / User List
- 运营管理 / Operations Management
- 数据配置 / Data Configuration
- 转盘抽奖品配置 / Spin Wheel Prize Configuration
- 国家管理 / Country Management
- 活动配置 / Activity Configuration
- 活动管理 / Activity Management
- 消息推送 / Message Push
- 运营日志 / Operations Logs
- 砸金蛋记录 / Golden Egg Smash Records
- APP管理员 / App Administrator
- App启动页 / App Launch Page
- 系统公告管理 / System Notice Management
- 语音房间 / Voice Rooms
- 房间资料列表 / Room Profile List
- 道具管理 / Prop Management
- 资源组配置 / Resource Group Configuration
- 系统工具 / System Tools
- Redis缓存管理 / Redis Cache Management
- 文件上传 / File Upload
- 主播中心 / Broadcaster Center
- 代理钻石政策 / Agent Diamond Policy
- 代理政策 / Agent Policy
- BD列表 / BD List
- 补充页面
- 抽奖概率配置 / Lottery Probability Settings
### 已完成文件平铺清单
- `src/views/login/index.vue`
- `src/components/Breadcrumb/index.vue`
- `src/components/HeaderSearch/index.vue`
- `src/components/SizeSelect/index.vue`
- `src/components/data/RestPassword/index.vue`
- `src/layout/components/NavbarLight.vue`
- `src/layout/components/Sidebar/Item.vue`
- `src/utils/get-page-title.js`
- `src/permission.js`
- `src/utils/ops-message.js`
- `src/views/dashboard/introduction/waiting-processing/index.vue`
- `src/views/dashboard/index.vue`
- `src/views/dashboard/dashboard.vue`
- `src/views/dashboard/dashboard-charts/index.vue`
- `src/views/dashboard/quick-link/index.vue`
- `src/views/dashboard/introduction/user-account-info/index.vue`
- `src/views/dashboard/introduction/user-navigation/index.vue`
- `src/views/dashboard/components/active-index/index.vue`
- `src/views/dashboard/components/daily-currency/index.vue`
- `src/views/dashboard/components/daily-currency/line-graph-charts.vue`
- `src/views/dashboard/components/daily-currency-gold/index.vue`
- `src/views/dashboard/components/daily-currency-gold/line-graph-charts.vue`
- `src/views/dashboard/components/daily-currency-gold-origin-top/index.vue`
- `src/views/dashboard/components/daily-currency-gold-origin-top/bar-graph-charts.vue`
- `src/views/dashboard/components/daily-purchase/index.vue`
- `src/views/dashboard/components/daily-purchase/line-graph-charts-general.vue`
- `src/views/dashboard/components/daily-purchase/line-graph-charts.vue`
- `src/views/dashboard/components/daily-register-user/index.vue`
- `src/views/dashboard/components/daily-register-user/line-graph-charts.vue`
- `src/views/dashboard/components/monthly-purchase/index.vue`
- `src/views/dashboard/components/monthly-purchase/line-graph-charts.vue`
- `src/views/dashboard/components/user-daily-currency-gold-top/index.vue`
- `src/views/dashboard/components/user-daily-currency-gold-top/bar-graph-charts.vue`
- `src/views/dashboard/components/user-daily-currency-recharge-top/index.vue`
- `src/views/dashboard/components/user-daily-currency-recharge-top/bar-graph-charts.vue`
- `src/components/data/PropsSalesOverviewCharts/index.vue`
- `src/components/data/PropsSalesOverviewCharts/dialog.vue`
- `src/components/data/PropsSalesOverviewCharts/bar-graph-charts.vue`
- `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`
- `src/views/user/user-table/index.vue`
- `src/components/data/UserInfo/BaseInfo/index.vue`
- `src/views/room/profile/index.vue`
- `src/views/team/policy/index.vue`
- `src/views/props/props-source-group/form-edit.vue`
- `src/views/cnf/lottery/index.vue`
- `src/views/activity/template-cnf/index.vue`
- `src/views/team/business-development/index.vue`
- `src/views/team/diamond-policy/index.vue`
- `src/components/data/AccountHanle/index.vue`
- `src/views/sys/region-config/region/index.vue`
- `src/components/data/RoomInfo/index.vue`
- `src/components/data/UserAuthForm/index.vue`
- `src/components/data/InappPurchaseApple/index.vue`(已核查,无需重复修改)
- `src/components/data/InAppPurchaseDetails/index.vue`
- `src/components/data/InappPurchaseGoogle/index.vue`
- `src/components/data/UserInfo/BankCardDrawer/index.vue`
- `src/components/data/UserInfo/BaseInfo/AssociatedDeviceTimeline.vue`
- `src/components/data/UserInfo/AccountStatusLatestLog/index.vue`
- `src/components/data/UserInfo/UserTableExhibit/index.vue`
- `src/components/data/SearchRoomInput/index.vue`
- `src/components/data/GiftHistoryDetailsDrawer/index.vue`
- `src/components/data/EditUserForm/index.vue`
- `src/components/data/EditUserGoldCoinRewardForm/index.vue`
- `src/components/data/EditUserDiamondRewardForm/index.vue`
- `src/views/game/lucky-box/standard-config/gift-details.vue`
- `src/views/game/lucky-box/standard-config/index.vue`
- `src/views/game/lucky-box/standard-config/edit.vue`
- `src/views/game/lucky-box/standard-config/probability.vue`
- `src/views/game/lucky-box/award-config/index.vue`
- `src/views/game/lucky-box/award-config/edit.vue`
- `src/views/game/lucky-box/award-config/details.vue`
- `src/views/game/lucky-box/bounty-config/index.vue`
- `src/views/game/lucky-box/bounty-config/edit.vue`
- `src/views/game/lucky-box/bounty-config/details.vue`
- `src/views/game/lucky-box/fortune-config/index.vue`(已复核完成;目录实际仅存在该文件)
- `src/views/game/egg/index.vue`
- `src/views/game/egg/egg-exchange-record/index.vue`
- `src/views/game/egg/egg-lottery-record/index.vue`
- `src/views/game/egg/egg-lottery-cnf/index.vue`
- `src/views/game/lottery-config/form-edit.vue`
- `src/views/game/lottery-config/select-propsr-popover.vue`
- `src/views/game/lottery-config/index.vue`
- `src/components/data/AccountInput/index.vue`
- `src/components/data/EditUserGameCouponDeductionForm/index.vue`
- `src/views/user/freight/index.vue`
- `src/views/user/freight/form-edit.vue`
- `src/views/user/freight/running-water.vue`
- `src/views/user/freight/seller-info.vue`
- `src/views/user/freight/freight-seller-running-water.vue`
- `src/views/sys/region-config/index.vue`
- `src/views/sys/region-config/assist/index.vue`
- `src/views/props/props-source-group/index.vue`
- `src/views/sys/country-code/index.vue`
- `src/views/user/app-manager/index.vue`
- `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/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`
- 表格展示适配:表头自动换行 + 默认最小宽度补齐
- 表格展示适配:表头 `.cell` 高度自适应 + 纵向居中修正
- 表单展示适配:标题纵向居中
## 进行中
- 临时任务:已完成 `src/views` 页面私有组件复查
- 状态:进行中
- 范围说明:
- 只检查 `src/views/**` 下已经标记“已完成”的页面目录
- 只复查页面私有组件,不包含公共组件目录 `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`
- 当前已发现但尚未补齐到已完成记录的私有组件:
- `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`
- 原因:当前未纳入入口渲染链,继续按既有排除项处理
- `src/views/props/props-source-group/select-propsr-popover.vue`
- 原因:当前未被入口页或 `form-edit.vue` 引用,继续按既有排除项处理
## 优先待处理
- 已完成 `src/views` 页面目录中的私有组件复查任务
- 主要路由中已部分完成、但仍需继续收口的入口页
- 主要路由中的其他页面
- 统一参考 `docs/i18n/主要路由.md`
- 当首页与本文件中的“优先待处理”完成后,再继续处理 `主要路由.md` 中剩余未完成页面
## 第一批高优先级页面
- `src/views/user/user-table/index.vue`:已完成
- `src/views/room/profile/index.vue`:已完成
- `src/views/team/policy/index.vue`:已完成
- `src/views/message/push/index.vue`:已完成(线程二;同步收口 `new-push-form.vue``push-log.vue``push-task.vue`
- `src/views/message/push/push-task.vue`:已完成
- `src/components/data/UserInfo/BaseInfo/index.vue`:已完成
## 第二批高优先级页面
- `src/views/props/props-source-group/form-edit.vue`:已完成静态文案,接口返回名称待补数据
- `src/views/cnf/lottery/index.vue`:已完成
- `src/views/activity/template-cnf/index.vue`:已完成
- `src/views/team/business-development/index.vue`:已完成
- `src/views/sys/region-config/region/index.vue`:已完成(线程一)
- `src/views/team/diamond-policy/index.vue`:已完成(线程二)
- `src/views/game/lottery-config/form-edit.vue`:已完成(线程二)
- `src/views/game/lottery-config/index.vue`:已完成(线程二)
## 高优先级组件
- `src/components/data/AccountHanle/index.vue`:已完成(线程二)
- `src/components/data/InappPurchaseApple/index.vue`:已核查完成(线程一)
- `src/components/data/InAppPurchaseDetails/index.vue`:已完成(线程二)
- `src/components/data/InappPurchaseGoogle/index.vue`:已完成(线程一)
- `src/components/data/RoomInfo/index.vue`:已完成(线程三)
- `src/components/data/UserInfo/BankCardDrawer/index.vue`:已完成(线程三)
- `src/components/data/UserAuthForm/index.vue`:已完成(线程三)
- `src/components/data/UserInfo/AccountStatusLatestLog/index.vue`:已完成(线程二)
## 本轮保留项
- `src/components/data/AccountHanle/index.vue`
- 默认“警告”描述文案保留中文
- 原因:该内容会直接写入提交表单并发给后端,不按纯展示文案处理
- `src/components/data/RoomInfo/index.vue`
- 注释掉的“装饰道具”代码块仍保留中文
- 原因:当前不渲染,不属于可见展示文案;领取任务前已按新规则重读 3 份协作文档 + `docs/i18n/主要路由.md` 后再认领
- `src/views/props/props-source-group/select-propsr-popover.vue`
- 当前保留原样
- 原因:已核对 `src/views/props/props-source-group/index.vue` 的入口 import 列表与模块目录文件列表;该文件当前未被入口页或 `form-edit.vue` 引用,不属于本轮入口渲染链
- `src/views/props/props-source-group/form-edit.vue`
- 仍保留接口返回名称类文本与注释类中文
- 原因:接口返回的 `badgeName``pItem.name`、资源名称等需依赖样例数据判断是否可安全翻译;注释不属于可见展示文案
- `src/views/user/app-manager/**/*.vue`
- 当前保留注释“去除校验”和注释掉的“管理房间”代码块原样
- 原因:均不属于可见展示文案;接口返回的 `resourceName` 等后端资源名称保持原样,不做翻译
- `src/views/sys/notice-message/index.vue`
- 当前保留字段行尾注释与“删除”注释原样
- 原因:均不属于可见展示文案;`permissionsSysOriginPlatforms``item.label` 作为公共系统来源数据,本轮未改其值
## 需持续关注
- 历史语言文件中的旧键名和新语义键并存
- 页面中文较多的文件需要分批推进,避免一次性改动过大
- 优先处理高频页面、公共组件、用户详情类抽屉和配置类页面

837
docs/i18n/翻译进度.md Normal file
View File

@ -0,0 +1,837 @@
# 国际化翻译进度
## 文档定位
- 本文件只负责记录全局进展、阶段成果、构建验证结果与下一步目标
- 主要路由的完整父子级结构、路由别名、路径、入口文件与主路由优先级,以 `docs/i18n/主要路由.md` 为准
- 文件级待办、进行中、已完成与认领状态,以 `docs/i18n/翻译清单.md` 为准
- 协作规则、读 md 顺序、认领要求与交接规范,以 `docs/i18n/线程协作.md` 为准
## 当前目标
- 将项目页面中出现的中文展示文本逐步接入国际化
- 当前先支持 `en``zh`
- 后续如果增加 `bn``ar``tr` 等语言,优先复用现有键名体系
## 任务优先级
- 全线程统一优先级:`已完成 src/views 页面私有组件复查 > 首页 > docs/i18n/主要路由.md > 其他路由`
- 在“已完成 `src/views` 页面私有组件复查”完成前,暂不继续领取新的未完成普通页面
- 只要首页 `Dashboard` 模块或 `主要路由.md` 中页面仍未完成,后续线程领取新任务时都应优先从这两部分中选择
- `主要路由.md` 中具体先领哪个页面仍可灵活分配,但不得跳过首页整体优先级
## 必须遵守的约束
- 只翻译展示文本
- 不修改功能逻辑
- 不修改接口参数
- 不修改路由路径
- 不修改权限码
- 不修改后端依赖的枚举值、状态值、别名值、提交值
- 凡是会传给接口、或依赖后端匹配的值,一律保持原样
- 已经本来就是英文的展示文本,不强制改动
## 展示适配要求
- 英文等较长展示文案需要考虑布局适配
- 表单标签在宽度不足时允许自动换行,并可按页面需要补充更宽的标签样式
- 表格列表列标题在宽度不足时允许自动换行,不再截断单词
- 对未设置 `min-width` 的普通表格列补全局默认最小宽度,先保证大部分标题至少能展示完整单词
- 个别仍然宽度不足的列,后续再按页面逐项微调
## 已完成
### 主要路由完成摘要
- 详细父子级结构、路由别名、路径与验收入口统一见 `docs/i18n/主要路由.md`
- 以下摘要已按 `docs/i18n/主要路由.md` 当前父子级顺序同步更新
- 已完成主路由摘要:
- 用户管理 / User Management
- 用户列表 / User List
- 运营管理 / Operations Management
- 数据配置 / Data Configuration
- 转盘抽奖品配置 / Spin Wheel Prize Configuration
- 国家管理 / Country Management
- 活动配置 / Activity Configuration
- 活动管理 / Activity Management
- 消息推送 / Message Push
- 运营日志 / Operations Logs
- 砸金蛋记录 / Golden Egg Smash Records
- APP管理员 / App Administrator
- App启动页 / App Launch Page
- 系统公告管理 / System Notice Management
- 语音房间 / Voice Rooms
- 房间资料列表 / Room Profile List
- 道具管理 / Prop Management
- 资源组配置 / Resource Group Configuration
- 系统工具 / System Tools
- Redis缓存管理 / Redis Cache Management
- 文件上传 / File Upload
- 主播中心 / Broadcaster Center
- 代理钻石政策 / Agent Diamond Policy
- 代理政策 / Agent Policy
- BD列表 / BD List
- 已完成但未在当前主要路由菜单返回中命中的补充页面:
- 抽奖概率配置 / Lottery Probability Settings
### 基础能力
- 已接入项目国际化基础能力
- 已使用 Vuex 存储当前语言
- 已接入 Element UI 语言包切换
- 已支持基于时区的默认语言识别
- 未匹配到现有语言类型时,默认回退到英文
### 导航与通用区域
- 已增加导航栏语言切换入口
- 已增加头像下拉中的语言切换入口
- 已支持侧边栏路由标题翻译
- 已调整侧边栏宽度、自动换行和图标居中表现
### 其他已处理内容
- 已移除 `src/views/dashboard/introduction/waiting-processing/index.vue` 中“每日一文”相关展示数据
- 已保留系统时间展示
- 已将默认语言检测与浏览器时区关联
- 已将语言切换结果持久化
- 刷新后优先使用用户上次选择的语言
- 只有首次没有持久化记录时,才使用默认语言判断逻辑
## 本轮新增进展
### 2026-04-20
- 已完成页面:
- `src/views/tools/upload/index.vue`
- 说明:已完成上传方式开关、拖拽上传提示、失败状态与超限提示国际化;接口参数、文件上传逻辑、路由路径与后端依赖值保持原样
- 页面私有组件状态:
- `src/views/tools/upload/index.vue`:无私有组件,已完成
- 已补充页面级语言 key
- `src/lang/messages/pages/en.js`
- `src/lang/messages/pages/zh.js`
- 新增范围:`pages.uploadFile`
- 已完成模块级复核:
- `src/views/tools/upload/index.vue`
- 说明:已执行中文残留扫描;当前未发现剩余中文可见文本
- 已完成构建验证:
- 直接执行 `npm run build:prod` 构建通过
- 说明:当前仍保留项目原有 `22` 条 warning未因本轮 `tools/upload` 国际化改动新增阻塞错误
- 已完成页面:
- `src/views/sys/start-page/index.vue`
- 说明:已完成筛选区 placeholder、状态下拉、活动类型展示映射、表格列头、按钮、删除确认与成功提示国际化接口参数、状态值、路由路径与系统来源数据保持原样
- 已处理页面私有子组件:
- `src/views/sys/start-page/form-edit.vue`
- 说明已完成弹窗标题、表单标签、placeholder、保存/取消按钮、必填校验提示与活动类型展示映射国际化;提交字段、上传逻辑与接口调用保持原样
- 已补充页面级语言 key
- `src/lang/messages/pages/en.js`
- `src/lang/messages/pages/zh.js`
- 新增范围:`pages.startPage`
- 已完成模块级复核:
- `src/views/sys/start-page/**/*.vue`
- 说明:已执行递归中文残留扫描;当前残余中文仅保留在模板注释与“删除”代码注释中
- 已完成构建验证:
- 直接执行 `npm run build:prod` 构建通过
- 说明:当前仍保留项目原有 `22` 条 warning未因本轮 `sys/start-page` 国际化改动新增阻塞错误
- 已完成页面:
- `src/views/tools/redis/index.vue`
- 说明:已完成入口快捷标签、空态文案、打卡缓存查询弹窗标题/按钮/校验提示国际化;接口方法名、缓存查询 key、路由路径与返回数据结构保持原样
- 已补充页面级语言 key
- `src/lang/messages/pages/en.js`
- `src/lang/messages/pages/zh.js`
- 新增范围:`pages.redisTools`
- 已完成模块级复核:
- `src/views/tools/redis/index.vue`
- 说明:已执行中文残留扫描;当前未发现剩余中文可见文本
- 已完成构建验证:
- 首次直接执行 `npm run build:prod` 触发 `ERR_OSSL_EVP_UNSUPPORTED`
- 改用 `$env:NODE_OPTIONS='--openssl-legacy-provider'; npm run build:prod` 后构建通过
- 说明:当前仍保留项目原有 `22` 条 warning未因本轮 `tools/redis` 国际化改动新增阻塞错误
- 已完成页面:
- `src/views/sys/notice-message/index.vue`
- 说明:已完成筛选区、表格列头、类型文案、按钮、发布/上架确认提示、成功提示、弹窗标题、表单标签、placeholder 与校验提示国际化;接口参数、状态值、类型值、路由路径与公共系统来源数据保持原样
- 已补充页面级语言 key
- `src/lang/messages/pages/en.js`
- `src/lang/messages/pages/zh.js`
- 新增范围:`pages.noticeMessage`
- 已完成模块级复核:
- `src/views/sys/notice-message/**/*.vue`
- 说明:已执行递归中文残留扫描;当前残余中文仅保留在字段行尾注释与“删除”注释中
- 已完成构建验证:
- 首次直接执行 `npm run build:prod` 触发 `ERR_OSSL_EVP_UNSUPPORTED`
- 改用 `$env:NODE_OPTIONS='--openssl-legacy-provider'; npm run build:prod` 后构建通过
- 说明:当前仍保留项目原有 `22` 条 warning未因本轮 `sys/notice-message` 国际化改动新增阻塞错误
- 已完成页面:
- `src/views/user/app-manager/index.vue`
- 说明:已完成入口页 tab 标题国际化,并按模块级检查方案一并收口两个可见子页与权限设置弹窗;接口参数、角色值、分组值、路由路径与后端资源名称保持原样
- 已处理页面私有子组件:
- `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`
- 说明:已完成筛选区、表格列头、状态文案、按钮、弹窗标题、表单标签、确认提示、校验提示与本地角色/分组映射国际化;接口返回的 `resourceName` 等后端数据未做翻译
- 已补充页面级语言 key
- `src/lang/messages/pages/en.js`
- `src/lang/messages/pages/zh.js`
- 新增范围:`pages.appManager`
- 已完成模块级复核:
- `src/views/user/app-manager/**/*.vue`
- 说明:已执行递归中文残留扫描;当前残余中文仅保留在注释“去除校验”与注释掉的“管理房间”代码块中
- 已完成构建验证:
- 首次直接执行 `npm run build:prod` 触发 `ERR_OSSL_EVP_UNSUPPORTED`
- 改用 `$env:NODE_OPTIONS='--openssl-legacy-provider'; npm run build:prod` 后构建通过
- 说明:当前仍保留项目原有 `22` 条 warning未因本轮 `user/app-manager` 国际化改动新增阻塞错误
- 已完成页面:
- `src/views/sys/country-code/index.vue`
- 说明:已完成筛选区 placeholder、表格列头、状态文案、弹窗标题、表单标签、按钮、上传按钮与校验提示国际化接口参数、提交字段、路由路径与后端依赖值保持原样
- 已补充页面级语言 key
- `src/lang/messages/pages/en.js`
- `src/lang/messages/pages/zh.js`
- 新增范围:`pages.countryCode`
- 已完成模块级复核:
- `src/views/sys/country-code/index.vue`
- 说明:已执行中文残留扫描;当前残余中文仅保留在注释中
- 已完成构建验证:
- 直接使用 `$env:NODE_OPTIONS='--openssl-legacy-provider'; npm run build:prod` 构建通过
- 说明:当前仍保留项目原有 `22` 条 warning包含项目既有 `getAddressUrl` 导出缺失 warning未因本轮 `sys/country-code` 国际化改动新增阻塞性错误
- 已完成页面:
- `src/views/props/props-source-group/index.vue`
- 说明:已完成入口页筛选区 placeholder、搜索与添加按钮、表格列头、时间展示、编辑按钮与 loading 文案国际化;接口查询参数、分页结构、上下架开关值与弹窗逻辑保持原样
- 已补充页面级语言 key
- `src/lang/messages/pages/en.js`
- `src/lang/messages/pages/zh.js`
- 新增范围:`pages.propsSourceGroup`
- 复用范围:`common.search``pages.propsSourceGroupForm`
- 已完成模块级复核:
- `src/views/props/props-source-group/index.vue`
- `src/views/props/props-source-group/form-edit.vue`
- 说明:已对 `src/views/props/props-source-group/**/*.vue` 执行递归中文扫描;当前 `index.vue` 残余中文仅在注释中,`form-edit.vue` 残余主要为注释与接口返回名称类文本,`select-propsr-popover.vue` 当前未接入入口渲染链
- 已完成构建验证:
- 首次直接执行 `npm run build:prod` 触发 `ERR_OSSL_EVP_UNSUPPORTED`
- 改用 `$env:NODE_OPTIONS='--openssl-legacy-provider'; npm run build:prod` 后构建通过
- 说明:当前仍保留项目原有 `22` 条 warning未因本轮 `props-source-group/index.vue` 国际化改动新增阻塞错误
- 已新增临时任务规则:
- 暂停继续领取新的未完成普通页面
- 先检查 `src/views` 下所有已标记完成页面的私有组件是否已完成国际化
- 后续页面完成记录必须额外写明“页面私有组件状态”
- 已形成首批历史页面私有组件复核队列:
- `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`
### 2026-04-17
- 已完成首页模块:
- `src/views/dashboard/index.vue`
- `src/views/dashboard/dashboard.vue`
- `src/views/dashboard/dashboard-charts/index.vue`
- `src/views/dashboard/quick-link/index.vue`
- `src/views/dashboard/introduction/user-account-info/index.vue`
- `src/views/dashboard/introduction/user-navigation/index.vue`
- `src/views/dashboard/introduction/waiting-processing/index.vue`
- `src/views/dashboard/components/active-index/index.vue`
- `src/views/dashboard/components/daily-currency/index.vue`
- `src/views/dashboard/components/daily-currency/line-graph-charts.vue`
- `src/views/dashboard/components/daily-currency-gold/index.vue`
- `src/views/dashboard/components/daily-currency-gold/line-graph-charts.vue`
- `src/views/dashboard/components/daily-currency-gold-origin-top/index.vue`
- `src/views/dashboard/components/daily-currency-gold-origin-top/bar-graph-charts.vue`
- `src/views/dashboard/components/daily-purchase/index.vue`
- `src/views/dashboard/components/daily-purchase/line-graph-charts-general.vue`
- `src/views/dashboard/components/daily-purchase/line-graph-charts.vue`
- `src/views/dashboard/components/daily-register-user/index.vue`
- `src/views/dashboard/components/daily-register-user/line-graph-charts.vue`
- `src/views/dashboard/components/monthly-purchase/index.vue`
- `src/views/dashboard/components/monthly-purchase/line-graph-charts.vue`
- `src/views/dashboard/components/user-daily-currency-gold-top/index.vue`
- `src/views/dashboard/components/user-daily-currency-gold-top/bar-graph-charts.vue`
- `src/views/dashboard/components/user-daily-currency-recharge-top/index.vue`
- `src/views/dashboard/components/user-daily-currency-recharge-top/bar-graph-charts.vue`
- `src/components/data/PropsSalesOverviewCharts/index.vue`
- `src/components/data/PropsSalesOverviewCharts/dialog.vue`
- `src/components/data/PropsSalesOverviewCharts/bar-graph-charts.vue`
- 说明:已完成首页入口、概览卡片、图表 tab、图表 legend/tooltip、快捷入口与首页直接依赖销售总览组件的国际化收口并补齐运行时语言切换所需的首页响应式联动
- 已补充页面级语言 key
- `src/lang/messages/pages/en.js`
- `src/lang/messages/pages/zh.js`
- 新增范围:`pages.dashboard`
- 已完成模块级复核:
- `src/views/dashboard/**/*`
- `src/components/data/PropsSalesOverviewCharts/**/*`
- 说明:模块内静态中文展示文案残留扫描结果为 0
- 已完成构建验证:
- `npm run build:prod`
- 说明:本轮首页 i18n 改动编译通过,仍保留项目既有 `22` 条 warning未新增阻塞错误
- 已完成页面:
- `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`
- 说明:已完成消息推送入口页 tab、新建 Push 表单与 Push 日志页的可见静态文案国际化,并补齐 `push-task.vue` 中遗留成功提示;未改动接口参数、路由路径、权限码与后端依赖值
- 已补充页面级语言 key
- `src/lang/messages/pages/en.js`
- `src/lang/messages/pages/zh.js`
- 新增范围:`pages.messagePush``pages.messagePushLog`
- 扩展范围:`pages.messagePushTask`
- 已完成模块级复核:
- `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`
- 说明:已执行中文残留扫描,当前残余中文仅保留在注释中;`push-text-store.vue` 仍未被入口页渲染,已在协作文档中标记为排除项
- 已完成构建验证:
- `npm run build:prod`
- 说明:构建通过,当前仍保留项目原有 `22` 条 warning未因本轮 `message/push` i18n 改动新增阻塞性错误
- 已完成页面:
- `src/views/user/freight/index.vue`
- `src/views/user/freight/form-edit.vue`
- `src/views/user/freight/running-water.vue`
- `src/views/user/freight/seller-info.vue`
- `src/views/user/freight/freight-seller-running-water.vue`
- 说明:已完成用户货运模块列表页、编辑页、流水页与卖家相关弹窗中的静态展示文案国际化;仅对展示层做本地化映射,未改动后端依赖提交值、接口参数、路由路径与业务逻辑
- 已补充页面级语言 key
- `src/lang/messages/pages/en.js`
- `src/lang/messages/pages/zh.js`
- 新增范围:`pages.userFreight`
- 已完成模块级复核:
- `src/views/user/freight/**/*.vue`
- 说明:已执行递归中文扫描;当前残余中文仅保留在注释、后端依赖提交值或当前非可见数据结构中
- 已完成构建验证:
- `npm run build:prod`
- 说明:构建通过,当前仍保留项目原有 `22` 条 warning未因本轮 i18n 改动新增阻塞性错误
- 已完成页面:
- `src/views/sys/region-config/index.vue`
- `src/views/sys/region-config/assist/index.vue`
- 说明:已完成区域配置入口页 tab 标题与辅助配置页可见静态文案国际化;`region/index.vue` 沿用既有国际化结果,本轮完成入口联动收口;未改动接口参数、路由路径、权限码与后端依赖值
- 已扩展页面级语言 key
- `src/lang/messages/pages/en.js`
- `src/lang/messages/pages/zh.js`
- 扩展范围:`pages.regionConfig.tabs``pages.regionConfig.assist`
- 已完成模块级复核:
- `src/views/sys/region-config/index.vue`
- `src/views/sys/region-config/assist/index.vue`
- 说明:已执行中文残留扫描;当前残余中文仅保留在注释中
- 已完成构建验证:
- `npm run build:prod`
- 说明:构建通过,当前仍保留项目原有 `22` 条 warning未因本轮 `sys/region-config` i18n 改动新增阻塞性错误
- 已处理组件:
- `src/components/data/EditUserDiamondRewardForm/index.vue`
- 说明已完成钻石奖励弹窗标题、字段标签、placeholder、按钮与校验提示国际化`userId``quantity``remarks` 提交字段与接口调用保持原样,表单标签“钻石”复用 `pages.propsSourceGroupForm.resourceType.diamond`
- 已补充页面级语言 key
- `src/lang/messages/pages/en.js`
- `src/lang/messages/pages/zh.js`
- 新增范围:`pages.editUserDiamondRewardForm`
- 已完成构建验证:
- `src/components/data/EditUserDiamondRewardForm/index.vue` 国际化改动编译通过
- 2026-04-17 当前环境直接执行 `npm run build:prod` 已通过,当前仍保留项目原有 `22` 条 warning未因本轮 i18n 改动新增阻塞性错误
- 已处理组件:
- `src/components/data/EditUserGameCouponDeductionForm/index.vue`
- 说明已完成扣除用户游戏券弹窗标题、表单标签、placeholder、按钮与校验提示国际化接口参数、提交数量与备注内容保持原样
- 已补充页面级语言 key
- `src/lang/messages/pages/en.js`
- `src/lang/messages/pages/zh.js`
- 新增范围:`pages.editUserGameCouponDeductionForm`
- 已完成构建验证:
- `src/components/data/EditUserGameCouponDeductionForm/index.vue` 国际化改动编译通过
- 2026-04-17 当前环境直接执行 `npm run build:prod` 已通过,当前仍保留项目原有 `22` 条 warning未因本轮 i18n 改动新增阻塞性错误
- 已沉淀线程经验到文档:
- 2026-04-17 当前 Node 环境下,`$env:NODE_OPTIONS='--openssl-legacy-provider'` 会直接报 `--openssl-legacy-provider is not allowed in NODE_OPTIONS`
- 后续构建验证优先直接执行 `npm run build:prod`
### 2026-04-16
- 已补领页面:
- `src/views/game/egg/egg-lottery-cnf/index.vue`
- 说明:线程一已按最新协作规则重新读取 3 份 md 与语言文件,并已把此前遗漏的 Egg 配置页正式登记为本轮进行中任务;后续将只处理可见静态展示文本,不改接口参数与页面逻辑
- 已完成页面:
- `src/views/game/egg/index.vue`
- `src/views/game/egg/egg-exchange-record/index.vue`
- `src/views/game/egg/egg-lottery-record/index.vue`
- `src/views/game/egg/egg-lottery-cnf/index.vue`
- 说明:线程一已完成 Egg 模块入口页、兑换记录页、抽奖记录页与配置页的静态展示文本国际化收口;配置页补充了筛选区、按钮、表格列头、占位文案、奖品类型下拉、校验提示与确认弹窗翻译,并新增 `pages.eggLotteryCnf`
- 已完成模块级复核:
- `src/views/game/egg/**/*.vue`
- 说明:已按“模块级最优检查方案”对 Egg 模块做递归中文扫描,当前未发现剩余中文可见文本
- 已完成构建验证:
- `npm run build:prod`
- 说明:本轮直接执行构建已通过,仍保留项目既有 `22` 条 warning未新增阻塞错误
- 发现检查缺口:
- `src/views/game/egg/egg-lottery-cnf/index.vue`
- 说明:该文件与 `src/views/game/egg/index.vue` 同属一个可见 tab 模块,上一轮线程一虽然在认领记录中写了“暂未纳入本次范围”,但没有把它同步登记为“可见但待处理文件”,也没有按模块目录做完整核对;这说明此前的目录级检查还不够严谨
- 已补充协作规范:
- `docs/i18n/线程协作.md`
- 新增“模块级最优检查方案”
- 目的:后续认领 `views` 模块时,先核对入口页 import、tab、动态组件与模块目录文件列表避免再次漏掉像 `egg-lottery-cnf/index.vue` 这样的同级可见页面
- 已完成页面:
- `src/views/game/lucky-box/fortune-config/index.vue`
- 说明:已完成 `fortune-config` 页面静态展示文本国际化复核;本轮确认目录实际仅存在 `index.vue`,协作文档里提到的 `edit.vue``details.vue` 并不存在,无需继续查找或翻译
- 已复核页面级语言 key
- `src/lang/messages/pages/en.js`
- `src/lang/messages/pages/zh.js`
- 复核范围:`pages.luckyBoxFortuneConfig`
- 说明:中英文语言项已齐备,`index.vue` 中无剩余中文展示文本
- 已完成构建验证:
- 首次直接执行 `npm run build:prod` 因当前 Node/OpenSSL 组合触发 `ERR_OSSL_EVP_UNSUPPORTED`
- 改用 `$env:NODE_OPTIONS='--openssl-legacy-provider'; npm run build:prod` 后构建通过
- 当前仍保留项目原有 `22` 条 warning未因本轮 fortune-config 收口新增阻塞错误
- 已同步修正文档:
- `docs/i18n/线程协作.md`
- `docs/i18n/翻译清单.md`
- `docs/i18n/翻译进度.md`
- 说明:已移除对不存在的 `src/views/game/lucky-box/fortune-config/edit.vue``src/views/game/lucky-box/fortune-config/details.vue` 的错误引用,避免后续线程误判任务范围
### 2026-04-15
- 已处理组件:
- `src/components/data/AccountInput/index.vue`
- 说明:已完成账号输入组件中类型选择 placeholder、类型选项和平台选择 placeholder 的国际化;账号输入值、平台值与外部传入参数保持原样
- 已补充页面级语言 key
- `src/lang/messages/pages/en.js`
- `src/lang/messages/pages/zh.js`
- 新增范围:`pages.accountInput`
- 已完成构建验证:
- `src/components/data/AccountInput/index.vue` 国际化改动编译通过
- 当前仍保留项目原有 `22` 条 warning未因本轮 i18n 改动新增阻塞性错误
- 已处理组件:
- `src/components/data/InappPurchaseGoogle/index.vue`
- 说明已完成弹窗标题、购买记录、Google 订阅单据、状态文案、复制按钮、空内容提示与 tooltip 的国际化订阅令牌、json 内容、订单状态与接口返回值保持原样
- 已补充页面级语言 key
- `src/lang/messages/pages/en.js`
- `src/lang/messages/pages/zh.js`
- 新增范围:`pages.inappPurchaseGoogle`
- 已完成构建验证:
- `src/components/data/InappPurchaseGoogle/index.vue` 国际化改动编译通过
- 当前仍保留项目原有 `22` 条 warning未因本轮 i18n 改动新增阻塞性错误
- 已处理组件:
- `src/components/data/InAppPurchaseDetails/index.vue`
- 说明:已完成抽屉标题、订单信息区、状态流转、商品信息、元数据、事件通知和状态映射的国际化;`createNickname``productDescriptor`、商品名称/内容/描述、原因、事件类型与通知数据保持接口返回原值
- 已补充页面级语言 key
- `src/lang/messages/pages/en.js`
- `src/lang/messages/pages/zh.js`
- 新增范围:`pages.inAppPurchaseDetails`
- 已完成构建验证:
- `src/components/data/InAppPurchaseDetails/index.vue` 国际化改动编译通过
- 当前仍保留项目原有 `22` 条 warning未因本轮 i18n 改动新增阻塞性错误
- 已完成核查组件:
- `src/components/data/InappPurchaseApple/index.vue`
- 说明:当前组件已接入 `pages.inappPurchaseApple`,源码中未发现剩余中文展示文本,本轮无需重复修改源码
- 已处理组件:
- `src/components/data/UserAuthForm/index.vue`
- 说明已完成抽屉标题、卡片标题、手机号绑定按钮、已绑定状态、placeholder、校验提示、确认弹窗与成功提示的国际化认证类型列表中的 `item.type` 与手机号数据保持接口返回原样
- 已补充页面级语言 key
- `src/lang/messages/pages/en.js`
- `src/lang/messages/pages/zh.js`
- 新增范围:`pages.userAuthForm`
- 已完成构建验证:
- `src/components/data/UserAuthForm/index.vue` 国际化改动编译通过
- 当前仍保留项目原有 `22` 条 warning未因本轮 i18n 改动新增阻塞性错误
- 已处理组件:
- `src/components/data/RoomInfo/index.vue`
- 说明:已完成折叠面板标题、基础资料字段、状态标签、布尔展示文案、设置信息字段、徽章 tooltip 文案的国际化;房间公告内容、国家码、平台值与接口返回数据保持原样
- 已补充页面级语言 key
- `src/lang/messages/pages/en.js`
- `src/lang/messages/pages/zh.js`
- 新增范围:`pages.roomInfo`
- 保留项:
- `src/components/data/RoomInfo/index.vue`
- 内容:注释掉的“装饰道具”代码块中的中文
- 原因:该代码块当前不渲染,不属于可见展示文案,按协作规范不作为本轮必翻项处理
- 已完成构建验证:
- `src/components/data/RoomInfo/index.vue` 国际化改动编译通过
- 当前仍保留项目原有 `22` 条 warning未因本轮 i18n 改动新增阻塞性错误
- 已完成页面:
- `src/views/sys/region-config/region/index.vue`
- 说明已完成筛选区、表格列头、操作菜单、抽屉标题、表单标签、placeholder、钱包设置、确认弹窗与校验提示的国际化接口参数、区域编码字段、提交结构与元数据 key 保持原样
- 已补充页面级语言 key
- `src/lang/messages/pages/en.js`
- `src/lang/messages/pages/zh.js`
- 新增范围:`pages.regionConfig`
- 已完成构建验证:
- `src/views/sys/region-config/region/index.vue` 国际化改动编译通过
- 当前仍保留项目原有 `22` 条 warning未因本轮 i18n 改动新增阻塞性错误
- 已处理组件:
- `src/components/data/AccountHanle/index.vue`
- 说明:已完成弹窗标题、表单标签、处罚分组名、处罚选项名、天数选项、按钮、校验提示与翻译失败提示文案国际化;处罚值、提交字段和默认警告内容保持原样
- 已补充页面级语言 key
- `src/lang/messages/pages/en.js`
- `src/lang/messages/pages/zh.js`
- 新增范围:`pages.accountHandle`
- 阻塞/保留项:
- `src/components/data/AccountHanle/index.vue`
- 内容:默认“警告”描述文案
- 原因:该文本会直接写入提交表单并发送给后端,属于提交内容,不按纯展示文案处理
- 已完成构建验证:
- `src/components/data/AccountHanle/index.vue` 国际化改动编译通过
- 已完成页面:
- `src/views/team/diamond-policy/index.vue`
- 说明:已完成筛选区、政策菜单、表格列头、编辑区、历史抽屉、校验提示与确认弹窗翻译;接口参数、政策类型值、提交结构和删除/发布逻辑保持原样
- 已处理页面私有子组件:
- `src/views/team/diamond-policy/props-form-edit.vue`
- 说明:已完成抽屉内静态展示文案翻译,并复用现有 `pages.propsSourceGroupForm` key避免重复新增同义 key
- 已补充页面级语言 key
- `src/lang/messages/pages/en.js`
- `src/lang/messages/pages/zh.js`
- 新增范围:`pages.teamDiamondPolicy`
- 已完成构建验证:
- `src/views/team/diamond-policy/index.vue` 国际化改动编译通过
- `src/views/team/diamond-policy/props-form-edit.vue` 国际化改动编译通过
- 当前仍保留项目原有 `22` 条 warning未因本轮 i18n 改动新增阻塞性错误
- 已完成页面:
- `src/views/game/lottery-config/form-edit.vue`
- 说明已完成弹窗标题、游戏类型、奖励列表、添加配置区、按钮、placeholder、校验提示与概率提示的国际化`lotteryGameTypes` 的共享常量值未改动,仅在页面展示层对 `EGG` 做了本地映射翻译
- 已处理页面私有子组件:
- `src/views/game/lottery-config/select-propsr-popover.vue`
- 说明:已完成弹出层标题、类型名称标签与选择按钮国际化
- 已补充页面级语言 key
- `src/lang/messages/pages/en.js`
- `src/lang/messages/pages/zh.js`
- 新增范围:`pages.gameLotteryConfigForm`
- 复用范围:`pages.propsSourceGroupForm.resourceType``pages.propsSourceGroupForm.validation`
- 保留项:
- `src/views/game/lottery-config/form-edit.vue`
- 内容:`specialIdTypes``pItem.name``badgeName` 等动态或接口返回内容
- 原因:这类文本不属于静态展示文案,本轮按原值展示
- 已完成构建验证:
- `src/views/game/lottery-config/form-edit.vue` 国际化改动编译通过
- `src/views/game/lottery-config/select-propsr-popover.vue` 国际化改动编译通过
- 当前仍保留项目原有 `22` 条 warning未因本轮 i18n 改动新增阻塞性错误
- 已完成页面:
- `src/views/game/lottery-config/index.vue`
- 说明:已完成筛选区 placeholder、搜索/添加按钮、游戏类型列、道具列、时间列、编辑按钮与游戏类型展示映射国际化;接口查询参数、分页结构和编辑逻辑保持原样
- 已补充页面级语言 key
- `src/lang/messages/pages/en.js`
- `src/lang/messages/pages/zh.js`
- 新增范围:`pages.gameLotteryConfig`
- 保留项:
- `src/views/game/lottery-config/index.vue`
- 内容:注释掉的“上/下架”筛选块中的中文
- 原因:该区块当前不渲染,不属于可见展示文案
- 已完成构建验证:
- `src/views/game/lottery-config/index.vue` 国际化改动编译通过
- 当前仍保留项目原有 `22` 条 warning未因本轮 i18n 改动新增阻塞性错误
- 已沉淀线程经验到文档:
- 新线程读取源码与 md 时统一使用 UTF-8 显式编码
- 每次领取新任务前都要重新读取 `docs/i18n/线程协作.md``docs/i18n/翻译进度.md``docs/i18n/翻译清单.md``docs/i18n/主要路由.md`,确认认领状态已经写入 md 后再决定是否领取
- 每次认领任务时,必须先更新 md 再开始改源码;写入认领记录前后都要复查 3 份协作文档 + `docs/i18n/主要路由.md`,确认没有线程冲突
- 构建验证统一使用 `npm run build:prod`
- 同目录私有子组件需和页面一起扫描,避免“主页面已翻、抽屉仍是中文”
### 2026-04-14
- 已建立页面级翻译文件目录:
- `src/lang/messages/pages/en.js`
- `src/lang/messages/pages/zh.js`
- 已将页面级翻译模块接入:
- `src/lang/messages/en.js`
- `src/lang/messages/zh.js`
- 已完成页面:
- `src/views/message/push/push-task.vue`
- 说明:仅替换展示文本,接口值如 `OFFICIAL_MESSAGE_NOTICE`、语言值 `ar/tr/en`、状态值等保持不变
- `src/views/user/user-table/index.vue`
- 说明:已完成页面可见文案翻译,接口查询字段、权限码、提交值和路由逻辑未改动
- 已处理组件:
- `src/components/data/UserInfo/BaseInfo/index.vue`
- 当前状态:已完成展示文本翻译,剩余中文仅存在注释中
- 已完成页面:
- `src/views/room/profile/index.vue`
- 说明:已完成筛选区、表格、操作菜单、弹窗、表单校验文案和下拉展示项翻译;接口参数、事件值、角色值、审批状态值保持原样
- 已完成页面:
- `src/views/team/policy/index.vue`
- 说明:已完成筛选区、政策列表、编辑区、历史政策抽屉、确认提示与状态文案翻译;政策类型值、查询参数、发布状态字段等后端依赖值保持原样
- 已完成可见静态文案:
- `src/views/props/props-source-group/form-edit.vue`
- 说明:已完成抽屉标题、表单标签、按钮、占位文案、校验提示、奖励摘要和本地资源类型名称翻译
- 待补接口样例:
- 奖励列表中的 `badgeName`
- 资源选择项中的 `pItem.name`
- 接口返回的礼物 / 徽章 / 表情包 / 通用资源名称
- 处理策略:这类文本依赖接口返回,当前先保留原样,等待你后续提供接口返回数据后再逐项补齐
- 已完成页面:
- `src/views/cnf/lottery/index.vue`
- 说明:已完成配置页中的卡片标题、表单标签、按钮、弹窗提示、默认奖品文案和默认配置区翻译;仅处理展示文案,抽奖配置结构、接口提交体和字段值保持原样
- 已完成页面:
- `src/views/activity/template-cnf/index.vue`
- 说明:已完成筛选区、帮助说明、表格列标题、状态标签、操作菜单、加载文案与确认弹窗翻译;活动状态值、权限码、接口查询参数和路由跳转参数保持原样
- 已完成页面:
- `src/views/team/business-development/index.vue`
- 说明:已完成筛选区、表格列头、操作菜单、无权限提示、复制结果提示与删除确认弹窗翻译;权限码、接口查询参数、分页参数和操作逻辑保持原样
- 已补充并固定文档目录:
- `docs/i18n/翻译进度.md`
- `docs/i18n/翻译清单.md`
- 后续翻译时需要同步更新的文档:
- `docs/i18n/翻译进度.md`
- `docs/i18n/翻译清单.md`
- 已将你的约束写入文档并作为后续执行规则:
- 只翻译展示文本
- 不修改功能逻辑
- 不修改接口参数
- 不修改路由路径
- 不修改权限码
- 不修改后端依赖值
- 已补充国际化表单适配样式:
- 为英文等较长标题增加可复用的表单标签宽度和自动换行方案
- 已补充表单标题纵向居中,避免标签宽度加大后文字贴上边
- 当前已接入页面:
- `src/views/message/push/push-task.vue`
- `src/views/room/profile/index.vue`
- 公共样式位置:
- `src/styles/element-ui.scss`
- 已完成表格展示适配方案:
- 表头标题支持自动换行,避免英文标题被直接截断
- 未设置 `min-width` 的普通 `el-table-column` 增加全局默认最小宽度
- 已修正表头 `.cell` 高度自适应与纵向居中,避免 `th` 足够高但标题文本仍贴边
- 相关入口位置:
- `src/styles/element-ui.scss`
- `src/main.js`
- 已完成两次构建验证:
- 语言切换持久化改动编译通过
- `src/views/room/profile/index.vue` 国际化改动编译通过
- `src/views/team/policy/index.vue` 国际化改动编译通过
- `src/views/props/props-source-group/form-edit.vue` 静态文案国际化改动编译通过
- `src/views/cnf/lottery/index.vue` 国际化改动编译通过
- `src/views/activity/template-cnf/index.vue` 国际化改动编译通过
- `src/views/team/business-development/index.vue` 国际化改动编译通过
- 表格标题自动换行与默认最小宽度适配编译通过
- 表格标题 `.cell` 高度自适应与纵向居中修正编译通过
- 表单标题纵向居中适配编译通过
- 已处理组件:
- `src/components/data/UserInfo/BankCardDrawer/index.vue`
- 说明已完成抽屉标题、卡片标签、审核状态、使用状态、操作按钮、表单标签、placeholder、校验提示与确认弹窗国际化`cardType` 与银行卡类型枚举值保持原样
- 已补充页面级语言 key
- `src/lang/messages/pages/en.js`
- `src/lang/messages/pages/zh.js`
- 新增范围:`pages.bankCardDrawer`
- 已完成构建验证:
- `src/components/data/UserInfo/BankCardDrawer/index.vue` 国际化改动编译通过
- 当前仍保留项目原有 `22` 条 warning未因本轮 i18n 改动新增阻塞性错误
- 已处理页面:
- `src/views/game/lucky-box/standard-config/gift-details.vue`
- 说明已完成列表弹窗、表格列头、操作按钮、提示文案、表单标签、placeholder、校验提示与礼物类型展示映射的国际化礼物类型实际值、接口参数与页面逻辑保持原样
- 已补充页面级语言 key
- `src/lang/messages/pages/en.js`
- `src/lang/messages/pages/zh.js`
- 新增范围:`pages.luckyBoxGiftDetails`
- 已完成构建验证:
- `src/views/game/lucky-box/standard-config/gift-details.vue` 国际化改动编译通过
- 当前仍保留项目原有 `22` 条 warning未因本轮 i18n 改动新增阻塞性错误
- 已完成页面:
- `src/views/game/lucky-box/standard-config/index.vue`
- 说明:已完成筛选区、表格列头、类型标签、操作按钮、删除确认与成功提示的国际化;列表筛选值、状态布尔值与接口逻辑保持原样
- 已处理页面私有子组件:
- `src/views/game/lucky-box/standard-config/edit.vue`
- `src/views/game/lucky-box/standard-config/probability.vue`
- 说明已完成编辑弹窗与概率配置弹窗中的标题、表单标签、placeholder、校验提示、说明提示、保存确认与错误提示国际化概率数值、礼物关联值与接口参数保持原样
- 已补充页面级语言 key
- `src/lang/messages/pages/en.js`
- `src/lang/messages/pages/zh.js`
- 新增范围:`pages.luckyBoxStandardConfig`
- 已完成构建验证:
- `src/views/game/lucky-box/standard-config/index.vue` 国际化改动编译通过
- `src/views/game/lucky-box/standard-config/edit.vue` 国际化改动编译通过
- `src/views/game/lucky-box/standard-config/probability.vue` 国际化改动编译通过
- 当前仍保留项目原有 `22` 条 warning未因本轮 i18n 改动新增阻塞性错误
- 已处理组件:
- `src/components/data/UserInfo/BaseInfo/AssociatedDeviceTimeline.vue`
- 说明已完成语言、时区、IP、客户端、设备信息、Push 信息与注册时间等标签国际化;`item.latestDevice.*``item.userProfile.createTime` 等动态数据保持原样
- 语言 key 处理:
- 本轮复用 `pages.userBaseInfo.labels`
- 本轮复用 `pages.userBaseInfo.words.activeIp`
- 无需新增页面级语言 key
- 已完成构建验证:
- `src/components/data/UserInfo/BaseInfo/AssociatedDeviceTimeline.vue` 国际化改动编译通过
- 当前仍保留项目原有 `22` 条 warning未因本轮 i18n 改动新增阻塞性错误
- 已处理组件:
- `src/components/data/UserInfo/AccountStatusLatestLog/index.vue`
- 说明:已完成最新账号处理日志标签文案国际化;`item.approvalUserName``item.statusName``item.createTime` 保持动态原值展示
- 已补充页面级语言 key
- `src/lang/messages/pages/en.js`
- `src/lang/messages/pages/zh.js`
- 新增范围:`pages.accountStatusLatestLog`
- 已完成构建验证:
- `src/components/data/UserInfo/AccountStatusLatestLog/index.vue` 国际化改动编译通过
- 当前仍保留项目原有 `22` 条 warning未因本轮 i18n 改动新增阻塞性错误
- 已处理组件:
- `src/components/data/UserInfo/UserTableExhibit/index.vue`
- 说明:已完成特殊 ID 账号展示后缀国际化;直接复用 `pages.userBaseInfo.labels.specialIdSuffix`,未新增页面级语言 key
- 已完成构建验证:
- `src/components/data/UserInfo/UserTableExhibit/index.vue` 国际化改动编译通过
- 当前仍保留项目原有 `22` 条 warning未因本轮 i18n 改动新增阻塞性错误
- 已处理组件:
- `src/components/data/SearchRoomInput/index.vue`
- 说明:已完成房间 ID 输入框 placeholder、类型选择、搜索结果弹窗与失败提示国际化房间数据与接口返回内容保持原样
- 已补充页面级语言 key
- `src/lang/messages/pages/en.js`
- `src/lang/messages/pages/zh.js`
- 新增范围:`pages.searchRoomInput`
- 已完成构建验证:
- `src/components/data/SearchRoomInput/index.vue` 国际化改动编译通过
- 当前仍保留项目原有 `22` 条 warning未因本轮 i18n 改动新增阻塞性错误
- 已处理组件:
- `src/components/data/GiftHistoryDetailsDrawer/index.vue`
- 说明:已完成抽屉标题、发送人/房间、礼物信息快照、接收用户标签、日志事件与单据 ID 按钮国际化;礼物值、房间资料、日志内容与接口返回数据保持原样
- 已补充页面级语言 key
- `src/lang/messages/pages/en.js`
- `src/lang/messages/pages/zh.js`
- 新增范围:`pages.giftHistoryDetailsDrawer`
- 已完成构建验证:
- `src/components/data/GiftHistoryDetailsDrawer/index.vue` 国际化改动编译通过
- 当前仍保留项目原有 `22` 条 warning未因本轮 i18n 改动新增阻塞性错误
- 已处理组件:
- `src/components/data/EditUserForm/index.vue`
- 说明:已完成用户编辑抽屉标题、昵称/性别表单项、出生日期 placeholder、关闭/提交按钮与校验提示国际化;国家选择与接口返回数据保持原样
- 已补充页面级语言 key
- `src/lang/messages/pages/en.js`
- `src/lang/messages/pages/zh.js`
- 新增范围:`pages.editUserForm`
- 已完成构建验证:
- `src/components/data/EditUserForm/index.vue` 国际化改动编译通过
- 当前仍保留项目原有 `22` 条 warning未因本轮 i18n 改动新增阻塞性错误
- 已处理组件:
- `src/components/data/EditUserGoldCoinRewardForm/index.vue`
- 说明已完成金币奖励弹窗标题、表单标签、placeholder、按钮与校验提示国际化`currencyRewardReasons` 的提交值与备注拼接内容保持原样,仅在页面展示层做奖励类型本地映射翻译
- 已补充页面级语言 key
- `src/lang/messages/pages/en.js`
- `src/lang/messages/pages/zh.js`
- 新增范围:`pages.editUserGoldCoinRewardForm`
- 保留项:
- `src/components/data/EditUserGoldCoinRewardForm/index.vue`
- 内容:模板注释中的中文
- 原因:该部分不渲染,不属于可见展示文案
- 已完成构建验证:
- `src/components/data/EditUserGoldCoinRewardForm/index.vue` 国际化改动编译通过
- 当前仍保留项目原有 `22` 条 warning未因本轮 i18n 改动新增阻塞性错误
- 已完成页面:
- `src/views/game/lucky-box/award-config/index.vue`
- 说明:已完成筛选区、表格列头、类型标签、操作按钮、删除确认与成功提示的国际化;列表筛选值与接口逻辑保持原样
- 已处理页面私有子组件:
- `src/views/game/lucky-box/award-config/edit.vue`
- `src/views/game/lucky-box/award-config/details.vue`
- 说明已完成编辑弹窗与奖励配置弹窗中的标题、表单标签、placeholder、校验提示、按钮与本地常量类型映射国际化资源值、目标次数、接口参数与逻辑保持原样
- 已补充页面级语言 key
- `src/lang/messages/pages/en.js`
- `src/lang/messages/pages/zh.js`
- 新增范围:`pages.luckyBoxAwardConfig`
- 已完成构建验证:
- `src/views/game/lucky-box/award-config/index.vue` 国际化改动编译通过
- `src/views/game/lucky-box/award-config/edit.vue` 国际化改动编译通过
- `src/views/game/lucky-box/award-config/details.vue` 国际化改动编译通过
- 当前仍保留项目原有 `22` 条 warning未因本轮 i18n 改动新增阻塞性错误
- 已完成页面:
- `src/views/game/lucky-box/bounty-config/index.vue`
- 说明:已完成筛选区、表格列头、类型标签、操作按钮、删除确认与成功提示的国际化;`DAILY``WEEKLY` 等实际值与接口逻辑保持原样
- 已处理页面私有子组件:
- `src/views/game/lucky-box/bounty-config/edit.vue`
- `src/views/game/lucky-box/bounty-config/details.vue`
- 说明已完成编辑弹窗与道具配置弹窗中的标题、表单标签、placeholder、校验提示、按钮、礼物类型/道具类型本地映射国际化;资源值、提交字段、接口参数与逻辑保持原样
- 已补充页面级语言 key
- `src/lang/messages/pages/en.js`
- `src/lang/messages/pages/zh.js`
- 新增范围:`pages.luckyBoxBountyConfig`
- 已完成构建验证:
- `src/views/game/lucky-box/bounty-config/index.vue` 国际化改动编译通过
- `src/views/game/lucky-box/bounty-config/edit.vue` 国际化改动编译通过
- `src/views/game/lucky-box/bounty-config/details.vue` 国际化改动编译通过
- 当前仍保留项目原有 `22` 条 warning未因本轮 i18n 改动新增阻塞性错误
## 当前进行中
- 历史已完成 `src/views` 页面私有组件复查任务当前优先于新页面认领
- 下一步目标:
- 先补查已标记完成页面目录中的私有组件,并把组件状态补写入进度文档
- 完成该临时任务后,再继续按 `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`,后续与其他线程一样优先处理历史已完成页面私有组件复查
## 扫描概况
### views 目录
- `src/views` 中命中中文展示文本总数:`9165`
- `src/views` 中涉及文件数:`430`
#### 中文密度较高的页面
- `src/views/user/user-table/index.vue`158
- `src/views/props/props-source-group/form-edit.vue`140
- `src/views/cnf/lottery/index.vue`133
- `src/views/room/profile/index.vue`128
- `src/views/team/policy/index.vue`125
- `src/views/activity/template-cnf/index.vue`90
- `src/views/team/business-development/index.vue`89
- `src/views/sys/region-config/region/index.vue`:已完成
- `src/views/team/diamond-policy/index.vue`:已完成
- `src/views/game/lottery-config/form-edit.vue`:已完成
### components 目录
- `src/components` 中命中中文展示文本总数:`752`
- `src/components` 中涉及文件数:`57`
#### 中文密度较高的组件
- `src/components/data/UserInfo/BaseInfo/index.vue`79
- `src/components/data/InappPurchaseApple/index.vue`54
- `src/components/data/AccountHanle/index.vue`:已完成
- `src/components/data/InAppPurchaseDetails/index.vue`45
- `src/components/data/InappPurchaseGoogle/index.vue`40
- `src/components/data/RoomInfo/index.vue`39
- `src/components/data/UserInfo/BankCardDrawer/index.vue`33
- `src/components/data/UserAuthForm/index.vue`32
## 建议执行顺序
### 第一批
- `src/views/user/user-table/index.vue`
- `src/components/data/UserInfo/BaseInfo/index.vue`
- `src/views/room/profile/index.vue`
- `src/views/message/push/push-task.vue`
- `src/views/team/policy/index.vue`
### 第二批
- `src/views/props/props-source-group/form-edit.vue`
- `src/views/cnf/lottery/index.vue`
- `src/views/activity/template-cnf/index.vue`
- `src/views/team/business-development/index.vue`
- `src/views/sys/region-config/region/index.vue`(已完成)
- `src/views/team/diamond-policy/index.vue`
- `src/views/game/lottery-config/form-edit.vue`(已完成)
### 第三批
- `src/components/data/AccountHanle/index.vue`
- `src/components/data/InappPurchaseApple/index.vue`
- `src/components/data/InAppPurchaseDetails/index.vue`
- `src/components/data/InappPurchaseGoogle/index.vue`
- `src/components/data/RoomInfo/index.vue`
## 备注
- 终端里部分文件曾出现乱码展示,已确认源码本身为 UTF-8后续按 UTF-8 读取和修改
- 在 PowerShell 中建议显式使用:`Get-Content -Raw -Encoding UTF8 <path>`
- 部分旧语言文件中存在历史遗留键值写法混用问题,后续会逐步收口,但不影响当前继续翻译页面
- 路由 / 菜单翻译与页面展示文案翻译分开处理,避免相互影响
- 校验提示、按钮、标签、空态、对话框标题属于安全翻译区
- 类似 `PASS``NOT_PASS``OFFICIAL_MESSAGE_NOTICE``NORMAL``FREEZE` 这类后端依赖值必须保持原样
- 本项目构建验证命令为:`npm run build:prod`

View File

@ -0,0 +1,212 @@
# 问题排查记录:权限平台默认值与开发服务 Network 地址
## 记录时间
- 2026-04
## 背景
本次排查涉及两个独立但连续出现的问题:
1. 页面初始化时,`this.permissionsSysOriginPlatforms[0]` 偶发拿不到,导致默认平台没有赋值,后续 `renderData(true)` 不执行。
2. 本地执行 `npm run dev` 后,控制台一直显示 `Network: unavailable`,但同机器的其他项目可以正常显示类似 `http://192.168.0.115:5173/` 的局域网访问地址。
---
## 一、权限平台默认值偶发为空
### 现象
页面中存在如下初始化逻辑:
```js
const querySystem = this.permissionsSysOriginPlatforms[0];
if (!querySystem) {
return;
}
that.listQuery.sysOrigins = [querySystem.value];
that.renderData(true);
```
`this.permissionsSysOriginPlatforms[0]` 为空时,页面会直接返回,默认查询条件与首屏数据都不会初始化。
### 结论
`permissionsSysOriginPlatforms` 不是接口直接返回的平台数组,而是:
1. 按钮权限接口先返回权限别名列表。
2. 前端再用本地写死的 `sysOriginPlatforms` 常量进行过滤。
3. 过滤结果再写入 Vuex页面最终通过 getter 读取。
也就是说:
- 平台候选项来源于前端常量。
- 当前用户能拿到哪些平台,取决于按钮权限接口返回的权限别名。
### 关键代码链路
- 页面读取位置:`src/views/user/user-table/index.vue`
- getter 映射位置:`src/store/getters.js`
- Vuex 组装位置:`src/store/modules/user.js`
- 平台常量定义:`src/constant/origin.js`
- 按钮权限接口:`src/api/ops-system.js`
### 根因
根因是时序问题,不是页面本地代码本身写错:
1. 路由守卫中会调用 `store.dispatch('user/buttonPermissions')`
2. 但原逻辑没有 `await` 这个异步请求。
3. 页面已经进入并执行 `created()` 时,权限数据可能还没回填到 Vuex。
4. 于是 `this.permissionsSysOriginPlatforms[0]` 偶发为空。
另外还有一个补充前提:
- 当前前端常量里只有 `ATYOU` 一个系统平台,并且依赖权限 `query:atyou`
- 如果接口没有返回这个权限,过滤后数组本身也会为空。
### 已做处理
已在 `src/permission.js` 中将:
```js
store.dispatch('user/buttonPermissions')
```
修改为:
```js
await store.dispatch('user/buttonPermissions')
```
### 处理结果
调整后,路由放行前会先等待按钮权限加载完成,页面 `created()` 再读取 `permissionsSysOriginPlatforms` 时,不会再因为异步竞争导致首项为空。
### 后续建议
- 若后续仍出现空数组,需要优先检查接口返回是否包含 `query:atyou`
- 如果页面还需要进一步增强健壮性,可以在页面侧增加 watcher 或兜底默认逻辑,但当前首先应保证路由阶段权限已完成回填。
---
## 二、开发服务 `Network: unavailable`
### 现象
执行 `npm run dev` 后输出类似:
```txt
App running at:
- Local: http://localhost:9528
- Network: unavailable
```
但同一台机器上的其他项目可以正常显示局域网访问地址。
### 初步排查结论
这不是项目无法启动,也不是没有局域网 IP而是 Vue CLI 选错了网卡。
通过系统网络信息确认:
- 当前机器存在可用私网地址:`192.168.0.115`
- 同时还存在一个虚拟网卡地址:`26.26.26.1`
### 根因
项目使用的是较老版本的 `@vue/cli-service 3.6.0`
该版本在生成 `Network` 地址时,会在内部调用 `address.ip()` 取一个 IPv4 地址,然后只接受以下私网地址范围:
- `10.x.x.x`
- `172.16.x.x``172.31.x.x`
- `192.168.x.x`
如果拿到的不是私网地址,就会直接将 `Network` 显示为 `unavailable`
本次机器环境中Vue CLI 优先拿到的是虚拟网卡 `26.26.26.1`,由于它不属于私网范围,因此被直接丢弃,最终控制台显示 `Network: unavailable`
### 关键代码位置
- 输出逻辑:`node_modules/@vue/cli-service/lib/commands/serve.js`
- 局域网地址选择逻辑:`node_modules/@vue/cli-service/lib/util/prepareURLs.js`
### 已尝试处理
#### 第一步
先在 `vue.config.js` 中增加:
```js
host: '0.0.0.0'
```
目的:
- 让 dev server 监听所有网卡,而不是只监听本机回环地址。
结果:
- 监听范围已放开。
- 但 `Network` 仍然显示 `unavailable`
- 说明问题不在监听地址,而在 Vue CLI 的网卡识别逻辑。
#### 第二步
`vue.config.js` 中新增自定义 LAN IP 选择逻辑,替代 Vue CLI 默认的“随便取一个 IPv4”的方式。
处理思路:
1. 使用 `os.networkInterfaces()` 枚举网卡。
2. 只保留私网 IPv4。
3. 优先选择 `WLAN / Wi-Fi / Ethernet / 以太网` 这类常见真实网卡。
4. 排除 `TAP / VPN / Virtual / VMware / Hyper-V / Docker / vEthernet` 等虚拟网卡。
5. 将最终结果写入 `devServer.public`
### 当前配置结果
`vue.config.js` 已调整为:
- `host: '0.0.0.0'`
- `public: lanIp ? \`${lanIp}:${port}\` : undefined`
并增加了自动识别私网地址的辅助函数。
### 处理结果
此修改的目的不是单纯让服务“能访问”,而是:
1. 明确让服务监听所有网卡。
2. 明确告诉旧版 Vue CLI 应该向外公布哪个局域网地址。
3. 避免其误选虚拟网卡后把 Network 判成 unavailable。
### 备用方案
如果后续本机 IP 变化,或当前网络环境特殊,也可以临时手动指定:
```powershell
$env:DEV_SERVER_PUBLIC_IP='192.168.0.115'
npm run dev
```
或:
```powershell
npm run dev -- --public 192.168.0.115:9528
```
---
## 本次涉及的改动文件
- `src/permission.js`
- `vue.config.js`
- `docs/问题排查记录-权限平台与开发服务地址.md`
## 本次经验总结
1. 页面里出现“偶发空值”时,优先检查数据来源是不是异步回填,而不是只盯页面局部逻辑。
2. `permissionsSysOriginPlatforms` 这类字段要区分“候选项来源”和“最终权限结果来源”。
3. 老版本 Vue CLI 对网卡选择并不稳定,机器上存在 VPN、TAP、虚拟网卡时`Network: unavailable` 往往是地址识别问题,不代表 dev server 本身不可用。
4. 对局域网访问有明确需求的项目,最好显式配置 `devServer.host``devServer.public`,不要完全依赖工具链自动推断。

View File

@ -4,8 +4,8 @@
<el-breadcrumb-item v-for="(item,index) in levelList" :key="item.path">
<!-- <span v-if="item.redirect==='noRedirect'||index==levelList.length-1" class="no-redirect">{{ item.meta.title }}</span>
<a v-else @click.prevent="handleLink(item)">{{ item.meta.title }}</a> -->
<span v-if="item.redirect==='noRedirect'||index==levelList.length-1" class="no-redirect">{{ item.meta.title }}</span>
<a v-else>{{ item.meta.title }}</a>
<span v-if="item.redirect==='noRedirect'||index==levelList.length-1" class="no-redirect">{{ translateTitle(item.meta.title) }}</span>
<a v-else>{{ translateTitle(item.meta.title) }}</a>
</el-breadcrumb-item>
</transition-group>
</el-breadcrumb>
@ -13,6 +13,7 @@
<script>
import pathToRegexp from 'path-to-regexp'
import { translateRouteTitle } from '@/lang'
export default {
data() {
@ -35,7 +36,7 @@ export default {
const first = matched[0]
if (!this.isDashboard(first)) {
matched = [{ path: '/dashboard', meta: { title: 'Dashboard' }}].concat(matched)
matched = [{ path: '/dashboard', meta: { title: 'nav.home' }}].concat(matched)
}
this.levelList = matched.filter(item => item.meta && item.meta.title && item.meta.breadcrumb !== false)
@ -47,6 +48,9 @@ export default {
}
return name.trim().toLocaleLowerCase() === 'Dashboard'.toLocaleLowerCase()
},
translateTitle(title) {
return translateRouteTitle(title)
},
pathCompile(path) {
// To solve this problem https://github.com/PanJiaChen/vue-element-admin/issues/561
const { params } = this.$route

View File

@ -8,7 +8,7 @@
filterable
default-first-option
remote
placeholder="Search"
:placeholder="$t('common.search')"
class="header-search-select"
@change="change"
>
@ -22,6 +22,7 @@
// make search results more in line with expectations
import Fuse from 'fuse.js'
import path from 'path'
import { translateRouteTitle } from '@/lang'
export default {
name: 'HeaderSearch',
@ -35,6 +36,9 @@ export default {
}
},
computed: {
language() {
return this.$store.getters.language
},
routes() {
return this.$store.getters.routers
}
@ -46,6 +50,9 @@ export default {
searchPool(list) {
this.initFuse(this.filterTitleGtLevelExcludeDashboard(list, 1))
},
language() {
this.searchPool = this.generateRoutes(this.routes)
},
show(value) {
if (value) {
document.body.addEventListener('click', this.close)
@ -119,7 +126,7 @@ export default {
}
if (router.meta && router.meta.title) {
data.title = [...data.title, router.meta.title]
data.title = [...data.title, translateRouteTitle(router.meta.title)]
if (router.redirect !== 'noRedirect') {
// only push the routes with title

View File

@ -0,0 +1,96 @@
<template>
<el-dropdown class="language-switch" :trigger="trigger" @command="handleCommand">
<span class="language-trigger">
<i v-if="showIcon" class="el-icon-s-flag" />
<span class="label">{{ currentLabel }}</span>
<i class="el-icon-arrow-down el-icon--right" />
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item
v-for="item in languageOptions"
:key="item.value"
:command="item.value"
:disabled="item.value === locale"
>
<span>{{ item.nativeLabel || item.label }}</span>
<i v-if="item.value === locale" class="el-icon-check option-check" />
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</template>
<script>
import { getLocaleOptions } from '@/lang'
export default {
name: 'LanguageSwitch',
props: {
trigger: {
type: String,
default: 'hover'
},
showIcon: {
type: Boolean,
default: true
}
},
computedLegacy: {
locale() {
return this.$store.getters.language
},
languageOptions() {
return [
{ value: 'en', label: 'English' },
{ value: 'zh', label: '中文' }
]
},
currentLabel() {
return this.locale === 'zh' ? '中文' : 'English'
}
},
computed: {
locale() {
return this.$store.getters.language
},
languageOptions() {
return getLocaleOptions()
},
currentOption() {
return this.languageOptions.find(item => item.value === this.locale) || this.languageOptions[0] || {}
},
currentLabel() {
return this.currentOption.nativeLabel || this.currentOption.label || this.locale
}
},
methods: {
handleCommand(locale) {
this.$setLocale(locale)
}
}
}
</script>
<style lang="scss" scoped>
.language-switch {
display: inline-flex;
align-items: center;
height: 100%;
.language-trigger {
display: inline-flex;
align-items: center;
color: #7A5C3F;
cursor: pointer;
user-select: none;
}
.label {
margin-left: 6px;
}
.option-check {
margin-left: 8px;
color: #FF9326;
}
}
</style>

View File

@ -1,12 +1,11 @@
<template>
<template>
<el-dropdown trigger="click" @command="handleSetSize">
<div>
<svg-icon class-name="size-icon" icon-class="size" />
</div>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item v-for="item of sizeOptions" :key="item.value" :disabled="size===item.value" :command="item.value">
{{
item.label }}
{{ item.label }}
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
@ -14,17 +13,15 @@
<script>
export default {
data() {
return {
sizeOptions: [
{ label: '默认(Default)', value: 'default' },
{ label: '中等(Medium)', value: 'medium' },
{ label: '小的(Small)', value: 'small' },
{ label: '迷你(Mini)', value: 'mini' }
]
}
},
computed: {
sizeOptions() {
return [
{ label: this.$t('size.default'), value: 'default' },
{ label: this.$t('size.medium'), value: 'medium' },
{ label: this.$t('size.small'), value: 'small' },
{ label: this.$t('size.mini'), value: 'mini' }
]
},
size() {
return this.$store.getters.size
}
@ -35,7 +32,7 @@ export default {
this.$store.dispatch('app/setSize', size)
this.refreshView()
this.$message({
message: 'Switch Size Success',
message: this.$t('size.switchSuccess'),
type: 'success'
})
},
@ -43,6 +40,5 @@ export default {
this.$router.go(0)
}
}
}
</script>

View File

@ -1,6 +1,6 @@
<template>
<el-dialog
title="账号处理"
:title="$t('pages.accountHandle.dialog.title')"
:visible="true"
width="30%"
:before-close="handleClose"
@ -9,13 +9,13 @@
ref="form"
v-loading="loading"
:model="form"
:rules="formRules"
:rules="localizedFormRules"
label-width="80px"
>
<el-form-item v-loading="loadingAccountStatus" label="当前状态">
<el-form-item v-loading="loadingAccountStatus" :label="$t('pages.accountHandle.form.currentStatus')">
{{ thisAccountStatus.value }}
</el-form-item>
<el-form-item label="处理状态" prop="accountStatusEnum">
<el-form-item :label="$t('pages.accountHandle.form.handleStatus')" prop="accountStatusEnum">
<el-select
v-model="form.accountStatusEnum"
:disabled="accountStatusTypes.length <= 0"
@ -38,45 +38,33 @@
</el-form-item>
<el-form-item
v-if="form.accountStatusEnum === 'FREEZE'"
label="冻结天数"
:label="$t('pages.accountHandle.form.freezeDays')"
prop="days"
>
<el-select v-model="form.days">
<el-option label="1天" value="1" />
<el-option label="2天" value="2" />
<el-option label="3天" value="3" />
<el-option label="4天" value="4" />
<el-option label="5天" value="5" />
<el-option label="6天" value="6" />
<el-option label="7天" value="7" />
<el-option label="8天" value="8" />
<el-option label="9天" value="9" />
<el-option label="10天" value="10" />
<el-option label="15天" value="15" />
<el-option label="30天" value="30" />
<el-option
v-for="item in dayOptions"
:key="`freeze-${item.value}`"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item
v-if="form.accountStatusEnum === 'ARCHIVE'"
label="天数"
:label="$t('pages.accountHandle.form.days')"
prop="days"
>
<el-select v-model="form.days">
<el-option label="1天" value="1" />
<el-option label="2天" value="2" />
<el-option label="3天" value="3" />
<el-option label="4天" value="4" />
<el-option label="5天" value="5" />
<el-option label="6天" value="6" />
<el-option label="7天" value="7" />
<el-option label="8天" value="8" />
<el-option label="9天" value="9" />
<el-option label="10天" value="10" />
<el-option label="15天" value="15" />
<el-option label="30天" value="30" />
<el-option
v-for="item in dayOptions"
:key="`archive-${item.value}`"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item label="备注" prop="description">
<el-form-item :label="$t('pages.accountHandle.form.remarks')" prop="description">
<el-input
v-model.trim="form.description"
type="textarea"
@ -88,14 +76,14 @@
type="text"
:disabled="disabledTranslate"
@click="clickTranslate"
>点击翻译</el-button
>{{ $t('pages.accountHandle.action.translate') }}</el-button
>
</el-form-item>
<el-form-item v-if="form.descriptionTranslate" label="译文">
<el-form-item v-if="form.descriptionTranslate" :label="$t('pages.accountHandle.form.translation')">
<el-input v-model="form.descriptionTranslate" type="textarea" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submit()">提交</el-button>
<el-button type="primary" @click="submit()">{{ $t('pages.accountHandle.action.submit') }}</el-button>
</el-form-item>
</el-form>
</el-dialog>
@ -120,9 +108,6 @@ export default {
}
},
data() {
const commonRules = [
{ required: true, message: "必填字段不可为空", trigger: "blur" }
];
return {
loadingTranslate: false,
loadingAccountStatus: true,
@ -131,21 +116,21 @@ export default {
loading: false,
accountStatusGroup: [
{
name: "执行处罚",
nameKey: "executePunishment",
options: [
{ value: "WARNING", name: "警告" },
{ value: "FREEZE", name: "冻结" },
{ value: "ARCHIVE", name: "封禁" },
{ value: "ARCHIVE1DAY", name: "封禁1天" },
{ value: "ARCHIVE_DEVICE", name: "封禁+设备" }
{ value: "WARNING", nameKey: "warning" },
{ value: "FREEZE", nameKey: "freeze" },
{ value: "ARCHIVE", nameKey: "archive" },
{ value: "ARCHIVE1DAY", nameKey: "archive1Day" },
{ value: "ARCHIVE_DEVICE", nameKey: "archiveDevice" }
]
},
{
name: "解除处罚",
nameKey: "releasePunishment",
options: [
{ value: "UNTIE_ACOOUNT", name: "账号解封" },
{ value: "UNTIE_DEVICE", name: "设备解封" },
{ value: "UNTIE_DEVICE_AND_ACCOUNT", name: "设备+账号解封" }
{ value: "UNTIE_ACOOUNT", nameKey: "untieAccount" },
{ value: "UNTIE_DEVICE", nameKey: "untieDevice" },
{ value: "UNTIE_DEVICE_AND_ACCOUNT", nameKey: "untieDeviceAndAccount" }
]
}
],
@ -156,26 +141,42 @@ export default {
descriptionTranslate: "",
beApprovalUserId: ""
},
formRules: {
description: commonRules,
days: commonRules,
accountStatusEnum: commonRules
},
accountStatusTypes: []
};
},
computed: {
...mapGetters(["buttonPermissions"]),
localizedFormRules() {
const commonRules = [this.getRequiredRule()];
return {
description: commonRules,
days: commonRules,
accountStatusEnum: commonRules
};
},
dayOptions() {
return ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "15", "30"].map(
value => ({
value,
label: this.getDayLabel(value)
})
);
},
accountStatus() {
const that = this;
const result = [];
that.accountStatusTypes = [];
that.accountStatusGroup.forEach(row => {
const newRow = deepClone(row);
newRow.name = that.$t(`pages.accountHandle.group.${row.nameKey}`);
result.push(newRow);
newRow.options = [];
row.options.forEach(item => {
if (that.buttonPermissions.includes(`user:account:${item.value}`)) {
newRow.options.push(item);
newRow.options.push({
...item,
name: that.$t(`pages.accountHandle.status.${item.nameKey}`)
});
that.accountStatusTypes.push(item);
}
});
@ -194,6 +195,21 @@ export default {
}
},
methods: {
getRequiredRule() {
return {
required: true,
message: this.$t("pages.accountHandle.validation.requiredField"),
trigger: "blur"
};
},
getDayLabel(day) {
return this.$t(
Number(day) === 1
? "pages.accountHandle.word.daySingle"
: "pages.accountHandle.word.dayPlural",
{ days: day }
);
},
loadAccountStatus(userId) {
const that = this;
that.loadingAccountStatus = true;
@ -263,11 +279,11 @@ export default {
that.loadingTranslate = false;
that.form.descriptionTranslate = res.body;
if (res.status === 3001) {
that.$opsMessage.fail("相同语言");
that.$opsMessage.fail(this.$t("pages.accountHandle.message.sameLanguage"));
return;
}
if (res.status === 3003) {
that.$opsMessage.fail("没有找到合适的语言");
that.$opsMessage.fail(this.$t("pages.accountHandle.message.noSuitableLanguage"));
return;
}
that.disabledTranslate = true;

View File

@ -1,12 +1,12 @@
<template>
<div class="user-input">
<el-input v-model.trim="inputValue" clearable :disabled="!sysOriginVal" :placeholder="placeholder" class="input-with-select" @input="handleInput">
<el-select slot="prepend" v-model="selectType" placeholder="类型" :disabled="!sysOriginVal" @change="selectTypeChange">
<el-option label="短ID" value="SHORT" />
<el-option label="长ID" value="LONG" />
<el-select slot="prepend" v-model="selectType" :placeholder="$t('pages.accountInput.placeholder.type')" :disabled="!sysOriginVal" @change="selectTypeChange">
<el-option :label="$t('pages.accountInput.option.shortId')" value="SHORT" />
<el-option :label="$t('pages.accountInput.option.longId')" value="LONG" />
</el-select>
<el-select v-if="showSysOriginTypeSelect && selectType !== 'LONG' && permissionsSysOriginPlatforms.length > 0" slot="append" v-model="sysOriginType" placeholder="平台" @change="sysOriginTypeChange">
<el-select v-if="showSysOriginTypeSelect && selectType !== 'LONG' && permissionsSysOriginPlatforms.length > 0" slot="append" v-model="sysOriginType" :placeholder="$t('pages.accountInput.placeholder.platform')" @change="sysOriginTypeChange">
<el-option
v-for="item in permissionsSysOriginPlatforms"
:key="item.value"

View File

@ -1,7 +1,7 @@
<template>
<div class="edit-user-gold-coin-deduction-form">
<el-dialog
title="奖励用户钻石"
:title="$t('pages.editUserDiamondRewardForm.dialog.title')"
:visible="true"
width="450px"
:before-close="handleClose"
@ -15,31 +15,31 @@
label-width="70px"
style="width: 300px; margin-left:50px;"
>
<el-form-item label="钻石" prop="candy">
<el-form-item :label="$t('pages.propsSourceGroupForm.resourceType.diamond')" prop="candy">
<el-input
v-model.trim="formData.candy"
placeholder="需要奖励的钻石数量"
:placeholder="$t('pages.editUserDiamondRewardForm.placeholder.diamondQuantity')"
/>
</el-form-item>
<el-form-item label="备注" prop="remarks">
<el-form-item :label="$t('pages.editUserDiamondRewardForm.form.remarks')" prop="remarks">
<el-input
v-model.trim="formData.remarks"
placeholder="奖励钻石的原因"
:placeholder="$t('pages.editUserDiamondRewardForm.placeholder.reason')"
/>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="handleClose()">
取消
{{ $t('pages.editUserDiamondRewardForm.action.cancel') }}
</el-button>
<el-button
v-loading="listLoading"
type="primary"
@click="handleSubmit()"
>
提交
{{ $t('pages.editUserDiamondRewardForm.action.submit') }}
</el-button>
</div>
</el-dialog>
@ -64,12 +64,12 @@ export default {
candy: [
{
required: true,
message: '请输入金额',
message: this.$t('pages.propsSourceGroupForm.validation.requiredField'),
trigger: 'blur'
},
{
pattern: /^\d{1,7}(\.\d{0,2})?$/,
message: 'double范围0~9999999小数最多两位',
message: this.$t('pages.editUserDiamondRewardForm.validation.amountRange'),
trigger: 'blur'
}
]

View File

@ -1,7 +1,7 @@
<template>
<div class="edit-user-form">
<el-drawer
title="编辑用户"
:title="$t('pages.editUserForm.dialog.title')"
:visible="true"
:before-close="handleClose"
:close-on-press-escape="false"
@ -20,38 +20,38 @@
label-position="left"
label-width="70px"
>
<el-form-item label="昵称" prop="userNickname">
<el-form-item :label="$t('pages.editUserForm.form.nickname')" prop="userNickname">
<el-input
v-model.trim="formData.userNickname"
placeholder="请输入用户昵称"
:placeholder="$t('pages.editUserForm.placeholder.nickname')"
/>
</el-form-item>
<el-form-item label="性别" prop="userSex">
<el-form-item :label="$t('pages.editUserForm.form.gender')" prop="userSex">
<el-select
v-model="formData.userSex"
placeholder="请选择性别"
:placeholder="$t('pages.editUserForm.placeholder.gender')"
style="width: 100%;"
>
<el-option
v-for="item in genders"
:key="item.value"
:label="item.name"
:label="getGenderLabel(item.value)"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item :label="'年龄'" prop="email">
<el-form-item :label="$t('pages.userBaseInfo.labels.age')" prop="email">
<el-date-picker
v-model="formAgeDate"
style="width: 100%;"
type="date"
placeholder="选择出生日期"
:placeholder="$t('pages.editUserForm.placeholder.birthDate')"
value-format="yyyy-MM-dd"
/>
</el-form-item>
<el-form-item :label="'国家'" prop="email">
<el-form-item :label="$t('pages.userBaseInfo.labels.country')" prop="email">
<contry-select
v-if="formData.countryId"
v-model="formData.countryId"
@ -61,8 +61,8 @@
</el-form>
</div>
<div class="drawer-footer">
<el-button @click="handleClose()">关闭</el-button>
<el-button type="primary" @click="handleSubmit()">提交</el-button>
<el-button @click="handleClose()">{{ $t('pages.editUserForm.action.close') }}</el-button>
<el-button type="primary" @click="handleSubmit()">{{ $t('pages.editUserForm.action.submit') }}</el-button>
</div>
</div>
</el-drawer>
@ -91,11 +91,15 @@ export default {
userNickname: [
{
required: true,
message: '用户昵称必填',
message: this.$t('pages.editUserForm.validation.nicknameRequired'),
trigger: 'blur'
}
],
userSex: [{ required: true, message: '性别必填', trigger: 'blur' }]
userSex: [{
required: true,
message: this.$t('pages.editUserForm.validation.genderRequired'),
trigger: 'blur'
}]
},
formData: {
userNickname: '',
@ -135,6 +139,11 @@ export default {
}
},
methods: {
getGenderLabel(value) {
return value === 0
? this.$t('pages.userBaseInfo.words.female')
: this.$t('pages.userBaseInfo.words.male')
},
renderData() {
const that = this
that.listLoading = true

View File

@ -1,7 +1,7 @@
<template>
<div class="edit-user-gold-coin-deduction-form">
<el-dialog
title="扣除用户游戏券"
:title="$t('pages.editUserGameCouponDeductionForm.dialog.title')"
:visible="true"
width="450px"
:before-close="handleClose"
@ -15,31 +15,31 @@
label-width="70px"
style="width: 300px; margin-left:50px;"
>
<el-form-item label="游戏券" prop="coupon">
<el-form-item :label="$t('pages.editUserGameCouponDeductionForm.form.gameCoupon')" prop="coupon">
<el-input
v-model.trim="formData.coupon"
placeholder="需要扣除的游戏券数量"
:placeholder="$t('pages.editUserGameCouponDeductionForm.placeholder.gameCouponQuantity')"
/>
</el-form-item>
<el-form-item label="备注" prop="remarks">
<el-form-item :label="$t('pages.editUserGameCouponDeductionForm.form.remarks')" prop="remarks">
<el-input
v-model.trim="formData.remarks"
placeholder="扣除游戏券的原因"
:placeholder="$t('pages.editUserGameCouponDeductionForm.placeholder.remarks')"
/>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="handleClose()">
取消
{{ $t('pages.editUserGameCouponDeductionForm.action.cancel') }}
</el-button>
<el-button
v-loading="listLoading"
type="primary"
@click="handleSubmit()"
>
提交
{{ $t('pages.editUserGameCouponDeductionForm.action.submit') }}
</el-button>
</div>
</el-dialog>
@ -64,12 +64,12 @@ export default {
coupon: [
{
required: true,
message: '请输入金额',
message: this.$t('pages.editUserGameCouponDeductionForm.validation.enterAmount'),
trigger: 'blur'
},
{
pattern: /^\d{1,7}(\.\d{0,2})?$/,
message: 'double范围0~9999999小数最多两位',
message: this.$t('pages.editUserGameCouponDeductionForm.validation.amountRange'),
trigger: 'blur'
}
]

View File

@ -1,7 +1,7 @@
<template>
<div class="edit-user-gold-coin-reward-form">
<el-dialog
title="奖励用户金币"
:title="$t('pages.editUserGoldCoinRewardForm.dialog.title')"
:visible="true"
width="450px"
:before-close="handleClose"
@ -16,41 +16,47 @@
style="width: 300px; margin-left:50px;"
>
<!-- 类型选择 下拉框 -->
<el-form-item label="类型" prop="rewardType">
<el-form-item :label="$t('pages.editUserGoldCoinRewardForm.form.type')" prop="rewardType">
<el-select
v-model="formData.rewardType"
placeholder="请选择"
:placeholder="$t('pages.editUserGoldCoinRewardForm.placeholder.select')"
style="width: 100%;"
>
<el-option
v-for="item in currencyRewardReasons"
:key="item.value"
:label="item.name"
:label="getRewardTypeLabel(item.value)"
:value="item.value"
/>
</el-select>
</el-form-item>
<!-- 金币数量 输入框 -->
<el-form-item label="金币" prop="candy">
<el-form-item :label="$t('pages.userBaseInfo.labels.gold')" prop="candy">
<el-input
v-model.trim="formData.candy"
placeholder="需要奖励的金币数量"
:placeholder="$t('pages.editUserGoldCoinRewardForm.placeholder.goldQuantity')"
/>
</el-form-item>
<!-- 美金 输入框 -->
<el-form-item
v-if="formData.rewardType == 3 || formData.rewardType == 4"
label="美金"
:label="$t('pages.editUserGoldCoinRewardForm.form.usd')"
prop="usdCandy"
>
<el-input v-model.trim="formData.usdCandy" placeholder="美金数量" />
<el-input
v-model.trim="formData.usdCandy"
:placeholder="$t('pages.editUserGoldCoinRewardForm.placeholder.usdQuantity')"
/>
</el-form-item>
<!-- 备注 输入框 -->
<el-form-item label="备注" prop="remarks">
<el-input v-model.trim="formData.remarks" placeholder="奖励备注" />
<el-form-item :label="$t('pages.editUserGoldCoinRewardForm.form.remarks')" prop="remarks">
<el-input
v-model.trim="formData.remarks"
:placeholder="$t('pages.editUserGoldCoinRewardForm.placeholder.remarks')"
/>
</el-form-item>
</el-form>
@ -58,7 +64,7 @@
<div slot="footer" class="dialog-footer">
<!-- 取消按钮 -->
<el-button @click="handleClose()">
取消
{{ $t('pages.editUserGoldCoinRewardForm.action.cancel') }}
</el-button>
<!-- 提交按钮 -->
@ -67,7 +73,7 @@
type="primary"
@click="handleSubmit()"
>
提交
{{ $t('pages.editUserGoldCoinRewardForm.action.submit') }}
</el-button>
</div>
</el-dialog>
@ -94,26 +100,26 @@ export default {
candy: [
{
required: true,
message: "必填字段不可为空",
message: this.$t("pages.propsSourceGroupForm.validation.requiredField"),
trigger: "blur"
},
{
pattern: /^\d{1,7}(\.\d{0,2})?$/,
message: "double范围0~9999999小数最多两位",
message: this.$t("pages.editUserGoldCoinRewardForm.validation.goldRange"),
trigger: "blur"
}
],
rewardType: [
{
required: true,
message: "必填字段不可为空",
message: this.$t("pages.propsSourceGroupForm.validation.requiredField"),
trigger: "blur"
}
],
usdCandy: [
{
required: true,
message: "必填字段不可为空",
message: this.$t("pages.propsSourceGroupForm.validation.requiredField"),
trigger: "blur"
}
]
@ -127,6 +133,16 @@ export default {
};
},
methods: {
getRewardTypeLabel(value) {
const keyMap = {
1: "reward",
2: "internal",
3: "salary",
4: "recharge",
5: "other"
};
return this.$t(`pages.editUserGoldCoinRewardForm.rewardType.${keyMap[value]}`);
},
handleSubmit() {
const that = this;
that.$refs.dataForm.validate(valid => {

View File

@ -1,7 +1,7 @@
<template>
<div class="gift-history-drawer">
<el-drawer
title="详情"
:title="$t('pages.giftHistoryDetailsDrawer.dialog.title')"
:visible="true"
:before-close="handleClose"
:close-on-press-escape="false"
@ -12,12 +12,12 @@
>
<div class="gift-history-content">
<div class="blockquote">
发送人
{{ $t('pages.giftHistoryDetailsDrawer.section.sender') }}
<el-tag
v-if="row.anchor === true"
type="danger"
size="mini"
>主播</el-tag>
>{{ $t('pages.userBaseInfo.labels.anchor') }}</el-tag>
</div>
<div class="content">
<user-table-exhibit
@ -25,7 +25,7 @@
:query-details="true"
/>
</div>
<div v-if="row.roomProfile" class="blockquote">发送房间</div>
<div v-if="row.roomProfile" class="blockquote">{{ $t('pages.giftHistoryDetailsDrawer.section.senderRoom') }}</div>
<div v-if="row.roomProfile" class="content">
<div class="flex-l" style="padding-bottom: 10px;">
<div class="room-avatar">
@ -53,13 +53,13 @@
</div>
</div>
</div>
<div v-if="row.roomProfile" class="blockquote">信息快照</div>
<div v-if="row.roomProfile" class="blockquote">{{ $t('pages.giftHistoryDetailsDrawer.section.snapshot') }}</div>
<div class="content gift-text">
<el-row :gutter="10">
<el-col :span="12">礼物ID: {{ row.giftId }}</el-col>
<el-col :span="12">{{ $t('pages.giftHistoryDetailsDrawer.label.giftId') }}: {{ row.giftId }}</el-col>
<el-col
:span="12"
>礼物封面:
>{{ $t('pages.giftHistoryDetailsDrawer.label.giftCover') }}:
<el-image
style="width: 30px; height: 30px"
:src="row.giftCover"
@ -69,36 +69,36 @@
</el-col>
<el-col
:span="12"
>礼物货币: {{ row.giftValue.currencyType }}</el-col>
<el-col :span="12">礼物类型: {{ row.giftValue.giftType }}</el-col>
<el-col :span="12">礼物单价: {{ row.giftValue.unitPrice }}</el-col>
<el-col :span="12">礼物价值: {{ row.giftValue.giftValue }}</el-col>
>{{ $t('pages.giftHistoryDetailsDrawer.label.giftCurrency') }}: {{ row.giftValue.currencyType }}</el-col>
<el-col :span="12">{{ $t('pages.giftHistoryDetailsDrawer.label.giftType') }}: {{ row.giftValue.giftType }}</el-col>
<el-col :span="12">{{ $t('pages.giftHistoryDetailsDrawer.label.giftUnitPrice') }}: {{ row.giftValue.unitPrice }}</el-col>
<el-col :span="12">{{ $t('pages.giftHistoryDetailsDrawer.label.giftValue') }}: {{ row.giftValue.giftValue }}</el-col>
<el-col
:span="12"
>实际价值: {{ row.giftValue.actualAmount }}</el-col>
>{{ $t('pages.giftHistoryDetailsDrawer.label.actualAmount') }}: {{ row.giftValue.actualAmount }}</el-col>
<el-col
:span="12"
>发送数量: {{ row.giftValue.quantity }} </el-col>
>{{ $t('pages.giftHistoryDetailsDrawer.label.sendQuantity') }}: {{ row.giftValue.quantity }} {{ $t('pages.giftHistoryDetailsDrawer.word.countUnit') }}</el-col>
<el-col
:span="12"
>接收用户: {{ row.giftValue.userSize }} </el-col>
>{{ $t('pages.giftHistoryDetailsDrawer.label.acceptUsers') }}: {{ row.giftValue.userSize }} {{ $t('pages.giftHistoryDetailsDrawer.word.peopleUnit') }}</el-col>
<!-- 这两个字段已移除(percentage&selfPercentage), 2023/4月份可移除-->
<el-col
v-if="row.giftValue.percentage"
:span="12"
>接收概率: {{ row.giftValue.percentage * 100 }}%</el-col>
>{{ $t('pages.giftHistoryDetailsDrawer.label.acceptProbability') }}: {{ row.giftValue.percentage * 100 }}%</el-col>
<el-col
v-if="row.giftValue.selfPercentage"
:span="12"
>自己接收概率: {{ row.giftValue.selfPercentage * 100 }}%</el-col>
>{{ $t('pages.giftHistoryDetailsDrawer.label.selfAcceptProbability') }}: {{ row.giftValue.selfPercentage * 100 }}%</el-col>
<el-col
:span="12"
>背包: {{ row.giftValue.bag ? "Yes" : "No" }}</el-col>
<el-col :span="12">跟踪ID: {{ row.trackId }}</el-col>
<el-col :span="12">请求平台: {{ row.requestPlatform }}</el-col>
>{{ $t('pages.giftHistoryDetailsDrawer.label.bag') }}: {{ row.giftValue.bag ? $t('pages.userBaseInfo.words.yes') : $t('pages.userBaseInfo.words.no') }}</el-col>
<el-col :span="12">{{ $t('pages.giftHistoryDetailsDrawer.label.trackId') }}: {{ row.trackId }}</el-col>
<el-col :span="12">{{ $t('pages.giftHistoryDetailsDrawer.label.requestPlatform') }}: {{ row.requestPlatform }}</el-col>
</el-row>
</div>
<div class="blockquote">接收用户</div>
<div class="blockquote">{{ $t('pages.giftHistoryDetailsDrawer.section.receivers') }}</div>
<div class="content">
<div
v-for="(item, index) in row.acceptUsers"
@ -114,15 +114,15 @@
v-if="item.anchor === true"
type="danger"
size="mini"
>主播</el-tag>
>{{ $t('pages.userBaseInfo.labels.anchor') }}</el-tag>
<el-tag
v-if="item.anchor === true"
size="mini"
>接收目标:{{ item.targetAmount }}</el-tag>
<el-tag size="mini">接收金额:{{ item.acceptAmount }}</el-tag>
>{{ $t('pages.giftHistoryDetailsDrawer.label.acceptTarget') }}: {{ item.targetAmount }}</el-tag>
<el-tag size="mini">{{ $t('pages.giftHistoryDetailsDrawer.label.acceptAmount') }}: {{ item.acceptAmount }}</el-tag>
<el-tag
size="mini"
>接收概率:
>{{ $t('pages.giftHistoryDetailsDrawer.label.acceptProbability') }}:
{{
(item.acceptUserId !== row.userId
? item.percentage
@ -131,23 +131,23 @@
<el-tag
v-if="item.region"
size="mini"
>区域(-): {{ item.region
>{{ $t('pages.giftHistoryDetailsDrawer.label.regionFlow') }}: {{ item.region
}}<span
v-if="item.hasOwnProperty('regionEq')"
>{{ item.regionEq ? "Yes" : "No" }}</span></el-tag>
>{{ item.regionEq ? $t('pages.userBaseInfo.words.yes') : $t('pages.userBaseInfo.words.no') }}</span></el-tag>
<el-tag
v-if="item.hasOwnProperty('countTargetAmount')"
size="mini"
>统计状态: {{ item.countTargetAmount ? "Yes" : "No" }}</el-tag>
>{{ $t('pages.giftHistoryDetailsDrawer.label.countStatus') }}: {{ item.countTargetAmount ? $t('pages.userBaseInfo.words.yes') : $t('pages.userBaseInfo.words.no') }}</el-tag>
<el-button
type="text"
size="mini"
@click="clickCopyReceiptId(item)"
><i class="el-icon-document-copy" />单据ID</el-button>
><i class="el-icon-document-copy" />{{ $t('pages.giftHistoryDetailsDrawer.action.receiptId') }}</el-button>
</div>
</div>
</div>
<div class="blockquote">日志事件</div>
<div class="blockquote">{{ $t('pages.giftHistoryDetailsDrawer.section.logEvents') }}</div>
<div class="content">
<el-timeline>
<el-timeline-item

View File

@ -1,7 +1,7 @@
<template>
<div class="in-app-purchase-details-drawer">
<el-drawer
title="详情"
:title="$t('pages.inAppPurchaseDetails.dialog.title')"
:visible="true"
:before-close="handleClose"
:close-on-press-escape="false"
@ -12,65 +12,65 @@
>
<div v-if="order" v-loading="orderLoading" class="in-app-purchase-details-content">
<div v-if="orderIsNull" style="text-align: center;">
没有更多信息
{{ $t("pages.inAppPurchaseDetails.message.noMoreInfo") }}
</div>
<div v-else>
<div v-if="order.acceptUserProfile" class="order-row">
<div class="blockquote">接收用户</div>
<div class="blockquote">{{ $t("pages.inAppPurchaseDetails.section.acceptUser") }}</div>
<div class="content">
<user-table-exhibit :user-profile="order.acceptUserProfile" :query-details="true" />
</div>
</div>
<div v-if="order.createUserProfile" class="order-row">
<div class="blockquote">创建用户</div>
<div class="blockquote">{{ $t("pages.inAppPurchaseDetails.section.createUser") }}</div>
<div class="content">
<user-table-exhibit :user-profile="order.createUserProfile" :query-details="true" />
</div>
</div>
<div v-if="order.createNickname" class="order-row">
<div class="blockquote">创建用户</div>
<div class="blockquote">{{ $t("pages.inAppPurchaseDetails.section.createUser") }}</div>
<div class="content" style="margin-bottom: 10px;">
{{ order.createNickname }}
</div>
</div>
<div v-if="order.details" class="order-row">
<div class="blockquote">订单信息</div>
<div class="blockquote">{{ $t("pages.inAppPurchaseDetails.section.orderInfo") }}</div>
<div class="content">
<div class="order-info flex-b flex-wrap">
<div class="order-info-col">环境:
<div class="order-info-col">{{ $t('pages.inAppPurchaseDetails.label.environment') }}:
<el-tag v-if="order.details.env === 'PROD'" type="success" size="mini">{{ order.details.env }}</el-tag>
<el-tag v-else-if="order.details.env === 'TEST'" type="warning" size="mini">{{ order.details.env }}</el-tag>
<el-tag v-else type="danger" size="mini">{{ order.details.env }}</el-tag>
</div>
<div class="order-info-col">类型: {{ orderReceiptTypeMap[order.details.receiptType] }}</div>
<div class="order-info-col">状态:
<el-tag :type=" orderStatusMap[order.details.status].tagType" size="mini">{{ orderStatusMap[order.details.status].label }}</el-tag>
<div class="order-info-col">{{ $t('pages.inAppPurchaseDetails.label.type') }}: {{ orderReceiptTypeMap[order.details.receiptType] }}</div>
<div class="order-info-col">{{ $t('pages.inAppPurchaseDetails.label.status') }}:
<el-tag :type="orderStatusMap[order.details.status].tagType" size="mini">{{ orderStatusMap[order.details.status].label }}</el-tag>
</div>
<div class="order-info-col">ID: {{ order.details.id }}</div>
<div class="order-info-col">跟踪ID: {{ order.details.trackId }}</div>
<div v-if="order.details.subscribeId" class="order-info-col">订阅ID: {{ order.details.subscribeId }}</div>
<div class="order-info-col" :span="24">订单ID: {{ order.details.orderId }}</div>
<div class="order-info-col">平台渠道:
<div class="order-info-col">{{ $t('pages.inAppPurchaseDetails.label.id') }}: {{ order.details.id }}</div>
<div class="order-info-col">{{ $t('pages.inAppPurchaseDetails.label.trackId') }}: {{ order.details.trackId }}</div>
<div v-if="order.details.subscribeId" class="order-info-col">{{ $t('pages.inAppPurchaseDetails.label.subscribeId') }}: {{ order.details.subscribeId }}</div>
<div class="order-info-col" :span="24">{{ $t('pages.inAppPurchaseDetails.label.orderId') }}: {{ order.details.orderId }}</div>
<div class="order-info-col">{{ $t('pages.inAppPurchaseDetails.label.platformChannel') }}:
<sys-origin-icon size="20px" :icon="order.details.sysOrigin" :desc="order.details.sysOrigin" />
<platform-svg-icon size="20px" :icon="order.details.factory.platform" />
<platform-svg-icon size="20px" :icon="order.details.factory.factoryCode" />
</div>
<div class="order-info-col">国家编号: {{ order.details.countryCode }}</div>
<div class="order-info-col">货币类型: {{ order.details.currency }}</div>
<div class="order-info-col">金额: {{ order.details.amount }} {{ order.details.currency }}</div>
<div class="order-info-col">美元: {{ order.details.amountUsd }} USD</div>
<div class="order-info-col">免费试用: {{ order.details.trialPeriod === true ? 'Yes' :'No' }}</div>
<div class="order-info-col">{{ $t('pages.inAppPurchaseDetails.label.countryCode') }}: {{ order.details.countryCode }}</div>
<div class="order-info-col">{{ $t('pages.inAppPurchaseDetails.label.currency') }}: {{ order.details.currency }}</div>
<div class="order-info-col">{{ $t('pages.inAppPurchaseDetails.label.amount') }}: {{ order.details.amount }} {{ order.details.currency }}</div>
<div class="order-info-col">{{ $t('pages.inAppPurchaseDetails.label.usd') }}: {{ order.details.amountUsd }} USD</div>
<div class="order-info-col">{{ $t('pages.inAppPurchaseDetails.label.freeTrial') }}: {{ order.details.trialPeriod === true ? $t('pages.inAppPurchaseDetails.word.yes') : $t('pages.inAppPurchaseDetails.word.no') }}</div>
<div v-if="order.details.reason" class="order-info-col">原因: {{ order.details.reason }}</div>
<div class="order-info-col">购买时间: {{ order.details.purchaseDateMs }}</div>
<div class="order-info-col">创建时间: {{ order.details.createTime }}</div>
<div class="order-info-col">修改时间: {{ order.details.updateTime }}</div>
<div class="order-info-col">锁版本: {{ order.details.version }}</div>
<div v-if="order.details.reason" class="order-info-col">{{ $t('pages.inAppPurchaseDetails.label.reason') }}: {{ order.details.reason }}</div>
<div class="order-info-col">{{ $t('pages.inAppPurchaseDetails.label.purchaseTime') }}: {{ order.details.purchaseDateMs }}</div>
<div class="order-info-col">{{ $t('pages.inAppPurchaseDetails.label.createdAt') }}: {{ order.details.createTime }}</div>
<div class="order-info-col">{{ $t('pages.inAppPurchaseDetails.label.updatedAt') }}: {{ order.details.updateTime }}</div>
<div class="order-info-col">{{ $t('pages.inAppPurchaseDetails.label.version') }}: {{ order.details.version }}</div>
</div>
</div>
</div>
<div v-if="order.details && order.details.statusSteps && order.details.statusSteps.length>0" class="order-row">
<div class="blockquote">状态流转</div>
<div class="blockquote">{{ $t('pages.inAppPurchaseDetails.section.statusFlow') }}</div>
<div class="content">
<el-steps align-center :space="200" :active="order.details.statusSteps.length" finish-status="success" style="margin: 10px 0px;">
<el-step v-for="(item, index) in order.details.statusSteps" :key="index" :title="orderStatusMap[item.status].label" :description="item.createTime" />
@ -78,7 +78,7 @@
</div>
</div>
<div v-if="order.details && order.details.products && order.details.products.length > 0" class="order-row">
<div class="blockquote">商品信息</div>
<div class="blockquote">{{ $t('pages.inAppPurchaseDetails.section.productInfo') }}</div>
<div class="content">
<div class="product-card">
<div v-if="order.details.productDescriptor" class="prodcut-desc">
@ -91,13 +91,13 @@
</div>
<div class="product-info">
<el-row>
<el-col :span="12">ID: {{ item.id }}</el-col>
<el-col :span="12">名称: {{ item.name }}</el-col>
<el-col :span="12">编号: {{ item.code }}</el-col>
<el-col :span="12">内容: {{ item.content }}</el-col>
<el-col :span="12">金额: {{ item.amountUsd }} USD</el-col>
<el-col :span="12">数量: {{ item.quantity }}</el-col>
<el-col :span="24">描述: {{ item.describe }}</el-col>
<el-col :span="12">{{ $t('pages.inAppPurchaseDetails.label.id') }}: {{ item.id }}</el-col>
<el-col :span="12">{{ $t('pages.inAppPurchaseDetails.label.name') }}: {{ item.name }}</el-col>
<el-col :span="12">{{ $t('pages.inAppPurchaseDetails.label.code') }}: {{ item.code }}</el-col>
<el-col :span="12">{{ $t('pages.inAppPurchaseDetails.label.content') }}: {{ item.content }}</el-col>
<el-col :span="12">{{ $t('pages.inAppPurchaseDetails.label.amount') }}: {{ item.amountUsd }} USD</el-col>
<el-col :span="12">{{ $t('pages.inAppPurchaseDetails.label.quantity') }}: {{ item.quantity }}</el-col>
<el-col :span="24">{{ $t('pages.inAppPurchaseDetails.label.description') }}: {{ item.describe }}</el-col>
</el-row>
</div>
</div>
@ -106,21 +106,21 @@
</div>
</div>
<div v-if="order.details && order.details.metadata && Object.keys(order.details.metadata).length > 0" class="order-row">
<div class="blockquote">元数据</div>
<div class="blockquote">{{ $t('pages.inAppPurchaseDetails.section.metadata') }}</div>
<div class="content">
<el-table
:data="metadataList"
element-loading-text="Loading"
:element-loading-text="$t('pages.inAppPurchaseDetails.message.loading')"
fit
highlight-current-row
>
<el-table-column prop="key" label="字段" align="center" />
<el-table-column prop="value" label="值" align="center" />
<el-table-column prop="key" :label="$t('pages.inAppPurchaseDetails.label.field')" align="center" />
<el-table-column prop="value" :label="$t('pages.inAppPurchaseDetails.label.value')" align="center" />
</el-table>
</div>
</div>
<div v-if="order.details && order.details.payNotices" class="order-row">
<div class="blockquote">事件通知</div>
<div class="blockquote">{{ $t('pages.inAppPurchaseDetails.section.eventNotice') }}</div>
<div class="content">
<el-timeline :reverse="true">
<el-timeline-item
@ -130,8 +130,8 @@
:timestamp="item.createTime"
>
<div class="event-info">
<div class="event-info-item">事件ID: {{ item.eventId }}</div>
<div class="event-info-item">事件类型: {{ item.noticeType }}</div>
<div class="event-info-item">{{ $t('pages.inAppPurchaseDetails.label.eventId') }}: {{ item.eventId }}</div>
<div class="event-info-item">{{ $t('pages.inAppPurchaseDetails.label.eventType') }}: {{ item.noticeType }}</div>
</div>
<div class="request-data">
<json-editor v-model="item.noticeData" :read-only="true" />
@ -163,40 +163,48 @@ export default {
return {
orderLoading: false,
order: {},
orderIsNull: false,
orderReceiptTypeMap: {
'PAYMENT': '付款',
'RECEIPT': '收款'
},
orderStatusMap: {
'CREATE': {
label: '创建',
tagType: ''
},
'SUCCESS': {
label: '成功',
tagType: 'success'
},
'FAIL': {
label: '失败',
tagType: 'danger'
},
'CANCEL': {
label: '取消',
tagType: 'info'
},
'HANG': {
label: '挂起',
tagType: 'warning'
},
'REFUND': {
label: '退款',
tagType: 'danger'
}
}
orderIsNull: false
}
},
computed: {
orderReceiptTypeMap() {
const locale = this.$i18n.locale
void locale
return {
PAYMENT: this.$t('pages.inAppPurchaseDetails.receiptType.payment'),
RECEIPT: this.$t('pages.inAppPurchaseDetails.receiptType.receipt')
}
},
orderStatusMap() {
const locale = this.$i18n.locale
void locale
return {
CREATE: {
label: this.$t('pages.inAppPurchaseDetails.status.create'),
tagType: ''
},
SUCCESS: {
label: this.$t('pages.inAppPurchaseDetails.status.success'),
tagType: 'success'
},
FAIL: {
label: this.$t('pages.inAppPurchaseDetails.status.fail'),
tagType: 'danger'
},
CANCEL: {
label: this.$t('pages.inAppPurchaseDetails.status.cancel'),
tagType: 'info'
},
HANG: {
label: this.$t('pages.inAppPurchaseDetails.status.hang'),
tagType: 'warning'
},
REFUND: {
label: this.$t('pages.inAppPurchaseDetails.status.refund'),
tagType: 'danger'
}
}
},
metadataList() {
if (!this.order.details || !this.order.details.metadata) {
return []
@ -213,7 +221,7 @@ export default {
},
watch: {
orderId: {
handler(newVal) {
handler() {
this.loadInAppPurchase()
},
immediate: true
@ -230,7 +238,7 @@ export default {
that.orderLoading = false
that.order = res.body || {}
that.orderIsNull = !(that.order && that.order.details && that.order.details.id)
}).catch(er => {
}).catch(() => {
that.orderLoading = false
})
}

View File

@ -2,7 +2,7 @@
<div>
<el-dialog
v-loading="loading"
title="Apple 内购订阅"
:title="$t('pages.inappPurchaseApple.dialog.title')"
:visible="true"
width="60%"
:before-close="handleClose"
@ -11,70 +11,70 @@
<div class="sub-block">
<el-timeline :hide-timestamp="true">
<el-timeline-item :type="reimburseDot">
<h3>购买记录(站内)</h3>
<h3>{{ $t('pages.inappPurchaseApple.section.purchaseRecord') }}</h3>
<el-card>
<div class="typesetting">
<div class="line">
<div class="label">环境{{ purchaseHistory.evn }}</div>
<div class="label">原事务ID{{ purchaseHistory.originalOrderId }}</div>
<div class="label">过期时间{{ purchaseHistory.expiresDateMs }}</div>
<div class="label">{{ $t('pages.inappPurchaseApple.label.environment') }}{{ purchaseHistory.evn }}</div>
<div class="label">{{ $t('pages.inappPurchaseApple.label.originalTransactionId') }}{{ purchaseHistory.originalOrderId }}</div>
<div class="label">{{ $t('pages.inappPurchaseApple.label.expireTime') }}{{ purchaseHistory.expiresDateMs }}</div>
</div>
<div class="line">
<div class="label">来源平台{{ purchaseHistory.platform }}</div>
<div class="label">事务ID{{ purchaseHistory.orderId }}</div>
<div class="label">购买时间{{ purchaseHistory.purchaseDateMs }}</div>
<div class="label">{{ $t('pages.inappPurchaseApple.label.sourcePlatform') }}{{ purchaseHistory.platform }}</div>
<div class="label">{{ $t('pages.inappPurchaseApple.label.transactionId') }}{{ purchaseHistory.orderId }}</div>
<div class="label">{{ $t('pages.inappPurchaseApple.label.purchaseTime') }}{{ purchaseHistory.purchaseDateMs }}</div>
</div>
<div class="line">
<div class="label">购买事件{{ purchaseHistory.event }}</div>
<div class="label">单价{{ purchaseHistory.unitPrice }} ({{ purchaseHistory.unit }})</div>
<div class="label">创建时间{{ purchaseHistory.createTime }}</div>
<div class="label">{{ $t('pages.inappPurchaseApple.label.purchaseEvent') }}{{ purchaseHistory.event }}</div>
<div class="label">{{ $t('pages.inappPurchaseApple.label.unitPrice') }}{{ purchaseHistory.unitPrice }} ({{ purchaseHistory.unit }})</div>
<div class="label">{{ $t('pages.inappPurchaseApple.label.createdAt') }}{{ purchaseHistory.createTime }}</div>
</div>
<div class="line">
<div class="label">站内产品ID{{ purchaseHistory.purchaseProductId }}</div>
<div class="label">订单状态{{ purchaseHistory.status }}</div>
<div class="label">修改时间{{ purchaseHistory.updateTime }}</div>
<div class="label">{{ $t('pages.inappPurchaseApple.label.internalProductId') }}{{ purchaseHistory.purchaseProductId }}</div>
<div class="label">{{ $t('pages.inappPurchaseApple.label.orderStatus') }}{{ purchaseHistory.status }}</div>
<div class="label">{{ $t('pages.inappPurchaseApple.label.updatedAt') }}{{ purchaseHistory.updateTime }}</div>
</div>
<div class="line">
<div class="label">APPLE产品ID{{ purchaseHistory.productId }}</div>
<div class="label">产品类型{{ purchaseHistory.productType }}</div>
<div class="label">{{ $t('pages.inappPurchaseApple.label.appleProductId') }}{{ purchaseHistory.productId }}</div>
<div class="label">{{ $t('pages.inappPurchaseApple.label.productType') }}{{ purchaseHistory.productType }}</div>
<div class="label" />
</div>
</div>
</el-card>
</el-timeline-item>
<el-timeline-item>
<h3>等待续费订阅(Apple)</h3>
<h3>{{ $t('pages.inappPurchaseApple.section.pendingRenewal') }}</h3>
<el-card>
<div class="typesetting">
<div class="line">
<div class="label">原事务ID{{ pendingRenewalInfo.originalTransactionId }}</div>
<div class="label">产品ID{{ pendingRenewalInfo.productId }}</div>
<div class="label">自动续费产品ID{{ pendingRenewalInfo.autoRenewProductId }}</div>
<div class="label">{{ $t('pages.inappPurchaseApple.label.originalTransactionId') }}{{ pendingRenewalInfo.originalTransactionId }}</div>
<div class="label">{{ $t('pages.inappPurchaseApple.label.productId') }}{{ pendingRenewalInfo.productId }}</div>
<div class="label">{{ $t('pages.inappPurchaseApple.label.autoRenewProductId') }}{{ pendingRenewalInfo.autoRenewProductId }}</div>
</div>
<div class="line">
<div class="label">自动续费状态{{ autoRenewStatusName }}</div>
<div class="label">{{ $t('pages.inappPurchaseApple.label.autoRenewStatus') }}{{ autoRenewStatusName }}</div>
<div class="label">
努力续费中{{ inBillingRetryPeriodName }}
<el-tooltip class="item" effect="dark" content="可能由于计费周期问题Apple正在努力尝试自动订阅中">
{{ $t('pages.inappPurchaseApple.label.inBillingRetryPeriod') }}{{ inBillingRetryPeriodName }}
<el-tooltip class="item" effect="dark" :content="$t('pages.inappPurchaseApple.tooltip.retryPeriod')">
<i class="el-icon-question" />
</el-tooltip>
</div>
<div class="label">续订宽限到期时间{{ pendingRenewalInfo.gracePeriodExpiresDateMs }}</div>
<div class="label">{{ $t('pages.inappPurchaseApple.label.gracePeriodExpiresAt') }}{{ pendingRenewalInfo.gracePeriodExpiresDateMs }}</div>
</div>
<div class="line">
<div class="label">订阅过期原因{{ pendingRenewalInfo.expirationIntent }}
<div class="label">{{ $t('pages.inappPurchaseApple.label.expirationReason') }}{{ pendingRenewalInfo.expirationIntent }}
<el-tooltip class="item" effect="dark">
<div slot="content">
<p>1.客户自愿取消订阅</p>
<p>2.帐单错误例如客户的付款信息不再有效</p>
<p>3.客户不同意最近涨价</p>
<p>4.产品更新时无法购买</p>
<p>5.未知错误</p>
<p>{{ $t('pages.inappPurchaseApple.tooltip.expirationReason1') }}</p>
<p>{{ $t('pages.inappPurchaseApple.tooltip.expirationReason2') }}</p>
<p>{{ $t('pages.inappPurchaseApple.tooltip.expirationReason3') }}</p>
<p>{{ $t('pages.inappPurchaseApple.tooltip.expirationReason4') }}</p>
<p>{{ $t('pages.inappPurchaseApple.tooltip.expirationReason5') }}</p>
</div>
<i class="el-icon-question" />
</el-tooltip>
@ -84,58 +84,58 @@
</el-card>
</el-timeline-item>
<el-timeline-item>
<h3>单据信息(Apple)</h3>
<h3>{{ $t('pages.inappPurchaseApple.section.receiptInfo') }}</h3>
<el-card>
<div class="typesetting">
<div class="line">
<div class="label">取消时间{{ latestReceipt.cancellationDateMs }}
<el-tooltip class="item" effect="dark" content="此字段仅适用于已退款的交易。">
<div class="label">{{ $t('pages.inappPurchaseApple.label.cancellationTime') }}{{ latestReceipt.cancellationDateMs }}
<el-tooltip class="item" effect="dark" :content="$t('pages.inappPurchaseApple.tooltip.cancellationTime')">
<i class="el-icon-question" />
</el-tooltip>
</div>
<div class="label">产品ID{{ latestReceipt.productId }}</div>
<div class="label">到期时间{{ latestReceipt.expiresDateMs }}</div>
<div class="label">{{ $t('pages.inappPurchaseApple.label.productId') }}{{ latestReceipt.productId }}</div>
<div class="label">{{ $t('pages.inappPurchaseApple.label.expireTime') }}{{ latestReceipt.expiresDateMs }}</div>
</div>
<div class="line">
<div class="label">取消原因{{ latestReceipt.cancellationReason }}
<div class="label">{{ $t('pages.inappPurchaseApple.label.cancellationReason') }}{{ latestReceipt.cancellationReason }}
<el-tooltip class="item" effect="dark">
<div slot="content">
<p> 0) 表示交易因其他原因被取消例如如果客户是意外购买的</p>
<p> 1) 表示客户因应用程序中的实际或感知问题而取消了交易</p>
<p>{{ $t('pages.inappPurchaseApple.tooltip.cancellationReason0') }}</p>
<p>{{ $t('pages.inappPurchaseApple.tooltip.cancellationReason1') }}</p>
</div>
<i class="el-icon-question" />
</el-tooltip>
</div>
<div class="label">原事务ID{{ latestReceipt.transactionId }}</div>
<div class="label">原购买时间{{ dateFormat(latestReceipt.originalPurchaseDateMs) }}</div>
<div class="label">{{ $t('pages.inappPurchaseApple.label.originalTransactionId') }}{{ latestReceipt.transactionId }}</div>
<div class="label">{{ $t('pages.inappPurchaseApple.label.originalPurchaseTime') }}{{ dateFormat(latestReceipt.originalPurchaseDateMs) }}</div>
</div>
<div class="line">
<div class="label">所属订阅组{{ latestReceipt.subscriptionGroupIdentifier }}</div>
<div class="label">事务ID{{ latestReceipt.transactionId }}</div>
<div class="label">购买时间{{ latestReceipt.purchaseDateMs }}</div>
<div class="label">{{ $t('pages.inappPurchaseApple.label.subscriptionGroup') }}{{ latestReceipt.subscriptionGroupIdentifier }}</div>
<div class="label">{{ $t('pages.inappPurchaseApple.label.transactionId') }}{{ latestReceipt.transactionId }}</div>
<div class="label">{{ $t('pages.inappPurchaseApple.label.purchaseTime') }}{{ latestReceipt.purchaseDateMs }}</div>
</div>
<div class="line">
<div class="label">网格列ID{{ latestReceipt.webOrderLineItemId }}</div>
<div class="label">创建时间{{ latestReceipt.createTime }}</div>
<div class="label">修改时间{{ latestReceipt.updateTime }}</div>
<div class="label">{{ $t('pages.inappPurchaseApple.label.webOrderLineItemId') }}{{ latestReceipt.webOrderLineItemId }}</div>
<div class="label">{{ $t('pages.inappPurchaseApple.label.createdAt') }}{{ latestReceipt.createTime }}</div>
<div class="label">{{ $t('pages.inappPurchaseApple.label.updatedAt') }}{{ latestReceipt.updateTime }}</div>
</div>
<div class="line">
<div class="label">免费试用{{ latestReceipt.trialPeriod === true ? '是' : '否' }}</div>
<div class="label"><el-button type="text" @click="openUrl('status')">收据状态</el-button>{{ latestReceipt.status }}
<div class="label">{{ $t('pages.inappPurchaseApple.label.trialPeriod') }}{{ latestReceipt.trialPeriod === true ? $t('pages.inappPurchaseApple.word.yes') : $t('pages.inappPurchaseApple.word.no') }}</div>
<div class="label"><el-button type="text" @click="openUrl('status')">{{ $t('pages.inappPurchaseApple.action.receiptStatus') }}</el-button>{{ latestReceipt.status }}
<el-tooltip class="item" effect="dark">
<div slot="content">
<p> 如果收据有效则为0如果有错误则为状态码 </p>
<p> 状态代码反映了整个应用收据的状态</p>
<p> 点击标题查看详细信息</p>
<p>{{ $t('pages.inappPurchaseApple.tooltip.receiptStatus1') }}</p>
<p>{{ $t('pages.inappPurchaseApple.tooltip.receiptStatus2') }}</p>
<p>{{ $t('pages.inappPurchaseApple.tooltip.receiptStatus3') }}</p>
</div>
<i class="el-icon-question" />
</el-tooltip>
</div>
<div class="label">Base64单据<el-button type="text" @click="copyReceipt($event)">点击复制</el-button></div>
<div class="label">{{ $t('pages.inappPurchaseApple.label.base64Receipt') }}<el-button type="text" @click="copyReceipt($event)">{{ $t('pages.inappPurchaseApple.action.copy') }}</el-button></div>
</div>
</div>
</el-card>
@ -164,7 +164,9 @@ export default {
},
computed: {
inBillingRetryPeriodName() {
return this.apple.pendingRenewalInfo && this.apple.pendingRenewalInfo.inBillingRetryPeriod === true ? '是' : '否'
return this.apple.pendingRenewalInfo && this.apple.pendingRenewalInfo.inBillingRetryPeriod === true
? this.$t('pages.inappPurchaseApple.word.yes')
: this.$t('pages.inappPurchaseApple.word.no')
},
purchaseHistory() {
return this.apple.purchaseHistory || {}
@ -179,7 +181,9 @@ export default {
return this.latestReceipt && this.apple.cancellationDateMs ? 'danger' : ''
},
autoRenewStatusName() {
return this.pendingRenewalInfo.autoRenewStatus === true ? '订阅中' : '已取消'
return this.pendingRenewalInfo.autoRenewStatus === true
? this.$t('pages.inappPurchaseApple.word.subscribing')
: this.$t('pages.inappPurchaseApple.word.cancelled')
}
},
created() {

View File

@ -2,7 +2,7 @@
<div>
<el-dialog
v-loading="loading"
title="Google 内购订阅"
:title="$t('pages.inappPurchaseGoogle.dialog.title')"
:visible="true"
width="60%"
:before-close="handleClose"
@ -11,75 +11,75 @@
<div class="sub-block">
<el-timeline :hide-timestamp="true">
<el-timeline-item :type="reimburseDot">
<h3>购买记录(站内)</h3>
<h3>{{ $t('pages.inappPurchaseGoogle.section.purchaseRecord') }}</h3>
<el-card>
<div class="typesetting">
<div class="line">
<div class="label">环境{{ purchaseHistory.evn }}</div>
<div class="label">原事务ID{{ purchaseHistory.originalOrderId }}</div>
<div class="label">过期时间{{ purchaseHistory.expiresDateMs }}</div>
<div class="label">{{ $t('pages.inappPurchaseGoogle.label.environment') }}{{ purchaseHistory.evn }}</div>
<div class="label">{{ $t('pages.inappPurchaseGoogle.label.originalTransactionId') }}{{ purchaseHistory.originalOrderId }}</div>
<div class="label">{{ $t('pages.inappPurchaseGoogle.label.expireTime') }}{{ purchaseHistory.expiresDateMs }}</div>
</div>
<div class="line">
<div class="label">来源平台{{ purchaseHistory.platform }}</div>
<div class="label">事务ID{{ purchaseHistory.orderId }}</div>
<div class="label">购买时间{{ purchaseHistory.purchaseDateMs }}</div>
<div class="label">{{ $t('pages.inappPurchaseGoogle.label.sourcePlatform') }}{{ purchaseHistory.platform }}</div>
<div class="label">{{ $t('pages.inappPurchaseGoogle.label.transactionId') }}{{ purchaseHistory.orderId }}</div>
<div class="label">{{ $t('pages.inappPurchaseGoogle.label.purchaseTime') }}{{ purchaseHistory.purchaseDateMs }}</div>
</div>
<div class="line">
<div class="label">购买事件{{ purchaseHistory.event }}</div>
<div class="label">单价{{ purchaseHistory.unitPrice }} ({{ purchaseHistory.unit }})</div>
<div class="label">创建时间{{ purchaseHistory.createTime }}</div>
<div class="label">{{ $t('pages.inappPurchaseGoogle.label.purchaseEvent') }}{{ purchaseHistory.event }}</div>
<div class="label">{{ $t('pages.inappPurchaseGoogle.label.unitPrice') }}{{ purchaseHistory.unitPrice }} ({{ purchaseHistory.unit }})</div>
<div class="label">{{ $t('pages.inappPurchaseGoogle.label.createdAt') }}{{ purchaseHistory.createTime }}</div>
</div>
<div class="line">
<div class="label">站内产品ID{{ purchaseHistory.purchaseProductId }}</div>
<div class="label">订单状态{{ purchaseHistory.status }}</div>
<div class="label">修改时间{{ purchaseHistory.updateTime }}</div>
<div class="label">{{ $t('pages.inappPurchaseGoogle.label.internalProductId') }}{{ purchaseHistory.purchaseProductId }}</div>
<div class="label">{{ $t('pages.inappPurchaseGoogle.label.orderStatus') }}{{ purchaseHistory.status }}</div>
<div class="label">{{ $t('pages.inappPurchaseGoogle.label.updatedAt') }}{{ purchaseHistory.updateTime }}</div>
</div>
<div class="line">
<div class="label">APPLE产品ID{{ purchaseHistory.productId }}</div>
<div class="label">产品类型{{ purchaseHistory.productType }}</div>
<div class="label">{{ $t('pages.inappPurchaseGoogle.label.productId') }}{{ purchaseHistory.productId }}</div>
<div class="label">{{ $t('pages.inappPurchaseGoogle.label.productType') }}{{ purchaseHistory.productType }}</div>
<div class="label" />
</div>
</div>
</el-card>
</el-timeline-item>
<el-timeline-item v-if="google.latestSubscription">
<h3>订阅单据(Google)</h3>
<h3>{{ $t('pages.inappPurchaseGoogle.section.subscriptionReceipt') }}</h3>
<el-card>
<div class="typesetting">
<div class="line">
<div class="label">授予订阅的时间{{ dateFormat(latestSubscription.startTimeMillis) }}</div>
<div class="label">自动续费状态{{ autoRenewStatusName }}</div>
<div class="label">订单ID{{ latestSubscription.orderId }}</div>
<div class="label">{{ $t('pages.inappPurchaseGoogle.label.subscriptionGrantedAt') }}{{ dateFormat(latestSubscription.startTimeMillis) }}</div>
<div class="label">{{ $t('pages.inappPurchaseGoogle.label.autoRenewStatus') }}{{ autoRenewStatusName }}</div>
<div class="label">{{ $t('pages.inappPurchaseGoogle.label.orderId') }}{{ latestSubscription.orderId }}</div>
</div>
<div class="line">
<div class="label">订阅到期的时间{{ dateFormat(latestSubscription.expiryTimeMillis) }} </div>
<div class="label">订阅的购买类型{{ latestSubscription.purchaseType }}</div>
<div class="label">付款状{{ latestSubscriptionPaymentStateName }}</div>
<div class="label">{{ $t('pages.inappPurchaseGoogle.label.subscriptionExpireAt') }}{{ dateFormat(latestSubscription.expiryTimeMillis) }} </div>
<div class="label">{{ $t('pages.inappPurchaseGoogle.label.subscriptionPurchaseType') }}{{ latestSubscription.purchaseType }}</div>
<div class="label">{{ $t('pages.inappPurchaseGoogle.label.paymentState') }}{{ latestSubscriptionPaymentStateName }}</div>
</div>
<div class="line">
<div class="label">创建时间{{ latestSubscription.createTime }}</div>
<div class="label">付款国家CODE{{ latestSubscription.countryCode }}</div>
<div class="label">令牌<el-button type="text" @click="copyReceipt(latestSubscription.linkedPurchaseToken)">点击复制</el-button></div>
<div class="label">{{ $t('pages.inappPurchaseGoogle.label.createdAt') }}{{ latestSubscription.createTime }}</div>
<div class="label">{{ $t('pages.inappPurchaseGoogle.label.paymentCountryCode') }}{{ latestSubscription.countryCode }}</div>
<div class="label">{{ $t('pages.inappPurchaseGoogle.label.token') }}<el-button type="text" @click="copyReceipt(latestSubscription.linkedPurchaseToken)">{{ $t('pages.inappPurchaseGoogle.action.copy') }}</el-button></div>
</div>
<div class="line">
<div class="label">修改时间{{ latestSubscription.updateTime }}</div>
<div class="label">订阅被取消{{ latestSubscription.cancelReason }}
<div class="label">{{ $t('pages.inappPurchaseGoogle.label.updatedAt') }}{{ latestSubscription.updateTime }}</div>
<div class="label">{{ $t('pages.inappPurchaseGoogle.label.subscriptionCancelled') }}{{ latestSubscription.cancelReason }}
<el-tooltip class="item" effect="dark">
<div slot="content">
<p>0.用户取消了订阅</p>
<p>1.例如由于计费问题系统取消了订阅</p>
<p>2.用新订阅替换了订阅</p>
<p>3.开发者取消了订阅</p>
<p>{{ $t('pages.inappPurchaseGoogle.tooltip.cancelReason0') }}</p>
<p>{{ $t('pages.inappPurchaseGoogle.tooltip.cancelReason1') }}</p>
<p>{{ $t('pages.inappPurchaseGoogle.tooltip.cancelReason2') }}</p>
<p>{{ $t('pages.inappPurchaseGoogle.tooltip.cancelReason3') }}</p>
</div>
<i class="el-icon-question" />
</el-tooltip>
</div>
<div class="label">json结构<el-button type="text" @click="copyReceipt(latestSubscription.lastJson)">点击复制</el-button></div>
<div class="label">{{ $t('pages.inappPurchaseGoogle.label.jsonStructure') }}<el-button type="text" @click="copyReceipt(latestSubscription.lastJson)">{{ $t('pages.inappPurchaseGoogle.action.copy') }}</el-button></div>
</div>
</div>
</el-card>
@ -120,22 +120,24 @@ export default {
return this.latestReceipt && this.google.cancellationDateMs ? 'danger' : ''
},
autoRenewStatusName() {
return this.latestSubscription.autoRenewing === true ? '订阅中' : '已取消'
return this.latestSubscription.autoRenewing === true
? this.$t('pages.inappPurchaseGoogle.word.subscribing')
: this.$t('pages.inappPurchaseGoogle.word.cancelled')
},
latestSubscriptionPaymentStateName() {
if (this.latestSubscription.paymentState === 0) {
return '待付款'
return this.$t('pages.inappPurchaseGoogle.paymentState.pending')
}
if (this.latestSubscription.paymentState === 1) {
return '收到付款'
return this.$t('pages.inappPurchaseGoogle.paymentState.received')
}
if (this.latestSubscription.paymentState === 2) {
return '免费试用'
return this.$t('pages.inappPurchaseGoogle.paymentState.freeTrial')
}
if (this.latestSubscription.paymentState === 3) {
return '待推迟的升级/降级'
return this.$t('pages.inappPurchaseGoogle.paymentState.deferredUpgradeDowngrade')
}
return '异常'
return this.$t('pages.inappPurchaseGoogle.paymentState.abnormal')
}
},
created() {
@ -154,7 +156,7 @@ export default {
},
copyReceipt(content) {
if (!content) {
this.$opsMessage.info('内容为空')
this.$opsMessage.info(this.$t('pages.inappPurchaseGoogle.message.emptyContent'))
return
}
copyText(content).then(() => {
@ -179,11 +181,14 @@ export default {
line-height: 30px;
.line {
display: flex;
flex-wrap: wrap;
.label {
width: 100%;
overflow: hidden;
text-overflow:ellipsis;
white-space: nowrap;
flex: 1 1 260px;
min-width: 0;
padding-right: 12px;
white-space: normal;
word-break: break-word;
line-height: 1.5;
}
}
}

View File

@ -1,11 +1,13 @@
<template>
<div class="register-logout-count-charts">
<div id="charts" ref="charts" :style="'width: 100%;height:'+ height +';'" />
<div id="charts" ref="charts" :style="'width: 100%;height:' + height + ';'" />
</div>
</template>
<script>
import { mapGetters } from 'vuex'
import { copyText } from '@/utils'
export default {
props: {
height: {
@ -22,50 +24,43 @@ export default {
charts: null
}
},
computed: {
...mapGetters(['language'])
},
watch: {
chartsData: {
immediate: true,
handler(newVal) {
handler() {
if (this.charts) {
this.charts.clear()
this.renderCharts()
}
}
},
language() {
if (this.charts) {
this.charts.clear()
this.renderCharts()
}
}
},
created() {
},
mounted() {
const that = this
that.$nextTick(() => {
that.charts = that.$echarts.init(that.$refs.charts)
that.renderCharts()
this.$nextTick(() => {
this.charts = this.$echarts.init(this.$refs.charts)
this.renderCharts()
window.addEventListener('resize', () => {
that.charts.resize()
this.charts.resize()
})
that.charts.on('click', param => {
this.charts.on('click', param => {
copyText(param.name).then(() => {
this.$opsMessage.success()
}).catch(er => {
}).catch(() => {
this.$opsMessage.fail()
})
})
})
},
methods: {
newSeries(name, data) {
return {
name,
type: 'bar',
stack: name,
smooth: 0.6,
symbol: 'none',
symbolSize: 10,
data,
areaStyle: {},
barWidth: 40
}
},
newRich(imageUrl) {
return {
height: 40,
@ -76,7 +71,6 @@ export default {
}
},
proccessData() {
const that = this
const charts = {
xAxisData: [],
seriesData: [],
@ -87,20 +81,22 @@ export default {
}
}
}
if (that.chartsData.length > 0) {
that.chartsData.forEach(item => {
if (this.chartsData.length > 0) {
this.chartsData.forEach(item => {
const propsSource = item.propsSource
charts.xAxisData.push(propsSource.id)
charts.seriesData.push([propsSource.id, item.saleQuantity || 0])
charts.richData[propsSource.id] = that.newRich(propsSource.cover)
charts.richData[propsSource.id] = this.newRich(propsSource.cover)
})
}
return charts
},
renderCharts() {
const that = this
const proccessChartsData = that.proccessData()
that.charts.setOption({
if (!this.charts) {
return
}
const proccessChartsData = this.proccessData()
this.charts.setOption({
color: ['#66b3ff', '#ce90e8', '#ff9c6e', '#5cdbd3'],
tooltip: {
trigger: 'axis',
@ -113,8 +109,8 @@ export default {
shadowStyle: { color: 'rgba(200,200,200,0.2)' }
}
},
formatter: function(val) {
return `数量: ${val[0].data[1]}`
formatter: val => {
return this.$t('pages.dashboard.propsSalesOverview.quantity', { value: val[0].data[1] })
}
},
legend: {
@ -130,26 +126,24 @@ export default {
borderColor: '#eee'
},
toolbox: { color: ['#1e90ff', '#1e90ff', '#1e90ff', '#1e90ff'], effectiveColor: '#ff4500' },
xAxis: [
{
xAxis: [{
show: true,
type: 'category',
data: proccessChartsData.xAxisData || [],
splitArea: {
show: true,
type: 'category',
data: proccessChartsData.xAxisData || [],
splitArea: {
show: true,
areaStyle: { color: ['rgba(250,250,250,0.1)', 'rgba(200,200,200,0.1)'] }
areaStyle: { color: ['rgba(250,250,250,0.1)', 'rgba(200,200,200,0.1)'] }
},
axisLine: { lineStyle: { color: '#b7bdc7' }},
splitLine: { lineStyle: { color: ['#eee'] }},
axisLabel: {
interval: 0,
formatter(value) {
return '{' + value + '| }'
},
axisLine: { lineStyle: { color: '#b7bdc7' }},
splitLine: { lineStyle: { color: ['#eee'] }},
axisLabel: {
interval: 0,
formatter: function(value) {
return '{' + value + '| }'
},
rich: proccessChartsData.richData || {}
}
rich: proccessChartsData.richData || {}
}
],
}],
yAxis: [{
type: 'value',
axisTick: { show: true, length: 0 },
@ -166,7 +160,6 @@ export default {
areaStyle: {},
barWidth: 40
}
}, true)
}
}

View File

@ -1,7 +1,7 @@
<template>
<div class="props-config-edit">
<el-dialog
title="总揽销售报表"
:title="$t('pages.dashboard.propsSalesOverview.dialogTitle')"
:visible="true"
:before-close="handleClose"
:close-on-click-modal="false"
@ -13,13 +13,12 @@
</el-dialog>
</div>
</template>
<script>
import PropsSalesOverviewCharts from '@/components/data/PropsSalesOverviewCharts'
export default {
components: { PropsSalesOverviewCharts },
data() {
return {}
},
methods: {
handleClose() {
this.$emit('close')
@ -27,6 +26,6 @@ export default {
}
}
</script>
<style scoped lang="scss">
<style scoped lang="scss">
</style>

View File

@ -1,10 +1,10 @@
<template>
<div class="sales-overview-charts">
<div class="sales-overview-charts" :data-language="language">
<div class="charts-body">
<div class="filter-container">
<el-select
v-model="listQuery.sysOrigin"
placeholder="系统"
:placeholder="$t('pages.dashboard.propsSalesOverview.systemPlaceholder')"
style="width: 120px"
class="filter-item"
@change="renderData"
@ -19,9 +19,10 @@
<span style="float: left;margin-left:10px">{{ item.label }}</span>
</el-option>
</el-select>
<el-select
v-model="listQuery.dateType"
placeholder="时间类型"
:placeholder="$t('pages.dashboard.propsSalesOverview.dateTypePlaceholder')"
style="width: 120px"
class="filter-item"
@change="changeDateType"
@ -33,14 +34,15 @@
:value="item.value"
/>
</el-select>
<el-select
v-model="listQuery.propsType"
placeholder="道具类型"
:placeholder="$t('pages.dashboard.propsSalesOverview.propsTypePlaceholder')"
style="width: 120px"
class="filter-item"
@change="renderData"
>
<el-option v-for="item in propsTypes" :key="item.value" :label="item.name" :value="item.value" />
<el-option v-for="item in localizedPropsTypes" :key="item.value" :label="item.name" :value="item.value" />
</el-select>
<div class="filter-item">
@ -49,29 +51,45 @@
:clearable="false"
:value-format="selectedDateType.dateValueFormat"
:type="selectedDateType.dateType"
placeholder="选择日期"
:placeholder="$t('pages.dashboard.propsSalesOverview.datePlaceholder')"
:editable="false"
@change="renderData"
/>
</div>
<div class="filter-item">
总额: {{ sale.total || '-' }}
{{ $t('pages.dashboard.propsSalesOverview.totalLabel') }}: {{ sale.total || '-' }}
</div>
</div>
<div v-loading="loading">
<bar-graph-charts :charts-data="sale.propsSales" :height="height" />
</div>
<div v-loading="loading">
<bar-graph-charts :key="'props-sales-' + language" :charts-data="sale.propsSales" :height="height" />
</div>
</div>
</div>
</template>
<script>
import { mapGetters } from 'vuex'
import { getPropsSale } from '@/api/statistics'
import { formatDate } from '@/utils'
import { propsTypes } from '@/constant/type'
import { mapGetters } from 'vuex'
import BarGraphCharts from './bar-graph-charts'
const PROP_TYPE_KEY_MAP = {
AVATAR_FRAME: 'avatarFrame',
RIDE: 'ride',
NOBLE_VIP: 'nobleVip',
THEME: 'theme',
LAYOUT: 'layout',
CHAT_BUBBLE: 'chatBubble',
FLOAT_PICTURE: 'floatPicture',
FRAGMENTS: 'fragments',
DATA_CARD: 'dataCard',
SPECIAL_ID: 'specialId',
CUSTOMIZE: 'customizeReadonly'
}
export default {
components: { BarGraphCharts },
props: {
@ -83,19 +101,19 @@ export default {
},
data() {
return {
propsTypes,
rawPropsTypes: propsTypes,
dateTypeMap: {
'DAY': {
DAY: {
dateType: 'date',
dateValueFormat: 'yyyyMMdd',
defaultDate: formatDate(new Date(), 'yyyyMMdd')
},
'MONTH': {
MONTH: {
dateType: 'month',
dateValueFormat: 'yyyyMM',
defaultDate: formatDate(new Date(), 'yyyyMM')
},
'YEAR': {
YEAR: {
dateType: 'year',
dateValueFormat: 'yyyy',
defaultDate: formatDate(new Date(), 'yyyy')
@ -108,17 +126,35 @@ export default {
propsType: 'AVATAR_FRAME',
date: ''
},
queryTypes: [
{ label: '天', value: 'DAY' },
{ label: '月', value: 'MONTH' },
{ label: '年', value: 'YEAR' }
],
loading: false,
sale: {}
}
},
computed: {
...mapGetters(['permissionsSysOriginPlatforms'])
...mapGetters(['permissionsSysOriginPlatforms', 'language']),
queryTypes() {
const language = this.language
return [
{ label: this.$t('pages.dashboard.propsSalesOverview.day'), value: 'DAY' },
{ label: this.$t('pages.dashboard.propsSalesOverview.month'), value: 'MONTH' },
{ label: this.$t('pages.dashboard.propsSalesOverview.year'), value: 'YEAR' }
]
},
localizedPropsTypes() {
const language = this.language
return this.rawPropsTypes.map(item => {
const key = PROP_TYPE_KEY_MAP[item.value]
return {
...item,
name: key ? this.$t(`pages.dashboard.propsSalesOverview.propTypes.${key}`) : item.name
}
})
}
},
watch: {
language() {
this.selectedDateType = this.dateTypeMap[this.listQuery.dateType]
}
},
created() {
this.selectedDateType = this.dateTypeMap[this.listQuery.dateType]
@ -130,13 +166,12 @@ export default {
},
methods: {
renderData() {
const that = this
that.loading = true
getPropsSale(that.listQuery).then(res => {
that.loading = false
that.sale = res.body || {}
}).catch(er => {
that.loading = false
this.loading = true
getPropsSale(this.listQuery).then(res => {
this.loading = false
this.sale = res.body || {}
}).catch(() => {
this.loading = false
})
},
changeDateType() {
@ -147,6 +182,6 @@ export default {
}
}
</script>
<style scoped lang="scss">
<style scoped lang="scss">
</style>

View File

@ -1,6 +1,6 @@
<template>
<template>
<el-drawer
title="重置密码"
:title="$t('password.title')"
:visible="true"
:before-close="clickClose"
:close-on-press-escape="false"
@ -9,56 +9,53 @@
:append-to-body="true"
custom-class="drawer-auto-layout"
>
<div class="drawer-form">
<el-form ref="dataForm" :model="formData" :rules="rules" label-width="80px">
<el-form-item label="原始密码" prop="oldPassword">
<el-form ref="dataForm" :model="formData" :rules="rules" label-width="120px">
<el-form-item :label="$t('password.old')" prop="oldPassword">
<el-input
v-model.trim="formData.oldPassword"
type="password"
placeholder="请输入原始密码"
:placeholder="$t('password.placeholderOld')"
/>
</el-form-item>
<el-form-item label="新密码" prop="newPassword">
<el-form-item :label="$t('password.new')" prop="newPassword">
<el-input
v-model.trim="formData.newPassword"
type="password"
placeholder="请输入新密码"
:placeholder="$t('password.placeholderNew')"
/>
</el-form-item>
<el-form-item label="确认密码" prop="rePassword">
<el-form-item :label="$t('password.confirm')" prop="rePassword">
<el-input
v-model.trim="formData.rePassword"
type="password"
placeholder="请输入确认密码"
:placeholder="$t('password.placeholderConfirm')"
/>
</el-form-item>
</el-form>
</div>
<div class="drawer-footer">
<el-button @click="clickClose">取消</el-button>
<el-button type="primary" :loading="submitLoading" :disabled="submitLoading" @click="clickSubmit">提交</el-button>
<el-button @click="clickClose">{{ $t('common.cancel') }}</el-button>
<el-button type="primary" :loading="submitLoading" :disabled="submitLoading" @click="clickSubmit">{{ $t('common.submit') }}</el-button>
</div>
</el-drawer>
</template>
<script>
import { resetPassword } from '@/api/ops-system'
export default {
data() {
const passwordValid = (rule, value, callback) => {
if (!value) {
callback(new Error('必填字体不可为空'))
callback(new Error(this.$t('password.required')))
} else if (value.length < 4) {
callback(new Error('密码不可小于4位'))
callback(new Error(this.$t('password.invalidLength')))
} else {
callback()
}
}
return {
submitLoading: false,
formData: {
@ -75,34 +72,34 @@ export default {
},
methods: {
clickSubmit() {
const that = this
that.$refs.dataForm.validate(valid => {
if (valid) {
if (that.formData.newPassword !== that.formData.rePassword) {
that.$opsMessage.fail('两次密码输入不一致')
return false
}
that.submitLoading = true
resetPassword(that.formData).then(res => {
that.submitLoading = false
that.$emit('success')
that.$store.dispatch('user/resetToken').then(() => {
// window.clearVuexAlong(true, true)
location.reload()
})
}).catch(er => {
that.submitLoading = false
that.$emit('fial', er)
})
} else {
console.error('error submit!!')
this.$refs.dataForm.validate(valid => {
if (!valid) {
return false
}
if (this.formData.newPassword !== this.formData.rePassword) {
this.$opsMessage.fail(this.$t('password.mismatch'))
return false
}
this.submitLoading = true
resetPassword(this.formData).then(() => {
this.submitLoading = false
this.$emit('success')
this.$store.dispatch('user/resetToken').then(() => {
location.reload()
})
}).catch(error => {
this.submitLoading = false
this.$emit('fail', error)
})
return true
})
},
clickClose() {
if (this.submitLoading === true) {
this.$opsMessage.warn('Processing submission!')
if (this.submitLoading) {
this.$opsMessage.warn(this.$t('password.processing'))
return
}
this.$emit('close')

View File

@ -12,7 +12,7 @@
<el-collapse v-model="nActiveCollapseNames">
<el-collapse-item title="" name="profileInfo">
<template slot="title">
<i class="el-icon-s-custom" />&nbsp;基本资料
<i class="el-icon-s-custom" />&nbsp;{{ $t('pages.roomInfo.section.profileInfo') }}
</template>
<div class="block-info">
@ -27,35 +27,35 @@
<div class="text-item">
<el-row>
<el-col :span="12">房间ID{{ roomDetails.id }}</el-col>
<el-col :span="12">账号{{ getAccountText() }}</el-col>
<el-col :span="12">{{ $t('pages.roomInfo.label.roomId') }}{{ roomDetails.id }}</el-col>
<el-col :span="12">{{ $t('pages.roomInfo.label.account') }}{{ getAccountText() }}</el-col>
</el-row>
</div>
<div class="text-item">
<el-row>
<el-col :span="12">用户ID{{ roomDetails.userId }}</el-col>
<el-col :span="12" class="icon-item">国家
<el-col :span="12">{{ $t('pages.roomInfo.label.userId') }}{{ roomDetails.userId }}</el-col>
<el-col :span="12" class="icon-item">{{ $t('pages.roomInfo.label.country') }}
<flag-icon class="flag-icon" :code="roomDetails.countryCode" :tooltip="roomDetails.countryName" size="28" />
</el-col>
</el-row>
</div>
<div class="text-item">
<el-row>
<el-col :span="12">状态
<el-tag v-if="roomDetails.event === 'AVAILABLE'" size="mini" type="success">正常</el-tag>
<el-tag v-else-if="roomDetails.event === 'ID_CHANGE'" size="mini" type="info">ID变更</el-tag>
<el-tag v-else-if="roomDetails.event === 'WAITING_CONFIRMED'" size="mini" type="warning">等待确认</el-tag>
<el-tag v-else-if="roomDetails.event === 'CLOSE'" size="mini" type="danger">关闭</el-tag>
<el-tag v-else size="mini" type="danger">未知</el-tag>
<el-col :span="12">{{ $t('pages.roomInfo.label.status') }}
<el-tag v-if="roomDetails.event === 'AVAILABLE'" size="mini" type="success">{{ $t('pages.roomInfo.status.available') }}</el-tag>
<el-tag v-else-if="roomDetails.event === 'ID_CHANGE'" size="mini" type="info">{{ $t('pages.roomInfo.status.idChange') }}</el-tag>
<el-tag v-else-if="roomDetails.event === 'WAITING_CONFIRMED'" size="mini" type="warning">{{ $t('pages.roomInfo.status.waitingConfirmed') }}</el-tag>
<el-tag v-else-if="roomDetails.event === 'CLOSE'" size="mini" type="danger">{{ $t('pages.roomInfo.status.close') }}</el-tag>
<el-tag v-else size="mini" type="danger">{{ $t('pages.roomInfo.status.unknown') }}</el-tag>
</el-col>
<el-col :span="12">注销
{{ roomDetails.del ? 'Yes' : 'NO' }}
<el-col :span="12">{{ $t('pages.roomInfo.label.deleted') }}
{{ getYesNoText(roomDetails.del) }}
</el-col>
</el-row>
</div>
<div class="text-item">
<el-row>
<el-col :span="12" class="icon-item">平台
<el-col :span="12" class="icon-item">{{ $t('pages.roomInfo.label.platform') }}
<div class="sys-icon">
<sys-origin-icon :icon="userProfile.sysOriginChild || userProfile.originSys" :desc="userProfile.originSys || userProfile.sysOriginChild" />
</div>
@ -63,15 +63,15 @@
</el-row>
</div>
<div>
<div>公告{{ roomDetails.roomDesc }}</div>
<div>{{ $t('pages.roomInfo.label.notice') }}{{ roomDetails.roomDesc }}</div>
</div>
<div class="text-item">
<el-row>
<el-col :span="12">
创建时间{{ roomDetails.createTime }}
{{ $t('pages.roomInfo.label.createdAt') }}{{ roomDetails.createTime }}
</el-col>
<el-col :span="12">
修改时间{{ roomDetails.updateTime }}
{{ $t('pages.roomInfo.label.updatedAt') }}{{ roomDetails.updateTime }}
</el-col>
</el-row>
</div>
@ -79,42 +79,42 @@
</el-collapse-item>
<el-collapse-item title="" name="settingInfo">
<template slot="title">
<i class="el-icon-s-opportunity" />&nbsp;设置信息
<i class="el-icon-s-opportunity" />&nbsp;{{ $t('pages.roomInfo.section.settingInfo') }}
</template>
<div class="block-info">
<div class="text-item">
<el-row>
<el-col :span="12">房间密码{{ roomDetails.setting.password ? roomDetails.setting.password : '无' }}</el-col>
<el-col :span="12">加入成员金币{{ roomDetails.setting.joinGolds || 0 }}</el-col>
<el-col :span="12">{{ $t('pages.roomInfo.label.roomPassword') }}{{ roomDetails.setting.password ? roomDetails.setting.password : $t('pages.roomInfo.word.none') }}</el-col>
<el-col :span="12">{{ $t('pages.roomInfo.label.joinGolds') }}{{ roomDetails.setting.joinGolds || 0 }}</el-col>
</el-row>
</div>
<div class="text-item">
<el-row>
<el-col :span="12">成员人数{{ roomDetails.counter.memberCount }}/{{ roomDetails.setting.maxMember }}</el-col>
<el-col :span="12">管理员人数{{ roomDetails.counter.adminCount }}/{{ roomDetails.setting.maxAdmin }}</el-col>
<el-col :span="12">{{ $t('pages.roomInfo.label.memberCount') }}{{ roomDetails.counter.memberCount }}/{{ roomDetails.setting.maxMember }}</el-col>
<el-col :span="12">{{ $t('pages.roomInfo.label.adminCount') }}{{ roomDetails.counter.adminCount }}/{{ roomDetails.setting.maxAdmin }}</el-col>
</el-row>
</div>
<div class="text-item">
<el-row>
<el-col :span="12">麦克风权限{{ roomDetails.setting.takeMicRole || '-' }}</el-col>
<el-col :span="12">麦克风数量{{ roomDetails.setting.mikeSize || '-' }}</el-col>
<el-col :span="12">{{ $t('pages.roomInfo.label.takeMicRole') }}{{ roomDetails.setting.takeMicRole || '-' }}</el-col>
<el-col :span="12">{{ $t('pages.roomInfo.label.mikeSize') }}{{ roomDetails.setting.mikeSize || '-' }}</el-col>
</el-row>
</div>
<div class="text-item">
<el-row>
<el-col :span="12">游客上麦{{ roomDetails.setting.touristMike ? '允许' : '不允许' }}</el-col>
<el-col :span="12">游客发消息{{ roomDetails.setting.touristMsg? '允许' : '不允许' }}</el-col>
<el-col :span="12">{{ $t('pages.roomInfo.label.touristMike') }}{{ getPermissionText(roomDetails.setting.touristMike) }}</el-col>
<el-col :span="12">{{ $t('pages.roomInfo.label.touristMsg') }}{{ getPermissionText(roomDetails.setting.touristMsg) }}</el-col>
</el-row>
</div>
<div class="text-item">
<el-row>
<el-col :span="12">播放音乐{{ roomDetails.setting.allowMusic ? '允许' : '不允许' }}</el-col>
<el-col :span="12">管理员锁麦克风{{ roomDetails.setting.adminLockSeat? '允许' : '不允许' }}</el-col>
<el-col :span="12">{{ $t('pages.roomInfo.label.allowMusic') }}{{ getPermissionText(roomDetails.setting.allowMusic) }}</el-col>
<el-col :span="12">{{ $t('pages.roomInfo.label.adminLockSeat') }}{{ getPermissionText(roomDetails.setting.adminLockSeat) }}</el-col>
</el-row>
</div>
<div class="text-item">
<el-row>
<el-col :span="12">显示心动值{{ roomDetails.setting.showHeartbeat ? '允许' : '不允许' }}</el-col>
<el-col :span="12">{{ $t('pages.roomInfo.label.showHeartbeat') }}{{ getPermissionText(roomDetails.setting.showHeartbeat) }}</el-col>
</el-row>
</div>
</div>
@ -148,17 +148,17 @@
</el-collapse-item> -->
<el-collapse-item title="" name="badgeInfo">
<template slot="title">
<i class="el-icon-s-opportunity" />&nbsp;装饰徽章
<i class="el-icon-s-opportunity" />&nbsp;{{ $t('pages.roomInfo.section.badgeInfo') }}
</template>
<div class="block-info">
<div v-if="roomDetails.useBadges && roomDetails.useBadges.length > 0" class="text-item flex-l">
<el-tooltip v-for="(item, index) in roomDetails.useBadges" :key="index" class="item" effect="dark">
<div slot="content">
<div>ID:{{ item.id }}</div>
<div>徽章名称:{{ item.badgeName }}</div>
<div>徽章类型:{{ item.type }}</div>
<div>徽章KEY:{{ item.badgeKey }}</div>
<div>过期时间:{{ item.expireTime | dateFormat('yyyy-MM-dd HH:mm:ss') }}</div>
<div>{{ $t('pages.roomInfo.label.badgeName') }}:{{ item.badgeName }}</div>
<div>{{ $t('pages.roomInfo.label.badgeType') }}:{{ item.type }}</div>
<div>{{ $t('pages.roomInfo.label.badgeKey') }}:{{ item.badgeKey }}</div>
<div>{{ $t('pages.roomInfo.label.expireTime') }}:{{ item.expireTime | dateFormat('yyyy-MM-dd HH:mm:ss') }}</div>
</div>
<el-image
style="width: 50px; height: 50px"
@ -217,10 +217,21 @@ export default {
handleClose() {
this.$emit('close')
},
getPermissionText(value) {
return this.$t(value ? 'pages.roomInfo.word.allow' : 'pages.roomInfo.word.notAllow')
},
getYesNoText(value) {
return this.$t(value ? 'pages.roomInfo.word.yes' : 'pages.roomInfo.word.no')
},
getAccountText() {
const account = this.userProfile.account
if (this.userProfile.ownSpecialId && this.userProfile.ownSpecialId.account) {
return `${account} / ${this.userProfile.ownSpecialId.account}`
const specialAccount = this.userProfile.ownSpecialId && this.userProfile.ownSpecialId.account
if (specialAccount) {
return this.$t('pages.roomInfo.text.accountWithSpecial', {
account,
specialAccount,
suffix: this.$t('pages.roomInfo.word.specialSuffix')
})
}
return account
},

View File

@ -14,29 +14,29 @@
success: roomProfile.id && roomProfile.id > 0,
fail: (!roomProfile.id || roomProfile.id < 0) && failShow
}"
placeholder="请输入房间ID"
:placeholder="$t('pages.searchRoomInput.placeholder.roomId')"
class="input-with-select"
@blur="clickSearch"
>
<el-select
slot="prepend"
v-model="selectType"
placeholder="类型"
:placeholder="$t('pages.searchRoomInput.placeholder.type')"
@change="clickSearch"
>
<el-option label="短ID" value="SHORT_ID" />
<el-option label="长ID" value="LONG_ID" />
<el-option :label="$t('pages.searchRoomInput.option.shortId')" value="SHORT_ID" />
<el-option :label="$t('pages.searchRoomInput.option.longId')" value="LONG_ID" />
</el-select>
</el-input>
</el-tooltip>
<el-dialog
title="房间搜索"
:title="$t('pages.searchRoomInput.dialog.title')"
:visible="visibleSelecteDialog"
:before-close="handleClose"
:append-to-body="true"
>
<span>找到多个结果, 请选择您期望</span>
<span>{{ $t('pages.searchRoomInput.dialog.selectRoom') }}</span>
<div class="select-room">
<div
v-for="(item, index) in roomProfiles"
@ -57,7 +57,7 @@
{{ item.roomName }}
</div>
<div class="selected-but">
<el-button type="text" @click="clickSelected(item)">选择</el-button>
<el-button type="text" @click="clickSelected(item)">{{ $t('pages.searchRoomInput.action.select') }}</el-button>
</div>
</div>
</div>
@ -158,11 +158,11 @@ export default {
return;
}
if (!/\d+/.test(that.content)) {
that.failTipsContent("输入内容必须是正整数!");
that.failTipsContent(this.$t("pages.searchRoomInput.message.contentMustBePositiveInteger"));
return;
}
if (that.content.length < 1) {
that.failTipsContent("输入账号必须>=1位数!");
that.failTipsContent(this.$t("pages.searchRoomInput.message.contentLengthInvalid"));
return;
}
if (
@ -182,7 +182,7 @@ export default {
that.loading = false;
const result = res.body;
if (!result) {
that.failTipsContent("没有找到房间信息!");
that.failTipsContent(this.$t("pages.searchRoomInput.message.roomNotFound"));
return;
}
@ -199,7 +199,7 @@ export default {
}
if (result.length === 0) {
that.failTipsContent("没有找到房间信息!");
that.failTipsContent(this.$t("pages.searchRoomInput.message.roomNotFound"));
return;
}
@ -209,7 +209,7 @@ export default {
.catch(er => {
that.loading = false;
console.error("clickSearch", er);
that.failTipsContent("请求数据错误!");
that.failTipsContent(this.$t("pages.searchRoomInput.message.requestFailed"));
});
},

View File

@ -1,7 +1,7 @@
<template>
<div class="bind-user-phone-form">
<el-drawer
title="认证信息"
:title="$t('pages.userAuthForm.drawer.title')"
:visible="true"
:before-close="handleClose"
:close-on-press-escape="false"
@ -13,7 +13,7 @@
<div v-loading="authLoading" class="user-auth-type" style="padding: 10px;">
<el-card class="box-card" style="margin-top:0px;">
<div slot="header" class="clearfix">
<span>已认证类型</span>
<span>{{ $t('pages.userAuthForm.section.authenticatedTypes') }}</span>
</div>
<div class="text item" style="">
<div
@ -35,24 +35,24 @@
</el-card>
<el-card class="box-card" style="margin-top: 10px;">
<div slot="header" class="clearfix">
<span>手机号绑定</span>
<el-button v-if="!(userMobileAuth || {}).id" type="text" @click="handleCreate()">绑定</el-button>
<span>{{ $t('pages.userAuthForm.section.phoneBinding') }}</span>
<el-button v-if="!(userMobileAuth || {}).id" type="text" @click="handleCreate()">{{ $t('pages.userAuthForm.action.bind') }}</el-button>
</div>
<div v-if="(userMobileAuth || {}).id && editBoxVisible == false" class="text item">
<div style="display: flex; align-items: center; justify-content: space-between;">
<div>{{ userMobileAuth.phonePrefix }}-{{ userMobileAuth.phoneNumber }}</div>
<div><el-tag type="success">已绑定</el-tag></div>
<div><el-tag type="success">{{ $t('pages.userAuthForm.status.bound') }}</el-tag></div>
<div>
<el-button type="text" @click="handleUpdateMobileAuth()">换绑</el-button>
<el-button type="text" @click="handleDelete()">删除</el-button>
<el-button type="text" @click="handleUpdateMobileAuth()">{{ $t('pages.userAuthForm.action.rebind') }}</el-button>
<el-button type="text" @click="handleDelete()">{{ $t('pages.userAuthForm.action.delete') }}</el-button>
</div>
</div>
</div>
<div v-if="editBoxVisible" class="text item">
<el-form
<el-form
ref="formData"
v-loading="listLoading"
:rules="rules"
:rules="localizedRules"
:model="formData"
label-position="left"
label-width="70px"
@ -65,7 +65,7 @@
<el-input
v-model.trim="formData.phonePrefix"
v-number
placeholder="区号"
:placeholder="$t('pages.userAuthForm.placeholder.phonePrefix')"
maxlength="7"
/>
</el-form-item>
@ -76,7 +76,7 @@
<el-input
v-model.trim="formData.phoneNumber"
v-number
placeholder="手机号"
:placeholder="$t('pages.userAuthForm.placeholder.phoneNumber')"
maxlength="20"
/>
</el-form-item>
@ -85,18 +85,18 @@
<el-form-item prop="pwd">
<el-input
v-model="formData.pwd"
placeholder="密码"
:placeholder="$t('pages.userAuthForm.placeholder.password')"
/>
</el-form-item>
<div style="text-align: center;">
<el-button @click="handleCancel()">
取消
{{ $t('pages.userAuthForm.action.cancel') }}
</el-button>
<el-button
type="primary"
@click="handleSubmit()"
>
提交
{{ $t('pages.userAuthForm.action.submit') }}
</el-button>
</div>
</el-form>
@ -104,20 +104,20 @@
</el-card>
<el-card class="box-card" style="margin-top: 10px;">
<div slot="header" class="clearfix">
<span>账号密码登录次数</span>
<el-button type="text" @click="initAccountLogin()">重置</el-button>
<span>{{ $t('pages.userAuthForm.section.accountPasswordLoginCount') }}</span>
<el-button type="text" @click="initAccountLogin()">{{ $t('pages.userAuthForm.action.reset') }}</el-button>
<br>
<span>账号登录</span>
<el-button type="text" @click="deleteAccountLogin()">删除</el-button>
<span>{{ $t('pages.userAuthForm.section.accountLogin') }}</span>
<el-button type="text" @click="deleteAccountLogin()">{{ $t('pages.userAuthForm.action.delete') }}</el-button>
</div>
</el-card>
<el-card class="box-card" style="margin-top: 10px;">
<div slot="header" class="clearfix">
<span>支付密码错误次数</span>
<el-button type="text" @click="initPayAuth()">重置</el-button>
<span>{{ $t('pages.userAuthForm.section.payPasswordErrorCount') }}</span>
<el-button type="text" @click="initPayAuth()">{{ $t('pages.userAuthForm.action.reset') }}</el-button>
<br>
<span>支付密码</span>
<el-button type="text" @click="deletePayAuth()">删除</el-button>
<span>{{ $t('pages.userAuthForm.section.payPassword') }}</span>
<el-button type="text" @click="deletePayAuth()">{{ $t('pages.userAuthForm.action.delete') }}</el-button>
</div>
</el-card>
</div>
@ -145,25 +145,25 @@ export default {
}
},
data() {
function commonFormRules() {
return [
{ required: true, message: '必填字段', trigger: 'blur' }
]
}
return {
listLoading: false,
authLoading: false,
userAuthList: [],
userMobileAuth: {},
editBoxVisible: false,
rules: {
phonePrefix: commonFormRules(),
phoneNumber: commonFormRules(),
pwd: commonFormRules()
},
formData: getFormData()
}
},
computed: {
localizedRules() {
const commonRules = [this.getRequiredRule()]
return {
phonePrefix: commonRules,
phoneNumber: commonRules,
pwd: commonRules
}
}
},
watch: {
userId: {
handler(newVal) {
@ -175,6 +175,13 @@ export default {
}
},
methods: {
getRequiredRule() {
return {
required: true,
message: this.$t('pages.userAuthForm.validation.requiredField'),
trigger: 'blur'
}
},
renderData() {
const that = this
that.authLoading = true
@ -189,13 +196,13 @@ export default {
},
handleDelete() {
const that = this
that.$confirm('确认删除吗?', '提示', {
that.$confirm(that.$t('pages.userAuthForm.confirm.delete'), that.$t('pages.userAuthForm.confirm.title'), {
type: 'warning'
}).then(() => {
that.listLoading = true
deleteUserMobileAuth(that.userId).then((res) => {
that.listLoading = false
that.$message.success('删除成功')
that.$message.success(that.$t('pages.userAuthForm.message.deleteSuccess'))
that.$emit('success', {})
that.renderData()
that.handleCancel()
@ -212,7 +219,7 @@ export default {
return false
}
if (Number((that.formData.phonePrefix + '').substring(0, 1)) <= 0) {
that.$message.error('区号不能以0开头')
that.$message.error(that.$t('pages.userAuthForm.validation.phonePrefixCannotStartWithZero'))
return
}
that.listLoading = true
@ -234,13 +241,13 @@ export default {
},
initAccountLogin() {
const that = this
that.$confirm('确认重置吗?', '提示', {
that.$confirm(that.$t('pages.userAuthForm.confirm.reset'), that.$t('pages.userAuthForm.confirm.title'), {
type: 'warning'
}).then(() => {
that.listLoading = true
initUserAccountLogin(that.userId).then((res) => {
that.listLoading = false
that.$message.success('重置成功')
that.$message.success(that.$t('pages.userAuthForm.message.resetSuccess'))
that.$emit('success', {})
that.renderData()
that.handleCancel()
@ -251,13 +258,13 @@ export default {
},
deleteAccountLogin() {
const that = this
that.$confirm('确认删除吗?', '提示', {
that.$confirm(that.$t('pages.userAuthForm.confirm.delete'), that.$t('pages.userAuthForm.confirm.title'), {
type: 'warning'
}).then(() => {
that.listLoading = true
deleteUserAccountLogin(that.userId).then((res) => {
that.listLoading = false
that.$message.success('删除成功')
that.$message.success(that.$t('pages.userAuthForm.message.deleteSuccess'))
that.$emit('success', {})
that.renderData()
that.handleCancel()
@ -267,13 +274,13 @@ export default {
},
initPayAuth() {
const that = this
that.$confirm('确认重置吗?', '提示', {
that.$confirm(that.$t('pages.userAuthForm.confirm.reset'), that.$t('pages.userAuthForm.confirm.title'), {
type: 'warning'
}).then(() => {
that.listLoading = true
initUserPayAuth(that.userId).then((res) => {
that.listLoading = false
that.$message.success('重置成功')
that.$message.success(that.$t('pages.userAuthForm.message.resetSuccess'))
that.$emit('success', {})
that.renderData()
that.handleCancel()
@ -284,13 +291,13 @@ export default {
},
deletePayAuth() {
const that = this
that.$confirm('确认删除吗?', '提示', {
that.$confirm(that.$t('pages.userAuthForm.confirm.delete'), that.$t('pages.userAuthForm.confirm.title'), {
type: 'warning'
}).then(() => {
that.listLoading = true
deleteUserPayAuth(that.userId).then((res) => {
that.listLoading = false
that.$message.success('删除成功')
that.$message.success(that.$t('pages.userAuthForm.message.deleteSuccess'))
that.$emit('success', {})
that.renderData()
that.handleCancel()

View File

@ -1,4 +1,3 @@
<!--账号处理日志列表-->
<template>
<div class="account-status-latest-log">
<div
@ -6,7 +5,12 @@
:key="index"
class="block"
>
<el-tag type="danger"> <strong>{{ item.approvalUserName }}</strong> 操作 <strong>{{ item.statusName }}</strong> {{ item.createTime }}</el-tag>
<el-tag type="danger">
<strong>{{ item.approvalUserName }}</strong>
{{ $t('pages.accountStatusLatestLog.message.operated') }}
<strong>{{ item.statusName }}</strong>
{{ item.createTime }}
</el-tag>
</div>
</div>
</template>

View File

@ -1,7 +1,7 @@
<template>
<div class="user-bank-card-list">
<el-drawer
title="银行卡列表"
:title="$t('pages.bankCardDrawer.drawer.title')"
:visible="true"
:before-close="handleClose"
:close-on-press-escape="false"
@ -18,73 +18,73 @@
style="margin: 10px;"
@click="handleCreate"
>
新增
{{ $t('pages.bankCardDrawer.action.create') }}
</el-button>
</div>
<div v-loading="listLoading">
<el-row v-for="(item, index) in list" :key="index">
<el-card style="margin: 10px;">
<div>
<el-tag effect="plain">卡号: {{ item.cardNo }}</el-tag>
<el-tag effect="plain">收款人: {{ item.payee }}</el-tag>
<el-tag effect="plain">银行: {{ item.cardName }}</el-tag>
<el-tag effect="plain">{{ $t('pages.bankCardDrawer.label.cardNo') }}: {{ item.cardNo }}</el-tag>
<el-tag effect="plain">{{ $t('pages.bankCardDrawer.label.payee') }}: {{ item.payee }}</el-tag>
<el-tag effect="plain">{{ $t('pages.bankCardDrawer.label.bank') }}: {{ item.cardName }}</el-tag>
<el-tag
v-if="item.status === 'PENDING'"
effect="plain"
type="info"
>审核状态: 待审核</el-tag>
>{{ $t('pages.bankCardDrawer.label.auditStatus') }}: {{ $t('pages.bankCardDrawer.status.pending') }}</el-tag>
<el-tag
v-if="item.status === 'PASS'"
effect="plain"
type="success"
>审核状态: 正常</el-tag>
>{{ $t('pages.bankCardDrawer.label.auditStatus') }}: {{ $t('pages.bankCardDrawer.status.pass') }}</el-tag>
<el-tag
v-if="item.status === 'NOT_PASS'"
effect="plain"
type="danger"
>审核状态: 驳回</el-tag>
>{{ $t('pages.bankCardDrawer.label.auditStatus') }}: {{ $t('pages.bankCardDrawer.status.notPass') }}</el-tag>
<el-tag
v-if="item.use"
effect="plain"
type="success"
>使用中: </el-tag>
>{{ $t('pages.bankCardDrawer.label.inUse') }}: {{ $t('pages.bankCardDrawer.word.yes') }}</el-tag>
<el-tag
v-if="!item.use"
effect="plain"
type="info"
>使用中: </el-tag>
>{{ $t('pages.bankCardDrawer.label.inUse') }}: {{ $t('pages.bankCardDrawer.word.no') }}</el-tag>
</div>
<div class="bottom clearfix">
<time class="time">创建时间:{{ item.createTime }}</time>
<time class="time">{{ $t('pages.bankCardDrawer.label.createdAt') }}:{{ item.createTime }}</time>
</div>
<div class="clearfix">
<el-button
type="text"
@click.native="handleUpdate(item)"
>修改</el-button>
>{{ $t('pages.bankCardDrawer.action.edit') }}</el-button>
<el-button
v-if="!item.use"
type="text"
@click.native="handleUse(item)"
>使用</el-button>
>{{ $t('pages.bankCardDrawer.action.use') }}</el-button>
<el-button
type="text"
@click.native="handleDelete(item)"
>删除</el-button>
>{{ $t('pages.bankCardDrawer.action.delete') }}</el-button>
</div>
</el-card>
</el-row>
</div>
<el-dialog
width="450px"
:title="textOptTitle2"
:title="formDialogTitle"
:visible.sync="form2Visible"
append-to-body
>
<div v-loading="submitLoading">
<el-form
ref="dataForm"
:rules="rules"
:rules="localizedRules"
:model="dataForm"
label-position="left"
label-width="70px"
@ -92,12 +92,12 @@
>
<el-form-item
v-if="dataForm.id === ''"
label="类型"
:label="$t('pages.bankCardDrawer.form.cardType')"
prop="cardType"
>
<el-select
v-model="dataForm.cardType"
placeholder="类型"
:placeholder="$t('pages.bankCardDrawer.placeholder.cardType')"
style="width: 120px"
class="filter-item"
@change="getBankCardTypeName(dataForm)"
@ -113,33 +113,33 @@
<el-form-item
v-if="dataForm.id === '' && dataForm.cardType === 'BANK'"
label="银行"
:label="$t('pages.bankCardDrawer.form.cardName')"
prop="cardName"
>
<el-input
v-model.trim="dataForm.cardName"
placeholder="请输入银行(英文)名称"
:placeholder="$t('pages.bankCardDrawer.placeholder.cardName')"
/>
</el-form-item>
<el-form-item label="收款人" prop="payee">
<el-input v-model.trim="dataForm.payee" placeholder="收款人" />
<el-form-item :label="$t('pages.bankCardDrawer.form.payee')" prop="payee">
<el-input v-model.trim="dataForm.payee" :placeholder="$t('pages.bankCardDrawer.placeholder.payee')" />
</el-form-item>
<el-form-item label="卡号" prop="cardNo">
<el-input v-model.trim="dataForm.cardNo" placeholder="卡号" />
<el-form-item :label="$t('pages.bankCardDrawer.form.cardNo')" prop="cardNo">
<el-input v-model.trim="dataForm.cardNo" :placeholder="$t('pages.bankCardDrawer.placeholder.cardNo')" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer" style="text-align: center;">
<el-button @click="form2Visible = false">
取消
{{ $t('pages.bankCardDrawer.action.cancel') }}
</el-button>
<el-button
v-loading="listLoading"
type="primary"
@click="handleSubmit()"
>
提交
{{ $t('pages.bankCardDrawer.action.submit') }}
</el-button>
</div>
</div>
@ -182,17 +182,7 @@ export default {
list: [],
form2Visible: false,
listLoading: false,
textOptTitle2: '添加',
rules: {
cardType: [
{ required: true, message: '请填写卡片类型', trigger: 'blur' }
],
payee: [{ required: true, message: '请填写收款人', trigger: 'blur' }],
cardNo: [{ required: true, message: '请填写卡号', trigger: 'blur' }],
cardName: [
{ required: true, message: '请填写银行英语名称', trigger: 'blur' }
]
},
dialogMode: 'create',
dataForm: {
id: '',
userId: '',
@ -205,10 +195,30 @@ export default {
}
}
},
computed: {
localizedRules() {
return {
cardType: [this.getRequiredRule('pages.bankCardDrawer.validation.cardTypeRequired')],
payee: [this.getRequiredRule('pages.bankCardDrawer.validation.payeeRequired')],
cardNo: [this.getRequiredRule('pages.bankCardDrawer.validation.cardNoRequired')],
cardName: [this.getRequiredRule('pages.bankCardDrawer.validation.cardNameRequired')]
}
},
formDialogTitle() {
return this.$t(`pages.bankCardDrawer.dialog.${this.dialogMode}`)
}
},
created() {
this.renderData()
},
methods: {
getRequiredRule(messageKey) {
return {
required: true,
message: this.$t(messageKey),
trigger: 'blur'
}
},
renderData() {
const that = this
that.listLoading = true
@ -262,7 +272,7 @@ export default {
handleDelete(row) {
const that = this
that
.$confirm('确认删除吗?', '提示', {
.$confirm(this.$t('pages.bankCardDrawer.confirm.delete'), this.$t('pages.bankCardDrawer.confirm.title'), {
type: 'warning'
})
.then(() => {
@ -282,7 +292,7 @@ export default {
handleUse(row) {
const that = this
that
.$confirm('确认使用该银行卡吗?', '提示', {
.$confirm(this.$t('pages.bankCardDrawer.confirm.use'), this.$t('pages.bankCardDrawer.confirm.title'), {
type: 'warning'
})
.then(() => {
@ -303,12 +313,12 @@ export default {
this.$emit('close')
},
handleCreate() {
this.textOptTitle2 = '添加'
this.dialogMode = 'create'
this.form2Visible = true
this.dataForm = getFormData()
},
handleUpdate(row) {
this.textOptTitle2 = '修改'
this.dialogMode = 'edit'
this.form2Visible = true
this.dataForm = Object.assign(this.dataForm, row)
},

View File

@ -17,16 +17,16 @@
<el-row>
<el-col
:span="12"
>语言{{ item.latestDevice.language }}</el-col>
<el-col :span="12">时区{{ item.latestDevice.zoneId }}</el-col>
>{{ $t('pages.userBaseInfo.labels.language') }}{{ item.latestDevice.language }}</el-col>
<el-col :span="12">{{ $t('pages.userBaseInfo.labels.timezone') }}{{ item.latestDevice.zoneId }}</el-col>
</el-row>
</div>
<div class="text-item">
<el-row>
<el-col :span="12">ip{{ item.latestDevice.ip }}</el-col>
<el-col :span="12">{{ $t('pages.userBaseInfo.words.activeIp') }}{{ item.latestDevice.ip }}</el-col>
<el-col
:span="12"
>客户端{{ item.latestDevice.requestClient }}</el-col>
>{{ $t('pages.userBaseInfo.labels.client') }}{{ item.latestDevice.requestClient }}</el-col>
</el-row>
</div>
<div class="text-item">
@ -34,12 +34,12 @@
<el-col
:span="12"
class="nowrap-ellipsis"
>设备型号<a :title="item.latestDevice.phoneModel">{{
>{{ $t('pages.userBaseInfo.labels.deviceModel') }}<a :title="item.latestDevice.phoneModel">{{
item.latestDevice.phoneModel
}}</a></el-col>
<el-col
:span="12"
>设备系统{{ item.latestDevice.phoneSysVersion }}</el-col>
>{{ $t('pages.userBaseInfo.labels.deviceSystem') }}{{ item.latestDevice.phoneSysVersion }}</el-col>
</el-row>
</div>
@ -47,10 +47,10 @@
<el-row>
<el-col
:span="12"
>编译版本{{ item.latestDevice.buildVersion }}</el-col>
>{{ $t('pages.userBaseInfo.labels.buildVersion') }}{{ item.latestDevice.buildVersion }}</el-col>
<el-col
:span="12"
>App版本{{ item.latestDevice.appVersion }}</el-col>
>{{ $t('pages.userBaseInfo.labels.appVersion') }}{{ item.latestDevice.appVersion }}</el-col>
</el-row>
</div>
@ -58,10 +58,10 @@
<el-row>
<el-col
:span="12"
>Push平台{{ item.latestDevice.deviceType }}</el-col>
>{{ $t('pages.userBaseInfo.labels.pushPlatform') }}{{ item.latestDevice.deviceType }}</el-col>
<el-col
:span="12"
>登记平台{{ item.latestDevice.sysOrigin }}</el-col>
>{{ $t('pages.userBaseInfo.labels.registerPlatformShort') }}{{ item.latestDevice.sysOrigin }}</el-col>
</el-row>
</div>
@ -70,7 +70,7 @@
<el-col
:span="24"
class="nowrap-ellipsis"
>Push设备<a :title="item.latestDevice.deviceId">{{
>{{ $t('pages.userBaseInfo.labels.pushDevice') }}<a :title="item.latestDevice.deviceId">{{
item.latestDevice.deviceId
}}</a></el-col>
</el-row>
@ -80,7 +80,7 @@
<el-col
:span="24"
class="nowrap-ellipsis"
>imei ID<a :title="item.latestDevice.imei">{{
>{{ $t('pages.userBaseInfo.labels.imei') }}<a :title="item.latestDevice.imei">{{
item.latestDevice.imei
}}</a></el-col>
</el-row>
@ -90,7 +90,7 @@
<el-col
:span="24"
class="nowrap-ellipsis"
>注册时间<a
>{{ $t('pages.userBaseInfo.labels.registerTime') }}<a
:title="item.userProfile.createTime | dateFormat"
>{{ item.userProfile.createTime | dateFormat }}</a></el-col>
</el-row>

View File

@ -11,8 +11,8 @@
<div class="content">
<div class="text">
{{ userBaseInfo.userNickname }}
<span v-if="userBaseInfo.userSex === 1"></span>
<span v-else> </span>
<span v-if="userBaseInfo.userSex === 1">{{ $t('pages.userBaseInfo.words.male') }}</span>
<span v-else> {{ $t('pages.userBaseInfo.words.female') }} </span>
</div>
<div
v-if="userRunProfile.wearBadge && userRunProfile.wearBadge.length > 0"
@ -26,11 +26,11 @@
>
<div slot="content">
<div>ID:{{ item.id }}</div>
<div>徽章名称:{{ item.badgeName }}</div>
<div>徽章类型:{{ item.type }}</div>
<div>徽章KEY:{{ item.badgeKey }}</div>
<div>{{ $t('pages.userBaseInfo.labels.badgeName') }}:{{ item.badgeName }}</div>
<div>{{ $t('pages.userBaseInfo.labels.badgeType') }}:{{ item.type }}</div>
<div>{{ $t('pages.userBaseInfo.labels.badgeKey') }}:{{ item.badgeKey }}</div>
<div>
过期时间:{{
{{ $t('pages.userBaseInfo.labels.expireTime') }}:{{
item.expireTime | dateFormat("yyyy-MM-dd HH:mm:ss")
}}
</div>
@ -54,11 +54,11 @@
<el-dropdown-menu slot="dropdown">
<el-dropdown-item
@click.native="queryUserRunProfile()"
>查看运行数据
>{{ $t('pages.userBaseInfo.actions.viewRuntimeData') }}
</el-dropdown-item>
<el-dropdown-item
@click.native="removeUserRunProfile()"
>删除运行数据
>{{ $t('pages.userBaseInfo.actions.deleteRuntimeData') }}
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
@ -68,16 +68,16 @@
<el-collapse v-model="nActiveCollapseNames" @change="collapseChange">
<el-collapse-item title="" name="accountInfo">
<template slot="title">
<i class="el-icon-s-opportunity" />&nbsp;账户信息
<i class="el-icon-s-opportunity" />&nbsp;{{ $t('pages.userBaseInfo.sections.accountInfo') }}
<span
v-if="userBaseInfo.del === true"
>已注销{{ userBaseInfo.updateTime | dateFormat }}</span>
>{{ $t('pages.userBaseInfo.labels.deletedAt') }}{{ userBaseInfo.updateTime | dateFormat }}</span>
<span v-else>
{{ userBaseInfo.accountStatusName }}
<span
v-if="userBaseInfo.accountStatus === 'FREEZE'"
>{{ userBaseInfo.freezingTime | dateFormat }}</span>
> {{ $t('pages.userBaseInfo.labels.operationFrozenAt') }}{{ userBaseInfo.freezingTime | dateFormat }}</span>
</span>
<!-- <i class="el-icon-s-opportunity" />&nbsp;认证信息
@ -92,7 +92,7 @@
size="38"
/>
<div class="tags">
<el-tag>金币{{ candy || 0 }}</el-tag>
<el-tag>{{ $t('pages.userBaseInfo.labels.gold') }}{{ candy || 0 }}</el-tag>
&nbsp;
<!-- <el-tag>当前订阅<a :title="notExpiredSubscriptionName">{{ notExpiredSubscriptionName }}</a> -->
<!-- <span v-if="balance.vipEvent > 0">{{ balance.vipExpireTime }}</span> </el-tag>-->
@ -111,7 +111,7 @@
</el-collapse-item>
<el-collapse-item title="" name="baseInfo">
<template slot="title">
<i class="el-icon-s-custom" />&nbsp;基本信息
<i class="el-icon-s-custom" />&nbsp;{{ $t('pages.userBaseInfo.sections.baseInfo') }}
</template>
<div class="block-info">
<div class="text-item">
@ -119,7 +119,7 @@
<el-col :span="12">ID:{{ userBaseInfo.id }}</el-col>
<el-col
:span="12"
>账号:{{ getAccountText(userBaseInfo) }}
>{{ $t('pages.userBaseInfo.labels.account') }}:{{ getAccountText(userBaseInfo) }}
</el-col>
</el-row>
</div>
@ -127,12 +127,12 @@
<el-row>
<el-col
:span="12"
>主播:{{ identity.anchor ? "Yes" : "No" }}
>{{ $t('pages.userBaseInfo.labels.anchor') }}:{{ identity.anchor ? $t('pages.userBaseInfo.words.yes') : $t('pages.userBaseInfo.words.no') }}
</el-col>
<el-col
:span="12"
>主播代理:{{ identity.agent ? "Yes" : "No" }} / 货运代理:{{
identity.freightAgent ? "Yes" : "No"
>{{ $t('pages.userBaseInfo.labels.anchorAgent') }}:{{ identity.agent ? $t('pages.userBaseInfo.words.yes') : $t('pages.userBaseInfo.words.no') }} / {{ $t('pages.userBaseInfo.labels.freightAgent') }}:{{
identity.freightAgent ? $t('pages.userBaseInfo.words.yes') : $t('pages.userBaseInfo.words.no')
}}
</el-col>
</el-row>
@ -141,18 +141,18 @@
<el-row>
<el-col
:span="12"
>BD:{{ identity.bd ? "Yes" : "No" }} / BD Leader:{{
identity.bdLeader ? "Yes" : "No"
>BD:{{ identity.bd ? $t('pages.userBaseInfo.words.yes') : $t('pages.userBaseInfo.words.no') }} / BD Leader:{{
identity.bdLeader ? $t('pages.userBaseInfo.words.yes') : $t('pages.userBaseInfo.words.no')
}}
</el-col>
<el-col
:span="12"
>等级Lv:
>{{ $t('pages.userBaseInfo.labels.level') }}:
<el-tag
size="mini"
>财富{{ userBaseInfo.wealthLevel }}
>{{ $t('pages.userBaseInfo.labels.wealth') }}{{ userBaseInfo.wealthLevel }}
</el-tag>
<el-tag size="mini">魅力{{ userBaseInfo.charmLevel }}</el-tag>
<el-tag size="mini">{{ $t('pages.userBaseInfo.labels.charm') }}{{ userBaseInfo.charmLevel }}</el-tag>
</el-col>
</el-row>
</div>
@ -160,43 +160,43 @@
<el-row>
<el-col
:span="12"
>生日<span
>{{ $t('pages.userBaseInfo.labels.birthday') }}<span
v-if="userBaseInfo.bornYear"
>{{ userBaseInfo.bornYear }}-{{ userBaseInfo.bornMonth }}-{{
userBaseInfo.bornDay
}}</span></el-col>
<el-col
:span="12"
>年龄<span
>{{ $t('pages.userBaseInfo.labels.age') }}<span
v-if="userBaseInfo.age"
>({{ userBaseInfo.age }})</span></el-col>
</el-row>
</div>
<div class="text-item">
<el-row>
<el-col :span="12">国家{{ userBaseInfo.countryName }}</el-col>
<el-col :span="12">邮箱{{ userRegisterInfo.email }}</el-col>
<el-col :span="12">{{ $t('pages.userBaseInfo.labels.country') }}{{ userBaseInfo.countryName }}</el-col>
<el-col :span="12">{{ $t('pages.userBaseInfo.labels.email') }}{{ userRegisterInfo.email }}</el-col>
</el-row>
</div>
<div class="text-item">
<el-row>
<el-col
:span="12"
>注册平台
>{{ $t('pages.userBaseInfo.labels.registerPlatform') }}
<platform-svg-icon
size="20px"
:icon="userRegisterInfo.originPlatform"
:desc="'注册平台' + userRegisterInfo.originPlatform"
:desc="$t('pages.userBaseInfo.labels.registerPlatform') + '' + userRegisterInfo.originPlatform"
/>
<platform-svg-icon
size="20px"
:icon="userRegisterInfo.authType"
:desc="'注册方式' + userRegisterInfo.authType"
:desc="$t('pages.userBaseInfo.labels.registerMethod') + '' + userRegisterInfo.authType"
/>
</el-col>
<el-col
:span="12"
>手机型号<a :title="userRegisterInfo.originPhoneModel">{{
>{{ $t('pages.userBaseInfo.labels.phoneModel') }}<a :title="userRegisterInfo.originPhoneModel">{{
userRegisterInfo.originPhoneModel
}}</a></el-col>
</el-row>
@ -206,12 +206,12 @@
<el-row>
<el-col
:span="12"
>账号类型
>{{ $t('pages.userBaseInfo.labels.accountType') }}
{{ userBaseInfo.userTypeName }}
</el-col>
<el-col
:span="12"
>注册时间<a :title="userBaseInfo.createTime | dateFormat">{{
>{{ $t('pages.userBaseInfo.labels.registerTime') }}<a :title="userBaseInfo.createTime | dateFormat">{{
userBaseInfo.createTime | dateFormat
}}</a></el-col>
</el-row>
@ -220,7 +220,7 @@
<el-row>
<el-col
:span="12"
>来源系统
>{{ $t('pages.userBaseInfo.labels.sourceSystem') }}
<span
v-if="
!userBaseInfo.sysOriginChild ||
@ -235,13 +235,13 @@
</el-col>
<el-col
:span="12"
>所属区域: {{ userBaseInfo.regionName }}
>{{ $t('pages.userBaseInfo.labels.region') }}: {{ userBaseInfo.regionName }}
</el-col>
</el-row>
</div>
<div v-if="identity && identity.historyIdentity && identity.historyIdentity.length > 0" class="text-item">
<el-row>
<el-col :span="12">历史身份: {{ identity.historyIdentity.join(",") }}</el-col>
<el-col :span="12">{{ $t('pages.userBaseInfo.labels.historyIdentity') }}: {{ identity.historyIdentity.join(",") }}</el-col>
</el-row>
</div>
<div class="text-item">
@ -252,7 +252,7 @@
:loading="authTypeLoading"
type="text"
@click="queryOpenId"
>查看OPENID
>{{ $t('pages.userBaseInfo.actions.viewOpenId') }}
</el-button>
<div v-else>
<el-tooltip class="item" effect="dark">
@ -260,7 +260,7 @@
<div v-if="authType.openId">
<p>{{ authType.openId }}</p>
</div>
<div v-else>Empty</div>
<div v-else>{{ $t('pages.userBaseInfo.words.empty') }}</div>
</div>
<i
style="cursor: pointer;"
@ -281,7 +281,7 @@
:loading="aTokenLoading"
type="text"
@click="queryAToken"
>查看登录令牌
>{{ $t('pages.userBaseInfo.actions.viewLoginToken') }}
</el-button>
<div v-else>
<div>
@ -290,10 +290,10 @@
<el-tooltip class="item" effect="dark">
<div slot="content">
<div v-if="aToken.token">
<p>点击可CopyToken</p>
<p>{{ $t('pages.userBaseInfo.words.clickToCopyToken') }}</p>
<p>AToken: {{ aToken.token }}</p>
</div>
<div v-else>未登录</div>
<div v-else>{{ $t('pages.userBaseInfo.words.notLoggedIn') }}</div>
</div>
<i
style="cursor: pointer;"
@ -301,7 +301,7 @@
@click="copyContent(aToken.token)"
/>
</el-tooltip>
AToken:{{ aToken.token || "未登录" }}
AToken:{{ aToken.token || $t('pages.userBaseInfo.words.notLoggedIn') }}
</el-col>
<el-col
v-if="
@ -320,7 +320,7 @@
<el-dropdown-menu slot="dropdown">
<el-dropdown-item
@click.native="delToken()"
>删除令牌
>{{ $t('pages.userBaseInfo.actions.deleteToken') }}
</el-dropdown-item>
<el-dropdown-item
@click.native="
@ -341,7 +341,7 @@
</el-row>
</div>
<div v-if="userRegisterInfo.residentialAddress" class="text-item">
居住地址{{ userRegisterInfo.residentialAddress }}
{{ $t('pages.userBaseInfo.labels.residentialAddress') }}{{ userRegisterInfo.residentialAddress }}
</div>
<div
@ -350,7 +350,7 @@
"
class="text-item flex-l"
>
佩戴道具:
{{ $t('pages.userBaseInfo.labels.wearingProps') }}:
<el-tooltip
v-for="(item, index) in userRunProfile.useProps"
:key="index"
@ -360,12 +360,12 @@
<div slot="content">
<div v-if="item.propsResources">
<div>ID:{{ item.propsResources.id }}</div>
<div>道具类型:{{ item.propsResources.type }}</div>
<div>道具编号:{{ item.propsResources.code }}</div>
<div>道具名称:{{ item.propsResources.name }}</div>
<div>{{ $t('pages.userBaseInfo.labels.propType') }}:{{ item.propsResources.type }}</div>
<div>{{ $t('pages.userBaseInfo.labels.propCode') }}:{{ item.propsResources.code }}</div>
<div>{{ $t('pages.userBaseInfo.labels.propName') }}:{{ item.propsResources.name }}</div>
</div>
<div>
过期时间:{{
{{ $t('pages.userBaseInfo.labels.expireTime') }}:{{
item.expireTime | dateFormat("yyyy-MM-dd HH:mm:ss")
}}
</div>
@ -383,7 +383,7 @@
</el-collapse-item>
<el-collapse-item title="" name="vipInfo">
<template slot="title">
<i class="el-icon-s-check" />&nbsp;VIP权益
<i class="el-icon-s-check" />&nbsp;{{ $t('pages.userBaseInfo.sections.vipInfo') }}
</template>
<div v-loading="vipLoading" class="vip-info">
<div class="blockquote-content block-info">
@ -391,13 +391,13 @@
<el-row>
<el-col
:span="12"
>任意发言:
{{ vipActualEquity.speak ? "Yes" : "No" }}
>{{ $t('pages.userBaseInfo.labels.anySpeech') }}:
{{ vipActualEquity.speak ? $t('pages.userBaseInfo.words.yes') : $t('pages.userBaseInfo.words.no') }}
</el-col>
<el-col
:span="12"
>防踢:
{{ vipActualEquity.prohibitKick ? "Yes" : "No" }}
>{{ $t('pages.userBaseInfo.labels.noKick') }}:
{{ vipActualEquity.prohibitKick ? $t('pages.userBaseInfo.words.yes') : $t('pages.userBaseInfo.words.no') }}
</el-col>
</el-row>
</div>
@ -405,13 +405,13 @@
<el-row>
<el-col
:span="12"
>任意上麦:
{{ vipActualEquity.microphone ? "Yes" : "No" }}
>{{ $t('pages.userBaseInfo.labels.anyMic') }}:
{{ vipActualEquity.microphone ? $t('pages.userBaseInfo.words.yes') : $t('pages.userBaseInfo.words.no') }}
</el-col>
</el-row>
</div>
<el-alert
title="具备实际权益的VIP来源: 购买,朋友赠送,活动获得; 系统后台赠送则不存在VIP实际权益,所以会产生VIP5被踢出房间的情况."
:title="$t('pages.userBaseInfo.messages.vipAlert')"
type="warning"
:closable="false"
/>
@ -428,15 +428,15 @@
name="deviceInfo"
>
<template slot="title">
<i class="el-icon-s-platform" />&nbsp;设备信息
<i class="el-icon-s-platform" />&nbsp;{{ $t('pages.userBaseInfo.sections.deviceInfo') }}
</template>
<div v-loading="deviceLoading" class="device-info">
<div v-if="deviceFlash && !deviceInfo" class="text-center">
没有设备信息
{{ $t('pages.userBaseInfo.labels.noDeviceInfo') }}
</div>
<div v-else>
<div class="blockquote">
当前最新设备
{{ $t('pages.userBaseInfo.labels.currentLatestDevice') }}
</div>
<div
v-if="deviceInfo.latestDevice"
@ -446,11 +446,11 @@
<el-row>
<el-col
:span="12"
>语言{{ deviceInfo.latestDevice.language }}
>{{ $t('pages.userBaseInfo.labels.language') }}{{ deviceInfo.latestDevice.language }}
</el-col>
<el-col
:span="12"
>时区{{ deviceInfo.latestDevice.zoneId }}
>{{ $t('pages.userBaseInfo.labels.timezone') }}{{ deviceInfo.latestDevice.zoneId }}
</el-col>
</el-row>
</div>
@ -462,7 +462,7 @@
</el-col>
<el-col
:span="12"
>客户端{{ deviceInfo.latestDevice.requestClient }}
>{{ $t('pages.userBaseInfo.labels.client') }}{{ deviceInfo.latestDevice.requestClient }}
</el-col>
</el-row>
</div>
@ -471,12 +471,12 @@
<el-col
:span="12"
class="nowrap-ellipsis"
>设备型号<a
>{{ $t('pages.userBaseInfo.labels.deviceModel') }}<a
:title="deviceInfo.latestDevice.phoneModel"
>{{ deviceInfo.latestDevice.phoneModel }}</a></el-col>
<el-col
:span="12"
>设备系统{{ deviceInfo.latestDevice.phoneSysVersion }}
>{{ $t('pages.userBaseInfo.labels.deviceSystem') }}{{ deviceInfo.latestDevice.phoneSysVersion }}
</el-col>
</el-row>
</div>
@ -485,11 +485,11 @@
<el-row>
<el-col
:span="12"
>编译版本{{ deviceInfo.latestDevice.buildVersion }}
>{{ $t('pages.userBaseInfo.labels.buildVersion') }}{{ deviceInfo.latestDevice.buildVersion }}
</el-col>
<el-col
:span="12"
>App版本{{ deviceInfo.latestDevice.appVersion }}
>{{ $t('pages.userBaseInfo.labels.appVersion') }}{{ deviceInfo.latestDevice.appVersion }}
</el-col>
</el-row>
</div>
@ -498,11 +498,11 @@
<el-row>
<el-col
:span="12"
>Push平台{{ deviceInfo.latestDevice.deviceType }}
>{{ $t('pages.userBaseInfo.labels.pushPlatform') }}{{ deviceInfo.latestDevice.deviceType }}
</el-col>
<el-col
:span="12"
>登记平台{{ deviceInfo.latestDevice.sysOrigin }}
>{{ $t('pages.userBaseInfo.labels.registerPlatformShort') }}{{ deviceInfo.latestDevice.sysOrigin }}
</el-col>
</el-row>
</div>
@ -512,7 +512,7 @@
<el-col
:span="24"
class="nowrap-ellipsis"
>Push设备<a :title="deviceInfo.latestDevice.deviceId">{{
>{{ $t('pages.userBaseInfo.labels.pushDevice') }}<a :title="deviceInfo.latestDevice.deviceId">{{
deviceInfo.latestDevice.deviceId
}}</a></el-col>
</el-row>
@ -522,7 +522,7 @@
<el-col
:span="24"
class="nowrap-ellipsis"
>imei ID<a :title="deviceInfo.latestDevice.imei">{{
>{{ $t('pages.userBaseInfo.labels.imei') }}<a :title="deviceInfo.latestDevice.imei">{{
deviceInfo.latestDevice.imei
}}</a></el-col>
</el-row>
@ -532,7 +532,7 @@
<el-col
:span="24"
class="nowrap-ellipsis"
>登记时间<a
>{{ $t('pages.userBaseInfo.labels.registerAt') }}<a
:title="deviceInfo.latestDevice.createTime | dateFormat"
>{{
deviceInfo.latestDevice.createTime | dateFormat
@ -541,14 +541,14 @@
</div>
</div>
<div v-else class="text-center">
当前用户没有登记设备信息
{{ $t('pages.userBaseInfo.labels.noRegisteredDeviceInfo') }}
</div>
<div
v-if="associatedDeviceNotEmpty || associatedIpNotEmpty"
class="blockquote"
>
设备关联信息
{{ $t('pages.userBaseInfo.labels.deviceAssociationInfo') }}
</div>
<div
v-if="associatedDeviceNotEmpty || associatedIpNotEmpty"
@ -557,7 +557,7 @@
<el-tabs v-model="associatedDeviceTabActiveName">
<el-tab-pane
v-if="associatedDeviceNotEmpty"
label="设备"
:label="$t('pages.userBaseInfo.words.device')"
name="associatedDevice"
>
<associated-device-timeline
@ -580,50 +580,50 @@
</el-collapse-item>
<el-collapse-item title="" name="expendInfo">
<template slot="title">
<i class="el-icon-s-grid" />&nbsp;扩展信息
<i class="el-icon-s-grid" />&nbsp;{{ $t('pages.userBaseInfo.sections.expendInfo') }}
</template>
<div v-loading="expendLoading" class="expend-info">
<div v-if="expendFlash && !expend.userId" class="text-center">
没有找到Expend信息,数据异常请联系管理员!!!
{{ $t('pages.userBaseInfo.labels.noExpendInfo') }}
</div>
<div v-else>
<div class="text-item">
<el-row>
<el-col :span="12">语言{{ expend.language }}</el-col>
<el-col :span="12">时区{{ expend.lastZoneId }}</el-col>
<el-col :span="12">{{ $t('pages.userBaseInfo.labels.language') }}{{ expend.language }}</el-col>
<el-col :span="12">{{ $t('pages.userBaseInfo.labels.timezone') }}{{ expend.lastZoneId }}</el-col>
</el-row>
</div>
<div class="text-item">
<el-row>
<el-col
:span="12"
>购买过{{ expend.purchasing === true ? "是" : "否" }}
>{{ $t('pages.userBaseInfo.labels.purchased') }}{{ expend.purchasing === true ? $t('pages.userBaseInfo.words.purchasedYes') : $t('pages.userBaseInfo.words.purchasedNo') }}
</el-col>
<el-col
:span="12"
>活跃时间{{ expend.lastActiveTime | dateFormat }}
>{{ $t('pages.userBaseInfo.labels.activeTime') }}{{ expend.lastActiveTime | dateFormat }}
</el-col>
</el-row>
</div>
<div v-if="expend.registerCountryCode" class="text-item">
注册国家{{ expend.registerCountryCode }}
{{ $t('pages.userBaseInfo.labels.registerCountry') }}{{ expend.registerCountryCode }}
</div>
<div v-if="expend.signature" class="text-item">
个性签名{{ expend.signature }}
{{ $t('pages.userBaseInfo.labels.signature') }}{{ expend.signature }}
</div>
</div>
</div>
</el-collapse-item>
<el-collapse-item title="" name="giftWall">
<template slot="title">
<i class="el-icon-s-cooperation" />&nbsp;礼物墙
<i class="el-icon-s-cooperation" />&nbsp;{{ $t('pages.userBaseInfo.sections.giftWall') }}
</template>
<div
v-if="giftWallsFlash && giftWalls.length <= 0"
class="text-center"
>
没有礼物信息, 快去给他送点吧!
{{ $t('pages.userBaseInfo.labels.noGiftInfo') }}
</div>
<div v-else>
<div class="photo-wall">
@ -647,7 +647,7 @@
</div>
<el-dialog
title="运行缓存数据"
:title="$t('pages.userBaseInfo.sections.runtimeCacheData')"
:visible.sync="userRunProfileVisible"
:append-to-body="true"
>
@ -859,7 +859,7 @@ export default {
}
const account = baseInfo.account
if (baseInfo.ownSpecialId && baseInfo.ownSpecialId.account) {
return `${account} / ${baseInfo.ownSpecialId.account}`
return `${account} / ${baseInfo.ownSpecialId.account}${this.$t('pages.userBaseInfo.labels.specialIdSuffix')}`
}
return account
},

View File

@ -105,7 +105,7 @@ export default {
return 'error'
}
if (this.isSpecialId) {
return `${this.userProfile.ownSpecialId.account}`
return `${this.userProfile.ownSpecialId.account}${this.$t('pages.userBaseInfo.labels.specialIdSuffix')}`
}
return this.userProfile.account
},

View File

@ -1,3 +1,5 @@
import { localizeCollection } from '@/lang'
/**
* Element UI 组件属性管理
* @auth pengliang
@ -62,5 +64,8 @@ const pickerOptions = {
}
}]
}
localizeCollection(pickerOptions)
export { pickerOptions }

View File

@ -1,3 +1,4 @@
import { localizeCollection } from '@/lang'
/**
* 家族等级Key集合
@ -40,6 +41,8 @@ const familyTypes = [
{ value: 'PLATINUM', name: '铂金' }
]
localizeCollection([familyLevelKeys, familyRoles, familyTypes])
export {
familyLevelKeys,
familyRoles,

View File

@ -1,4 +1,6 @@
// 注册来源
import { localizeCollection } from '@/lang'
const registerOrigins = [
{ value: "MOBILE", name: "手机号码" },
{ value: "APPLE", name: "Apple" },
@ -84,6 +86,20 @@ const badgeTypes = [
{ value: "HONOR_ACTIVITY", name: "荣誉-活动" }
];
localizeCollection([
registerOrigins,
originPlatforms,
appPlatforms,
deviceTypes,
payPlatforms,
sysPlatforms,
sysOriginPlatforms,
giftType,
giftInfo,
propsGiveTypes,
badgeTypes
])
export {
badgeTypes,
propsGiveTypes,

View File

@ -1,3 +1,5 @@
import { localizeCollection } from '@/lang'
/**
* 团队相关类型定义.
*/
@ -131,6 +133,19 @@ const bdApprovalReasons = [
{ value: 'ACCEPT_BD_INVITE', name: '接受成为BD邀请' }
]
localizeCollection([
teamStatus,
contactTypes,
bankCardType,
teamMemberRoles,
teamBillStatus,
teamBillSettleTypes,
teamApplicationProcessStatusList,
teamReasons,
teamApprovalReasons,
bdApprovalReasons
])
export {
teamBillSettleTypeMap,
teamBillSettleTypes,

249
src/lang/index.js Normal file
View File

@ -0,0 +1,249 @@
import elementLocale from 'element-ui/lib/locale'
import enElement from 'element-ui/lib/locale/lang/en'
import zhElement from 'element-ui/lib/locale/lang/zh-CN'
const DEFAULT_LOCALE = 'en'
const MESSAGE_FIELDS = ['name', 'label', 'title', 'help', 'msg', 'text', 'placeholder', 'content']
const TIMEZONE_LOCALE_MAP = {
zh: [
'Asia/Shanghai',
'Asia/Urumqi',
'Asia/Chongqing',
'Asia/Harbin',
'Asia/Hong_Kong',
'Asia/Macau',
'Asia/Taipei'
],
bn: [
'Asia/Dhaka'
],
tr: [
'Europe/Istanbul'
],
ar: [
'Asia/Riyadh',
'Asia/Dubai',
'Asia/Kuwait',
'Asia/Qatar',
'Asia/Bahrain',
'Asia/Muscat',
'Asia/Aden',
'Asia/Baghdad',
'Asia/Damascus',
'Asia/Amman',
'Asia/Beirut',
'Asia/Jerusalem',
'Africa/Cairo',
'Africa/Tripoli',
'Africa/Tunis',
'Africa/Algiers',
'Africa/Casablanca',
'Africa/Khartoum'
]
}
let activeStore = null
const elementLocales = {
en: enElement,
zh: zhElement
}
const messageContext = require.context('./messages', false, /\.js$/)
const messages = messageContext.keys().reduce((result, filePath) => {
const locale = filePath.replace('./', '').replace(/\.js$/, '')
const localeModule = messageContext(filePath)
result[locale] = localeModule.default || localeModule
return result
}, {})
const localeOptions = Object.keys(messages)
.sort((left, right) => {
if (left === DEFAULT_LOCALE) {
return -1
}
if (right === DEFAULT_LOCALE) {
return 1
}
return left.localeCompare(right)
})
.map(locale => {
const meta = messages[locale] && messages[locale].meta ? messages[locale].meta : {}
const fallbackLabel = locale.toUpperCase()
return {
value: locale,
label: meta.label || fallbackLabel,
nativeLabel: meta.nativeLabel || meta.label || fallbackLabel
}
})
function normalizeLocale(locale) {
return Object.prototype.hasOwnProperty.call(messages, locale) ? locale : DEFAULT_LOCALE
}
export function getLocaleOptions() {
return localeOptions.slice()
}
export function getCurrentTimeZone() {
try {
return new Intl.DateTimeFormat().resolvedOptions().timeZone || ''
} catch (error) {
return ''
}
}
export function detectLocaleByTimeZone(timeZone = getCurrentTimeZone()) {
if (!timeZone) {
return DEFAULT_LOCALE
}
const matchedLocale = Object.keys(TIMEZONE_LOCALE_MAP).find(locale => {
if (!Object.prototype.hasOwnProperty.call(messages, locale)) {
return false
}
return TIMEZONE_LOCALE_MAP[locale].some(pattern => {
return timeZone === pattern || timeZone.indexOf(`${pattern}/`) === 0
})
})
return normalizeLocale(matchedLocale || DEFAULT_LOCALE)
}
function getMessage(locale, key) {
if (!key || !messages[locale]) {
return undefined
}
const localeMessages = messages[locale]
if (Object.prototype.hasOwnProperty.call(localeMessages, key)) {
return localeMessages[key]
}
return key.split('.').reduce((result, current) => {
if (result == null) {
return undefined
}
return result[current]
}, localeMessages)
}
export function getLocale() {
if (!activeStore || !activeStore.state || !activeStore.state.app) {
return DEFAULT_LOCALE
}
return normalizeLocale(activeStore.state.app.language)
}
export function translate(key, params) {
if (typeof key !== 'string' || key.length === 0) {
return key
}
const locale = getLocale()
const localeText = getMessage(locale, key)
let text = localeText
if (text == null) {
if (locale === 'zh') {
text = key
} else {
text = getMessage(DEFAULT_LOCALE, key)
}
}
if (text == null) {
text = key
}
if (!params || typeof text !== 'string') {
return text
}
return Object.keys(params).reduce((result, name) => {
return result.replace(new RegExp(`\\{${name}\\}`, 'g'), params[name])
}, text)
}
export function translateRouteTitle(title) {
return translate(title)
}
function bindField(target, field) {
if (!target || !Object.prototype.hasOwnProperty.call(target, field)) {
return
}
const descriptor = Object.getOwnPropertyDescriptor(target, field)
if (!descriptor || descriptor.get || typeof descriptor.value !== 'string') {
return
}
const cacheKey = `__i18n_${field}`
Object.defineProperty(target, cacheKey, {
value: descriptor.value,
enumerable: false,
configurable: true,
writable: true
})
Object.defineProperty(target, field, {
enumerable: true,
configurable: true,
get() {
return translate(this[cacheKey])
},
set(value) {
this[cacheKey] = value
}
})
}
export function localizeCollection(collection, fields = MESSAGE_FIELDS, weakSet = new WeakSet()) {
if (!collection || typeof collection !== 'object' || weakSet.has(collection)) {
return collection
}
weakSet.add(collection)
if (Array.isArray(collection)) {
collection.forEach(item => localizeCollection(item, fields, weakSet))
return collection
}
fields.forEach(field => bindField(collection, field))
Object.keys(collection).forEach(key => {
localizeCollection(collection[key], fields, weakSet)
})
return collection
}
export function setLocale(locale) {
const nextLocale = normalizeLocale(locale)
if (activeStore && activeStore.state && activeStore.state.app && activeStore.state.app.language !== nextLocale) {
activeStore.dispatch('app/setLanguage', nextLocale)
}
elementLocale.use(elementLocales[nextLocale] || elementLocales[DEFAULT_LOCALE])
return nextLocale
}
export function attachStore(store) {
activeStore = store
}
const i18nPlugin = {
install(Vue) {
Vue.prototype.$t = function(key, params) {
return translate(key, params)
}
Vue.prototype.$setLocale = function(locale) {
return setLocale(locale)
}
Object.defineProperty(Vue.prototype, '$locale', {
get() {
return getLocale()
}
})
}
}
export default i18nPlugin

View File

@ -0,0 +1,48 @@
import menuMessages from './menu-en'
export default {
meta: {
label: 'English',
nativeLabel: 'English'
},
nav: {
home: 'Home'
},
common: {
language: 'Language',
search: 'Search',
globalSize: 'Global Size',
resetPassword: 'Reset Password',
logout: 'Logout',
cancel: 'Cancel',
submit: 'Submit'
},
auth: {
login: 'Login',
username: 'Username',
password: 'Password',
invalidUsername: 'Please enter the correct user name',
invalidPasswordLength: 'The password can not be less than 4 digits'
},
password: {
title: 'Reset Password',
old: 'Original Password',
new: 'New Password',
confirm: 'Confirm Password',
placeholderOld: 'Please enter the original password',
placeholderNew: 'Please enter the new password',
placeholderConfirm: 'Please confirm the password',
required: 'Required field cannot be empty',
invalidLength: 'The password can not be less than 4 digits',
mismatch: 'Passwords do not match',
processing: 'Processing submission, please wait'
},
size: {
default: 'Default',
medium: 'Medium',
small: 'Small',
mini: 'Mini',
switchSuccess: 'Switch Size Success'
},
...menuMessages
}

View File

@ -0,0 +1,192 @@
export default {
'系统管理': 'System Management',
'用户管理': 'User Management',
'角色管理': 'Role Management',
'菜单管理': 'Menu Management',
'资源管理': 'Resource Management',
'App系统': 'App System',
'版本管理': 'Version Management',
'APP运行日志': 'App Runtime Logs',
'IM通讯': 'IM Communication',
'请求黑名单': 'Request Blacklist',
'工具管理': 'Tools Management',
'Redis缓存管理': 'Redis Cache Management',
'订单补偿': 'Order Compensation',
'金币分析': 'Gold Analysis',
'小工具': 'Utilities',
'文件上传': 'File Upload',
'Api请求日志': 'API Request Logs',
'金币流水分析': 'Gold Transaction Analysis',
'Socket测试': 'Socket Test',
'金币流水图表': 'Gold Transaction Charts',
'系统监控': 'System Monitor',
'用户列表': 'User List',
'用户专属礼物制作': 'Custom User Gift Creation',
'封禁设备列表': 'Blocked Device List',
'最新登记设备': 'Latest Registered Devices',
'喇叭黑名单': 'Trumpet Blacklist',
'用户充值黑名单': 'User Recharge Blacklist',
'运营管理': 'Operations Management',
'营收支出': 'Revenue and Expenses',
'消费商品订单': 'Consumable Product Orders',
'金币收支记录': 'Gold Transaction Records',
'钻石流水列表': 'Diamond Transaction List',
'积分收支记录': 'Points Transaction Records',
'金币代理流水': 'Gold Agent Transactions',
'钻石余额列表': 'Diamond Balance List',
'高产用户列表': 'High Output User List',
'Stripe订单': 'Stripe Orders',
'异常订单': 'Abnormal Orders',
'退款记录': 'Refund Records',
'游戏券收支记录': 'Game Coupon Transaction Records',
'收款单': 'Receipts',
'订单详情': 'Order Details',
'内部操作金币': 'Internal Gold Operations',
'数据配置': 'Data Configuration',
'参数配置': 'Parameter Settings',
'幸运礼物规则配置': 'Lucky Gift Rule Configuration',
'幸运礼物规格配置': 'Lucky Gift Specification Configuration',
'用户观看广告配置': 'User Ad View Configuration',
'礼物管理': 'Gift Management',
'IM账号管理': 'IM Account Management',
'Banner管理': 'Banner Management',
'房间主题管理': 'Room Theme Management',
'国家管理': 'Country Management',
'徽章配置管理': 'Badge Configuration Management',
'表情管理': 'Emoji Management',
'转盘抽奖品配置': 'Spin Wheel Prize Configuration',
'靓号管理': 'Special ID Management',
'邀请用户配置': 'Invite User Configuration',
'用户友谊卡配置': 'User Friendship Card Configuration',
'游戏列表': 'Game List',
'名人堂': 'Hall of Fame',
'CP小屋配置': 'CP Cabin Configuration',
'麦位类型管理': 'Mic Slot Type Management',
'活动图片配置': 'Activity Image Configuration',
'摩天轮任务配置': 'Ferris Wheel Task Configuration',
'运营日志': 'Operations Logs',
'礼物赠送记录': 'Gift Sending Records',
'用户道具流水': 'User Prop Transactions',
'红包发送记录': 'Red Packet Send Records',
'退款扣除目标': 'Refund Deduction Targets',
'房间支持活动': 'Room Support Activities',
'代理活动历史': 'Agent Activity History',
'现金邀请用户': 'Cash Invite Users',
'邀请配置': 'Invite Configuration',
'助力日志': 'Assistance Logs',
'临时提现': 'Temporary Withdrawals',
'红包邀新用户': 'Red Packet User Invitations',
'转盘配置': 'Carousel Configuration',
'邀新配置': 'Invite-New Configuration',
'用户目标': 'User Targets',
'开红包记录': 'Red Packet Opening Records',
'抽奖日志': 'Draw Logs',
'邀请用户': 'Invite Users',
'幸运礼物送礼记录': 'Lucky Gift Sending Records',
'注销日志': 'Logout Logs',
'注销申请': 'Logout Requests',
'活动领奖记录': 'Activity Reward Records',
'用户签到': 'User Check-ins',
'砸金蛋记录': 'Golden Egg Smash Records',
'登录日志': 'Login Logs',
'Ludo飞行棋': 'Ludo',
'PK记录': 'PK Records',
'CP申请日志': 'CP Application Logs',
'世界杯': 'World Cup',
'购买麦位记录': 'Mic Slot Purchase Records',
'用户密码日志': 'User Password Logs',
'内购配置': 'In-App Purchase Configuration',
'渠道管理': 'Channel Management',
'厂商管理': 'Provider Management',
'区域关联支付国家': 'Region-Payment Country Relations',
'开通支付国家': 'Enabled Payment Countries',
'应用管理': 'Application Management',
'Google/Apple-V2': 'Google/Apple V2',
'活动配置': 'Activity Configuration',
'活动管理': 'Activity Management',
'活动模版': 'Activity Templates',
'活动规则配置': 'Activity Rule Configuration',
'KTV音乐': 'KTV Music',
'音乐管理': 'Music Management',
'礼物配置': 'Gift Configuration',
'热门歌曲': 'Popular Songs',
'点歌记录': 'Song Request Records',
'消息推送': 'Message Push',
'数据大屏': 'Data Dashboard',
'APP管理员': 'App Administrators',
'系统公告管理': 'System Notice Management',
'金币代理': 'Gold Agents',
'申请靓号': 'Apply for Special ID',
'App启动页': 'App Launch Page',
'客服列表': 'Customer Service List',
'金币余额榜': 'Gold Balance Leaderboard',
'区域配置': 'Region Configuration',
'用户银行': 'User Bank',
'目标兑换工资记录': 'Target Salary Exchange Records',
'用户银行流水': 'User Bank Transactions',
'用户银行账户': 'User Bank Accounts',
'美金兑换金币记录': 'USD-to-Gold Exchange Records',
'现金提现申请': 'Cash Withdrawal Applications',
'自动支付工资凭据': 'Automatic Salary Payment Vouchers',
'用户工资钻石': 'User Salary Diamonds',
'用户工资钻石账户': 'User Salary Diamond Accounts',
'用户工资钻石流水': 'User Salary Diamond Transactions',
'钻石提现申请': 'Diamond Withdrawal Applications',
'钻石兑换金币记录': 'Diamond-to-Gold Exchange Records',
'语音房间': 'Voice Rooms',
'在线房间': 'Online Rooms',
'房间贡献余额': 'Room Contribution Balance',
'房间资料列表': 'Room Profile List',
'置顶房间': 'Pinned Rooms',
'热门房间': 'Popular Rooms',
'房间黑名单': 'Room Blacklist',
'家族管理': 'Family Management',
'家族配置': 'Family Configuration',
'家族列表': 'Family List',
'审批管理': 'Approval Management',
'用户头像': 'User Avatar',
'用户照片墙': 'User Photo Wall',
'用户昵称': 'User Nickname',
'举报信息': 'Reports',
'意见反馈': 'Feedback',
'用户个性签名': 'User Profile Signatures',
'房间封面': 'Room Covers',
'房间通知': 'Room Notices',
'房间名称': 'Room Names',
'违规历史记录': 'Violation History',
'CP告白审核': 'CP Confession Review',
'游戏白名单': 'Game Whitelist',
'KYC身份审核': 'KYC Identity Review',
'房间主题审批': 'Room Theme Approval',
'家族资料审批': 'Family Profile Review',
'动态内容审核': 'Post Content Review',
'动态举报审批': 'Post Report Review',
'喇叭审批': 'Trumpet Review',
'用户银行卡审批': 'User Bank Card Review',
'道具管理': 'Prop Management',
'资源配置': 'Resource Configuration',
'资源组配置': 'Resource Group Configuration',
'道具赠送': 'Prop Gift',
'道具商店': 'Prop Store',
'宠物管理': 'Pet Management',
'宠物池': 'Pet Pool',
'豆子余额列表': 'Bean Balance List',
'豆子流水': 'Bean Transactions',
'宠物喂养记录': 'Pet Feeding Records',
'动态管理': 'Dynamic Management',
'标签': 'Tags',
'动态配置': 'Post Configuration',
'用户动态': 'User Posts',
'动态黑名单': 'Post Blacklist',
'主播中心': 'Broadcaster Center',
'代理钻石政策': 'Agent Diamond Policy',
'BD Lead': 'BD Lead',
'代理政策': 'Agent Policy',
'BD工作统计': 'BD Work Statistics',
'BD列表': 'BD List',
'代理列表': 'Agent List',
'主播成员': 'Broadcaster Members',
'账单列表': 'Bill List',
'主播工作': 'Broadcaster Work',
'成员审核日志': 'Member Review Logs'
}

View File

@ -0,0 +1,45 @@
export default {
meta: {
label: 'Chinese',
nativeLabel: '\u4e2d\u6587'
},
nav: {
home: '\u9996\u9875'
},
common: {
language: '\u8bed\u8a00',
search: '\u641c\u7d22',
globalSize: '\u5168\u5c40\u5c3a\u5bf8',
resetPassword: '\u4fee\u6539\u5bc6\u7801',
logout: '\u9000\u51fa\u767b\u5f55',
cancel: '\u53d6\u6d88',
submit: '\u63d0\u4ea4'
},
auth: {
login: '\u767b\u5f55',
username: '\u7528\u6237\u540d',
password: '\u5bc6\u7801',
invalidUsername: '\u8bf7\u8f93\u5165\u6b63\u786e\u7684\u7528\u6237\u540d',
invalidPasswordLength: '\u5bc6\u7801\u4e0d\u80fd\u5c11\u4e8e4\u4f4d'
},
password: {
title: '\u91cd\u7f6e\u5bc6\u7801',
old: '\u539f\u59cb\u5bc6\u7801',
new: '\u65b0\u5bc6\u7801',
confirm: '\u786e\u8ba4\u5bc6\u7801',
placeholderOld: '\u8bf7\u8f93\u5165\u539f\u59cb\u5bc6\u7801',
placeholderNew: '\u8bf7\u8f93\u5165\u65b0\u5bc6\u7801',
placeholderConfirm: '\u8bf7\u8f93\u5165\u786e\u8ba4\u5bc6\u7801',
required: '\u5fc5\u586b\u5b57\u6bb5\u4e0d\u80fd\u4e3a\u7a7a',
invalidLength: '\u5bc6\u7801\u4e0d\u80fd\u5c11\u4e8e4\u4f4d',
mismatch: '\u4e24\u6b21\u5bc6\u7801\u8f93\u5165\u4e0d\u4e00\u81f4',
processing: '\u6b63\u5728\u5904\u7406\u63d0\u4ea4\uff0c\u8bf7\u7a0d\u5019'
},
size: {
default: '\u9ed8\u8ba4',
medium: '\u4e2d\u7b49',
small: '\u5c0f',
mini: '\u8ff7\u4f60',
switchSuccess: '\u5207\u6362\u5c3a\u5bf8\u6210\u529f'
}
}

306
src/lang/messages/en.js Normal file
View File

@ -0,0 +1,306 @@
import semantic from './base/en'
import pageMessages from './pages/en'
export default {
...semantic,
...pageMessages,
'首页': 'Home',
'用户详情': 'User Details',
'团队管理': 'Team Management',
'团队政策': 'Team Policy',
'团队钻石政策': 'Team Diamond Policy',
'团队列表': 'Team List',
'成员列表': 'Members',
'账单列表': 'Bills',
'成员工作': 'Member Work',
'成员审核日志': 'Member Review Logs',
'动态管理': 'Dynamic Management',
'标签列表': 'Tag List',
'权重配置': 'Weight Configuration',
'用户动态': 'User Posts',
'黑名单': 'Blacklist',
'家族管理': 'Family Management',
'家族配置': 'Family Settings',
'家族列表': 'Family List',
'宠物管理': 'Pet Management',
'宠物喂养记录': 'Pet Feeding Records',
'豆子账户': 'Bean Account',
'豆子流水': 'Bean Transactions',
'宠物池': 'Pet Pool',
'道具管理': 'Props Management',
'道具赠送': 'Props Gift',
'资源配置': 'Resource Configuration',
'道具资源组配置': 'Prop Resource Group Settings',
'活动道具规则配置': 'Activity Prop Rule Settings',
'道具商店': 'Prop Store',
'工具管理': 'Tools',
'请求黑名单': 'Request Blacklist',
'金币分析': 'Gold Analysis',
'Redis缓存管理': 'Redis Cache Management',
'文件上传': 'File Upload',
'Socket测试': 'Socket Test',
'App系统管理': 'App System Management',
'登陆日志': 'Login Logs',
'房间黑名单': 'Room Blacklist',
'请求日志': 'Request Logs',
'靓号管理': 'Special ID Management',
'文档管理': 'Document Management',
'IM通讯': 'IM Communication',
'版本管理': 'Version Management',
'参数配置管理': 'Parameter Management',
'参数配置': 'Parameter Settings',
'内购产品配置V2': 'In-app Product Configuration V2',
'封禁设备': 'Blocked Devices',
'表情管理': 'Emoji Management',
'系统管理': 'System Management',
'区域配置': 'Region Settings',
'用户管理': 'User Management',
'角色管理': 'Role Management',
'菜单管理': 'Menu Management',
'资源管理': 'Resource Management',
'统计管理': 'Statistics',
'数据大屏': 'Data Dashboard',
'运营管理': 'Operations',
'代理活动': 'Agent Activities',
'金币余额': 'Gold Balance',
'自动发放工资凭据': 'Automatic Payroll Vouchers',
'用户银行卡提现现金申请': 'User Bank Card Cash Withdrawal Requests',
'用户银行卡提现钻石申请': 'User Bank Card Diamond Withdrawal Requests',
'用户银行金币兑换申请': 'User Bank Gold Exchange Requests',
'用户银行卡钻石兑换金币申请': 'User Bank Card Diamond-to-Gold Requests',
'用户银行账户': 'User Bank Accounts',
'用户银行流水': 'User Bank Transactions',
'客服列表': 'Customer Service List',
'CP申请': 'CP Applications',
'PK记录': 'PK Records',
'邀请用户日志': 'Invite User Logs',
'活动配置': 'Activity Configuration',
'活动模版': 'Activity Templates',
'用户友谊卡配置': 'User Friendship Card Settings',
'邀请用户配置': 'Invite User Settings',
'用户观看广告配置': 'User Ad View Settings',
'Ludo飞行棋': 'Ludo',
'置顶房间': 'Pinned Rooms',
'热门房间': 'Popular Rooms',
'退款扣除目标': 'Refund Deduction Targets',
'开通支付国家': 'Enabled Payment Countries',
'支付区域关系': 'Payment Region Relations',
'应用管理': 'Application Management',
'支付厂商': 'Payment Providers',
'支付渠道': 'Payment Channels',
'抽奖品配置': 'Lottery Prize Settings',
'货运代理': 'Freight Agent',
'BD列表': 'BD List',
'邀请用户奖励记录': 'Invite User Reward Records',
'IM账号管理': 'IM Account Management',
'用户签到': 'User Check-ins',
'用户列表': 'User List',
'用户最新设备列表': 'Latest User Devices',
'APP管理员': 'App Administrators',
'订单补偿': 'Order Compensation',
'小工具': 'Utilities',
'异常订单': 'Abnormal Orders',
'货运代理流水': 'Freight Agent Transactions',
'高产用户列表': 'High Output Users',
'金币收支记录': 'Gold Transaction Records',
'内部操作金币': 'Internal Gold Operations',
'游戏券收支记录': 'Game Coupon Transaction Records',
'积分收支记录': 'Points Transaction Records',
'订单详情': 'Order Details',
'退款记录': 'Refund Records',
'礼物管理': 'Gift Management',
'抽奖概率配置': 'Lottery Probability Settings',
'徽章配置管理': 'Badge Configuration Management',
'靓号池管理': 'Special ID Pool Management',
'礼物赠送记录': 'Gift Giving Records',
'房间贡献余额': 'Room Contribution Balance',
'消息推送': 'Message Push',
'在线房间': 'Online Rooms',
'房间资料信息': 'Room Profile Information',
'系统banner管理': 'System Banner Management',
'麦位类型管理': 'Mic Slot Type Management',
'游戏列表': 'Game List',
'App启动页': 'App Launch Page',
'系统公告管理': 'System Notice Management',
'国家管理': 'Country Management',
'用户道具流水': 'User Prop Transactions',
'用户钻石余额列表': 'User Diamond Balances',
'用户钻石流水列表': 'User Diamond Transactions',
'购买麦位类型流水': 'Mic Slot Purchase Transactions',
'红包发送列表': 'Red Packet Sends',
'用户活动领奖记录': 'User Activity Reward Records',
'幸运礼物规则配置': 'Lucky Gift Rule Settings',
'幸运礼物规格配置': 'Lucky Gift Specification Settings',
'幸运礼物送礼记录': 'Lucky Gift Sending Records',
'LuckyBox抽奖记录': 'LuckyBox Draw Records',
'摩天轮游戏每日数据': 'Ferris Wheel Daily Data',
'摩天轮任务配置': 'Ferris Wheel Task Settings',
'砸金蛋记录': 'Golden Egg Smash Records',
'炸金花': 'Teen Patti',
'用户申请靓号记录': 'User Special ID Applications',
'自定义名人堂': 'Custom Hall of Fame',
'喇叭列表': 'Trumpet List',
'房间支持活动': 'Room Support Activities',
'审批管理': 'Approval Management',
'头像审核': 'Avatar Approval',
'昵称审核': 'Nickname Approval',
'照片墙审核': 'Photo Wall Approval',
'房间封面审批': 'Room Cover Approval',
'用户个性签名审批': 'User Profile Signature Approval',
'房间通知审批': 'Room Notice Approval',
'房间名称审批': 'Room Name Approval',
'房间主题审批': 'Room Theme Approval',
'举报管理': 'Report Management',
'意见反馈': 'Feedback',
'违规历史记录': 'Violation History',
'家族头像审批': 'Family Avatar Approval',
'动态内容审批': 'Post Content Approval',
'动态举报审批': 'Post Report Approval',
'用户银行卡审批': 'User Bank Card Approval',
'语言': 'Language',
English: 'English',
'中文': 'Chinese',
'切换主题': 'Change Theme',
'选择主题': 'Select Theme',
'正常模式': 'Light Mode',
'暗黑模式': 'Dark Mode',
'自动切换': 'Auto',
'07:00~18:00 正常模式19:00~06:00 进入暗黑模式': '07:00~18:00 uses light mode, 19:00~06:00 uses dark mode',
'修改密码': 'Reset Password',
'退出登录': 'Logout',
Search: 'Search',
'Global Size': 'Global Size',
'默认': 'Default',
'中等': 'Medium',
'小': 'Small',
'迷你': 'Mini',
'切换尺寸成功': 'Switch Size Success',
'重置密码': 'Reset Password',
'原始密码': 'Original Password',
'新密码': 'New Password',
'确认密码': 'Confirm Password',
'请输入原始密码': 'Please enter the original password',
'请输入新密码': 'Please enter the new password',
'请输入确认密码': 'Please confirm the password',
'必填字段不能为空': 'Required field cannot be empty',
'密码不能少于4位': 'The password can not be less than 4 digits',
'两次密码输入不一致': 'Passwords do not match',
'取消': 'Cancel',
'提交': 'Submit',
'正在处理提交,请稍候': 'Processing submission, please wait',
Successful: 'Successful',
Failed: 'Failed',
Unknown: 'Unknown',
Login: 'Login',
Username: 'Username',
Password: 'Password',
'Please enter the correct user name': 'Please enter the correct user name',
'The password can not be less than 4 digits': 'The password can not be less than 4 digits',
'最近2小时': 'Last 2 Hours',
'最近6小时': 'Last 6 Hours',
'最近12小时': 'Last 12 Hours',
'最近一天': 'Last 1 Day',
'最近一周': 'Last 1 Week',
'最近一个月': 'Last 1 Month',
'最近三个月': 'Last 3 Months',
'正常': 'Normal',
'关闭': 'Closed',
'等待确认': 'Pending Confirmation',
'ID变更': 'ID Changed',
'房主': 'Room Owner',
'管理员': 'Admin',
'成员': 'Member',
'代理': 'Agent',
'收入': 'Income',
'支出': 'Expense',
'未处理': 'Pending',
'已处理': 'Processed',
'已发布': 'Published',
'未发布': 'Unpublished',
'奖励': 'Reward',
'内部': 'Internal',
'工资': 'Salary',
'充值': 'Recharge',
'其他': 'Other',
'违规': 'Violation',
'多发': 'Overpayment',
'已领取': 'Claimed',
'未领取': 'Unclaimed',
'推送': 'Push',
'无': 'None',
'会员': 'Member',
'非会员': 'Non-member',
'男': 'Male',
'女': 'Female',
'通过': 'Approved',
'不通过': 'Rejected',
'等待审核': 'Pending Review',
'图文': 'Image and Text',
'视频': 'Video',
'收藏': 'Favorited',
'未收藏': 'Not Favorited',
'聊天消息': 'Chat Message',
'系统招呼': 'System Greeting',
'全部': 'All',
'免费池': 'Free Pool',
'女生池': 'Girls Pool',
'女神池': 'Goddess Pool',
'优质女性': 'Premium Female',
'主播': 'Host',
'普通女性': 'Regular Female',
'主播与优质女性': 'Host and Premium Female',
'男性会员': 'Male Member',
'购买过糖果的男性': 'Male User Who Purchased Candy',
'普通男性': 'Regular Male',
'会员与购买过糖果男性': 'Member and Candy Buyer',
'提现': 'Withdraw',
'转账': 'Transfer',
'转账金币': 'Transfer Gold',
'接收转账': 'Receive Transfer',
'兑换金币': 'Exchange Gold',
'系统补偿': 'System Compensation',
'系统扣除': 'System Deduction',
'创建银行账户': 'Create Bank Account',
'手机号码': 'Mobile Number',
'谷歌': 'Google',
'礼物': 'Gift',
'道具': 'Props',
'钻石': 'Diamond',
'徽章': 'Badge',
'用户-成就徽章': 'User - Achievement Badge',
'用户-管理员': 'User - Administrator',
'用户-活动徽章': 'User - Activity Badge',
'荣誉-管理员': 'Honor - Administrator',
'荣誉-活动': 'Honor - Activity',
'黑铁': 'Black Iron',
'青铜': 'Bronze',
'白银': 'Silver',
'黄金': 'Gold',
'铂金': 'Platinum',
'族长': 'Patriarch',
'电子钱包': 'E-wallet',
'银行卡': 'Bank Card',
'银行卡转账': 'Bank Card Transfer',
'线下非银网点': 'Offline Non-bank Outlets',
'运营商计费': 'Carrier Billing',
'网上银行': 'Online Banking',
'快捷支付': 'Quick Payment',
'延迟支付': 'Delayed Payment',
'无密支付': 'Password-free Payment',
'便利店': 'Convenience Store',
'点卡支付': 'Point Card Payment',
'超商条码': 'Supermarket Barcode',
'行动/电话小额付': 'Mobile / Phone Payment',
'实体ATM': 'Physical ATM',
'ATM线上': 'ATM Online',
'ATM线下': 'ATM Offline',
'银行虚拟账号': 'Bank Virtual Account',
'银行柜台': 'Bank Counter',
'预付费卡': 'Prepaid Card',
'西联汇款': 'Western Union',
'转账汇款': 'Transfer',
'刚刚': 'Just now',
'分钟前': ' minutes ago',
'小时前': ' hours ago',
'1天前': '1 day ago'
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

45
src/lang/messages/zh.js Normal file
View File

@ -0,0 +1,45 @@
import semantic from './base/zh'
import pageMessages from './pages/zh'
export default {
...semantic,
...pageMessages,
Language: '语言',
English: 'English',
Chinese: '中文',
Search: '搜索',
'Global Size': '全局尺寸',
Default: '默认',
Medium: '中等',
Small: '小',
Mini: '迷你',
'Switch Size Success': '切换尺寸成功',
Login: '登录',
Username: '用户名',
Password: '密码',
'Please enter the correct user name': '请输入正确的用户名',
'The password can not be less than 4 digits': '密码不能少于4位',
'Change Theme': '切换主题',
'Select Theme': '选择主题',
'Light Mode': '正常模式',
'Dark Mode': '暗黑模式',
Auto: '自动切换',
'07:00~18:00 uses light mode, 19:00~06:00 uses dark mode': '07:00~18:00 正常模式19:00~06:00 进入暗黑模式',
'Reset Password': '重置密码',
'Original Password': '原始密码',
'New Password': '新密码',
'Confirm Password': '确认密码',
'Please enter the original password': '请输入原始密码',
'Please enter the new password': '请输入新密码',
'Please confirm the password': '请输入确认密码',
'Required field cannot be empty': '必填字段不能为空',
'Passwords do not match': '两次密码输入不一致',
Cancel: '取消',
Submit: '提交',
Logout: '退出登录',
'Processing submission, please wait': '正在处理提交,请稍候',
Successful: '成功',
Failed: '失败',
Unknown: '未知',
Home: '首页'
}

View File

@ -1,4 +1,4 @@
<template>
<template>
<div class="navbar">
<hamburger :is-active="sidebar.opened" class="hamburger-container" @toggleClick="toggleSideBar" />
@ -10,9 +10,13 @@
<screenfull id="screenfull" class="right-menu-item hover-effect" />
<el-tooltip content="Global Size" effect="dark" placement="bottom">
<el-tooltip :content="$t('common.globalSize')" effect="dark" placement="bottom">
<size-select id="size-select" class="right-menu-item hover-effect" />
</el-tooltip>
<div class="right-menu-item hover-effect language-entry">
<language-switch />
</div>
</template>
<el-dropdown class="avatar-container" trigger="click">
@ -21,8 +25,17 @@
<i class="el-icon-caret-bottom" />
</div>
<el-dropdown-menu slot="dropdown" class="user-dropdown">
<el-dropdown-item @click.native="resetPwd">修改密码</el-dropdown-item>
<el-dropdown-item @click.native="logout">退出登录</el-dropdown-item>
<el-dropdown-item disabled>{{ $t('common.language') }}</el-dropdown-item>
<el-dropdown-item
v-for="item in languageOptions"
:key="item.value"
:disabled="$locale === item.value"
@click.native="changeLanguage(item.value)"
>
{{ item.nativeLabel || item.label }}
</el-dropdown-item>
<el-dropdown-item divided @click.native="resetPwd">{{ $t('common.resetPassword') }}</el-dropdown-item>
<el-dropdown-item @click.native="logout">{{ $t('common.logout') }}</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</div>
@ -39,6 +52,8 @@ import RestPassword from '@/components/data/RestPassword'
import Screenfull from '@/components/Screenfull'
import SizeSelect from '@/components/SizeSelect'
import Search from '@/components/HeaderSearch'
import LanguageSwitch from '@/components/LanguageSwitch'
import { getLocaleOptions } from '@/lang'
import { extractColorByText } from '@/utils'
export default {
@ -49,7 +64,8 @@ export default {
RestPassword,
Screenfull,
SizeSelect,
Search
Search,
LanguageSwitch
},
data() {
return {
@ -64,6 +80,9 @@ export default {
'dashboard',
'name'
]),
languageOptions() {
return getLocaleOptions()
},
userAvatarColor() {
return 'background-color:' + extractColorByText(this.name) + ';'
},
@ -78,6 +97,9 @@ export default {
resetPwd() {
this.resetPasswordVisible = true
},
changeLanguage(locale) {
this.$setLocale(locale)
},
async logout() {
this.$store.dispatch('user/resetToken').then(() => {
location.reload()
@ -142,6 +164,10 @@ export default {
}
}
.language-entry {
font-size: 14px;
}
.avatar-container {
margin-right: 30px;

View File

@ -1,4 +1,6 @@
<script>
import { translateRouteTitle } from '@/lang'
export default {
name: 'MenuItem',
functional: true,
@ -15,13 +17,14 @@ export default {
render(h, context) {
const { icon, title } = context.props
const vnodes = []
const translatedTitle = title ? translateRouteTitle(title) : ''
if (icon) {
vnodes.push(<svg-icon icon-class={icon}/>)
vnodes.push(<svg-icon class='menu-icon' icon-class={icon}/>)
}
if (title) {
vnodes.push(<span slot='title'>{(title)}</span>)
if (translatedTitle) {
vnodes.push(<span class='menu-title' slot='title' attrs={{ title: translatedTitle }}>{translatedTitle}</span>)
}
return vnodes
}

View File

@ -2,17 +2,17 @@ import Vue from 'vue'
import Cookies from 'js-cookie'
import 'normalize.css/normalize.css' // A modern alternative to CSS resets
import ElementUI from 'element-ui'
import ElementUI, { TableColumn } from 'element-ui'
import '@/styles/theme/dark/index.css'
import 'element-ui/lib/theme-chalk/index.css'
import 'element-ui/lib/theme-chalk/display.css'
import locale from 'element-ui/lib/locale/lang/zh-CN' // lang i18n
import '@/styles/index.scss' // global css
import App from './App'
import store from './store'
import router from './router'
import i18nPlugin, { attachStore, setLocale } from './lang'
import '@/directive'
import '@/components-register'
import '@/permission' // permission control
@ -39,8 +39,31 @@ if (isMock()) {
mockXHR()
}
// set ElementUI lang to zh-CN
Vue.use(ElementUI, { locale, size: Cookies.get('size') || 'medium' })
// initialize ElementUI and runtime locale
const DEFAULT_TABLE_COLUMN_MIN_WIDTH = 120
if (TableColumn && TableColumn.methods && !TableColumn.methods.__defaultMinWidthPatched__) {
const originalSetColumnWidth = TableColumn.methods.setColumnWidth
TableColumn.methods.setColumnWidth = function(column) {
const nextColumn = originalSetColumnWidth.call(this, column)
const hasExplicitWidth = this.width !== undefined && this.width !== null && this.width !== ''
const hasExplicitMinWidth = this.minWidth !== undefined && this.minWidth !== null && this.minWidth !== ''
const isSpecialType = ['selection', 'index', 'expand'].includes(nextColumn.type)
if (!hasExplicitWidth && !hasExplicitMinWidth && !isSpecialType && nextColumn.minWidth < DEFAULT_TABLE_COLUMN_MIN_WIDTH) {
nextColumn.minWidth = DEFAULT_TABLE_COLUMN_MIN_WIDTH
nextColumn.realWidth = nextColumn.width === undefined ? nextColumn.minWidth : nextColumn.width
}
return nextColumn
}
TableColumn.methods.__defaultMinWidthPatched__ = true
}
Vue.use(ElementUI, { size: Cookies.get('size') || 'medium' })
Vue.use(i18nPlugin)
attachStore(store)
setLocale(store.getters.language || 'en')
// 如果想要中文版 element-ui按如下方式声明
// Vue.use(ElementUI)

View File

@ -1,4 +1,4 @@
import router from './router'
import router from './router'
import store from './store'
import { Message } from 'element-ui'
import NProgress from 'nprogress' // progress bar
@ -6,6 +6,7 @@ import 'nprogress/nprogress.css' // progress bar style
import { getToken } from '@/utils/auth' // get token from cookie
import { checkSwitchTheme } from '@/utils/theme'
import getPageTitle from '@/utils/get-page-title'
import { translate, translateRouteTitle } from '@/lang'
NProgress.configure({ showSpinner: false }) // NProgress Configuration
const whiteList = ['/login'] // no redirect whitelist
@ -15,7 +16,7 @@ router.beforeEach(async(to, from, next) => {
NProgress.start()
stackRouter.push()
// set page title
document.title = getPageTitle(to.meta.title)
document.title = getPageTitle(translateRouteTitle(to.meta && to.meta.title))
// determine whether the user has logged in
const hasToken = getToken()
@ -32,7 +33,7 @@ router.beforeEach(async(to, from, next) => {
store.dispatch('user/addNavigationRecentPreviews', {
id: to.name || 'none',
type: 'SYSTEM',
name: to.meta.title || '未知',
name: translateRouteTitle(to.meta && to.meta.title),
link: to.fullPath
})
}
@ -58,7 +59,7 @@ router.beforeEach(async(to, from, next) => {
} catch (error) {
// remove token and go to login page to re-login
await store.dispatch('user/resetToken')
Message.error(error || 'Has Error')
Message.error(translate(error || 'Failed'))
next(`/login?redirect=${to.path}`)
NProgress.done()
}
@ -82,3 +83,4 @@ router.afterEach(() => {
// finish progress bar
NProgress.done()
})

View File

@ -1,6 +1,7 @@
const getters = {
sidebar: state => state.app.sidebar,
size: state => state.app.size,
language: state => state.app.language,
device: state => state.app.device,
token: state => state.user.token,
avatar: state => state.user.avatar,

View File

@ -1,4 +1,7 @@
import Cookies from 'js-cookie'
import { detectLocaleByTimeZone } from '@/lang'
const LANGUAGE_STORAGE_KEY = 'language'
const state = {
sidebar: {
@ -7,6 +10,7 @@ const state = {
},
device: 'desktop',
size: Cookies.get('size') || 'medium',
language: Cookies.get(LANGUAGE_STORAGE_KEY) || detectLocaleByTimeZone(),
runTask: []
}
@ -32,6 +36,10 @@ const mutations = {
state.size = size
Cookies.set('size', size)
},
SET_LANGUAGE: (state, language) => {
state.language = language || 'en'
Cookies.set(LANGUAGE_STORAGE_KEY, state.language)
},
PUSH_RUN_TASK: (state, runTask) => {
if (!runTask) {
return
@ -74,6 +82,9 @@ const actions = {
setSize({ commit }, size) {
commit('SET_SIZE', size)
},
setLanguage({ commit }, language) {
commit('SET_LANGUAGE', language)
},
pushRunTask({ commit }, runTask) {
commit('PUSH_RUN_TASK', runTask)
},

View File

@ -67,6 +67,33 @@
color:#6E5138;
}
.el-table th.el-table__cell {
vertical-align: middle;
}
.el-table th.el-table__cell > .cell {
white-space: normal !important;
word-break: normal;
overflow-wrap: normal;
overflow: visible;
text-overflow: clip;
line-height: 1.35;
height: auto !important;
min-height: 100%;
display: flex;
align-items: center;
justify-content: flex-start;
box-sizing: border-box;
}
.el-table th.is-center > .cell {
justify-content: center;
}
.el-table th.is-right > .cell {
justify-content: flex-end;
}
.el-table th.el-table__cell.is-leaf, .el-table td.el-table__cell {
border-bottom: none !important;
}
@ -370,6 +397,50 @@
border-radius: 999px;
}
.el-form.i18n-form {
--i18n-form-label-width: 132px;
.el-form-item__label-wrap {
display: flex;
align-items: center;
}
.el-form-item__label {
width: var(--i18n-form-label-width) !important;
min-height: 36px;
white-space: normal;
word-break: normal;
overflow-wrap: break-word;
line-height: 1.35;
padding-right: 12px;
display: flex;
align-items: center;
justify-content: flex-end;
text-align: right;
}
.el-form-item__content {
margin-left: var(--i18n-form-label-width) !important;
min-height: 36px;
}
}
.el-form.i18n-form.i18n-form--wide {
--i18n-form-label-width: 150px;
}
.el-form.i18n-form.i18n-form--narrow {
--i18n-form-label-width: 116px;
}
@media (max-width: 768px) {
.el-form.i18n-form,
.el-form.i18n-form.i18n-form--wide,
.el-form.i18n-form.i18n-form--narrow {
--i18n-form-label-width: 116px;
}
}
.el-backtop {
background: linear-gradient(135deg, #FF9326 0%, #FEB219 100%);
color: #FFFFFF;

View File

@ -56,6 +56,7 @@
.svg-icon {
margin-right: 16px;
flex: 0 0 auto;
}
.el-menu {
@ -68,12 +69,38 @@
.el-menu-item,
.el-submenu__title {
position: relative;
display: flex;
align-items: center;
margin: 6px 12px;
width: calc(100% - 24px) !important;
min-height: 52px;
height: auto !important;
line-height: 1.4 !important;
padding-top: 10px;
padding-bottom: 10px;
border-radius: 14px;
transition: all .2s ease;
}
.el-submenu__title {
padding-right: 44px !important;
}
.el-submenu__icon-arrow {
right: 16px !important;
}
.menu-title {
display: block;
flex: 1 1 auto;
min-width: 0;
white-space: normal;
word-break: normal;
overflow-wrap: break-word;
hyphens: auto;
line-height: 1.35;
}
.el-menu-item:hover,
.el-submenu__title:hover {
transform: translateX(2px);
@ -146,6 +173,14 @@
margin: 0;
}
}
> .svg-icon,
> .menu-icon {
position: absolute;
left: 50%;
transform: translateX(-50%);
margin: 0 !important;
}
}
.el-submenu {
@ -156,6 +191,7 @@
display: flex !important;
align-items: center;
justify-content: center;
min-height: 52px;
.svg-icon {
margin: 0;
@ -164,6 +200,14 @@
.el-submenu__icon-arrow {
display: none;
}
> .svg-icon,
> .menu-icon {
position: absolute;
left: 50%;
transform: translateX(-50%);
margin: 0 !important;
}
}
}

View File

@ -9,7 +9,7 @@ $menuHover: #372316;
$subMenuBg: #1B120C;
$subMenuHover: #422A19;
$sideBarWidth: 210px;
$sideBarWidth: 246px;
// the :export directive is the magic sauce for webpack
// https://www.bluematador.com/blog/how-to-share-variables-between-js-and-sass

View File

@ -1,10 +1,11 @@
import defaultSettings from "@/settings";
import { translateRouteTitle } from '@/lang'
const title = defaultSettings.title || "Aslan";
export default function getPageTitle(pageTitle) {
if (pageTitle) {
return `${pageTitle} - ${title}`;
return `${translateRouteTitle(pageTitle)} - ${title}`;
}
return `${title}`;
}

View File

@ -5,6 +5,7 @@
* 2. 能快速统一的更管提示框样式
*/
import { Message } from 'element-ui'
import { translate } from '@/lang'
/**
* 提示消息.
@ -12,28 +13,28 @@ import { Message } from 'element-ui'
const opsMessage = {
success: (text) => {
Message({
message: text || 'Successful',
message: translate(text || 'Successful'),
type: 'success',
duration: 3 * 1000
})
},
fail: (text) => {
Message({
message: text || 'Failed',
message: translate(text || 'Failed'),
type: 'error',
duration: 3 * 1000
})
},
warn: (text) => {
Message({
message: text,
message: translate(text),
type: 'warning',
duration: 3 * 1000
})
},
info: (text) => {
Message({
message: text,
message: translate(text),
type: 'info',
duration: 3 * 1000
})

View File

@ -6,7 +6,7 @@
<!-- 系统 -->
<el-select
v-model="listQuery.sysOrigin"
placeholder="系统"
:placeholder="$t('pages.activityTemplateManage.filter.system')"
style="width: 120px"
class="filter-item"
@change="handleSearch"
@ -27,19 +27,19 @@
<!-- 状态 -->
<el-select
v-model="listQuery.showcase"
placeholder="状态"
:placeholder="$t('pages.activityTemplateManage.filter.status')"
style="width: 120px"
class="filter-item"
clearable
@change="handleSearch"
>
<el-option-group label="运行状态">
<el-option label="已开始" :value="1" />
<el-option label="已结束" :value="2" />
<el-option-group :label="$t('pages.activityTemplateManage.group.runtimeStatus')">
<el-option :label="$t('pages.activityTemplateManage.word.started')" :value="1" />
<el-option :label="$t('pages.activityTemplateManage.word.ended')" :value="2" />
</el-option-group>
<el-option-group label="管理状态">
<el-option label="上架" :value="3" />
<el-option label="下架" :value="4" />
<el-option-group :label="$t('pages.activityTemplateManage.group.managementStatus')">
<el-option :label="$t('pages.activityTemplateManage.word.onShelf')" :value="3" />
<el-option :label="$t('pages.activityTemplateManage.word.offShelf')" :value="4" />
</el-option-group>
</el-select>
@ -47,7 +47,7 @@
<el-input
v-model.trim="listQuery.id"
v-number
placeholder="活动ID"
:placeholder="$t('pages.activityTemplateManage.filter.activityId')"
style="width: 200px;"
class="filter-item"
/>
@ -56,7 +56,7 @@
<el-input
v-model.trim="listQuery.templateId"
v-number
placeholder="模版ID"
:placeholder="$t('pages.activityTemplateManage.filter.templateId')"
style="width: 200px;"
class="filter-item"
/>
@ -68,7 +68,7 @@
icon="el-icon-search"
@click="handleSearch"
>
搜索
{{ $t('pages.activityTemplateManage.action.search') }}
</el-button>
<!-- 新增按钮 -->
@ -78,42 +78,38 @@
icon="el-icon-edit"
@click="clickCreate"
>
新增
{{ $t('pages.activityTemplateManage.action.create') }}
</el-button>
<!-- 帮助按钮 -->
<el-popover title="操作手册" trigger="hover">
<el-popover :title="$t('pages.activityTemplateManage.help.title')" trigger="hover">
<div class="content">
<div class="help">
<strong>怎么创建一个活动?</strong>
<p>1. 活动模版 创建一个模版页面, 如果已创建可以直接使用</p>
<p>2.在当前 活动管理 创建一个活动并关联 活动模版</p>
<p>3.使用 复制活动链接 获得活动地址</p>
<p>4. Banner 配置活动地址</p>
<strong>活动排行榜计算规则?</strong>
<p>1.活动处于上架状态</p>
<strong>{{ $t('pages.activityTemplateManage.help.createTitle') }}</strong>
<p>{{ $t('pages.activityTemplateManage.help.createStep1') }}</p>
<p>{{ $t('pages.activityTemplateManage.help.createStep2') }}</p>
<p>{{ $t('pages.activityTemplateManage.help.createStep3') }}</p>
<p>{{ $t('pages.activityTemplateManage.help.createStep4') }}</p>
<strong>{{ $t('pages.activityTemplateManage.help.rankTitle') }}</strong>
<p>{{ $t('pages.activityTemplateManage.help.rankStep1') }}</p>
<p>
2.活动进行中: 系统当前时间 >= 开始时间 并且 系统当前时间 &lt;=
结束时间
{{ $t('pages.activityTemplateManage.help.rankStep2') }}
</p>
<p>3.活动中包含礼物</p>
<p>满足上面条件的情况下, 用户发送礼物触发</p>
<strong>注意事项</strong>
<p>{{ $t('pages.activityTemplateManage.help.rankStep3') }}</p>
<p>{{ $t('pages.activityTemplateManage.help.rankStep4') }}</p>
<strong>{{ $t('pages.activityTemplateManage.help.noticeTitle') }}</strong>
<p>
1.活动结束后不可用在次编辑, 如果需要重写激活请使用 复制活动
重写创建一个新的活动
{{ $t('pages.activityTemplateManage.help.noticeStep1') }}
</p>
<p>
2.排行榜只能在活动进行中可查看, 活动结束后通过系统 发送奖品
系统将缓存TOP20 通过 奖品发送记录 查看
{{ $t('pages.activityTemplateManage.help.noticeStep2') }}
</p>
<p>
3.一个 活动模版 如果被多个活动关联,
修改模版所有关联活动将全部会改变
{{ $t('pages.activityTemplateManage.help.noticeStep3') }}
</p>
</div>
</div>
<span slot="reference"><i class="el-icon-info" />帮助</span>
<span slot="reference"><i class="el-icon-info" />{{ $t('pages.activityTemplateManage.action.help') }}</span>
</el-popover>
</div>
@ -127,7 +123,7 @@
>
<!-- 关联模版 -->
<el-table-column
label="关联模版"
:label="$t('pages.activityTemplateManage.table.relatedTemplate')"
prop="templateName"
align="center"
min-width="200"
@ -135,7 +131,7 @@
<!-- 备注 -->
<el-table-column
label="备注"
:label="$t('pages.activityTemplateManage.table.remark')"
prop="config.remark"
align="center"
min-width="200"
@ -143,7 +139,7 @@
<!-- 状态 -->
<el-table-column
label="状态"
:label="$t('pages.activityTemplateManage.table.status')"
prop="config.startTime"
align="center"
min-width="200"
@ -152,55 +148,55 @@
<el-tag
:type="scope.row.config.showcase === true ? 'primary' : 'danger'"
>
{{ scope.row.config.showcase === true ? "上架" : "下架" }}
{{ scope.row.config.showcase === true ? $t("pages.activityTemplateManage.word.onShelf") : $t("pages.activityTemplateManage.word.offShelf") }}
</el-tag>
<el-tag v-if="scope.row.status === 0" type="info">未开始</el-tag>
<el-tag v-if="scope.row.status === 1" type="success">已开始</el-tag>
<el-tag v-if="scope.row.status === 2" type="warning">已结束</el-tag>
<el-tag v-if="scope.row.status === 0" type="info">{{ $t('pages.activityTemplateManage.word.notStarted') }}</el-tag>
<el-tag v-if="scope.row.status === 1" type="success">{{ $t('pages.activityTemplateManage.word.started') }}</el-tag>
<el-tag v-if="scope.row.status === 2" type="warning">{{ $t('pages.activityTemplateManage.word.ended') }}</el-tag>
</template>
</el-table-column>
<!-- 活动时间 -->
<el-table-column
label="活动时间"
:label="$t('pages.activityTemplateManage.table.activityTime')"
prop="config.startTime"
align="center"
min-width="200"
>
<template slot-scope="scope">
<div>开始: {{ scope.row.config.startTime | dateFormat }}</div>
<div>结束: {{ scope.row.config.endTime | dateFormat }}</div>
<div>{{ $t('pages.activityTemplateManage.label.start') }}: {{ scope.row.config.startTime | dateFormat }}</div>
<div>{{ $t('pages.activityTemplateManage.label.end') }}: {{ scope.row.config.endTime | dateFormat }}</div>
</template>
</el-table-column>
<!-- 修改/创建人 -->
<el-table-column
label="修改/创建人"
:label="$t('pages.activityTemplateManage.table.updatedCreatedBy')"
prop="config.createTime"
align="center"
min-width="200"
>
<template slot-scope="scope">
<div>修改: {{ scope.row.updateUserName }}</div>
<div>创建: {{ scope.row.createUserName }}</div>
<div>{{ $t('pages.activityTemplateManage.label.update') }}: {{ scope.row.updateUserName }}</div>
<div>{{ $t('pages.activityTemplateManage.label.create') }}: {{ scope.row.createUserName }}</div>
</template>
</el-table-column>
<!-- 修改/创建时间 -->
<el-table-column
label="修改/创建时间"
:label="$t('pages.activityTemplateManage.table.updatedCreatedTime')"
prop="config.createTime"
align="center"
min-width="200"
>
<template slot-scope="scope">
<div>修改: {{ scope.row.config.updateTime | dateFormat }}</div>
<div>创建: {{ scope.row.config.createTime | dateFormat }}</div>
<div>{{ $t('pages.activityTemplateManage.label.update') }}: {{ scope.row.config.updateTime | dateFormat }}</div>
<div>{{ $t('pages.activityTemplateManage.label.create') }}: {{ scope.row.config.createTime | dateFormat }}</div>
</template>
</el-table-column>
<!-- 操作栏 -->
<el-table-column fixed="right" label="操作" align="center" width="80">
<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">
@ -210,7 +206,7 @@
<!-- 未开始/已开始:编辑,已结束:查看 -->
<el-dropdown-item @click.native="clickEdit(scope.row)">
{{
scope.row.status !== 2 ? "编辑" : "查看"
scope.row.status !== 2 ? $t("pages.activityTemplateManage.action.edit") : $t("pages.activityTemplateManage.action.view")
}}</el-dropdown-item
>
@ -219,7 +215,7 @@
v-if="scope.row.status !== 0"
@click.native="clickRank(scope.row)"
>
排行榜
{{ $t('pages.activityTemplateManage.action.ranking') }}
</el-dropdown-item>
<!-- 未开始/已开始:编辑模版 -->
@ -227,12 +223,12 @@
v-if="scope.row.status !== 2"
@click.native="clickEditTemplate(scope.row)"
>
编辑模版
{{ $t('pages.activityTemplateManage.action.editTemplate') }}
</el-dropdown-item>
<!-- 复制活动 -->
<el-dropdown-item @click.native="clickCopyActivity(scope.row)">
复制活动
{{ $t('pages.activityTemplateManage.action.copyActivity') }}
</el-dropdown-item>
<!-- 已结束:发送奖品 -->
@ -242,8 +238,8 @@
>
{{
scope.row.config.awardStatus === true
? "奖品发送记录"
: "发送奖品"
? $t("pages.activityTemplateManage.action.rewardRecord")
: $t("pages.activityTemplateManage.action.sendReward")
}}
</el-dropdown-item>
@ -251,19 +247,19 @@
<el-dropdown-item
@click.native="copyContent(scope.row.config.id)"
>
复制活动ID
{{ $t('pages.activityTemplateManage.action.copyActivityId') }}
</el-dropdown-item>
<!-- 复制模版ID -->
<el-dropdown-item
@click.native="copyContent(scope.row.config.templateId)"
>
复制模版ID
{{ $t('pages.activityTemplateManage.action.copyTemplateId') }}
</el-dropdown-item>
<!-- 复制活动链接 -->
<el-dropdown-item @click.native="clickActivityLink(scope.row)">
复制活动链接
{{ $t('pages.activityTemplateManage.action.copyActivityLink') }}
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
@ -273,14 +269,14 @@
<!-- 加载模块 -->
<div v-if="listQuery.lastId" class="load-more">
<span v-if="notData">已加载全部</span>
<span v-if="notData">{{ $t('pages.activityTemplateManage.word.loadedAll') }}</span>
<el-button
v-else
size="mini"
:disabled="loadMoreLoading"
:loading="loadMoreLoading"
@click="clickLoadMore"
>加载更多</el-button
>{{ $t('pages.activityTemplateManage.action.loadMore') }}</el-button
>
</div>
@ -453,11 +449,11 @@ export default {
const that = this;
that
.$confirm(
"注意: 模版发生改变后相关引用都会同步发生变化, 是否继续?",
"提示",
that.$t("pages.activityTemplateManage.confirm.editTemplateMessage"),
that.$t("pages.activityTemplateManage.confirm.title"),
{
confirmButtonText: "确定",
cancelButtonText: "取消",
confirmButtonText: that.$t("pages.activityTemplateManage.action.confirm"),
cancelButtonText: that.$t("pages.activityTemplateManage.action.cancel"),
type: "warning"
}
)

View File

@ -22,23 +22,23 @@
<div v-for="(item,index) in form.prizes" :key="index" class="flex-l">
<img :src="item.imgs[0].src" width="40" height="40">
<div style="width: 40%;padding: 0px 5px;" class="font-info">
库存: {{ item.unlimited ? '不限' : item.inStock }}
{{ $t('pages.lotteryConfig.stock.stock') }}: {{ item.unlimited ? $t('pages.lotteryConfig.word.unlimited') : item.inStock }}
</div>
<div style="width: 40%;padding: 0px 5px;" class="font-danger">
消耗: {{ item.consumeInStock || '0' }}
{{ $t('pages.lotteryConfig.stock.consume') }}: {{ item.consumeInStock || '0' }}
</div>
</div>
</div>
</el-col>
<el-col class="lottery-conf" :md="6" :xs="24">
<el-form ref="form" class="form" :model="form" label-width="100px">
<el-form ref="form" class="form i18n-form i18n-form--wide" :model="form" label-width="140px">
<div class="card bottom-line">
<div class="title">属性</div>
<div class="title">{{ $t('pages.lotteryConfig.section.attributes') }}</div>
<div class="content">
<el-form-item label="归属系统">
<el-form-item :label="$t('pages.lotteryConfig.form.system')">
<el-select
v-model="listQuery.sysOrigin"
placeholder="系统"
:placeholder="$t('pages.lotteryConfig.placeholder.system')"
style="width: 100%"
class="filter-item"
@change="loadLotteryConf"
@ -55,16 +55,16 @@
</el-select>
</el-form-item>
<el-form-item label="启动消费">
<el-form-item :label="$t('pages.lotteryConfig.form.startConsume')">
<el-input v-model="form.startConsume" v-number />
</el-form-item>
<el-form-item label="抽奖类型">
<el-form-item :label="$t('pages.lotteryConfig.form.lotteryType')">
<el-radio-group v-model="form.type">
<el-radio :label="0">转盘</el-radio>
<el-radio :label="1" :disabled="true">九宫格</el-radio>
<el-radio :label="0">{{ $t('pages.lotteryConfig.word.wheel') }}</el-radio>
<el-radio :label="1" :disabled="true">{{ $t('pages.lotteryConfig.word.gridNine') }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="转盘直径">
<el-form-item :label="$t('pages.lotteryConfig.form.wheelDiameter')">
<el-input v-model="form.diameter" @change="renderLuckyWheel" />
</el-form-item>
</div>
@ -72,48 +72,48 @@
<div class="card bottom-line">
<div class="title">
<el-row>
<el-col :span="21">背景</el-col>
<el-col :span="3"><el-button type="text" @click="addBlocks"><i class="el-icon-circle-plus" />添加</el-button></el-col>
<el-col :span="21">{{ $t('pages.lotteryConfig.section.background') }}</el-col>
<el-col :span="3"><el-button type="text" @click="addBlocks"><i class="el-icon-circle-plus" />{{ $t('pages.lotteryConfig.action.add') }}</el-button></el-col>
</el-row>
</div>
<div class="content">
<el-tabs v-model="blocksIndex" type="border-card" closable @tab-remove="removeBlocks">
<el-tab-pane v-for="(item, index) in form.blocks" :key="index" :name="String(index)">
<span slot="label">{{ index }}</span>
<el-form-item label="内间距">
<el-form-item :label="$t('pages.lotteryConfig.form.padding')">
<el-input v-model="item.padding" />
</el-form-item>
<el-form-item label="背景颜色">
<el-form-item :label="$t('pages.lotteryConfig.form.backgroundColor')">
<el-color-picker v-model="item.background" />
</el-form-item>
<div class="card">
<div class="title">
<el-row>
<el-col :span="21">图片</el-col>
<el-col :span="3"><el-button type="text" @click="addBlocksImg(item)"><i class="el-icon-circle-plus" />添加</el-button></el-col>
<el-col :span="21">{{ $t('pages.lotteryConfig.section.image') }}</el-col>
<el-col :span="3"><el-button type="text" @click="addBlocksImg(item)"><i class="el-icon-circle-plus" />{{ $t('pages.lotteryConfig.action.add') }}</el-button></el-col>
</el-row>
</div>
<div :key="attributeIndex" class="content">
<el-tabs v-if="item.imgs.length > 0" v-model="item.imageIndex" type="border-card" closable @tab-remove="index => removeImg(item, index)">
<el-tab-pane v-for="(imgItem, imgIndex) in item.imgs" :key="imgIndex" :name="String(imgIndex)">
<span slot="label">{{ imgIndex }}</span>
<el-form-item label="图片链接">
<el-form-item :label="$t('pages.lotteryConfig.form.imageLink')">
<el-input v-model="imgItem.src" />
</el-form-item>
<el-form-item label="顶部间距">
<el-form-item :label="$t('pages.lotteryConfig.form.topSpacing')">
<el-input v-model="imgItem.top" />
</el-form-item>
<el-form-item label="图片宽">
<el-form-item :label="$t('pages.lotteryConfig.form.imageWidth')">
<el-input v-model="imgItem.width" />
</el-form-item>
<el-form-item label="图片高">
<el-form-item :label="$t('pages.lotteryConfig.form.imageHeight')">
<el-input v-model="imgItem.height" />
</el-form-item>
<el-form-item label="背景转动">
<el-form-item :label="$t('pages.lotteryConfig.form.backgroundRotate')">
<el-radio-group v-model="imgItem.rotate">
<el-radio :label="true"></el-radio>
<el-radio :label="false"></el-radio>
<el-radio :label="true">{{ $t('pages.lotteryConfig.word.yes') }}</el-radio>
<el-radio :label="false">{{ $t('pages.lotteryConfig.word.no') }}</el-radio>
</el-radio-group>
</el-form-item>
</el-tab-pane>
@ -127,50 +127,50 @@
<div class="card bottom-line">
<div class="title">
<el-row>
<el-col :span="21">奖品</el-col>
<el-col :span="3"><el-button type="text" @click="addPrize"><i class="el-icon-circle-plus" />添加</el-button></el-col>
<el-col :span="21">{{ $t('pages.lotteryConfig.section.prize') }}</el-col>
<el-col :span="3"><el-button type="text" @click="addPrize"><i class="el-icon-circle-plus" />{{ $t('pages.lotteryConfig.action.add') }}</el-button></el-col>
</el-row>
</div>
<div :key="attributeIndex" class="content">
<el-tabs v-model="prizesIndex" type="border-card" closable @tab-remove="removePrize">
<el-tab-pane v-for="(item, index) in form.prizes" :key="index" :name="String(index)">
<span slot="label">{{ index }}</span>
<el-form-item label="奖品编号">
<el-form-item :label="$t('pages.lotteryConfig.form.prizeCode')">
<el-input v-model="item.code" />
</el-form-item>
<el-form-item label="中奖概率">
<el-form-item :label="$t('pages.lotteryConfig.form.winProbability')">
<el-input v-model="item.probability" />
</el-form-item>
<el-form-item label="库存数量">
<el-form-item :label="$t('pages.lotteryConfig.form.stockQuantity')">
<el-col :span="4" style="text-align: center;">
<el-checkbox v-model="item.unlimited">不限</el-checkbox>
<el-checkbox v-model="item.unlimited">{{ $t('pages.lotteryConfig.word.unlimited') }}</el-checkbox>
</el-col>
<el-col v-if="!item.unlimited" :span="10" style="text-align: center;">
库存: {{ item.inStock }}
{{ $t('pages.lotteryConfig.stock.stock') }}: {{ item.inStock }}
<i class="el-icon-edit cursor-pointer" style="color:#FF9326;" @click="clickInStockEdit(item)" />
</el-col>
<el-col v-if="!item.unlimited" :span="10" style="text-align: center;">
消费: {{ item.consumeInStock }}
{{ $t('pages.lotteryConfig.stock.consume') }}: {{ item.consumeInStock }}
<i class="el-icon-refresh-left cursor-pointer" style="color:#FF9326;" @click="clickRestConsumeInStock(item)" />
</el-col>
<div v-if="item.inStock < item.consumeInStock" class="font-danger">
库存 &lt; 消费 点击保存系统会清理消费数重新计算
{{ $t('pages.lotteryConfig.message.stockLessThanConsume') }}
</div>
</el-form-item>
<el-form-item label="背景颜色">
<el-form-item :label="$t('pages.lotteryConfig.form.backgroundColor')">
<el-color-picker v-model="item.background" @change="renderLuckyWheel()" />
</el-form-item>
<el-form-item label="奖品关联">
<el-button type="text" @click="clickSelectPrize(item)"><i class="el-icon-circle-plus" />选择奖品</el-button>
<el-form-item :label="$t('pages.lotteryConfig.form.prizeRelation')">
<el-button type="text" @click="clickSelectPrize(item)"><i class="el-icon-circle-plus" />{{ $t('pages.lotteryConfig.action.selectPrize') }}</el-button>
<div v-if="item.prize.content" class="select-prize flex-l">
<img :src="item.prize.cover" width="100px" height="100px">
<div class="content" style="padding:10px;color:#999999;">
<div>内容: {{ item.prize.content }}</div>
<div>数量: {{ item.prize.quantity }} {{ item.prize.unit }}</div>
<div>{{ $t('pages.lotteryConfig.label.content') }}: {{ item.prize.content }}</div>
<div>{{ $t('pages.lotteryConfig.label.quantity') }}: {{ item.prize.quantity }} {{ item.prize.unit }}</div>
<div>
<el-popover
placement="bottom"
title="选择设置图片位置"
:title="$t('pages.lotteryConfig.popover.selectImagePosition')"
trigger="click"
>
<div class="content">
@ -178,12 +178,12 @@
{{ itemImgIndex }}
</el-button>
</div>
<el-button slot="reference" type="text">设置图片</el-button>
<el-button slot="reference" type="text">{{ $t('pages.lotteryConfig.action.setImage') }}</el-button>
</el-popover>
<el-popover
placement="bottom"
title="选择设置文字"
:title="$t('pages.lotteryConfig.popover.selectTextTarget')"
trigger="click"
>
<div class="content">
@ -191,7 +191,7 @@
{{ itemFontIndex }}
</el-button>
</div>
<el-button slot="reference" type="text">设置文字</el-button>
<el-button slot="reference" type="text">{{ $t('pages.lotteryConfig.action.setText') }}</el-button>
</el-popover>
</div>
</div>
@ -200,30 +200,30 @@
<div class="card">
<div class="title">
<el-row>
<el-col :span="21">文字</el-col>
<el-col :span="3"><el-button type="text" @click="addFont(item)"><i class="el-icon-circle-plus" />添加</el-button></el-col>
<el-col :span="21">{{ $t('pages.lotteryConfig.section.text') }}</el-col>
<el-col :span="3"><el-button type="text" @click="addFont(item)"><i class="el-icon-circle-plus" />{{ $t('pages.lotteryConfig.action.add') }}</el-button></el-col>
</el-row>
</div>
<div class="content">
<el-tabs v-if="item.fonts.length > 0" v-model="item.fontIndex" type="border-card" closable @tab-remove="index => removeFont(item, index)">
<el-tab-pane v-for="(fontItem, fontIndex) in item.fonts" :key="fontIndex" :name="String(fontIndex)">
<span slot="label">{{ fontIndex }}</span>
<el-form-item label="字体内容">
<el-form-item :label="$t('pages.lotteryConfig.form.fontContent')">
<el-input v-model="fontItem.text" />
</el-form-item>
<el-form-item label="顶部间距">
<el-form-item :label="$t('pages.lotteryConfig.form.topSpacing')">
<el-input v-model="fontItem.top" />
</el-form-item>
<el-form-item label="字体颜色">
<el-form-item :label="$t('pages.lotteryConfig.form.fontColor')">
<el-color-picker v-model="fontItem.fontColor" />
</el-form-item>
<el-form-item label="字体大小">
<el-form-item :label="$t('pages.lotteryConfig.form.fontSize')">
<el-input v-model="fontItem.fontSize" />
</el-form-item>
<el-form-item label="字体样式">
<el-form-item :label="$t('pages.lotteryConfig.form.fontStyle')">
<el-input v-model="fontItem.fontStyle" />
</el-form-item>
<el-form-item label="字体粗细">
<el-form-item :label="$t('pages.lotteryConfig.form.fontWeight')">
<el-select v-model="fontItem.fontWeight" style="width:100%;">
<el-option
v-for="val in fontWeights"
@ -233,16 +233,16 @@
/>
</el-select>
</el-form-item>
<el-form-item label="字体行高">
<el-form-item :label="$t('pages.lotteryConfig.form.lineHeight')">
<el-input v-model="fontItem.lineHeight" />
</el-form-item>
<el-form-item label="自动换行">
<el-form-item :label="$t('pages.lotteryConfig.form.wordWrap')">
<el-radio-group v-model="fontItem.wordWrap">
<el-radio :label="true"></el-radio>
<el-radio :label="false"></el-radio>
<el-radio :label="true">{{ $t('pages.lotteryConfig.word.yes') }}</el-radio>
<el-radio :label="false">{{ $t('pages.lotteryConfig.word.no') }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="换行宽度">
<el-form-item :label="$t('pages.lotteryConfig.form.wrapWidth')">
<el-input v-model="fontItem.lengthLimit" />
</el-form-item>
</el-tab-pane>
@ -252,24 +252,24 @@
<div class="card">
<div class="title">
<el-row>
<el-col :span="21">图片</el-col>
<el-col :span="3"><el-button type="text" @click="addImg(item)"><i class="el-icon-circle-plus" />添加</el-button></el-col>
<el-col :span="21">{{ $t('pages.lotteryConfig.section.image') }}</el-col>
<el-col :span="3"><el-button type="text" @click="addImg(item)"><i class="el-icon-circle-plus" />{{ $t('pages.lotteryConfig.action.add') }}</el-button></el-col>
</el-row>
</div>
<div class="content">
<el-tabs v-if="item.imgs.length > 0" v-model="item.imageIndex" type="border-card" closable @tab-remove="index => removeImg(item, index)">
<el-tab-pane v-for="(imgItem, imgIndex) in item.imgs" :key="imgIndex" :name="String(imgIndex)">
<span slot="label">{{ imgIndex }}</span>
<el-form-item label="图片链接">
<el-form-item :label="$t('pages.lotteryConfig.form.imageLink')">
<el-input v-model="imgItem.src" />
</el-form-item>
<el-form-item label="顶部间距">
<el-form-item :label="$t('pages.lotteryConfig.form.topSpacing')">
<el-input v-model="imgItem.top" />
</el-form-item>
<el-form-item label="图片宽">
<el-form-item :label="$t('pages.lotteryConfig.form.imageWidth')">
<el-input v-model="imgItem.width" />
</el-form-item>
<el-form-item label="图片高">
<el-form-item :label="$t('pages.lotteryConfig.form.imageHeight')">
<el-input v-model="imgItem.height" />
</el-form-item>
</el-tab-pane>
@ -284,53 +284,53 @@
<div class="card bottom-line">
<div class="title">
<el-row>
<el-col :span="21">按钮</el-col>
<el-col :span="3"><el-button type="text" @click="addButton"><i class="el-icon-circle-plus" />添加</el-button></el-col>
<el-col :span="21">{{ $t('pages.lotteryConfig.section.button') }}</el-col>
<el-col :span="3"><el-button type="text" @click="addButton"><i class="el-icon-circle-plus" />{{ $t('pages.lotteryConfig.action.add') }}</el-button></el-col>
</el-row>
</div>
<div :key="attributeIndex" class="content">
<el-tabs v-model="buttonIndex" type="border-card" closable @tab-remove="removeButton">
<el-tab-pane v-for="(item, index) in form.buttons" :key="index" :name="String(index)">
<span slot="label">{{ index }}</span>
<el-form-item label="按钮半径">
<el-form-item :label="$t('pages.lotteryConfig.form.buttonRadius')">
<el-input v-model="item.radius" />
</el-form-item>
<el-form-item label="是否显示指针">
<el-form-item :label="$t('pages.lotteryConfig.form.showPointer')">
<el-radio-group v-model="item.pointer">
<el-radio :label="true"></el-radio>
<el-radio :label="false"></el-radio>
<el-radio :label="true">{{ $t('pages.lotteryConfig.word.yes') }}</el-radio>
<el-radio :label="false">{{ $t('pages.lotteryConfig.word.no') }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="背景颜色">
<el-form-item :label="$t('pages.lotteryConfig.form.backgroundColor')">
<el-color-picker v-model="item.background" />
</el-form-item>
<div class="card">
<div class="title">
<el-row>
<el-col :span="21">文字</el-col>
<el-col :span="3"><el-button type="text" @click="addFont(item)"><i class="el-icon-circle-plus" />添加</el-button></el-col>
<el-col :span="21">{{ $t('pages.lotteryConfig.section.text') }}</el-col>
<el-col :span="3"><el-button type="text" @click="addFont(item)"><i class="el-icon-circle-plus" />{{ $t('pages.lotteryConfig.action.add') }}</el-button></el-col>
</el-row>
</div>
<div class="content">
<el-tabs v-if="item.fonts.length > 0" v-model="item.fontIndex" type="border-card" closable @tab-remove="index => removeFont(item, index)">
<el-tab-pane v-for="(fontItem, fontIndex) in item.fonts" :key="fontIndex" :name="String(fontIndex)">
<span slot="label">{{ fontIndex }}</span>
<el-form-item label="字体内容">
<el-form-item :label="$t('pages.lotteryConfig.form.fontContent')">
<el-input v-model="fontItem.text" />
</el-form-item>
<el-form-item label="顶部间距">
<el-form-item :label="$t('pages.lotteryConfig.form.topSpacing')">
<el-input v-model="fontItem.top" />
</el-form-item>
<el-form-item label="字体颜色">
<el-form-item :label="$t('pages.lotteryConfig.form.fontColor')">
<el-color-picker v-model="fontItem.fontColor" />
</el-form-item>
<el-form-item label="字体大小">
<el-form-item :label="$t('pages.lotteryConfig.form.fontSize')">
<el-input v-model="fontItem.fontSize" />
</el-form-item>
<el-form-item label="字体样式">
<el-form-item :label="$t('pages.lotteryConfig.form.fontStyle')">
<el-input v-model="fontItem.fontStyle" />
</el-form-item>
<el-form-item label="字体粗细">
<el-form-item :label="$t('pages.lotteryConfig.form.fontWeight')">
<el-select v-model="fontItem.fontWeight" style="width:100%;">
<el-option
v-for="val in fontWeights"
@ -340,16 +340,16 @@
/>
</el-select>
</el-form-item>
<el-form-item label="字体行高">
<el-form-item :label="$t('pages.lotteryConfig.form.lineHeight')">
<el-input v-model="fontItem.lineHeight" />
</el-form-item>
<el-form-item label="自动换行">
<el-form-item :label="$t('pages.lotteryConfig.form.wordWrap')">
<el-radio-group v-model="fontItem.wordWrap">
<el-radio :label="true"></el-radio>
<el-radio :label="false"></el-radio>
<el-radio :label="true">{{ $t('pages.lotteryConfig.word.yes') }}</el-radio>
<el-radio :label="false">{{ $t('pages.lotteryConfig.word.no') }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="换行宽度">
<el-form-item :label="$t('pages.lotteryConfig.form.wrapWidth')">
<el-input v-model="fontItem.lengthLimit" />
</el-form-item>
</el-tab-pane>
@ -359,24 +359,24 @@
<div class="card">
<div class="title">
<el-row>
<el-col :span="21">图片</el-col>
<el-col :span="3"><el-button type="text" @click="addImg(item)"><i class="el-icon-circle-plus" />添加</el-button></el-col>
<el-col :span="21">{{ $t('pages.lotteryConfig.section.image') }}</el-col>
<el-col :span="3"><el-button type="text" @click="addImg(item)"><i class="el-icon-circle-plus" />{{ $t('pages.lotteryConfig.action.add') }}</el-button></el-col>
</el-row>
</div>
<div class="content">
<el-tabs v-if="item.imgs.length > 0" v-model="item.imageIndex" type="border-card" closable @tab-remove="index => removeImg(item, index)">
<el-tab-pane v-for="(imgItem, imgIndex) in item.imgs" :key="imgIndex" :name="String(imgIndex)">
<span slot="label">{{ imgIndex }}</span>
<el-form-item label="图片链接">
<el-form-item :label="$t('pages.lotteryConfig.form.imageLink')">
<el-input v-model="imgItem.src" />
</el-form-item>
<el-form-item label="顶部间距">
<el-form-item :label="$t('pages.lotteryConfig.form.topSpacing')">
<el-input v-model="imgItem.top" />
</el-form-item>
<el-form-item label="图片宽">
<el-form-item :label="$t('pages.lotteryConfig.form.imageWidth')">
<el-input v-model="imgItem.width" />
</el-form-item>
<el-form-item label="图片高">
<el-form-item :label="$t('pages.lotteryConfig.form.imageHeight')">
<el-input v-model="imgItem.height" />
</el-form-item>
</el-tab-pane>
@ -388,44 +388,44 @@
</div>
</div>
<div class="card">
<div class="title">默认配置</div>
<div class="title">{{ $t('pages.lotteryConfig.section.defaultConfig') }}</div>
<div class="content">
<el-form-item label="扇形间隙">
<el-form-item :label="$t('pages.lotteryConfig.form.gutter')">
<el-input-number v-model="form.defaultConfig.gutter" :min="0" style="width:100%;" />
</el-form-item>
<el-form-item label="停止范围">
<el-form-item :label="$t('pages.lotteryConfig.form.stopRange')">
<el-slider v-model="form.defaultConfig.stopRange" />
</el-form-item>
<el-form-item label="偏移角度">
<el-form-item :label="$t('pages.lotteryConfig.form.offsetDegree')">
<el-input-number v-model="form.defaultConfig.offsetDegree" :min="0" style="width:100%;" />
</el-form-item>
<el-form-item label="速度">
<el-form-item :label="$t('pages.lotteryConfig.form.speed')">
<el-input-number v-model="form.defaultConfig.speed" :min="0" style="width:100%;" />
</el-form-item>
<el-form-item label="开始旋转时间">
<el-form-item :label="$t('pages.lotteryConfig.form.accelerationTime')">
<el-input-number v-model="form.defaultConfig.accelerationTime" :min="0" style="width:100%;" />
</el-form-item>
<el-form-item label="缓慢停止时间">
<el-form-item :label="$t('pages.lotteryConfig.form.decelerationTime')">
<el-input-number v-model="form.defaultConfig.decelerationTime" :min="0" style="width:100%;" />
</el-form-item>
</div>
</div>
<div class="card">
<div class="title">默认样式</div>
<div class="title">{{ $t('pages.lotteryConfig.section.defaultStyle') }}</div>
<div class="content">
<el-form-item label="背景颜色">
<el-form-item :label="$t('pages.lotteryConfig.form.backgroundColor')">
<el-color-picker v-model="form.defaultStyle.background" />
</el-form-item>
<el-form-item label="字体颜色">
<el-form-item :label="$t('pages.lotteryConfig.form.fontColor')">
<el-input v-model="form.defaultStyle.fontColor" />
</el-form-item>
<el-form-item label="字体大小">
<el-form-item :label="$t('pages.lotteryConfig.form.fontSize')">
<el-input v-model="form.defaultStyle.fontSize" />
</el-form-item>
<el-form-item label="字体样式">
<el-form-item :label="$t('pages.lotteryConfig.form.fontStyle')">
<el-input v-model="form.defaultStyle.fontStyle" />
</el-form-item>
<el-form-item label="字体粗细">
<el-form-item :label="$t('pages.lotteryConfig.form.fontWeight')">
<el-select v-model="form.defaultStyle.fontWeight" style="width:100%;">
<el-option
v-for="item in fontWeights"
@ -435,26 +435,26 @@
/>
</el-select>
</el-form-item>
<el-form-item label="字体行高">
<el-form-item :label="$t('pages.lotteryConfig.form.lineHeight')">
<el-input v-model="form.defaultStyle.lineHeight" />
</el-form-item>
<el-form-item label="自动换行">
<el-form-item :label="$t('pages.lotteryConfig.form.wordWrap')">
<el-radio-group v-model="form.defaultStyle.wordWrap">
<el-radio :label="true"></el-radio>
<el-radio :label="false"></el-radio>
<el-radio :label="true">{{ $t('pages.lotteryConfig.word.yes') }}</el-radio>
<el-radio :label="false">{{ $t('pages.lotteryConfig.word.no') }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="换行宽度">
<el-form-item :label="$t('pages.lotteryConfig.form.wrapWidth')">
<el-input v-model="form.defaultStyle.lengthLimit" />
</el-form-item>
</div>
</div>
</el-form>
<div class="operation-but">
<el-button :loading="formLoading" @click="loadLotteryConf">重置</el-button>
<el-button @click="copyForm">复制</el-button>
<el-button @click="importForm">导入</el-button>
<el-button type="primary" :loading="formSubmitLoading" @click="clickSubmit">保存</el-button>
<el-button :loading="formLoading" @click="loadLotteryConf">{{ $t('pages.lotteryConfig.action.reset') }}</el-button>
<el-button @click="copyForm">{{ $t('pages.lotteryConfig.action.copy') }}</el-button>
<el-button @click="importForm">{{ $t('pages.lotteryConfig.action.import') }}</el-button>
<el-button type="primary" :loading="formSubmitLoading" @click="clickSubmit">{{ $t('pages.lotteryConfig.action.save') }}</el-button>
</div>
</el-col>
</el-row>
@ -571,7 +571,7 @@ export default {
for (let index = 0; index < 6; index++) {
const prize = this.getEmptyPrizeObj()
prize.background = index % 2 === 0 ? '#f8d384' : '#f9e3bb'
prize.fonts[0].text = index + 1 + ' 元红包'
prize.fonts[0].text = this.$t('pages.lotteryConfig.defaultPrizeText', { index: index + 1 })
form.prizes.push(prize)
}
return form
@ -590,11 +590,15 @@ export default {
},
importForm() {
const that = this
that.$prompt('JSON配置信息', '导入配置', {
confirmButtonText: '确定',
cancelButtonText: '取消',
closeOnClickModal: false
}).then(({ value }) => {
that.$prompt(
that.$t('pages.lotteryConfig.prompt.jsonConfig'),
that.$t('pages.lotteryConfig.prompt.importConfig'),
{
confirmButtonText: that.$t('pages.lotteryConfig.action.confirm'),
cancelButtonText: that.$t('pages.lotteryConfig.action.cancel'),
closeOnClickModal: false
}
).then(({ value }) => {
that.form = JSON.parse(value)
that.renderLuckyWheel()
}).catch(() => {
@ -690,7 +694,7 @@ export default {
},
addBlocks() {
if (this.form.blocks.length > 10) {
this.$opsMessage.warn('有点多了,哥哥!')
this.$opsMessage.warn(this.$t('pages.lotteryConfig.message.tooMany'))
return
}
this.form.blocks.push({
@ -703,7 +707,7 @@ export default {
},
removeBlocks(index) {
if (this.form.blocks.length === 1) {
this.$opsMessage.warn('求求你做个人,留下一个吧!')
this.$opsMessage.warn(this.$t('pages.lotteryConfig.message.keepAtLeastOne'))
return
}
this.form.blocks.splice(index, 1)
@ -711,7 +715,7 @@ export default {
},
addPrize() {
if (this.form.prizes.length > 10) {
this.$opsMessage.warn('有点多了,哥哥!')
this.$opsMessage.warn(this.$t('pages.lotteryConfig.message.tooMany'))
return
}
const prize = this.getEmptyPrizeObj()
@ -722,7 +726,7 @@ export default {
},
removePrize(index) {
if (this.form.prizes.length === 1) {
this.$opsMessage.warn('求求你做个人,留下一个吧!')
this.$opsMessage.warn(this.$t('pages.lotteryConfig.message.keepAtLeastOne'))
return
}
this.form.prizes.splice(index, 1)
@ -730,7 +734,7 @@ export default {
},
addButton() {
if (this.form.buttons.length > 10) {
this.$opsMessage.warn('有点多了,哥哥!')
this.$opsMessage.warn(this.$t('pages.lotteryConfig.message.tooMany'))
return
}
this.form.buttons.push(this.getEmptyPrizeObj())
@ -739,7 +743,7 @@ export default {
},
removeButton(index) {
if (this.form.buttons.length === 1) {
this.$opsMessage.warn('求求你做个人,留下一个吧!')
this.$opsMessage.warn(this.$t('pages.lotteryConfig.message.keepAtLeastOne'))
return
}
this.form.buttons.splice(index, 1)
@ -747,7 +751,7 @@ export default {
},
addBlocksImg(item) {
if (item.imgs.length > 10) {
this.$opsMessage.warn('有点多了,哥哥!')
this.$opsMessage.warn(this.$t('pages.lotteryConfig.message.tooMany'))
return
}
item.imgs.push(this.getEmptyImageObj())
@ -759,7 +763,7 @@ export default {
},
addImg(item) {
if (item.imgs.length > 10) {
this.$opsMessage.warn('有点多了,哥哥!')
this.$opsMessage.warn(this.$t('pages.lotteryConfig.message.tooMany'))
return
}
item.imgs.push(this.getEmptyBaseImageObj())
@ -778,7 +782,7 @@ export default {
},
addFont(item, index) {
if (item.fonts.length > 10) {
this.$opsMessage.warn('有点多了,哥哥!')
this.$opsMessage.warn(this.$t('pages.lotteryConfig.message.tooMany'))
return
}
item.fonts.push(this.getEmptyFontObj())
@ -811,36 +815,40 @@ export default {
},
clickInStockEdit(item) {
const that = this
that.$prompt('请输入库存数量', '库存编辑', {
confirmButtonText: '确定',
cancelButtonText: '取消',
inputPattern: /^\d+$/,
inputErrorMessage: '请输入正整数',
closeOnClickModal: false
}).then(({ value }) => {
that.$prompt(
that.$t('pages.lotteryConfig.prompt.enterStock'),
that.$t('pages.lotteryConfig.prompt.stockEdit'),
{
confirmButtonText: that.$t('pages.lotteryConfig.action.confirm'),
cancelButtonText: that.$t('pages.lotteryConfig.action.cancel'),
inputPattern: /^\d+$/,
inputErrorMessage: that.$t('pages.lotteryConfig.validation.positiveInteger'),
closeOnClickModal: false
}
).then(({ value }) => {
item.inStock = value
}).catch(() => {
this.$message({
type: 'info',
message: '取消输入'
message: this.$t('pages.lotteryConfig.message.inputCancelled')
})
})
},
clickRestConsumeInStock(item) {
const that = this
that.$confirm('是否重置消费库存', '重置', {
confirmButtonText: '确定',
cancelButtonText: '取消',
that.$confirm(that.$t('pages.lotteryConfig.confirm.resetConsumeStock'), that.$t('pages.lotteryConfig.action.reset'), {
confirmButtonText: that.$t('pages.lotteryConfig.action.confirm'),
cancelButtonText: that.$t('pages.lotteryConfig.action.cancel'),
type: 'warning'
}).then(() => {
this.$message({
type: 'success',
message: '重置成功!'
message: this.$t('pages.lotteryConfig.message.resetSuccess')
})
}).catch(() => {
this.$message({
type: 'info',
message: '已取消重置'
message: this.$t('pages.lotteryConfig.message.resetCancelled')
})
})
}

View File

@ -1,21 +1,24 @@
<template>
<div v-loading="latestActiveListLoading" class="active-index">
<div class="title">第三方活跃指标
<div v-loading="latestActiveListLoading" class="active-index" :data-language="language">
<div class="title">
{{ $t('pages.dashboard.activeIndex.thirdPartyTitle') }}
<span class="desc">All {{ latestActiveIndex.date || '-' }}</span>
</div>
<div class=" flex-l flex-wrap">
<div class="flex-l flex-wrap">
<div
v-for="(item, index) in activeIndexs"
:key="index"
class="block-card"
:class="{'block-card-selected': item.type === selectedIndex.data.type}"
@click="clickQueryCharts('IM_ACTIVE',item)"
:class="{ 'block-card-selected': item.type === selectedIndex.data.type }"
@click="clickQueryCharts('IM_ACTIVE', item)"
>
<div class="block-card-title nowrap-ellipsis"><a :title="item.name">{{ item.name }}</a></div>
<div class="cite">{{ latestActiveIndex.imActiveIndex ? latestActiveIndex.imActiveIndex[item.field] || '0' : '-' }}</div>
</div>
</div>
<div class="title">自定义活跃指标
<div class="title">
{{ $t('pages.dashboard.activeIndex.customTitle') }}
<span class="desc">{{ latestActiveIndex.date || '-' }}</span>
</div>
<div class="flex-l flex-wrap">
@ -23,28 +26,65 @@
v-for="(item, index) in customizeIndexs"
:key="index"
class="block-card"
:class="{'block-card-selected': item.type === selectedIndex.data.type}"
@click="clickQueryCharts('CUTOMIZE_ACTIVE',item)"
:class="{ 'block-card-selected': item.type === selectedIndex.data.type }"
@click="clickQueryCharts('CUTOMIZE_ACTIVE', item)"
>
<div class="block-card-title nowrap-ellipsis"><a :title="item.name">{{ item.name }}</a></div>
<div class="cite">{{ latestActiveIndex.customizeActiveIndex ? latestActiveIndex.customizeActiveIndex[item.field] || '0' : '-' }}</div>
</div>
</div>
<div class="title">数据折线图
<div class="title">
{{ $t('pages.dashboard.activeIndex.chartTitle') }}
<span class="desc">{{ chartsData.title }}</span>
</div>
<div class="charts">
<line-graph-charts :charts-data="chartsData" height="300px" />
<line-graph-charts :key="'active-index-chart-' + language" :charts-data="chartsData" height="300px" />
</div>
</div>
</template>
<script>
import { formatDate } from '@/utils'
import { mapGetters } from 'vuex'
import { listUserActiveIndex } from '@/api/statistics'
import LineGraphCharts from './line-graph-charts'
const ACTIVE_INDEX_CONFIGS = [
{ key: 'activeUsers', field: 'activeUserNum', type: 'ACTIVE_USER_NUM' },
{ key: 'newRegistrations', field: 'registUserNumOneDay', type: 'REGIST_USER_NUM_ONE_DAY' },
{ key: 'totalRegistrations', field: 'registUserNumTotal', type: 'REGIST_USER_NUM_TOTAL' },
{ key: 'loginTimes', field: 'loginTimes', type: 'LOGIN_TIMES' },
{ key: 'loginUsers', field: 'loginUserNum', type: 'LOGIN_USER_NUM' },
{ key: 'upMessages', field: 'upMsgNum', type: 'UP_MSG_NUM' },
{ key: 'sendMessageUsers', field: 'sendMsgUserNum', type: 'SEND_MSG_USER_NUM' },
{ key: 'apnsMessages', field: 'apnsMsgNum', type: 'APNS_MSG_NUM' },
{ key: 'c2cUpMessages', field: 'c2cUpMsgNum', type: 'C2C_UP_MSG_NUM' },
{ key: 'c2cDownMessages', field: 'c2cDownMsgNum', type: 'C2C_DOWN_MSG_NUM' },
{ key: 'c2cSendUsers', field: 'c2cSendMsgUserNum', type: 'C2C_SEND_MSG_USER_NUM' },
{ key: 'c2cApnsMessages', field: 'c2cAPNSMsgNum', type: 'C2C_APNS_MSG_NUM' },
{ key: 'maxOnlineUsers', field: 'maxOnlineNum', type: 'MAX_ONLINE_NUM' },
{ key: 'downMessagesTotal', field: 'downMsgNum', type: 'FOWN_MSG_NUM' },
{ key: 'chainIncrease', field: 'chainIncrease', type: 'CHAIN_INCREASE' },
{ key: 'chainDecrease', field: 'chainDecrease', type: 'CHAIN_DECREASE' },
{ key: 'groupUpMessages', field: 'groupUpMsgNum', type: 'GROUP_UP_MSG_NUM' },
{ key: 'groupDownMessages', field: 'groupDownMsgNum', type: 'GROUP_DOWN_MSG_NUM' },
{ key: 'groupSendUsers', field: 'groupSendMsgUserNum', type: 'GROUP_SEND_MSG_USER_NUM' },
{ key: 'groupApnsMessages', field: 'groupAPNSMsgNum', type: 'GROUP_APNS_MSG_NUM' },
{ key: 'groupSendCount', field: 'groupSendMsgGroupNum', type: 'GROUP_SEND_MSG_GROUP_NUM' },
{ key: 'groupJoinTotal', field: 'groupJoinGroupTimes', type: 'GROUP_JOIN_GROUP_TIMES' },
{ key: 'groupQuitTotal', field: 'groupQuitGroupTimes', type: 'GROUP_QUIT_GROUP_TIMES' },
{ key: 'groupNewCount', field: 'groupNewGroupNum', type: 'GROUP_NEW_GROUP_NUM' },
{ key: 'groupTotalCount', field: 'groupAllGroupNum', type: 'GROUP_ALL_GROUP_NUM' },
{ key: 'groupDestroyCount', field: 'groupDestroyGroupNum', type: 'GROUP_DESTORY_GROUP_NUM' },
{ key: 'callbackRequests', field: 'callbackReq', type: 'CALLBACK_REQ' },
{ key: 'callbackResponses', field: 'callbackRsp', type: 'CALLBACK_RSP' }
]
const CUSTOM_ACTIVE_INDEX_CONFIGS = [
{ key: 'ordinaryUsers', field: 'ordinaryActiveNum', type: 'GENERAL_USER_ACTIVE_NUM' },
{ key: 'hosts', field: 'anchorActiveNum', type: 'HOST_USER_ACTIVE_NUM' }
]
export default {
components: { LineGraphCharts },
data() {
@ -52,42 +92,6 @@ export default {
latestActiveListLoading: true,
latestActiveList: [],
latestActiveIndex: {},
//
activeIndexs: [
{ name: '活跃用户数', field: 'activeUserNum', type: 'ACTIVE_USER_NUM' },
{ name: '新增注册人数', field: 'registUserNumOneDay', type: 'REGIST_USER_NUM_ONE_DAY' },
{ name: '累计注册人数', field: 'registUserNumTotal', type: 'REGIST_USER_NUM_TOTAL' },
{ name: '登录次数', field: 'loginTimes', type: 'LOGIN_TIMES' },
{ name: '登录人数', field: 'loginUserNum', type: 'LOGIN_USER_NUM' },
{ name: '上行消息数', field: 'upMsgNum', type: 'UP_MSG_NUM' },
{ name: '发消息人数', field: 'sendMsgUserNum', type: 'SEND_MSG_USER_NUM' },
{ name: 'APNs 推送数', field: 'apnsMsgNum', type: 'APNS_MSG_NUM' },
{ name: '上行消息数C2C', field: 'c2cUpMsgNum', type: 'C2C_UP_MSG_NUM' },
{ name: '下行消息数C2C', field: 'c2cDownMsgNum', type: 'C2C_DOWN_MSG_NUM' },
{ name: '发消息人数C2C', field: 'c2cSendMsgUserNum', type: 'C2C_SEND_MSG_USER_NUM' },
{ name: 'APNs 推送数C2C', field: 'c2cAPNSMsgNum', type: 'C2C_APNS_MSG_NUM' },
{ name: '最高在线人数', field: 'maxOnlineNum', type: 'MAX_ONLINE_NUM' },
{ name: '下行消息总数C2C和群', field: 'downMsgNum', type: 'FOWN_MSG_NUM' },
{ name: '关系链对数增加量', field: 'chainIncrease', type: 'CHAIN_INCREASE' },
{ name: '关系链对数删除量', field: 'chainDecrease', type: 'CHAIN_DECREASE' },
{ name: '上行消息数(群)', field: 'groupUpMsgNum', type: 'GROUP_UP_MSG_NUM' },
{ name: '下行消息数(群)', field: 'groupDownMsgNum', type: 'GROUP_DOWN_MSG_NUM' },
{ name: '发消息人数(群)', field: 'groupSendMsgUserNum', type: 'GROUP_SEND_MSG_USER_NUM' },
{ name: 'APNs 推送数(群)', field: 'groupAPNSMsgNum', type: 'GROUP_APNS_MSG_NUM' },
{ name: '发消息群组数', field: 'groupSendMsgGroupNum', type: 'GROUP_SEND_MSG_GROUP_NUM' },
{ name: '入群总数', field: 'groupJoinGroupTimes', type: 'GROUP_JOIN_GROUP_TIMES' },
{ name: '退群总数', field: 'groupQuitGroupTimes', type: 'GROUP_QUIT_GROUP_TIMES' },
{ name: '新增群组数', field: 'groupNewGroupNum', type: 'GROUP_NEW_GROUP_NUM' },
{ name: '累计群组数', field: 'groupAllGroupNum', type: 'GROUP_ALL_GROUP_NUM' },
{ name: '解散群个数', field: 'groupDestroyGroupNum', type: 'GROUP_DESTORY_GROUP_NUM' },
{ name: '回调请求数', field: 'callbackReq', type: 'CALLBACK_REQ' },
{ name: '回调应答数', field: 'callbackRsp', type: 'CALLBACK_RSP' }
],
//
customizeIndexs: [
{ name: '普通用户活跃数', field: 'ordinaryActiveNum', type: 'GENERAL_USER_ACTIVE_NUM' },
{ name: '主播活跃数', field: 'anchorActiveNum', type: 'HOST_USER_ACTIVE_NUM' }
],
chartsData: {
data: [],
title: ''
@ -98,7 +102,29 @@ export default {
}
}
},
computed: {
...mapGetters(['language']),
activeIndexs() {
const language = this.language
return ACTIVE_INDEX_CONFIGS.map(item => ({
...item,
name: this.$t(`pages.dashboard.activeIndex.metrics.${item.key}`)
}))
},
customizeIndexs() {
const language = this.language
return CUSTOM_ACTIVE_INDEX_CONFIGS.map(item => ({
...item,
name: this.$t(`pages.dashboard.activeIndex.customMetrics.${item.key}`)
}))
}
},
watch: {
language() {
this.syncSelectedIndexByType()
this.proccessIndexChartsData()
}
},
created() {
this.selectedIndex = {
type: 'IM_ACTIVE',
@ -108,33 +134,38 @@ export default {
},
methods: {
renderData() {
const that = this
that.latestActiveListLoading = true
this.latestActiveListLoading = true
listUserActiveIndex().then(res => {
that.latestActiveListLoading = false
this.latestActiveListLoading = false
const { body } = res
that.latestActiveList = body || []
if (that.latestActiveList.length > 0) {
that.latestActiveIndex = that.latestActiveList[that.latestActiveList.length - 1]
this.latestActiveList = body || []
if (this.latestActiveList.length > 0) {
this.latestActiveIndex = this.latestActiveList[this.latestActiveList.length - 1]
}
that.proccessIndexChartsData()
that.renderCharts()
}).catch(er => {
that.latestActiveListLoading = false
this.proccessIndexChartsData()
}).catch(() => {
this.latestActiveListLoading = false
})
},
syncSelectedIndexByType() {
const source = this.selectedIndex.type === 'CUTOMIZE_ACTIVE' ? this.customizeIndexs : this.activeIndexs
const matched = source.find(item => item.type === this.selectedIndex.data.type) || source[0] || {}
this.selectedIndex = {
...this.selectedIndex,
data: matched
}
},
proccessIndexChartsData() {
const that = this
const type = that.selectedIndex.type
const data = that.selectedIndex.data
const type = this.selectedIndex.type
const data = this.selectedIndex.data
const field = data.field
that.chartsData.title = data.name
that.chartsData.data = []
if (!that.latestActiveList || that.latestActiveList.length <= 0 || !field) {
this.chartsData.title = data.name
this.chartsData.data = []
if (!this.latestActiveList || this.latestActiveList.length <= 0 || !field) {
return
}
const resultData = []
that.latestActiveList.forEach(activeIndex => {
this.latestActiveList.forEach(activeIndex => {
if (type === 'IM_ACTIVE') {
const value = activeIndex.imActiveIndex[field] || 0
resultData.push([activeIndex.date, value])
@ -144,7 +175,7 @@ export default {
resultData.push([activeIndex.date, value])
}
})
that.chartsData = {
this.chartsData = {
title: data.name,
data: resultData
}
@ -155,105 +186,31 @@ export default {
data: item
}
this.proccessIndexChartsData()
},
handleSearch() {
this.renderData()
},
handleListToChartsData() {
const that = this
const chartsData = []
if (that.list.length > 0) {
that.list.forEach(item => {
const date = formatDate(item.statisticsTime, 'yyyy-MM-dd')
chartsData.push([date, (item.quantity || 0)])
})
}
return chartsData
},
renderCharts() {
const that = this
const thatDayData = that.handleListToChartsData()
that.subsidyIntegralCharts.setOption({
color: ['#66b3ff', '#ce90e8', '#ff9c6e', '#5cdbd3'],
tooltip: {
trigger: 'axis',
backgroundColor: 'rgba(50,50,50,0.5)',
axisPointer: {
type: 'cross',
label: {
lineStyle: { color: '#009688' },
crossStyle: { color: '#008acd' },
shadowStyle: { color: 'rgba(200,200,200,0.2)' }
}
}
},
grid: {
x: 20,
y: 50,
x2: 20,
y2: 0,
containLabel: true,
borderColor: '#eee'
},
toolbox: { color: ['#1e90ff', '#1e90ff', '#1e90ff', '#1e90ff'], effectiveColor: '#ff4500' },
xAxis: [
{
show: false,
type: 'category',
boundaryGap: false,
splitArea: {
show: true,
areaStyle: { color: ['rgba(250,250,250,0.1)', 'rgba(200,200,200,0.1)'] }
},
axisLabel: {
rotate: 38
},
splitLine: { lineStyle: { color: ['#eee'] }},
splitNumber: 24,
axisLine: { lineStyle: { color: '#b7bdc7' }}
}
],
yAxis: [{
type: 'value',
axisTick: { show: true, length: 0 },
splitNumber: 5,
splitLine: { lineStyle: { color: ['#eee'] }},
axisLine: { lineStyle: { color: '#b7bdc7' }}
}],
legend: { y: 10, textStyle: { color: '#8e929b' }},
series: [
{
name: '补贴积分',
type: 'line',
stack: '补贴积分',
smooth: 0.6,
symbol: 'none',
symbolSize: 10,
data: thatDayData,
areaStyle: {}
}]
}, true)
}
}
}
</script>
<style scoped lang="scss">
.active-index {
.title {
line-height: 60px;
font-size: 20px;
font-weight: bold;
line-height: 60px;
.desc {
color: #999999;
font-size: 14px;
font-weight: 400;
color: #999999;
}
}
.block-card {
width: 130px;
margin-right: 5px;
cursor: pointer;
border-radius: 2px;
margin-right:5px;
.date {
font-weight: bold;
line-height: 30px;

View File

@ -1,6 +1,6 @@
<template>
<div class="daily-currency-charts">
<el-select v-model="typeValue" placeholder="请选择" size="mini" @change="typeValueChange">
<div class="daily-currency-charts" :data-language="language">
<el-select v-model="typeValue" :placeholder="$t('pages.dashboard.shared.select')" size="mini" @change="typeValueChange">
<el-option
v-for="item in types"
:key="item.value"
@ -8,13 +8,12 @@
:value="item.value"
/>
</el-select>
<div id="barCharts" ref="barCharts" :style="'width: 100%;height:'+ height +';'" />
<div id="barCharts" ref="barCharts" :style="'width: 100%;height:' + height + ';'" />
</div>
</template>
<script>
// import { currencyOrigins } from '@/constant/type'
import { mapGetters } from 'vuex'
export default {
props: {
@ -29,10 +28,6 @@ export default {
},
data() {
return {
types: [
{ value: 0, name: '收入' },
{ value: 1, name: '支出' }
],
dateNumber: '',
typeValue: 0,
barCharts: null,
@ -42,37 +37,47 @@ export default {
}
}
},
computed: {
...mapGetters(['language']),
types() {
const language = this.language
return [
{ value: 0, name: this.$t('pages.dashboard.shared.income') },
{ value: 1, name: this.$t('pages.dashboard.shared.expenditure') }
]
}
},
watch: {
chartsData: {
handler(newVal) {
const that = this
that.groupChartsData = that.groupChartsDataByType()
that.$nextTick(() => {
that.renderCharts()
handler() {
this.groupChartsData = this.groupChartsDataByType()
this.$nextTick(() => {
this.renderCharts()
})
},
immediate: true
},
language() {
this.$nextTick(() => {
this.renderCharts()
})
}
},
created() {
},
mounted() {
const that = this
that.barCharts = that.$echarts.init(that.$refs.barCharts)
that.renderCharts()
this.barCharts = this.$echarts.init(this.$refs.barCharts)
this.renderCharts()
window.addEventListener('resize', () => {
that.barCharts.resize()
this.barCharts.resize()
})
},
methods: {
groupChartsDataByType() {
const that = this
const obj = {
income: [],
expenditure: []
}
if (that.chartsData.length > 0) {
that.chartsData.forEach(item => {
if (this.chartsData.length > 0) {
this.chartsData.forEach(item => {
if (item.type === 0) {
obj.income.push(item)
}
@ -84,7 +89,6 @@ export default {
return obj
},
getBarData(list) {
const that = this
const dataKeys = []
const dataList = []
if (!list || list.length === 0) {
@ -99,16 +103,18 @@ export default {
name: item.originName,
value: item.quantity,
itemStyle: {
color: that.getRandomColor()
color: this.getRandomColor()
}
})
}
return { keys: dataKeys, data: dataList }
},
renderCharts() {
const that = this
const barData = that.getBarData(that.typeValue === 0 ? that.groupChartsData.income : that.groupChartsData.expenditure)
that.barCharts.setOption({
if (!this.barCharts) {
return
}
const barData = this.getBarData(this.typeValue === 0 ? this.groupChartsData.income : this.groupChartsData.expenditure)
this.barCharts.setOption({
color: ['#66b3ff', '#ce90e8', '#ff9c6e', '#5cdbd3'],
tooltip: {
trigger: 'axis',
@ -130,11 +136,9 @@ export default {
containLabel: true,
borderColor: '#eee'
},
xAxis: [
{
show: false
}
],
xAxis: [{
show: false
}],
yAxis: [{
type: 'category',
data: barData.keys,
@ -146,7 +150,7 @@ export default {
width: 15,
overflow: 'truncate',
ellipsis: '...',
formatter: function(value) {
formatter(value) {
return value.length > 10 ? value.substring(0, 10) + '...' : value
}
}
@ -173,9 +177,8 @@ export default {
return 'rgba(' + r + ',' + g + ',' + b + ',0.8)'
},
typeValueChange() {
const that = this
that.$nextTick(() => {
that.renderCharts()
this.$nextTick(() => {
this.renderCharts()
})
}
}

View File

@ -1,10 +1,10 @@
<template>
<div v-loading="loading" class="dily-register-user">
<div v-loading="loading" class="dily-register-user" :data-language="language">
<el-date-picker
v-model="dateNumber"
style="margin-bottom: 10px;"
type="date"
placeholder="选择日期"
:placeholder="$t('pages.dashboard.goldOriginTop.datePlaceholder')"
value-format="yyyyMMdd"
size="mini"
:picker-options="pickerOptions"
@ -14,16 +14,16 @@
<div v-for="(item, index) in list" :key="index">
<el-card v-if="sysOriginPlatforms.includes(item.sysOrigin)" class="box-card">
<div slot="header" class="clearfix">
<span>金币TOP榜({{ item.sysOriginName }})</span>
<span>{{ $t('pages.dashboard.goldOriginTop.title', { sysOriginName: item.sysOriginName }) }}</span>
</div>
<div style="padding: 10px 0px">
<div style="padding: 10px 0">
<el-row>
<el-col class="card-col" :md="24" :xs="24">
<bar-graph-charts
ref="lineGraphCharts"
:key="item.sysOrigin"
:key="item.sysOrigin + '-' + language"
:charts-data="item.list"
:height="(item.list.length * 20)+'px'"
:height="(item.list.length * 20) + 'px'"
/>
</el-col>
</el-row>
@ -58,7 +58,7 @@ export default {
}
},
computed: {
...mapGetters(['permissionsSysOriginPlatformAlls']),
...mapGetters(['permissionsSysOriginPlatformAlls', 'language']),
sysOriginPlatforms() {
if (!this.permissionsSysOriginPlatformAlls || this.permissionsSysOriginPlatformAlls.length <= 0) {
return []
@ -71,30 +71,31 @@ export default {
},
methods: {
loadOrigin() {
const that = this
that.loading = true
this.loading = true
latestStatisticsGoldOriginDetailsAll(this.dateNumber).then(res => {
that.loading = false
this.loading = false
const { body } = res
const list = body || []
that.list = list.filter(item => !item.sysOrigin || that.permissionsSysOriginPlatformAlls.some(sys => sys.value === item.sysOrigin))
this.list = list.filter(item => !item.sysOrigin || this.permissionsSysOriginPlatformAlls.some(sys => sys.value === item.sysOrigin))
}).catch(() => {
that.loading = false
this.loading = false
})
},
querySearch(queryString, cb) {
var restaurants = currencyOrigins
var results = queryString ? restaurants.filter(this.createFilter(queryString)) : restaurants
const restaurants = currencyOrigins
const results = queryString ? restaurants.filter(this.createFilter(queryString)) : restaurants
cb(results)
},
createFilter(queryString) {
return (restaurant) => {
return (restaurant.value.toLowerCase().indexOf(queryString.toLowerCase()) >= 0) || (restaurant.name.toLowerCase().indexOf(queryString.toLowerCase()) >= 0)
return restaurant => {
return restaurant.value.toLowerCase().indexOf(queryString.toLowerCase()) >= 0 ||
restaurant.name.toLowerCase().indexOf(queryString.toLowerCase()) >= 0
}
},
handleSelect(item) {
this.selectOrigin = item.value
this.selectOriginName = item.name
const selectedItem = item || currencyOrigins[0]
this.selectOrigin = selectedItem.value
this.selectOriginName = selectedItem.name
this.loadOrigin()
},
clickAnalyze() {
@ -111,15 +112,16 @@ export default {
.box-card {
margin-bottom: 10px;
padding: 10px;
.card-col {
margin-bottom: 10px;
}
}
.register-logout-count {
.date {
font-weight: bold;
line-height: 30px;
}
.date {
font-weight: bold;
line-height: 30px;
}
}
</style>

View File

@ -1,21 +1,18 @@
<template>
<div v-loading="loading" class="dily-register-user">
<div v-loading="loading" class="dily-register-user" :data-language="language">
<el-row :gutter="10">
<el-col :span="24">
<el-autocomplete
v-model="selectOriginName"
popper-class="my-autocomplete"
:fetch-suggestions="querySearch"
placeholder="请输选择查看来源类型"
:placeholder="$t('pages.dashboard.dailyCurrencyGold.originTypePlaceholder')"
clearable
style="width: 100%; padding-bottom: 10px;"
@select="handleSelect"
@clear="handleSelect"
>
<i
slot="suffix"
class="el-icon-edit el-input__icon"
/>
<i slot="suffix" class="el-icon-edit el-input__icon" />
<template slot-scope="{ item }">
<span class="name">{{ item.name }}</span>
<div class="value font-info" style="font-size:12px;line-height: 12px; padding-bottom: 10px;">{{ item.value }}</div>
@ -26,12 +23,20 @@
<div v-for="(item, index) in list" :key="index">
<el-card v-if="sysOriginPlatforms.includes(item.sysOrigin)" class="box-card">
<div slot="header" class="clearfix">
<span>近30金币来源明细({{ item.sysOriginName }})&nbsp;<a class="font-blue" @click="clickAnalyze">实时分析</a></span>
<span>
{{ $t('pages.dashboard.dailyCurrencyGold.title', { sysOriginName: item.sysOriginName }) }}&nbsp;
<a class="font-blue" @click="clickAnalyze">{{ $t('pages.dashboard.dailyCurrencyGold.realTimeAnalysis') }}</a>
</span>
</div>
<div style="padding: 10px 0px">
<div style="padding: 10px 0">
<el-row>
<el-col class="card-col" :md="24" :xs="24">
<line-graph-charts ref="lineGraphCharts" :key="item.sysOrigin" :charts-data="item.list" height="300px" />
<line-graph-charts
ref="lineGraphCharts"
:key="item.sysOrigin + '-' + language"
:charts-data="item.list"
height="300px"
/>
</el-col>
</el-row>
</div>
@ -60,7 +65,7 @@ export default {
}
},
computed: {
...mapGetters(['permissionsSysOriginPlatformAlls']),
...mapGetters(['permissionsSysOriginPlatformAlls', 'language']),
sysOriginPlatforms() {
if (!this.permissionsSysOriginPlatformAlls || this.permissionsSysOriginPlatformAlls.length <= 0) {
return []
@ -73,30 +78,31 @@ export default {
},
methods: {
loadOrigin() {
const that = this
that.loading = true
latestStatisticsGoldOriginDetails(that.selectOrigin).then(res => {
that.loading = false
this.loading = true
latestStatisticsGoldOriginDetails(this.selectOrigin).then(res => {
this.loading = false
const { body } = res
const list = body || []
that.list = list.filter(item => !item.sysOrigin || that.permissionsSysOriginPlatformAlls.some(sys => sys.value === item.sysOrigin))
this.list = list.filter(item => !item.sysOrigin || this.permissionsSysOriginPlatformAlls.some(sys => sys.value === item.sysOrigin))
}).catch(() => {
that.loading = false
this.loading = false
})
},
querySearch(queryString, cb) {
var restaurants = currencyOrigins
var results = queryString ? restaurants.filter(this.createFilter(queryString)) : restaurants
const restaurants = currencyOrigins
const results = queryString ? restaurants.filter(this.createFilter(queryString)) : restaurants
cb(results)
},
createFilter(queryString) {
return (restaurant) => {
return (restaurant.value.toLowerCase().indexOf(queryString.toLowerCase()) >= 0) || (restaurant.name.toLowerCase().indexOf(queryString.toLowerCase()) >= 0)
return restaurant => {
return restaurant.value.toLowerCase().indexOf(queryString.toLowerCase()) >= 0 ||
restaurant.name.toLowerCase().indexOf(queryString.toLowerCase()) >= 0
}
},
handleSelect(item) {
this.selectOrigin = item.value
this.selectOriginName = item.name
const selectedItem = item || currencyOrigins[0]
this.selectOrigin = selectedItem.value
this.selectOriginName = selectedItem.name
this.loadOrigin()
},
clickAnalyze() {
@ -110,15 +116,16 @@ export default {
.box-card {
margin-bottom: 10px;
padding: 10px;
.card-col {
margin-bottom: 10px;
}
}
.register-logout-count {
.date {
font-weight: bold;
line-height: 30px;
}
.date {
font-weight: bold;
line-height: 30px;
}
}
</style>

View File

@ -1,6 +1,6 @@
<template>
<div class="daily-currency-charts">
<el-select v-model="typeValue" placeholder="请选择" size="mini" @change="typeValueChange">
<div class="daily-currency-charts" :data-language="language">
<el-select v-model="typeValue" :placeholder="$t('pages.dashboard.shared.select')" size="mini" @change="typeValueChange">
<el-option
v-for="item in types"
:key="item.value"
@ -8,14 +8,12 @@
:value="item.value"
/>
</el-select>
<div id="videoCharts" ref="videoCharts" :style="'width: 100%;height:'+ height +';'" />
<div id="videoCharts" ref="videoCharts" :style="'width: 100%;height:' + height + ';'" />
</div>
</template>
<script>
// import { currencyOrigins } from '@/constant/type'
// import { toMap } from '@/utils'
import { mapGetters } from 'vuex'
export default {
name: 'DailyCurrencyCharts',
@ -31,11 +29,6 @@ export default {
},
data() {
return {
// currencyOriginMap: toMap(currencyOrigins, 'value'),
types: [
{ value: 0, name: '收入' },
{ value: 1, name: '支出' }
],
typeValue: 0,
videoRealTimeCharts: null,
groupChartsData: {
@ -44,37 +37,47 @@ export default {
}
}
},
computed: {
...mapGetters(['language']),
types() {
const language = this.language
return [
{ value: 0, name: this.$t('pages.dashboard.shared.income') },
{ value: 1, name: this.$t('pages.dashboard.shared.expenditure') }
]
}
},
watch: {
chartsData: {
handler(newVal) {
const that = this
that.groupChartsData = that.groupChartsDataByType()
that.$nextTick(() => {
that.renderCharts()
handler() {
this.groupChartsData = this.groupChartsDataByType()
this.$nextTick(() => {
this.renderCharts()
})
},
immediate: true
},
language() {
this.$nextTick(() => {
this.renderCharts()
})
}
},
created() {
},
mounted() {
const that = this
that.videoRealTimeCharts = that.$echarts.init(that.$refs.videoCharts)
that.renderCharts()
this.videoRealTimeCharts = this.$echarts.init(this.$refs.videoCharts)
this.renderCharts()
window.addEventListener('resize', () => {
that.videoRealTimeCharts.resize()
this.videoRealTimeCharts.resize()
})
},
methods: {
groupChartsDataByType() {
const that = this
const obj = {
income: [],
expenditure: []
}
if (that.chartsData.length > 0) {
that.chartsData.forEach(item => {
if (this.chartsData.length > 0) {
this.chartsData.forEach(item => {
if (item.type === 0) {
obj.income.push(item)
}
@ -89,7 +92,6 @@ export default {
if (!list || list.length === 0) {
return []
}
// const that = this
const groupOrigin = {}
const size = list.length - 1
for (let index = size; index >= 0; index--) {
@ -124,8 +126,10 @@ export default {
return resultSeries
},
renderCharts() {
const that = this
that.videoRealTimeCharts.setOption({
if (!this.videoRealTimeCharts) {
return
}
this.videoRealTimeCharts.setOption({
color: ['#66b3ff', '#ce90e8', '#ff9c6e', '#5cdbd3'],
tooltip: {
trigger: 'axis',
@ -148,23 +152,21 @@ export default {
borderColor: '#eee'
},
toolbox: { color: ['#1e90ff', '#1e90ff', '#1e90ff', '#1e90ff'], effectiveColor: '#ff4500' },
xAxis: [
{
show: false,
type: 'category',
boundaryGap: false,
splitArea: {
show: true,
areaStyle: { color: ['rgba(250,250,250,0.1)', 'rgba(200,200,200,0.1)'] }
},
axisLabel: {
rotate: 38
},
axisLine: { lineStyle: { color: '#b7bdc7' }},
splitLine: { lineStyle: { color: ['#eee'] }},
splitNumber: 24
}
],
xAxis: [{
show: false,
type: 'category',
boundaryGap: false,
splitArea: {
show: true,
areaStyle: { color: ['rgba(250,250,250,0.1)', 'rgba(200,200,200,0.1)'] }
},
axisLabel: {
rotate: 38
},
axisLine: { lineStyle: { color: '#b7bdc7' }},
splitLine: { lineStyle: { color: ['#eee'] }},
splitNumber: 24
}],
yAxis: [{
type: 'value',
axisTick: { show: true, length: 0 },
@ -176,13 +178,12 @@ export default {
y: 10,
textStyle: { color: '#8e929b' }
},
series: that.createSeries(that.typeValue === 0 ? that.groupChartsData.income : that.groupChartsData.expenditure)
series: this.createSeries(this.typeValue === 0 ? this.groupChartsData.income : this.groupChartsData.expenditure)
}, true)
},
typeValueChange() {
const that = this
that.$nextTick(() => {
that.renderCharts()
this.$nextTick(() => {
this.renderCharts()
})
}
}

View File

@ -1,46 +1,16 @@
<template>
<div class="dily-register-user">
<div class="dily-register-user" :data-language="language">
<el-row :gutter="10">
<el-col :span="24">
<div v-for="(item, index) in list" :key="index">
<el-card v-if="sysOriginPlatforms.includes(item.sysOrigin)" class="box-card">
<div slot="header" class="clearfix">
<span>近30天货币收支({{ item.sysOriginName }})</span>
<span>{{ $t('pages.dashboard.dailyCurrency.title', { sysOriginName: item.sysOriginName }) }}</span>
</div>
<div v-loading="loading" style="padding: 10px 0px">
<!-- <el-row :gutter="20" class="register-logout-count">
<el-col v-for="(nItem, nIndex) in item.lastMonths" :key="nIndex" :span="24" >
<div class="date">{{ nItem.statisticsTime | dateFormat('yyyy-MM-dd') }}</div>
</el-col>
<el-col :md="6" :xs="24">
<div class="block-card">
<div class="block-card-title"></div>
<div class="block-card-content cite">{{ nItem.quantity || 0 }}</div>
</div>
</el-col>
<el-col :md="6" :xs="24">
<div class="block-card">
<div class="block-card-title">注销男</div>
<div class="block-card-content cite">{{ item.latest ? item.latest.maleLogout || 0 : 0 }}</div>
</div>
</el-col>
<el-col :md="6" :xs="24">
<div class="block-card">
<div class="block-card-title"></div>
<div class="block-card-content cite">{{ item.latest ? item.latest.femaleCount || 0 :0 }}</div>
</div>
</el-col>
<el-col :md="6" :xs="24">
<div class="block-card">
<div class="block-card-title">注销女</div>
<div class="block-card-content cite">{{ item.latest ?item.latest.femaleLogout || 0 :0 }}</div>
</div>
</el-col>
</el-row> -->
<div v-loading="loading" style="padding: 10px 0">
<el-row>
<el-col class="card-col" :md="24" :xs="24">
<line-graph-charts :key="item.sysOrigin" :charts-data="item.lastMonths" height="300px" />
<line-graph-charts :key="item.sysOrigin + '-' + language" :charts-data="item.lastMonths" height="300px" />
</el-col>
</el-row>
</div>
@ -66,7 +36,7 @@ export default {
}
},
computed: {
...mapGetters(['permissionsSysOriginPlatformAlls']),
...mapGetters(['permissionsSysOriginPlatformAlls', 'language']),
sysOriginPlatforms() {
if (!this.permissionsSysOriginPlatformAlls || this.permissionsSysOriginPlatformAlls.length <= 0) {
return []
@ -79,15 +49,14 @@ export default {
},
methods: {
renderData() {
const that = this
that.loading = true
this.loading = true
latestStatisticsCurrency().then(res => {
that.loading = false
this.loading = false
const { body } = res
const list = body || []
that.list = list.filter(item => !item.sysOrigin || that.permissionsSysOriginPlatformAlls.some(sys => sys.value === item.sysOrigin))
this.list = list.filter(item => !item.sysOrigin || this.permissionsSysOriginPlatformAlls.some(sys => sys.value === item.sysOrigin))
}).catch(() => {
that.loading = false
this.loading = false
})
}
}
@ -98,15 +67,16 @@ export default {
.box-card {
margin-bottom: 10px;
padding: 10px;
.card-col {
margin-bottom: 10px;
}
}
.register-logout-count {
.date {
font-weight: bold;
line-height: 30px;
}
.date {
font-weight: bold;
line-height: 30px;
}
}
</style>

View File

@ -1,12 +1,13 @@
<template>
<div class="daily-currency-charts">
<div id="videoCharts" ref="videoCharts" :style="'width: 100%;height:'+ height +';'" />
<div id="videoCharts" ref="videoCharts" :style="'width: 100%;height:' + height + ';'" />
</div>
</template>
<script>
import { mapGetters } from 'vuex'
import { formatDate } from '@/utils'
export default {
name: 'DailyCurrencyCharts',
props: {
@ -24,27 +25,37 @@ export default {
videoRealTimeCharts: null
}
},
created() {
computed: {
...mapGetters(['language'])
},
watch: {
chartsData: {
handler() {
this.renderCharts()
},
deep: true
},
language() {
this.renderCharts()
}
},
mounted() {
const that = this
that.videoRealTimeCharts = that.$echarts.init(that.$refs.videoCharts)
that.renderCharts()
this.videoRealTimeCharts = this.$echarts.init(this.$refs.videoCharts)
this.renderCharts()
window.addEventListener('resize', () => {
that.videoRealTimeCharts.resize()
this.videoRealTimeCharts.resize()
})
},
methods: {
handleListToChartsData() {
const that = this
const charts = {
integralIncome: [],
integralExpenditure: [],
goldIncome: [],
goldExpenditure: []
}
if (that.chartsData.length > 0) {
that.chartsData.forEach(item => {
if (this.chartsData.length > 0) {
this.chartsData.forEach(item => {
const date = formatDate(item.statisticsTime, 'yyyy-MM-dd')
if (item.currencyType === 'GOLD' && item.type === 0) {
charts.goldIncome.push([date, (item.quantity || 0)])
@ -60,9 +71,11 @@ export default {
return charts
},
renderCharts() {
const that = this
const thatDayData = that.handleListToChartsData()
that.videoRealTimeCharts.setOption({
if (!this.videoRealTimeCharts) {
return
}
const thatDayData = this.handleListToChartsData()
this.videoRealTimeCharts.setOption({
color: ['#66b3ff', '#ce90e8', '#ff9c6e', '#5cdbd3'],
tooltip: {
trigger: 'axis',
@ -85,23 +98,21 @@ export default {
borderColor: '#eee'
},
toolbox: { color: ['#1e90ff', '#1e90ff', '#1e90ff', '#1e90ff'], effectiveColor: '#ff4500' },
xAxis: [
{
show: false,
type: 'category',
boundaryGap: false,
splitArea: {
show: true,
areaStyle: { color: ['rgba(250,250,250,0.1)', 'rgba(200,200,200,0.1)'] }
},
axisLabel: {
rotate: 38
},
axisLine: { lineStyle: { color: '#b7bdc7' }},
splitLine: { lineStyle: { color: ['#eee'] }},
splitNumber: 24
}
],
xAxis: [{
show: false,
type: 'category',
boundaryGap: false,
splitArea: {
show: true,
areaStyle: { color: ['rgba(250,250,250,0.1)', 'rgba(200,200,200,0.1)'] }
},
axisLabel: {
rotate: 38
},
axisLine: { lineStyle: { color: '#b7bdc7' }},
splitLine: { lineStyle: { color: ['#eee'] }},
splitNumber: 24
}],
yAxis: [{
type: 'value',
axisTick: { show: true, length: 0 },
@ -115,9 +126,9 @@ export default {
},
series: [
{
name: '金币收入',
name: this.$t('pages.dashboard.dailyCurrency.goldIncome'),
type: 'line',
stack: '金币收入',
stack: this.$t('pages.dashboard.dailyCurrency.goldIncome'),
smooth: 0.6,
symbol: 'none',
symbolSize: 10,
@ -125,18 +136,19 @@ export default {
areaStyle: {}
},
{
name: '金币支出',
name: this.$t('pages.dashboard.dailyCurrency.goldExpenditure'),
type: 'line',
stack: '金币支出',
stack: this.$t('pages.dashboard.dailyCurrency.goldExpenditure'),
smooth: 0.6,
symbol: 'none',
symbolSize: 10,
data: thatDayData.goldExpenditure,
areaStyle: {}
}, {
name: '积分收入',
},
{
name: this.$t('pages.dashboard.dailyCurrency.integralIncome'),
type: 'line',
stack: '金币收入',
stack: this.$t('pages.dashboard.dailyCurrency.goldIncome'),
smooth: 0.6,
symbol: 'none',
symbolSize: 10,
@ -144,15 +156,16 @@ export default {
areaStyle: {}
},
{
name: '积分支出',
name: this.$t('pages.dashboard.dailyCurrency.integralExpenditure'),
type: 'line',
stack: '金币支出',
stack: this.$t('pages.dashboard.dailyCurrency.goldExpenditure'),
smooth: 0.6,
symbol: 'none',
symbolSize: 10,
data: thatDayData.integralExpenditure,
areaStyle: {}
}]
}
]
}, true)
}
}

View File

@ -1,8 +1,14 @@
<template>
<div class="dily-register-user">
<div class="dily-register-user" :data-language="language">
<el-row :gutter="10">
<el-col :span="24">
<el-select v-model="selectAreaCode" placeholder="请选择" style="margin-bottom: 10px;" size="mini" @change="changeAreaCode">
<el-select
v-model="selectAreaCode"
:placeholder="$t('pages.dashboard.shared.select')"
style="margin-bottom: 10px;"
size="mini"
@change="changeAreaCode"
>
<el-option
v-for="item in selecteOptions"
:key="item.value"
@ -12,15 +18,15 @@
</el-select>
</el-col>
<el-col v-if="isAllowLatestTotal" :span="24" style="margin-bottom: 10px;">
<line-graph-charts-general height="300px" :area-code="selectAreaCode" />
<line-graph-charts-general :key="'general-' + language" height="300px" :area-code="selectAreaCode" />
</el-col>
<el-col v-if="list && list.length > 0" :span="24">
<div v-for="(item, index) in list" :key="index">
<el-card v-if="sysOriginPlatforms.includes(item.sysOrigin)" class="box-card">
<div slot="header" class="clearfix">
<span>近30天内购({{ item.sysOriginName }})</span>
<span>{{ $t('pages.dashboard.dailyPurchase.title', { sysOriginName: item.sysOriginName }) }}</span>
</div>
<div v-loading="loading" style="padding: 10px 0px">
<div v-loading="loading" style="padding: 10px 0">
<el-row :gutter="10" class="daily-purchase">
<el-col :span="24">
<div class="date">{{ (item.last ? item.last.statisticsTime : '') | dateFormat('yyyy-MM-dd') }}</div>
@ -40,23 +46,22 @@
<el-col :md="6" :xs="24">
<div class="block-card">
<div class="block-card-title">H5</div>
<div class="block-card-content cite">{{ item.last ? item.last.h5Quantity || 0 :0 }}</div>
<div class="block-card-content cite">{{ item.last ? item.last.h5Quantity || 0 : 0 }}</div>
</div>
</el-col>
<el-col :md="6" :xs="24">
<div class="block-card">
<div class="block-card-title">有效/退款</div>
<div class="block-card-content cite">{{ item.last ?item.last.completeQuantity || 0 :0 }} / {{ item.last ?item.last.reimburseQuantity || 0 :0 }}</div>
<div class="block-card-title">{{ $t('pages.dashboard.dailyPurchase.validRefund') }}</div>
<div class="block-card-content cite">{{ item.last ? item.last.completeQuantity || 0 : 0 }} / {{ item.last ? item.last.reimburseQuantity || 0 : 0 }}</div>
</div>
</el-col>
<el-col class="card-col" :md="24" :xs="24">
<line-graph-charts ref="sysOriginCharts" :key="item.sysOrigin" :charts-data="item.lastMonths" height="300px" />
<line-graph-charts :key="item.sysOrigin + '-' + language" ref="sysOriginCharts" :charts-data="item.lastMonths" height="300px" />
</el-col>
</el-row>
</div>
</el-card>
</div>
</el-col>
</el-row>
</div>
@ -74,18 +79,21 @@ export default {
data() {
return {
selectAreaCode: 'DAILY',
selecteOptions: [
{ label: '每日', value: 'DAILY' },
{ label: '沙特', value: 'ar' },
{ label: '土耳其', value: 'tr' },
{ label: '其他', value: 'OTHER' }
],
loading: false,
list: []
}
},
computed: {
...mapGetters(['permissionsSysOriginPlatformAlls']),
...mapGetters(['permissionsSysOriginPlatformAlls', 'language']),
selecteOptions() {
const language = this.language
return [
{ label: this.$t('pages.dashboard.dailyPurchase.regionDaily'), value: 'DAILY' },
{ label: this.$t('pages.dashboard.dailyPurchase.regionSaudi'), value: 'ar' },
{ label: this.$t('pages.dashboard.dailyPurchase.regionTurkey'), value: 'tr' },
{ label: this.$t('pages.dashboard.dailyPurchase.regionOther'), value: 'OTHER' }
]
},
isAllowLatestTotal() {
return this.permissionsSysOriginPlatformAlls && this.permissionsSysOriginPlatformAlls.length > 1
},
@ -104,18 +112,17 @@ export default {
},
methods: {
renderData() {
const that = this
that.loading = true
latestPurchaseAmountPlatform(that.selectAreaCode).then(res => {
that.loading = false
this.loading = true
latestPurchaseAmountPlatform(this.selectAreaCode).then(res => {
this.loading = false
const { body } = res
const list = body || []
that.list = []
that.$nextTick(() => {
that.list = list.filter(item => !item.sysOrigin || that.permissionsSysOriginPlatformAlls.some(sys => sys.value === item.sysOrigin))
this.list = []
this.$nextTick(() => {
this.list = list.filter(item => !item.sysOrigin || this.permissionsSysOriginPlatformAlls.some(sys => sys.value === item.sysOrigin))
})
}).catch(() => {
that.loading = false
this.loading = false
})
},
changeAreaCode() {
@ -129,15 +136,16 @@ export default {
.box-card {
margin-bottom: 10px;
padding: 10px;
.card-col {
margin-bottom: 10px;
}
}
.daily-purchase {
.date {
font-weight: bold;
line-height: 30px;
}
.date {
font-weight: bold;
line-height: 30px;
}
}
</style>

View File

@ -2,16 +2,16 @@
<div class="subsidy-integral-charts">
<el-card v-loading="listLoading">
<div slot="header" class="clearfix">
<span>近30天内购(总揽)</span>
<span>{{ $t('pages.dashboard.dailyPurchase.overviewTitle') }}</span>
</div>
<el-tag v-for="(item ,index) in platformCounts" :key="index" style="margin-right: 10px;">{{ item.label }}({{ item.type }}): {{ item.val }}</el-tag>
<div id="subsidyIntegralCharts" ref="subsidyIntegralCharts" :style="'width: 100%;height:'+ height +';'" />
<el-tag v-for="(item, index) in platformCounts" :key="index" style="margin-right: 10px;">{{ item.label }}({{ item.type }}): {{ item.val }}</el-tag>
<div id="subsidyIntegralCharts" ref="subsidyIntegralCharts" :style="'width: 100%;height:' + height + ';'" />
</el-card>
</div>
</template>
<script>
import { mapGetters } from 'vuex'
import { formatDate } from '@/utils'
import { getDailyPurchaseAmountThirtyDays } from '@/api/statistics'
@ -35,6 +35,9 @@ export default {
platformCounts: []
}
},
computed: {
...mapGetters(['language'])
},
watch: {
areaCode: {
handler(newVal) {
@ -43,81 +46,30 @@ export default {
}
},
immediate: true
},
language() {
this.$nextTick(() => {
this.renderCharts()
})
}
},
mounted() {
const that = this
that.subsidyIntegralCharts = that.$echarts.init(that.$refs.subsidyIntegralCharts)
this.subsidyIntegralCharts = this.$echarts.init(this.$refs.subsidyIntegralCharts)
window.addEventListener('resize', () => {
that.subsidyIntegralCharts.resize()
this.subsidyIntegralCharts.resize()
})
},
methods: {
renderData() {
const that = this
that.listLoading = true
getDailyPurchaseAmountThirtyDays(that.areaCode).then(res => {
this.listLoading = true
getDailyPurchaseAmountThirtyDays(this.areaCode).then(res => {
const { body } = res
that.list = body || []
that.renderCharts()
that.listLoading = false
// that.platformCounts = that.count(that.list)
this.list = body || []
this.renderCharts()
this.listLoading = false
})
},
count(list) {
if (!list || list.length <= 0) {
return []
}
let androidPurchaseCount = 0
let androidPurchaseIndex = 0
let iosPurchaseCount = 0
let iosPurchaseIndex = 0
let h5PurchaseCount = 0
let h5PurchaseCountIndex = 0
let totalCount = 0
let totalIndex = 0
let refundCount = 0
const size = list.length
for (let index = 0; index < size; index++) {
const item = list[index]
totalCount += item.purchase
refundCount += item.refund
if (item && item.platform === 'Android') {
androidPurchaseIndex += 1
totalIndex += 1
androidPurchaseCount += item.purchase
}
if (item && item.platform === 'iOS') {
iosPurchaseIndex += 1
totalIndex += 1
iosPurchaseCount += item.purchase
}
if (item && item.platform === 'H5') {
h5PurchaseCountIndex += 1
totalIndex += 1
h5PurchaseCount += item.purchase
}
}
return [
{ type: 'avg', label: 'Android', val: this.calAvg(androidPurchaseCount, androidPurchaseIndex) },
{ type: 'avg', label: 'iOS', val: this.calAvg(iosPurchaseCount, iosPurchaseIndex) },
{ type: 'avg', label: 'H5', val: this.calAvg(h5PurchaseCount, h5PurchaseCountIndex) },
{ type: 'avg', label: 'Total2', val: this.calAvg(totalCount, totalIndex) },
{ type: 'count', label: 'Refund', val: refundCount }
]
},
calAvg(num, size) {
if (!size || size <= 0) {
return 0
}
return (num / size).toFixed(2) * 100 / 100
},
handleSearch() {
this.renderData()
},
handleChartsData() {
const that = this
const iosPurchase = []
const iosRefund = []
const googlePurchase = []
@ -125,8 +77,8 @@ export default {
const h5Purchase = []
const h5Refund = []
const offline = []
if (that.list.length > 0) {
that.list.forEach(item => {
if (this.list.length > 0) {
this.list.forEach(item => {
const date = formatDate(item.statisticsTime, 'yyyyMMdd')
if (item.platform === 'iOS') {
iosPurchase.push([date, (item.purchase || 0)])
@ -141,106 +93,112 @@ export default {
h5Purchase.push([date, (item.purchase || 0)])
h5Refund.push([date, (item.refund || 0)])
}
if (item.platform === 'Offline' && that.areaCode === 'DAILY') {
if (item.platform === 'Offline' && this.areaCode === 'DAILY') {
offline.push([date, (item.purchase || 0)])
}
})
}
return { iosPurchase, iosRefund, googlePurchase, googleRefund, h5Purchase, h5Refund }
return { iosPurchase, iosRefund, googlePurchase, googleRefund, h5Purchase, h5Refund, offline }
},
renderCharts() {
const that = this
const thatDayData = that.handleChartsData()
const series = [{
name: 'Adnroid内购',
type: 'line',
stack: 'Adnroid内购',
smooth: 0.6,
symbol: 'none',
symbolSize: 10,
data: thatDayData.googlePurchase,
areaStyle: {},
itemStyle: {
color: '#3eda86'
if (!this.subsidyIntegralCharts) {
return
}
const thatDayData = this.handleChartsData()
const series = [
{
name: this.$t('pages.dashboard.dailyPurchase.androidPurchase'),
type: 'line',
stack: this.$t('pages.dashboard.dailyPurchase.androidPurchase'),
smooth: 0.6,
symbol: 'none',
symbolSize: 10,
data: thatDayData.googlePurchase,
areaStyle: {},
itemStyle: {
color: '#3eda86'
}
},
{
name: this.$t('pages.dashboard.dailyPurchase.androidRefund'),
type: 'line',
stack: this.$t('pages.dashboard.dailyPurchase.androidRefund'),
smooth: 0.6,
symbol: 'none',
symbolSize: 10,
data: thatDayData.googleRefund,
areaStyle: {},
itemStyle: {
color: '#55886d'
}
},
{
name: this.$t('pages.dashboard.dailyPurchase.iosPurchase'),
type: 'line',
stack: this.$t('pages.dashboard.dailyPurchase.iosPurchase'),
smooth: 0.6,
symbol: 'none',
symbolSize: 10,
data: thatDayData.iosPurchase,
areaStyle: {},
itemStyle: {
color: '#303133'
}
},
{
name: this.$t('pages.dashboard.dailyPurchase.iosRefund'),
type: 'line',
stack: this.$t('pages.dashboard.dailyPurchase.iosRefund'),
smooth: 0.6,
symbol: 'none',
symbolSize: 10,
data: thatDayData.iosRefund,
areaStyle: {},
itemStyle: {
color: '#828486'
}
},
{
name: 'Offline',
type: 'line',
stack: 'Offline',
smooth: 0.6,
symbol: 'none',
symbolSize: 10,
data: thatDayData.offline,
areaStyle: {},
itemStyle: {
color: '#6c4c49'
}
},
{
name: this.$t('pages.dashboard.dailyPurchase.h5Purchase'),
type: 'line',
stack: this.$t('pages.dashboard.dailyPurchase.h5Purchase'),
smooth: 0.6,
symbol: 'none',
symbolSize: 10,
data: thatDayData.h5Purchase,
areaStyle: {},
itemStyle: {
color: '#e9640e'
}
},
{
name: this.$t('pages.dashboard.dailyPurchase.h5Refund'),
type: 'line',
stack: this.$t('pages.dashboard.dailyPurchase.h5Refund'),
smooth: 0.6,
symbol: 'none',
symbolSize: 10,
data: thatDayData.h5Refund,
areaStyle: {},
itemStyle: {
color: '#ff9c6e'
}
}
}, {
name: 'Adnroid退款',
type: 'line',
stack: 'Adnroid退款',
smooth: 0.6,
symbol: 'none',
symbolSize: 10,
data: thatDayData.googleRefund,
areaStyle: {},
itemStyle: {
color: '#55886d'
}
},
{
name: 'iOS内购',
type: 'line',
stack: 'iOS内购',
smooth: 0.6,
symbol: 'none',
symbolSize: 10,
data: thatDayData.iosPurchase,
areaStyle: {},
itemStyle: {
color: '#303133'
}
}, {
name: 'iOS退款',
type: 'line',
stack: 'iOS退款',
smooth: 0.6,
symbol: 'none',
symbolSize: 10,
data: thatDayData.iosRefund,
areaStyle: {},
itemStyle: {
color: '#828486'
}
},
{
name: 'Offline',
type: 'line',
stack: 'Offline',
smooth: 0.6,
symbol: 'none',
symbolSize: 10,
data: thatDayData.offline,
areaStyle: {},
itemStyle: {
color: '#6c4c49'
}
}, {
name: 'H5内购',
type: 'line',
stack: 'H5内购',
smooth: 0.6,
symbol: 'none',
symbolSize: 10,
data: thatDayData.h5Purchase,
areaStyle: {},
itemStyle: {
color: '#e9640e'
}
}, {
name: 'H5退款',
type: 'line',
stack: 'H5退款',
smooth: 0.6,
symbol: 'none',
symbolSize: 10,
data: thatDayData.h5Refund,
areaStyle: {},
itemStyle: {
color: '#ff9c6e'
}
}]
that.subsidyIntegralCharts.setOption({
// color: ['#3eda86', '#55886d', '#303133', '#828486', '#6c4c49', '#e9640e', '#ff9c6e', '#66b3ff', '#E6A23C', '#ce90e8', '#5cdbd3'],
]
this.subsidyIntegralCharts.setOption({
tooltip: {
trigger: 'axis',
backgroundColor: 'rgba(50,50,50,0.5)',
@ -262,23 +220,21 @@ export default {
borderColor: '#eee'
},
toolbox: { color: ['#1e90ff', '#1e90ff', '#1e90ff', '#1e90ff'], effectiveColor: '#ff4500' },
xAxis: [
{
show: false,
type: 'category',
boundaryGap: false,
splitArea: {
show: true,
areaStyle: { color: ['rgba(250,250,250,0.1)', 'rgba(200,200,200,0.1)'] }
},
axisLabel: {
rotate: 38
},
splitLine: { lineStyle: { color: ['#eee'] }},
splitNumber: 24,
axisLine: { lineStyle: { color: '#b7bdc7' }}
}
],
xAxis: [{
show: false,
type: 'category',
boundaryGap: false,
splitArea: {
show: true,
areaStyle: { color: ['rgba(250,250,250,0.1)', 'rgba(200,200,200,0.1)'] }
},
axisLabel: {
rotate: 38
},
splitLine: { lineStyle: { color: ['#eee'] }},
splitNumber: 24,
axisLine: { lineStyle: { color: '#b7bdc7' }}
}],
yAxis: [{
type: 'value',
axisTick: { show: true, length: 0 },

View File

@ -1,11 +1,13 @@
<template>
<div class="daily-currency-charts">
<el-tag v-for="(item ,index) in platformCounts" :key="index" style="margin-right: 10px;">{{ item.label }}({{ item.type }}): {{ item.val }}</el-tag>
<div id="videoCharts" ref="videoCharts" :style="'width: 100%;height:'+ height +';'" />
<el-tag v-for="(item, index) in platformCounts" :key="index" style="margin-right: 10px;">{{ item.label }}({{ item.type }}): {{ item.val }}</el-tag>
<div id="videoCharts" ref="videoCharts" :style="'width: 100%;height:' + height + ';'" />
</div>
</template>
<script>
import { mapGetters } from 'vuex'
export default {
name: 'DailyCurrencyCharts',
props: {
@ -24,14 +26,25 @@ export default {
platformCounts: []
}
},
created() {
computed: {
...mapGetters(['language'])
},
watch: {
chartsData: {
handler() {
this.renderCharts()
},
deep: true
},
language() {
this.renderCharts()
}
},
mounted() {
const that = this
that.videoRealTimeCharts = that.$echarts.init(that.$refs.videoCharts)
that.renderCharts()
this.videoRealTimeCharts = this.$echarts.init(this.$refs.videoCharts)
this.renderCharts()
window.addEventListener('resize', () => {
that.videoRealTimeCharts.resize()
this.videoRealTimeCharts.resize()
})
},
methods: {
@ -66,7 +79,6 @@ export default {
return (num / size).toFixed(2) * 100 / 100
},
handleListToChartsData() {
const that = this
const dataCharts = {
androidQuantity: [],
iosQuantity: [],
@ -75,26 +87,27 @@ export default {
reimburseQuantity: [],
offlineQuantity: []
}
if (that.chartsData.length > 0) {
that.chartsData.forEach(item => {
if (this.chartsData.length > 0) {
this.chartsData.forEach(item => {
const date = item.dateNumber
dataCharts.androidQuantity.push([date, (item.androidQuantity || 0)])
dataCharts.iosQuantity.push([date, (item.iosQuantity || 0)])
dataCharts.h5Quantity.push([date, (item.h5Quantity || 0)])
dataCharts.completeQuantity.push([date, (item.completeQuantity || 0)])
dataCharts.reimburseQuantity.push([date, (item.reimburseQuantity || 0)])
if (that.areaCode === 'DAILY') {
if (this.areaCode === 'DAILY') {
dataCharts.offlineQuantity.push([date, (item.offlineQuantity || 0)])
}
})
}
// that.platformCounts = that.count(that.chartsData)
return dataCharts
},
renderCharts() {
const that = this
const thatDayData = that.handleListToChartsData()
if (!this.videoRealTimeCharts) {
return
}
const thatDayData = this.handleListToChartsData()
const series = [
{
name: 'Android',
@ -149,9 +162,9 @@ export default {
}
},
{
name: '有效',
name: this.$t('pages.dashboard.dailyPurchase.valid'),
type: 'line',
stack: '有效',
stack: this.$t('pages.dashboard.dailyPurchase.valid'),
smooth: 0.6,
symbol: 'none',
symbolSize: 10,
@ -162,9 +175,9 @@ export default {
}
},
{
name: '退款',
name: this.$t('pages.dashboard.dailyPurchase.refund'),
type: 'line',
stack: '退款',
stack: this.$t('pages.dashboard.dailyPurchase.refund'),
smooth: 0.6,
symbol: 'none',
symbolSize: 10,
@ -173,9 +186,9 @@ export default {
itemStyle: {
color: '#F56C6C'
}
}]
that.videoRealTimeCharts.setOption({
// color: ['#3eda86', '#303133', '#e9640e', '#6c4c49', '#66b3ff', '#ce90e8', '#ff9c6e', '#5cdbd3', '#E6A23C'],
}
]
this.videoRealTimeCharts.setOption({
tooltip: {
trigger: 'axis',
backgroundColor: 'rgba(50,50,50,0.5)',
@ -197,23 +210,21 @@ export default {
borderColor: '#eee'
},
toolbox: { color: ['#1e90ff', '#1e90ff', '#1e90ff', '#1e90ff'], effectiveColor: '#ff4500' },
xAxis: [
{
show: false,
type: 'category',
boundaryGap: false,
splitArea: {
show: true,
areaStyle: { color: ['rgba(250,250,250,0.1)', 'rgba(200,200,200,0.1)'] }
},
axisLabel: {
rotate: 38
},
axisLine: { lineStyle: { color: '#b7bdc7' }},
splitLine: { lineStyle: { color: ['#eee'] }},
splitNumber: 24
}
],
xAxis: [{
show: false,
type: 'category',
boundaryGap: false,
splitArea: {
show: true,
areaStyle: { color: ['rgba(250,250,250,0.1)', 'rgba(200,200,200,0.1)'] }
},
axisLabel: {
rotate: 38
},
axisLine: { lineStyle: { color: '#b7bdc7' }},
splitLine: { lineStyle: { color: ['#eee'] }},
splitNumber: 24
}],
yAxis: [{
type: 'value',
axisTick: { show: true, length: 0 },

View File

@ -1,43 +1,43 @@
<template>
<div class="dily-register-user">
<div class="dily-register-user" :data-language="language">
<el-row :gutter="10">
<el-col :span="24">
<div v-for="(item, index) in list" :key="index">
<el-card v-if="sysOriginPlatforms.includes(item.sysOrigin)" class="box-card">
<div slot="header" class="clearfix">
<span>近30天新增({{ item.sysOriginName }})</span>
<span>{{ $t('pages.dashboard.dailyRegisterUser.title', { sysOriginName: item.sysOriginName }) }}</span>
</div>
<div v-loading="loading" style="padding: 10px 0px">
<div v-loading="loading" style="padding: 10px 0">
<el-row :gutter="20" class="register-logout-count">
<el-col :span="24">
<div class="date">{{ item.registerLogout ? item.registerLogout.statisticsTime : '' | dateFormat('yyyy-MM-dd') }}</div>
</el-col>
<el-col :md="6" :xs="24">
<div class="block-card">
<div class="block-card-title"></div>
<div class="block-card-title">{{ $t('pages.dashboard.dailyRegisterUser.male') }}</div>
<div class="block-card-content cite">{{ item.registerLogout ? item.registerLogout.maleCount || 0 : 0 }}</div>
</div>
</el-col>
<el-col :md="6" :xs="24">
<div class="block-card">
<div class="block-card-title">注销男</div>
<div class="block-card-title">{{ $t('pages.dashboard.dailyRegisterUser.maleLogout') }}</div>
<div class="block-card-content cite">{{ item.registerLogout ? item.registerLogout.maleLogout || 0 : 0 }}</div>
</div>
</el-col>
<el-col :md="6" :xs="24">
<div class="block-card">
<div class="block-card-title"></div>
<div class="block-card-content cite">{{ item.registerLogout ? item.registerLogout.femaleCount || 0 :0 }}</div>
<div class="block-card-title">{{ $t('pages.dashboard.dailyRegisterUser.female') }}</div>
<div class="block-card-content cite">{{ item.registerLogout ? item.registerLogout.femaleCount || 0 : 0 }}</div>
</div>
</el-col>
<el-col :md="6" :xs="24">
<div class="block-card">
<div class="block-card-title">注销女</div>
<div class="block-card-content cite">{{ item.registerLogout ?item.registerLogout.femaleLogout || 0 :0 }}</div>
<div class="block-card-title">{{ $t('pages.dashboard.dailyRegisterUser.femaleLogout') }}</div>
<div class="block-card-content cite">{{ item.registerLogout ? item.registerLogout.femaleLogout || 0 : 0 }}</div>
</div>
</el-col>
<el-col class="card-col" :md="24" :xs="24">
<line-graph-charts :key="item.sysOrigin" :charts-data="item.lastMonths" height="300px" />
<line-graph-charts :key="item.sysOrigin + '-' + language" :charts-data="item.lastMonths" height="300px" />
</el-col>
</el-row>
</div>
@ -52,6 +52,7 @@
import { latestRegisterLogout } from '@/api/statistics'
import LineGraphCharts from './line-graph-charts'
import { mapGetters } from 'vuex'
export default {
name: 'DilyRegisterUser',
components: { LineGraphCharts },
@ -62,7 +63,7 @@ export default {
}
},
computed: {
...mapGetters(['permissionsSysOriginPlatformAlls']),
...mapGetters(['permissionsSysOriginPlatformAlls', 'language']),
sysOriginPlatforms() {
if (!this.permissionsSysOriginPlatformAlls || this.permissionsSysOriginPlatformAlls.length <= 0) {
return []
@ -78,15 +79,14 @@ export default {
},
methods: {
renderData() {
const that = this
that.loading = true
this.loading = true
latestRegisterLogout().then(res => {
that.loading = false
this.loading = false
const { body } = res
const list = body || []
that.list = list.filter(item => !item.sysOrigin || that.permissionsSysOriginPlatformAlls.some(sys => sys.value === item.sysOrigin))
this.list = list.filter(item => !item.sysOrigin || this.permissionsSysOriginPlatformAlls.some(sys => sys.value === item.sysOrigin))
}).catch(() => {
that.loading = false
this.loading = false
})
}
}
@ -97,15 +97,16 @@ export default {
.box-card {
margin-bottom: 10px;
padding: 10px;
.card-col {
margin-bottom: 10px;
}
}
.register-logout-count {
.date {
font-weight: bold;
line-height: 30px;
}
.date {
font-weight: bold;
line-height: 30px;
}
}
</style>

View File

@ -1,12 +1,13 @@
<template>
<div class="register-logout-count-charts">
<div id="videoCharts" ref="videoCharts" :style="'width: 100%;height:'+ height +';'" />
<div id="videoCharts" ref="videoCharts" :style="'width: 100%;height:' + height + ';'" />
</div>
</template>
<script>
import { mapGetters } from 'vuex'
import { formatDate } from '@/utils'
export default {
name: 'RegisterLogoutCountCharts',
props: {
@ -24,19 +25,29 @@ export default {
videoRealTimeCharts: null
}
},
created() {
computed: {
...mapGetters(['language'])
},
watch: {
chartsData: {
handler() {
this.renderCharts()
},
deep: true
},
language() {
this.renderCharts()
}
},
mounted() {
const that = this
that.videoRealTimeCharts = that.$echarts.init(that.$refs.videoCharts)
that.renderCharts()
this.videoRealTimeCharts = this.$echarts.init(this.$refs.videoCharts)
this.renderCharts()
window.addEventListener('resize', () => {
that.videoRealTimeCharts.resize()
this.videoRealTimeCharts.resize()
})
},
methods: {
handleListToChartsData() {
const that = this
const chartsData = {
newMales: [],
maleLogouts: [],
@ -47,8 +58,8 @@ export default {
const maleLogouts = chartsData.maleLogouts
const newFamles = chartsData.newFamles
const femaleLogouts = chartsData.femaleLogouts
if (that.chartsData.length > 0) {
that.chartsData.forEach(item => {
if (this.chartsData.length > 0) {
this.chartsData.forEach(item => {
const date = formatDate(item.statisticsTime, 'yyyy-MM-dd')
newMales.push([date, (item.maleCount || 0)])
maleLogouts.push([date, (item.maleLogout || 0)])
@ -59,9 +70,11 @@ export default {
return chartsData
},
renderCharts() {
const that = this
const thatDayData = that.handleListToChartsData()
that.videoRealTimeCharts.setOption({
if (!this.videoRealTimeCharts) {
return
}
const thatDayData = this.handleListToChartsData()
this.videoRealTimeCharts.setOption({
color: ['#66b3ff', '#ce90e8', '#ff9c6e', '#5cdbd3'],
tooltip: {
trigger: 'axis',
@ -84,23 +97,21 @@ export default {
borderColor: '#eee'
},
toolbox: { color: ['#1e90ff', '#1e90ff', '#1e90ff', '#1e90ff'], effectiveColor: '#ff4500' },
xAxis: [
{
show: false,
type: 'category',
boundaryGap: false,
splitArea: {
show: true,
areaStyle: { color: ['rgba(250,250,250,0.1)', 'rgba(200,200,200,0.1)'] }
},
axisLabel: {
rotate: 38
},
axisLine: { lineStyle: { color: '#b7bdc7' }},
splitLine: { lineStyle: { color: ['#eee'] }},
splitNumber: 24
}
],
xAxis: [{
show: false,
type: 'category',
boundaryGap: false,
splitArea: {
show: true,
areaStyle: { color: ['rgba(250,250,250,0.1)', 'rgba(200,200,200,0.1)'] }
},
axisLabel: {
rotate: 38
},
axisLine: { lineStyle: { color: '#b7bdc7' }},
splitLine: { lineStyle: { color: ['#eee'] }},
splitNumber: 24
}],
yAxis: [{
type: 'value',
axisTick: { show: true, length: 0 },
@ -111,9 +122,9 @@ export default {
legend: { y: 10, textStyle: { color: '#8e929b' }},
series: [
{
name: '新增男生',
name: this.$t('pages.dashboard.dailyRegisterUser.newMale'),
type: 'line',
stack: '新增男生',
stack: this.$t('pages.dashboard.dailyRegisterUser.newMale'),
smooth: 0.6,
symbol: 'none',
symbolSize: 10,
@ -121,9 +132,9 @@ export default {
areaStyle: {}
},
{
name: '新增女生',
name: this.$t('pages.dashboard.dailyRegisterUser.newFemale'),
type: 'line',
stack: '新增女生',
stack: this.$t('pages.dashboard.dailyRegisterUser.newFemale'),
smooth: 0.6,
symbol: 'none',
symbolSize: 10,
@ -131,9 +142,9 @@ export default {
areaStyle: {}
},
{
name: '注销男生',
name: this.$t('pages.dashboard.dailyRegisterUser.logoutMale'),
type: 'line',
stack: '注销男生',
stack: this.$t('pages.dashboard.dailyRegisterUser.logoutMale'),
smooth: 0.6,
symbol: 'none',
symbolSize: 10,
@ -141,15 +152,16 @@ export default {
areaStyle: {}
},
{
name: '注销女生',
name: this.$t('pages.dashboard.dailyRegisterUser.logoutFemale'),
type: 'line',
stack: '新增女生',
stack: this.$t('pages.dashboard.dailyRegisterUser.newFemale'),
smooth: 0.6,
symbol: 'none',
symbolSize: 10,
data: thatDayData.femaleLogouts,
areaStyle: {}
}]
}
]
}, true)
}
}

View File

@ -1,13 +1,13 @@
<template>
<div class="dily-register-user">
<div class="dily-register-user" :data-language="language">
<el-row :gutter="10">
<el-col :span="24">
<div v-for="(item, index) in list" :key="index">
<el-card v-if="sysOriginPlatforms.includes(item.sysOrigin)" class="box-card">
<div slot="header" class="clearfix">
<span>近12月内购({{ item.sysOriginName }})</span>
<span>{{ $t('pages.dashboard.monthlyPurchase.title', { sysOriginName: item.sysOriginName }) }}</span>
</div>
<div v-loading="loading" style="padding: 10px 0px">
<div v-loading="loading" style="padding: 10px 0">
<el-row :gutter="10" class="daily-purchase">
<el-col :span="24">
<div class="date">{{ item.last ? item.last.dateNumber : '' }}</div>
@ -27,17 +27,17 @@
<el-col :md="6" :xs="24">
<div class="block-card">
<div class="block-card-title">H5</div>
<div class="block-card-content cite">{{ item.last ? item.last.h5Quantity || 0 :0 }}</div>
<div class="block-card-content cite">{{ item.last ? item.last.h5Quantity || 0 : 0 }}</div>
</div>
</el-col>
<el-col :md="6" :xs="24">
<div class="block-card">
<div class="block-card-title">有效/退款</div>
<div class="block-card-content cite">{{ item.last ?item.last.completeQuantity || 0 :0 }} / {{ item.last ?item.last.reimburseQuantity || 0 :0 }}</div>
<div class="block-card-title">{{ $t('pages.dashboard.monthlyPurchase.validRefund') }}</div>
<div class="block-card-content cite">{{ item.last ? item.last.completeQuantity || 0 : 0 }} / {{ item.last ? item.last.reimburseQuantity || 0 : 0 }}</div>
</div>
</el-col>
<el-col class="card-col" :md="24" :xs="24">
<line-graph-charts :key="item.sysOrigin" :charts-data="item.lastMonths" height="300px" />
<line-graph-charts :key="item.sysOrigin + '-' + language" :charts-data="item.lastMonths" height="300px" />
</el-col>
</el-row>
</div>
@ -63,7 +63,7 @@ export default {
}
},
computed: {
...mapGetters(['permissionsSysOriginPlatformAlls']),
...mapGetters(['permissionsSysOriginPlatformAlls', 'language']),
sysOriginPlatforms() {
if (!this.permissionsSysOriginPlatformAlls || this.permissionsSysOriginPlatformAlls.length <= 0) {
return []
@ -79,15 +79,14 @@ export default {
},
methods: {
renderData() {
const that = this
that.loading = true
this.loading = true
latestMonthlyPurchaseAmountPlatform().then(res => {
that.loading = false
this.loading = false
const { body } = res
const list = body || []
that.list = list.filter(item => !item.sysOrigin || that.permissionsSysOriginPlatformAlls.some(sys => sys.value === item.sysOrigin))
this.list = list.filter(item => !item.sysOrigin || this.permissionsSysOriginPlatformAlls.some(sys => sys.value === item.sysOrigin))
}).catch(() => {
that.loading = false
this.loading = false
})
}
}
@ -98,15 +97,16 @@ export default {
.box-card {
margin-bottom: 10px;
padding: 10px;
.card-col {
margin-bottom: 10px;
}
}
.daily-purchase {
.date {
font-weight: bold;
line-height: 30px;
}
.date {
font-weight: bold;
line-height: 30px;
}
}
</style>

View File

@ -1,12 +1,12 @@
<template>
<div class="daily-currency-charts">
<div id="videoCharts" ref="videoCharts" :style="'width: 100%;height:'+ height +';'" />
<div id="videoCharts" ref="videoCharts" :style="'width: 100%;height:' + height + ';'" />
</div>
</template>
<script>
import { mapGetters } from 'vuex'
import { formatDate } from '@/utils'
export default {
name: 'DailyCurrencyCharts',
props: {
@ -24,19 +24,29 @@ export default {
videoRealTimeCharts: null
}
},
created() {
computed: {
...mapGetters(['language'])
},
watch: {
chartsData: {
handler() {
this.renderCharts()
},
deep: true
},
language() {
this.renderCharts()
}
},
mounted() {
const that = this
that.videoRealTimeCharts = that.$echarts.init(that.$refs.videoCharts)
that.renderCharts()
this.videoRealTimeCharts = this.$echarts.init(this.$refs.videoCharts)
this.renderCharts()
window.addEventListener('resize', () => {
that.videoRealTimeCharts.resize()
this.videoRealTimeCharts.resize()
})
},
methods: {
handleListToChartsData() {
const that = this
const dataCharts = {
androidQuantity: [],
iosQuantity: [],
@ -45,8 +55,8 @@ export default {
reimburseQuantity: [],
offlineQuantity: []
}
if (that.chartsData.length > 0) {
that.chartsData.forEach(item => {
if (this.chartsData.length > 0) {
this.chartsData.forEach(item => {
const date = item.dateNumber
dataCharts.androidQuantity.push([date, (item.androidQuantity || 0)])
dataCharts.iosQuantity.push([date, (item.iosQuantity || 0)])
@ -59,9 +69,11 @@ export default {
return dataCharts
},
renderCharts() {
const that = this
const thatDayData = that.handleListToChartsData()
that.videoRealTimeCharts.setOption({
if (!this.videoRealTimeCharts) {
return
}
const thatDayData = this.handleListToChartsData()
this.videoRealTimeCharts.setOption({
color: ['#3eda86', '#303133', '#e9640e', '#6c4c49', '#66b3ff', '#ce90e8', '#ff9c6e', '#5cdbd3', '#E6A23C'],
tooltip: {
trigger: 'axis',
@ -84,23 +96,21 @@ export default {
borderColor: '#eee'
},
toolbox: { color: ['#1e90ff', '#1e90ff', '#1e90ff', '#1e90ff'], effectiveColor: '#ff4500' },
xAxis: [
{
show: false,
type: 'category',
boundaryGap: false,
splitArea: {
show: true,
areaStyle: { color: ['rgba(250,250,250,0.1)', 'rgba(200,200,200,0.1)'] }
},
axisLabel: {
rotate: 38
},
axisLine: { lineStyle: { color: '#b7bdc7' }},
splitLine: { lineStyle: { color: ['#eee'] }},
splitNumber: 24
}
],
xAxis: [{
show: false,
type: 'category',
boundaryGap: false,
splitArea: {
show: true,
areaStyle: { color: ['rgba(250,250,250,0.1)', 'rgba(200,200,200,0.1)'] }
},
axisLabel: {
rotate: 38
},
axisLine: { lineStyle: { color: '#b7bdc7' }},
splitLine: { lineStyle: { color: ['#eee'] }},
splitNumber: 24
}],
yAxis: [{
type: 'value',
axisTick: { show: true, length: 0 },
@ -154,9 +164,9 @@ export default {
areaStyle: {}
},
{
name: '有效',
name: this.$t('pages.dashboard.monthlyPurchase.valid'),
type: 'line',
stack: '有效',
stack: this.$t('pages.dashboard.monthlyPurchase.valid'),
smooth: 0.6,
symbol: 'none',
symbolSize: 10,
@ -164,15 +174,16 @@ export default {
areaStyle: {}
},
{
name: '退款',
name: this.$t('pages.dashboard.monthlyPurchase.refund'),
type: 'line',
stack: '退款',
stack: this.$t('pages.dashboard.monthlyPurchase.refund'),
smooth: 0.6,
symbol: 'none',
symbolSize: 10,
data: thatDayData.reimburseQuantity,
areaStyle: {}
}]
}
]
}, true)
}
}

View File

@ -1,6 +1,6 @@
<template>
<div class="daily-currency-charts">
<el-select v-model="typeValue" placeholder="请选择" size="mini" @change="typeValueChange">
<div class="daily-currency-charts" :data-language="language">
<el-select v-model="typeValue" :placeholder="$t('pages.dashboard.shared.select')" size="mini" @change="typeValueChange">
<el-option
v-for="item in types"
:key="item.value"
@ -8,13 +8,12 @@
:value="item.value"
/>
</el-select>
<div id="barCharts" ref="barCharts" :style="'width: 100%;height:'+ height +';'" />
<div id="barCharts" ref="barCharts" :style="'width: 100%;height:' + height + ';'" />
</div>
</template>
<script>
// import { currencyOrigins } from '@/constant/type'
import { mapGetters } from 'vuex'
export default {
props: {
@ -29,10 +28,6 @@ export default {
},
data() {
return {
types: [
{ value: 0, name: '收入' },
{ value: 1, name: '支出' }
],
dateNumber: '',
typeValue: 0,
barCharts: null,
@ -42,37 +37,47 @@ export default {
}
}
},
computed: {
...mapGetters(['language']),
types() {
const language = this.language
return [
{ value: 0, name: this.$t('pages.dashboard.shared.income') },
{ value: 1, name: this.$t('pages.dashboard.shared.expenditure') }
]
}
},
watch: {
chartsData: {
handler(newVal) {
const that = this
that.groupChartsData = that.groupChartsDataByType()
that.$nextTick(() => {
that.renderCharts()
handler() {
this.groupChartsData = this.groupChartsDataByType()
this.$nextTick(() => {
this.renderCharts()
})
},
immediate: true
},
language() {
this.$nextTick(() => {
this.renderCharts()
})
}
},
created() {
},
mounted() {
const that = this
that.barCharts = that.$echarts.init(that.$refs.barCharts)
that.renderCharts()
this.barCharts = this.$echarts.init(this.$refs.barCharts)
this.renderCharts()
window.addEventListener('resize', () => {
that.barCharts.resize()
this.barCharts.resize()
})
},
methods: {
groupChartsDataByType() {
const that = this
const obj = {
income: [],
expenditure: []
}
if (that.chartsData.length > 0) {
that.chartsData.forEach(item => {
if (this.chartsData.length > 0) {
this.chartsData.forEach(item => {
if (item.type === 0) {
obj.income.push(item)
}
@ -84,7 +89,6 @@ export default {
return obj
},
getBarData(list) {
const that = this
const dataKeys = []
const dataList = []
if (!list || list.length === 0) {
@ -99,16 +103,18 @@ export default {
name: item.originName,
value: item.quantity,
itemStyle: {
color: that.getRandomColor()
color: this.getRandomColor()
}
})
}
return { keys: dataKeys, data: dataList }
},
renderCharts() {
const that = this
const barData = that.getBarData(that.typeValue === 0 ? that.groupChartsData.income : that.groupChartsData.expenditure)
that.barCharts.setOption({
if (!this.barCharts) {
return
}
const barData = this.getBarData(this.typeValue === 0 ? this.groupChartsData.income : this.groupChartsData.expenditure)
this.barCharts.setOption({
color: ['#66b3ff', '#ce90e8', '#ff9c6e', '#5cdbd3'],
tooltip: {
trigger: 'axis',
@ -130,11 +136,9 @@ export default {
containLabel: true,
borderColor: '#eee'
},
xAxis: [
{
show: false
}
],
xAxis: [{
show: false
}],
yAxis: [{
type: 'category',
data: barData.keys,
@ -146,7 +150,7 @@ export default {
width: 15,
overflow: 'truncate',
ellipsis: '...',
formatter: function(value) {
formatter(value) {
return value.length > 10 ? value.substring(0, 10) + '...' : value
}
}
@ -173,9 +177,8 @@ export default {
return 'rgba(' + r + ',' + g + ',' + b + ',0.8)'
},
typeValueChange() {
const that = this
that.$nextTick(() => {
that.renderCharts()
this.$nextTick(() => {
this.renderCharts()
})
}
}

View File

@ -1,10 +1,10 @@
<template>
<div v-loading="loading" class="dily-register-user">
<div v-loading="loading" class="dily-register-user" :data-language="language">
<el-date-picker
v-model="dateNumber"
style="margin-bottom: 10px;"
type="date"
placeholder="选择日期"
:placeholder="$t('pages.dashboard.userGoldTop.datePlaceholder')"
value-format="yyyyMMdd"
size="mini"
:picker-options="pickerOptions"
@ -14,10 +14,10 @@
<div v-for="(item, index) in list" :key="index">
<el-card v-if="sysOriginPlatforms.includes(item.sysOrigin)" class="box-card">
<div slot="header" class="clearfix">
<span>金币收支Top50({{ item.sysOriginName }})</span>
<span>{{ $t('pages.dashboard.userGoldTop.title', { sysOriginName: item.sysOriginName }) }}</span>
</div>
<div class="content">
<div style="font-size: 12px;color: #666;margin-bottom: 10px;">当日金币收支抽取前TOP50, 不含外部游戏</div>
<div style="font-size: 12px;color: #666;margin-bottom: 10px;">{{ $t('pages.dashboard.userGoldTop.description') }}</div>
<el-table
:data="item.items"
element-loading-text="Loading"
@ -27,25 +27,25 @@
<el-table-column label="No" width="50" align="center">
<template slot-scope="scope">{{ scope.$index + 1 }}</template>
</el-table-column>
<el-table-column prop="roomProfile.roomName" label="用户" align="center" min-width="200">
<el-table-column :label="$t('pages.dashboard.userGoldTop.user')" prop="roomProfile.roomName" 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="收入" width="100" align="center">
<el-table-column :label="$t('pages.dashboard.userGoldTop.income')" width="100" align="center">
<template slot-scope="scope">
<div>{{ scope.row.groupTypeQuantity['0'] || 0 }}</div>
</template>
</el-table-column>
<el-table-column label="支出" width="100" align="center">
<el-table-column :label="$t('pages.dashboard.userGoldTop.expenditure')" width="100" align="center">
<template slot-scope="scope">
<div>{{ scope.row.groupTypeQuantity['1'] || 0 }}</div>
</template>
</el-table-column>
<el-table-column prop="quantity" label="合计" align="center" width="100" />
<el-table-column fixed="right" label="操作" align="center" width="80">
<el-table-column :label="$t('pages.dashboard.userGoldTop.total')" prop="quantity" align="center" width="100" />
<el-table-column fixed="right" :label="$t('pages.dashboard.userGoldTop.action')" align="center" width="80">
<template slot-scope="scope">
<el-button type="text" @click.native="clickQueryDetails(scope.row)">详情</el-button>
<el-button type="text" @click.native="clickQueryDetails(scope.row)">{{ $t('pages.dashboard.userGoldTop.details') }}</el-button>
</template>
</el-table-column>
</el-table>
@ -55,8 +55,8 @@
<div class="user-daily-currency-gold-top-drawer">
<el-drawer
title="详情"
:before-close="() => queryDetailsVisible=false"
:title="$t('pages.dashboard.userGoldTop.details')"
:before-close="() => queryDetailsVisible = false"
:visible="queryDetailsVisible"
:close-on-press-escape="false"
:wrapper-closable="false"
@ -64,13 +64,12 @@
:append-to-body="true"
custom-class="drawer-auto-layout"
>
<div class="bar-graph-charts" style="padding: 0px 10px">
<div class="bar-graph-charts" style="padding: 0 10px">
<bar-graph-charts
ref="lineGraphCharts"
:key="queryDetailsRow ? queryDetailsRow.userId : 'tmp'"
:key="(queryDetailsRow ? queryDetailsRow.userId : 'tmp') + '-' + language"
:charts-data="queryDetailsRow.items"
:height="(queryDetailsRow.items ? queryDetailsRow.items.length > 5 ? queryDetailsRow.items.length * 40 : queryDetailsRow.items.length * 80: 300)+'px'"
:height="(queryDetailsRow.items ? queryDetailsRow.items.length > 5 ? queryDetailsRow.items.length * 40 : queryDetailsRow.items.length * 80 : 300) + 'px'"
/>
</div>
</el-drawer>
@ -105,7 +104,7 @@ export default {
}
},
computed: {
...mapGetters(['permissionsSysOriginPlatformAlls']),
...mapGetters(['permissionsSysOriginPlatformAlls', 'language']),
sysOriginPlatforms() {
if (!this.permissionsSysOriginPlatformAlls || this.permissionsSysOriginPlatformAlls.length <= 0) {
return []
@ -118,30 +117,31 @@ export default {
},
methods: {
loadOrigin() {
const that = this
that.loading = true
this.loading = true
listDailyUserGoldTop(this.dateNumber).then(res => {
that.loading = false
this.loading = false
const { body } = res
const list = body || []
that.list = list.filter(item => !item.sysOrigin || that.permissionsSysOriginPlatformAlls.some(sys => sys.value === item.sysOrigin))
this.list = list.filter(item => !item.sysOrigin || this.permissionsSysOriginPlatformAlls.some(sys => sys.value === item.sysOrigin))
}).catch(() => {
that.loading = false
this.loading = false
})
},
querySearch(queryString, cb) {
var restaurants = currencyOrigins
var results = queryString ? restaurants.filter(this.createFilter(queryString)) : restaurants
const restaurants = currencyOrigins
const results = queryString ? restaurants.filter(this.createFilter(queryString)) : restaurants
cb(results)
},
createFilter(queryString) {
return (restaurant) => {
return (restaurant.value.toLowerCase().indexOf(queryString.toLowerCase()) >= 0) || (restaurant.name.toLowerCase().indexOf(queryString.toLowerCase()) >= 0)
return restaurant => {
return restaurant.value.toLowerCase().indexOf(queryString.toLowerCase()) >= 0 ||
restaurant.name.toLowerCase().indexOf(queryString.toLowerCase()) >= 0
}
},
handleSelect(item) {
this.selectOrigin = item.value
this.selectOriginName = item.name
const selectedItem = item || currencyOrigins[0]
this.selectOrigin = selectedItem.value
this.selectOriginName = selectedItem.name
this.loadOrigin()
},
clickAnalyze() {
@ -162,15 +162,16 @@ export default {
.box-card {
margin-bottom: 10px;
padding: 10px;
.card-col {
margin-bottom: 10px;
}
}
.register-logout-count {
.date {
font-weight: bold;
line-height: 30px;
}
.date {
font-weight: bold;
line-height: 30px;
}
}
</style>

View File

@ -1,6 +1,6 @@
<template>
<div class="daily-currency-charts">
<el-select v-model="typeValue" placeholder="请选择" size="mini" @change="typeValueChange">
<div class="daily-currency-charts" :data-language="language">
<el-select v-model="typeValue" :placeholder="$t('pages.dashboard.shared.select')" size="mini" @change="typeValueChange">
<el-option
v-for="item in types"
:key="item.value"
@ -8,13 +8,12 @@
:value="item.value"
/>
</el-select>
<div id="barCharts" ref="barCharts" :style="'width: 100%;height:'+ height +';'" />
<div id="barCharts" ref="barCharts" :style="'width: 100%;height:' + height + ';'" />
</div>
</template>
<script>
// import { currencyOrigins } from '@/constant/type'
import { mapGetters } from 'vuex'
export default {
props: {
@ -29,10 +28,6 @@ export default {
},
data() {
return {
types: [
{ value: 0, name: '收入' },
{ value: 1, name: '支出' }
],
dateNumber: '',
typeValue: 0,
barCharts: null,
@ -42,37 +37,47 @@ export default {
}
}
},
computed: {
...mapGetters(['language']),
types() {
const language = this.language
return [
{ value: 0, name: this.$t('pages.dashboard.shared.income') },
{ value: 1, name: this.$t('pages.dashboard.shared.expenditure') }
]
}
},
watch: {
chartsData: {
handler(newVal) {
const that = this
that.groupChartsData = that.groupChartsDataByType()
that.$nextTick(() => {
that.renderCharts()
handler() {
this.groupChartsData = this.groupChartsDataByType()
this.$nextTick(() => {
this.renderCharts()
})
},
immediate: true
},
language() {
this.$nextTick(() => {
this.renderCharts()
})
}
},
created() {
},
mounted() {
const that = this
that.barCharts = that.$echarts.init(that.$refs.barCharts)
that.renderCharts()
this.barCharts = this.$echarts.init(this.$refs.barCharts)
this.renderCharts()
window.addEventListener('resize', () => {
that.barCharts.resize()
this.barCharts.resize()
})
},
methods: {
groupChartsDataByType() {
const that = this
const obj = {
income: [],
expenditure: []
}
if (that.chartsData.length > 0) {
that.chartsData.forEach(item => {
if (this.chartsData.length > 0) {
this.chartsData.forEach(item => {
if (item.type === 0) {
obj.income.push(item)
}
@ -84,7 +89,6 @@ export default {
return obj
},
getBarData(list) {
const that = this
const dataKeys = []
const dataList = []
if (!list || list.length === 0) {
@ -99,16 +103,18 @@ export default {
name: item.originName,
value: item.quantity,
itemStyle: {
color: that.getRandomColor()
color: this.getRandomColor()
}
})
}
return { keys: dataKeys, data: dataList }
},
renderCharts() {
const that = this
const barData = that.getBarData(that.typeValue === 0 ? that.groupChartsData.income : that.groupChartsData.expenditure)
that.barCharts.setOption({
if (!this.barCharts) {
return
}
const barData = this.getBarData(this.typeValue === 0 ? this.groupChartsData.income : this.groupChartsData.expenditure)
this.barCharts.setOption({
color: ['#66b3ff', '#ce90e8', '#ff9c6e', '#5cdbd3'],
tooltip: {
trigger: 'axis',
@ -130,11 +136,9 @@ export default {
containLabel: true,
borderColor: '#eee'
},
xAxis: [
{
show: false
}
],
xAxis: [{
show: false
}],
yAxis: [{
type: 'category',
data: barData.keys,
@ -146,7 +150,7 @@ export default {
width: 15,
overflow: 'truncate',
ellipsis: '...',
formatter: function(value) {
formatter(value) {
return value.length > 10 ? value.substring(0, 10) + '...' : value
}
}
@ -173,9 +177,8 @@ export default {
return 'rgba(' + r + ',' + g + ',' + b + ',0.8)'
},
typeValueChange() {
const that = this
that.$nextTick(() => {
that.renderCharts()
this.$nextTick(() => {
this.renderCharts()
})
}
}

View File

@ -1,10 +1,10 @@
<template>
<div v-loading="loading" class="dily-register-user">
<div v-loading="loading" class="dily-register-user" :data-language="language">
<el-date-picker
v-model="dateNumber"
style="margin-bottom: 10px;"
type="date"
placeholder="选择日期"
:placeholder="$t('pages.dashboard.rechargeTop.datePlaceholder')"
value-format="yyyyMMdd"
size="mini"
:picker-options="pickerOptions"
@ -14,10 +14,10 @@
<div v-for="(item, index) in list" :key="index">
<el-card v-if="sysOriginPlatforms.includes(item.sysOrigin)" class="box-card">
<div slot="header" class="clearfix">
<span>充值Top50({{ item.sysOriginName }})</span>
<span>{{ $t('pages.dashboard.rechargeTop.title', { sysOriginName: item.sysOriginName }) }}</span>
</div>
<div class="content">
<div style="font-size: 12px;color: #666;margin-bottom: 10px;">当日充值总额, 包含退款</div>
<div style="font-size: 12px;color: #666;margin-bottom: 10px;">{{ $t('pages.dashboard.rechargeTop.description') }}</div>
<el-table
:data="item.items"
element-loading-text="Loading"
@ -27,12 +27,12 @@
<el-table-column label="No" width="50" align="center">
<template slot-scope="scope">{{ scope.$index + 1 }}</template>
</el-table-column>
<el-table-column prop="roomProfile.roomName" label="用户" align="center" min-width="200">
<el-table-column :label="$t('pages.dashboard.rechargeTop.user')" prop="roomProfile.roomName" 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="金额" min-width="100" align="center">
<el-table-column :label="$t('pages.dashboard.rechargeTop.amount')" min-width="100" align="center">
<template slot-scope="scope">
<div>{{ scope.row.quantity || 0 }}</div>
</template>
@ -41,20 +41,17 @@
</div>
</el-card>
</div>
</div>
</template>
<script>
import { listDailyUserRechargeTop } from '@/api/statistics'
// import BarGraphCharts from './bar-graph-charts'
import { currencyOrigins } from '@/constant/type'
import { mapGetters } from 'vuex'
import { formatDate, beforeDateObject } from '@/utils'
export default {
name: 'DilyRegisterUser',
// components: { BarGraphCharts },
data() {
return {
pickerOptions: {
@ -71,7 +68,7 @@ export default {
}
},
computed: {
...mapGetters(['permissionsSysOriginPlatformAlls']),
...mapGetters(['permissionsSysOriginPlatformAlls', 'language']),
sysOriginPlatforms() {
if (!this.permissionsSysOriginPlatformAlls || this.permissionsSysOriginPlatformAlls.length <= 0) {
return []
@ -84,30 +81,31 @@ export default {
},
methods: {
loadOrigin() {
const that = this
that.loading = true
this.loading = true
listDailyUserRechargeTop(this.dateNumber).then(res => {
that.loading = false
this.loading = false
const { body } = res
const list = body || []
that.list = list.filter(item => !item.sysOrigin || that.permissionsSysOriginPlatformAlls.some(sys => sys.value === item.sysOrigin))
this.list = list.filter(item => !item.sysOrigin || this.permissionsSysOriginPlatformAlls.some(sys => sys.value === item.sysOrigin))
}).catch(() => {
that.loading = false
this.loading = false
})
},
querySearch(queryString, cb) {
var restaurants = currencyOrigins
var results = queryString ? restaurants.filter(this.createFilter(queryString)) : restaurants
const restaurants = currencyOrigins
const results = queryString ? restaurants.filter(this.createFilter(queryString)) : restaurants
cb(results)
},
createFilter(queryString) {
return (restaurant) => {
return (restaurant.value.toLowerCase().indexOf(queryString.toLowerCase()) >= 0) || (restaurant.name.toLowerCase().indexOf(queryString.toLowerCase()) >= 0)
return restaurant => {
return restaurant.value.toLowerCase().indexOf(queryString.toLowerCase()) >= 0 ||
restaurant.name.toLowerCase().indexOf(queryString.toLowerCase()) >= 0
}
},
handleSelect(item) {
this.selectOrigin = item.value
this.selectOriginName = item.name
const selectedItem = item || currencyOrigins[0]
this.selectOrigin = selectedItem.value
this.selectOriginName = selectedItem.name
this.loadOrigin()
},
changeDateNumber() {
@ -121,15 +119,16 @@ export default {
.box-card {
margin-bottom: 10px;
padding: 10px;
.card-col {
margin-bottom: 10px;
}
}
.register-logout-count {
.date {
font-weight: bold;
line-height: 30px;
}
.date {
font-weight: bold;
line-height: 30px;
}
}
</style>

View File

@ -1,8 +1,12 @@
<template>
<div class="customize-charts">
<div class="customize-charts" :data-language="language">
<el-row :gutter="10">
<el-col v-for="item in tablePermissions" :key="item.component" :md="4" :sm="8" :xs="12">
<div class="charts-tag flex-c" :class="{'charts-tag-selected': activeName === item.component}" @click="clickTag(item)">
<div
class="charts-tag flex-c"
:class="{ 'charts-tag-selected': activeName === item.component }"
@click="clickTag(item)"
>
<div class="nowrap-ellipsis">
{{ item.title }}
</div>
@ -30,58 +34,69 @@ import { mapGetters } from 'vuex'
export default {
name: 'DashboardCharts',
components: { UserDailyCurrencyRechargeTop, UserDailyCurrencyGoldTop, DailyCurrencyGoldOriginTop, DailyRegisterUser, DailyCurrency, DailyPurchase, MonthlyPurchase, ActiveIndex, PropsSalesOverviewCharts, DailyCurrencyGold },
components: {
UserDailyCurrencyRechargeTop,
UserDailyCurrencyGoldTop,
DailyCurrencyGoldOriginTop,
DailyRegisterUser,
DailyCurrency,
DailyPurchase,
MonthlyPurchase,
ActiveIndex,
PropsSalesOverviewCharts,
DailyCurrencyGold
},
data() {
return {
activeName: '',
tables: {
'dashboard:added:daily': {
title: '每日新增',
titleKey: 'pages.dashboard.chartTabs.dailyAdded',
component: 'DailyRegisterUser',
sort: 1
},
'dashboard:active:situation': {
title: '活跃情况',
titleKey: 'pages.dashboard.chartTabs.activeOverview',
component: 'ActiveIndex',
sort: 2
},
'dashboard:daily:inapp:purchases': {
title: '每日内购',
titleKey: 'pages.dashboard.chartTabs.dailyPurchase',
component: 'DailyPurchase',
sort: 3
},
'dashboard:monthly:inapp:purchases': {
title: '每月内购',
titleKey: 'pages.dashboard.chartTabs.monthlyPurchase',
component: 'MonthlyPurchase',
sort: 4
},
'props:sales:overview:charts': {
title: '道具销售情况',
titleKey: 'pages.dashboard.chartTabs.propsSales',
component: 'PropsSalesOverviewCharts',
sort: 5
},
'dashboard:money:balance': {
title: '货币收支',
titleKey: 'pages.dashboard.chartTabs.currencyBalance',
component: 'DailyCurrency',
sort: 6
},
'dashboard:daily:gold': {
title: '每日金币(分析)',
titleKey: 'pages.dashboard.chartTabs.dailyGoldAnalysis',
component: 'DailyCurrencyGold',
sort: 7
},
'dashboard:daily:gold:origin:top': {
title: '每日金币(来源榜)',
titleKey: 'pages.dashboard.chartTabs.dailyGoldSourceTop',
component: 'DailyCurrencyGoldOriginTop',
sort: 8
},
'dashboard:daily:gold:user:top': {
title: '用户金币(Top50)',
titleKey: 'pages.dashboard.chartTabs.userGoldTop',
component: 'UserDailyCurrencyGoldTop',
sort: 9
},
'dashboard:daily:recharge:rank': {
title: '每日充值(Top50)',
titleKey: 'pages.dashboard.chartTabs.dailyRechargeTop',
component: 'UserDailyCurrencyRechargeTop',
sort: 10
}
@ -89,8 +104,9 @@ export default {
}
},
computed: {
...mapGetters(['buttonPermissions']),
...mapGetters(['buttonPermissions', 'language']),
tablePermissions() {
const language = this.language
if (!this.buttonPermissions || this.buttonPermissions.length <= 0) {
return []
}
@ -102,7 +118,10 @@ export default {
}
const item = this.tables[permission]
if (item) {
tabMap[item.sort] = item
tabMap[item.sort] = {
...item,
title: this.$t(item.titleKey)
}
}
}
@ -134,14 +153,17 @@ export default {
color: #333;
background-color: #eaeff2;
}
.charts-tag:hover {
background-color: #dfe9ee;
}
.charts-tag-selected {
background-color: #cadce5;
}
}
</style>
<style scoped lang="scss">
.customize-charts {
.charts-tag {
@ -151,13 +173,10 @@ export default {
margin-bottom: 8px;
font-size: 12px;
overflow: hidden;
-webkit-border-radius: 2px;
-moz-border-radius: 2px;
border-radius: 2px;
cursor: pointer;
white-space: nowrap;
text-decoration: none;
}
}
</style>

View File

@ -1,16 +1,20 @@
<template>
<div class="dashboard">
<div class="dashboard" :data-language="language">
<div v-if="activeName">
<el-tabs v-model="activeName">
<el-tab-pane v-for="item in tablePermissions" :key="item.component" :label="item.title" :name="item.component" />
<el-tab-pane
v-for="item in tablePermissions"
:key="item.component"
:label="item.title"
:name="item.component"
/>
<component :is="activeName" />
</el-tabs>
</div>
<div v-else>
<div class="welcome">
<img src="@/assets/welcome.png" alt="">
内部运营, 数据管理系统
<!-- <sapn v-if="name">, 当前登录用户: {{ name }}</sapn> -->
{{ $t('pages.dashboard.welcome') }}
</div>
</div>
</div>
@ -21,6 +25,7 @@ import Introduction from './introduction'
import DashboardCharts from './dashboard-charts'
import { mapGetters } from 'vuex'
import { deepClone } from '@/utils'
export default {
name: 'Dashboard',
components: { DashboardCharts, Introduction },
@ -29,12 +34,12 @@ export default {
activeName: '',
tables: {
'dashboard:generalize': {
title: '概括',
titleKey: 'pages.dashboard.tabs.overview',
component: 'Introduction',
sort: 1
},
'dashboard:dashboard:charts': {
title: '数据图表',
titleKey: 'pages.dashboard.tabs.dataCharts',
component: 'DashboardCharts',
sort: 2
}
@ -42,8 +47,9 @@ export default {
}
},
computed: {
...mapGetters(['buttonPermissions', 'name']),
...mapGetters(['buttonPermissions', 'language']),
tablePermissions() {
const language = this.language
if (!this.buttonPermissions || this.buttonPermissions.length <= 0) {
return []
}
@ -57,7 +63,10 @@ export default {
}
const item = this.tables[permission]
if (item) {
tabMap[item.sort] = item
tabMap[item.sort] = {
...item,
title: this.$t(item.titleKey)
}
}
}
@ -83,17 +92,17 @@ export default {
if (this.tablePermissions && this.tablePermissions.length > 0) {
this.activeName = this.tablePermissions[0].component
}
},
methods: {
}
}
</script>
<style scoped lang="scss">
.welcome {
width: 60%;
padding: 100px 0px;
padding: 100px 0;
margin: auto;
color: #666666;
img {
width: 100%;
}

View File

@ -1,11 +1,11 @@
<template>
<div class="account-info region-card">
<div class="account-info region-card" :data-language="language">
<div class="card">
<div class="account-info flex-l">
<div class="avatar flex-c" :style="userAvatarColor">{{ firstNameStr }}</div>
<div class="info">
<div class="nickname">{{ name }}</div>
<div class="account">账号: {{ loginName }}</div>
<div class="account">{{ $t('pages.dashboard.account.accountLabel') }}: {{ loginName }}</div>
</div>
</div>
</div>
@ -15,27 +15,21 @@
<script>
import { mapGetters } from 'vuex'
import { extractColorByText } from '@/utils'
export default {
name: 'AccountInfo',
data() {
return {}
},
computed: {
...mapGetters(['buttonPermissions', 'name', 'loginName']),
...mapGetters(['buttonPermissions', 'name', 'loginName', 'language']),
userAvatarColor() {
return 'background-color:' + extractColorByText(this.name) + ';'
},
firstNameStr() {
return this.name ? this.name.trim().charAt(0) : '?'
}
},
created() {
},
methods: {
}
}
</script>
<style lang="scss">
.account-info {
.avatar {
@ -45,11 +39,11 @@ export default {
color: #FFFFFF;
font-weight: bold;
}
.info {
padding: 0 10px;
font-size: 12px;
line-height: 18px;
padding: 0px 10px;
}
}
</style>

View File

@ -1,9 +1,9 @@
<template>
<div class="my-native region-card">
<div class="my-native region-card" :data-language="language">
<div class="card">
<div class="title">我的导航</div>
<div class="subtitle">最近访问</div>
<div class="content ">
<div class="title">{{ $t('pages.dashboard.userNavigation.title') }}</div>
<div class="subtitle">{{ $t('pages.dashboard.userNavigation.recentVisits') }}</div>
<div class="content">
<el-row :gutter="10">
<el-col v-for="(item, index) in dashboard.recentPreviews" :key="index" :md="4" :sm="8" :xs="12">
<div
@ -12,12 +12,16 @@
@mouseenter="mouseoverRecentPreviews(item)"
>
<div class="tag region-tag-color flex-c">
<a :href="item.type === 'CUSTOM' ? item.link : 'javascript:void(0);'" :target="item.type === 'CUSTOM'?'_blank': ''" style="width: 100%" @click="clickCustomNavigations(item)">
<a
:href="item.type === 'CUSTOM' ? item.link : 'javascript:void(0);'"
:target="item.type === 'CUSTOM' ? '_blank' : ''"
style="width: 100%"
@click="clickCustomNavigations(item)"
>
<div class="nowrap-ellipsis" style="width: 100%">
{{ item.name }}
</div>
</a>
<!-- <i v-if="mouseoveRecentSeletedId === item.id" class="el-icon-error" style="font-size: 18px;" @click.stop="removeRecentPreviews(item)" /> -->
</div>
</div>
</el-col>
@ -25,10 +29,9 @@
</div>
<div class="native-content">
<div class="native-item">
<div class="subtitle">自定义快捷</div>
<div class="content ">
<div class="subtitle">{{ $t('pages.dashboard.userNavigation.customShortcuts') }}</div>
<div class="content">
<el-row :gutter="10">
<el-col v-for="(item, index) in dashboard.customNavigations" :key="index" :md="4" :sm="8" :xs="12">
<div
@ -41,39 +44,59 @@
{{ item.name }}
</div>
</a>
<i v-if="mouseoveCustomNavigationId === item.id" class="el-icon-error" style="font-size: 18px;" @click.stop="removeCustomNavigations(item)" />
<i
v-if="mouseoveCustomNavigationId === item.id"
class="el-icon-error"
style="font-size: 18px;"
@click.stop="removeCustomNavigations(item)"
/>
</div>
</div>
</el-col>
<el-col :md="4" :sm="8" :xs="12">
<el-popover v-model="customNavigationFormVisible" width="400" trigger="click">
<div class="add-native">
<!-- <el-tabs v-model="activeAddNative">
<el-tab-pane v-for="item in addNativeTables" :key="item.component" :label="item.title" :name="item.component" />
<component :is="activeAddNative" />
</el-tabs> -->
<div class="blockquote">您可以使用此功能自由添加任意页面至快捷入口目前仅支持 http https 协议</div>
<div class="blockquote">{{ $t('pages.dashboard.userNavigation.protocolTip') }}</div>
<el-form ref="customNavigationForm" :model="customNavigationForm" :rules="customNavigationFormRules">
<el-form-item label="入口名称" prop="name">
<el-input v-model="customNavigationForm.name" placeholder="自定义导航入口名称" minlength="1" maxlength="30" show-word-limit />
<el-form-item :label="$t('pages.dashboard.userNavigation.entryName')" prop="name">
<el-input
v-model="customNavigationForm.name"
:placeholder="$t('pages.dashboard.userNavigation.entryNamePlaceholder')"
minlength="1"
maxlength="30"
show-word-limit
/>
</el-form-item>
<el-form-item label="入口链接" prop="link">
<el-input v-model="customNavigationForm.link" placeholder="自定义导航入口链接" minlength="1" maxlength="300" show-word-limit />
<el-form-item :label="$t('pages.dashboard.userNavigation.entryLink')" prop="link">
<el-input
v-model="customNavigationForm.link"
:placeholder="$t('pages.dashboard.userNavigation.entryLinkPlaceholder')"
minlength="1"
maxlength="300"
show-word-limit
/>
</el-form-item>
<el-form-item>
<el-button type="primary" :loading="customNavigationFormLoading" :disabled="customNavigationFormLoading" @click="onSubmitCustomNavigationForm">添加</el-button>
<el-button
type="primary"
:loading="customNavigationFormLoading"
:disabled="customNavigationFormLoading"
@click="onSubmitCustomNavigationForm"
>
{{ $t('pages.dashboard.userNavigation.add') }}
</el-button>
</el-form-item>
</el-form>
</div>
<div slot="reference">
<div class="tag region-tag-color flex-c">
<div class="nowrap-ellipsis">
<i class="el-icon-circle-plus-outline" style="margin: 0px 3px; font-weight: bold;" /><strong>添加快捷入口</strong>
<i class="el-icon-circle-plus-outline" style="margin: 0 3px; font-weight: bold;" />
<strong>{{ $t('pages.dashboard.userNavigation.addShortcut') }}</strong>
</div>
</div>
</div>
</el-popover>
</el-col>
</el-row>
</div>
@ -86,23 +109,25 @@
<script>
import { mapGetters } from 'vuex'
import { addDashboardCustom, removeDashboardPreviews, removeDashboardCustom, addDashboardPreviews } from '@/api/ops-system'
export default {
name: 'MyNavigation',
data() {
var checkLink = (rule, value, callback) => {
const checkLink = (rule, value, callback) => {
if (!value) {
return callback(new Error('请输入支持 http 或 https 协议'))
return callback(new Error(this.$t('pages.dashboard.userNavigation.protocolValidation')))
}
if (value.startsWith('http://') || value.startsWith('https://')) {
return callback()
}
return callback(new Error('请输入支持 http 或 https 协议'))
return callback(new Error(this.$t('pages.dashboard.userNavigation.protocolValidation')))
}
return {
addNativeTables: [
{ title: '搜索添加', component: 'DailyPurchase' },
{ title: '自定义添加', component: 'MonthlyPurchase' }
{ titleKey: 'pages.dashboard.userNavigation.searchAdd', component: 'DailyPurchase' },
{ titleKey: 'pages.dashboard.userNavigation.customAdd', component: 'MonthlyPurchase' }
],
customNavigationFormVisible: false,
customNavigationFormLoading: false,
@ -113,7 +138,7 @@ export default {
link: ''
},
customNavigationFormRules: {
name: [{ required: true, message: '必填字段', trigger: 'blur' }],
name: [{ required: true, message: this.$t('pages.dashboard.userNavigation.requiredField'), trigger: 'blur' }],
link: [{ required: true, trigger: 'blur', validator: checkLink }]
},
mouseoveRecentSeletedId: '',
@ -121,15 +146,33 @@ export default {
}
},
computed: {
...mapGetters(['buttonPermissions', 'name', 'dashboard'])
...mapGetters(['buttonPermissions', 'name', 'dashboard', 'language'])
},
created() {
watch: {
language() {
this.customNavigationFormRules = {
name: [{ required: true, message: this.$t('pages.dashboard.userNavigation.requiredField'), trigger: 'blur' }],
link: [{
required: true,
trigger: 'blur',
validator: (rule, value, callback) => {
if (!value) {
return callback(new Error(this.$t('pages.dashboard.userNavigation.protocolValidation')))
}
if (value.startsWith('http://') || value.startsWith('https://')) {
return callback()
}
return callback(new Error(this.$t('pages.dashboard.userNavigation.protocolValidation')))
}
}]
}
}
},
methods: {
loadDashboard() {
this.$store.dispatch('user/getUserDashboard').then(res => {
console.log('success')
}).catch(er => {
this.$store.dispatch('user/getUserDashboard').catch(er => {
console.error('loadDashboard', er)
})
},
@ -146,59 +189,56 @@ export default {
this.$router.push(item.link)
}
},
mouseoutCustomNavigations(item) {
mouseoutCustomNavigations() {
this.mouseoveCustomNavigationId = ''
},
mouseoverCustomNavigations(item) {
this.mouseoveCustomNavigationId = item.id
},
removeCustomNavigations(item) {
const that = this
removeDashboardCustom(item.id).then(res => {
that.$opsMessage.success()
that.loadDashboard()
removeDashboardCustom(item.id).then(() => {
this.$opsMessage.success()
this.loadDashboard()
}).catch(er => {
console.error('removeCustomNavigations', er)
})
},
mouseoutRecentPreviews(item) {
mouseoutRecentPreviews() {
this.mouseoveRecentSeletedId = ''
},
mouseoverRecentPreviews(item) {
this.mouseoveRecentSeletedId = item.id
},
removeRecentPreviews(item) {
const that = this
removeDashboardPreviews(item.id).then(res => {
that.$opsMessage.success()
that.loadDashboard()
removeDashboardPreviews(item.id).then(() => {
this.$opsMessage.success()
this.loadDashboard()
}).catch(er => {
console.error('removeRecentPreviews', er)
})
},
onSubmitCustomNavigationForm() {
const that = this
that.$refs.customNavigationForm.validate((valid) => {
this.$refs.customNavigationForm.validate((valid) => {
if (!valid) {
console.error('error submit!!')
return false
}
that.customNavigationFormLoading = true
addDashboardCustom(that.customNavigationForm).then(res => {
that.$opsMessage.success()
that.customNavigationFormLoading = false
that.customNavigationFormVisible = false
that.loadDashboard()
}).catch(er => {
that.customNavigationFormLoading = false
that.customNavigationFormVisible = false
this.customNavigationFormLoading = true
addDashboardCustom(this.customNavigationForm).then(() => {
this.$opsMessage.success()
this.customNavigationFormLoading = false
this.customNavigationFormVisible = false
this.loadDashboard()
}).catch(() => {
this.customNavigationFormLoading = false
this.customNavigationFormVisible = false
})
})
}
}
}
</script>
<style scoped lang="scss">
.my-native {
.tag {
@ -207,15 +247,12 @@ export default {
line-height: 32px;
padding: 0 12px;
margin-bottom: 8px;
font-size: 12px;
overflow: hidden;
-webkit-border-radius: 2px;
-moz-border-radius: 2px;
border-radius: 2px;
font-size: 12px;
cursor: pointer;
white-space: nowrap;
text-decoration: none;
}
}
</style>

View File

@ -1,41 +1,10 @@
<template>
<div class="waiting-processing region-card">
<div class="waiting-processing region-card" :data-language="language">
<div class="card">
<div class="title">每日一文</div>
<div class="tips-text">{{ textKey }}</div>
<!-- <div class="content">
<el-row :gutter="10">
<el-col :md="8">
<div class="tag region-tag-color">
<div class="tag-title">待处理</div>
<div class="quantity">0</div>
</div>
</el-col>
<el-col :md="8">
<div class="tag region-tag-color">
<div class="tag-title">待定</div>
<div class="quantity">0</div>
</div>
</el-col>
<el-col :md="8">
<div class="tag region-tag-color">
<div class="tag-title">已处理</div>
<div class="quantity">0</div>
</div>
</el-col>
<el-col :span="24">
开发中, 敬请期待...
</el-col>
</el-row>
</div> -->
</div>
<div class="card">
<div class="title">系统时间</div>
<div class="title">{{ $t('pages.dashboard.systemTime.title') }}</div>
<div class="system-date">
<div>时区: {{ nowDateTimeZone }}</div>
<p>时间: {{ nowDateTime | dateFormat }}</p>
<div>{{ $t('pages.dashboard.systemTime.timezone') }}: {{ nowDateTimeZone }}</div>
<p>{{ $t('pages.dashboard.systemTime.time') }}: {{ nowDateTime | dateFormat }}</p>
</div>
</div>
</div>
@ -43,154 +12,34 @@
<script>
import { mapGetters } from 'vuex'
import Cookies from 'js-cookie'
import { getCurrentTimeZone } from '@/lang'
export default {
name: 'AccountInfo',
data() {
return {
nowDateTime: new Date(),
nowDateTimeZone: new Intl.DateTimeFormat().resolvedOptions().timeZone,
textKey: 'DailyText',
toipsText: '',
copywriting: [
'得到的失去的在乎的忘掉的都让它随风而去。',
'再多的不幸,都是曾经,都会过去,一如窗外的雨,淋过,湿过,走了,远了。曾经的美好,留于心底,曾经的悲伤,置于脑后,不恋,不恨。',
'别人相信的事实,未必是你的。',
'当你带着同情心去看时,你可以在你的心中创造一个美丽的空间。',
'有时候,记忆是甜的,照片无论怎么老旧泛黄。',
'当我回到我的中心,一个包裹着优雅的提醒等待着我:永远从温柔中流淌。',
'漫长的人生,其实只是瞬间,一定学着,让自己在人生途中,不后悔。',
'给事留一个机会;给人留一个空间,给己留一份尊严。有些事想不开别想,得不到别要;看不开,就背着;放不下,就记着;舍不得,就留着。晚安!',
'过分的理性,令女人失去了女人的属性。',
'强者创造事变,弱者受制于上帝给他铺排的事变。',
'快乐来自于以感激的心情,去接受眼前的生活。',
'少时浪费生命寻找金钱,老时浪费金钱寻找生命。',
'生活需要微笑,是微笑让这个世界变得更加温暖,变得更加的美丽生动,变得丰富多彩了有了微笑河流才那么清澈,有了微笑天空才那么蔚蓝。',
'大鸟是关不住的,它总要飞向天空。',
'用%的灵感加上%的汗水,去换取%的辉煌成就。',
'有信心就会有成功,有思索就会有思路,有努力就会有收获。',
'中国式管理,讲起来就是水的管理。和美国式管理偏向火的管理颇为不同。我们主张以柔克刚,先礼后兵,继旧开新,生生不息,无一不和水有关。晚安!',
'只有抵住最黑的暗,才能收获最光的亮。',
'人到无求多自由,人品若山极崇峻。',
'这个世界,我们第一次来,也是最后一次。',
'你改变不了过去,但你可以改变现在。',
'行不通的真理是假真理,说不通的道理是假道理。',
'清晨的暖阳,浩瀚的夜空,过去的美好,余下的人生。',
'有喜就有悲,有阴影也就有光,不悲观不乐观,活在当下最重要。',
'在我的生命中,从未遭受过失败,我所遇到的,都是暂时的挫折罢了。',
'清新的空气,快乐的气息,透过空气射入你的灵魂里,将阳光呼吸,将幸福抱起,泡一杯甜蜜的咖啡,品尝幸福的意义。',
'不管忙或是闲,放飞心情就是休闲。不管近或是远,内心宁静路就不远。',
'勇者,脚下都是路;智者,知道走哪一条路最好。',
'日久不一定生情,但必定见人心。有时候也怕,时间会说出真话。',
'你要让你的能力,配得上你的虚荣,让你的优秀配得上你的自尊,让你的视野配得上你的骄傲。',
'世界上最勇敢的事情是微笑着听你说你们之间的爱情,你的回忆里没有我,而我还听得那么认真。',
'女人对待感情常有惯性,有了爱,就希望能一直爱下去。',
'人生至高无上的幸福,莫过于确信自己被人所爱。',
'人生就是一个个选择,有对有错,选择了,就不要后悔。',
'给自己一个机会,重新开始。',
'有时候我们觉得累,是因为在人生的道路上,忘记了去哪。',
'经历了那么那么多,才知道自己不过是一个供人娱乐的玩具而已',
'有钱的人把自己的房子装饰得漂亮;有德的人把自己的身心修养得很好。',
'追逐梦想,寻觅梦想的清香。',
'能做自己的人非常少,但是必须努力呀。',
'工作做得好,你会更自豪。',
'既然选择了,就要咬牙坚持走下去。',
'年后的年轻美貌,与你是否每一天坚持喝颜如玉有关,保养,是女人最好的修行!',
'拼搏实现梦想,坚持铸就辉煌。',
'人生路上,留一点遗憾在生活中,也许比完美更觉得美!天地本不全,人间便不可能完整,也算是对不完美的缺憾,一种自我安慰吧!佛且这么说,何况我们人呢!',
'我们要学会在顺境中感恩和体会幸福,在逆境中成熟和坚强!',
'你从来就不应该从一个时刻跳到另一个时刻,而应该停留在你脚下的那个时刻。',
'当我们在责备别人时,岂不是也在间接宽容自己。',
'你的生活,源于你的选择。',
'我们的目的是什么?是胜利!不惜一切代价争取胜利!',
'我在书上看到过这样一句话,说是倘若我们的生命中遭遇到了遮蔽阳光的云层,那么唯一的理由是,我们的心灵飞得还不够高。所以我就在想啊,如果我能不断地努力,不断地到达一个更高的地方,那么是不是就再没有那么多能够遮蔽我视线的障碍了。晚安!',
'我与不属于我的东西和谐相处。我用爱让它自由自在。',
'人活着首先是要对自己负责,负责让自己这辈子都幸福。',
'我们很在乎得到,一旦拥有就难以放下。其实拥有只是短暂的,那些东西即使再好,和你再密不可分,到最后都会离你而去。',
'当你开始依照习惯行事,你的进取精神就会因此而丧失。',
'凡人看人是看你拥有多少钱财,上帝看人看的是你做了多少善事。',
'我希望你有足够的自由来瞥见你心中的爱,并对它的深处感到惊奇。',
'是你忘了带我走。我们就这样迷散在陌生的风雨里,从此天各一方,两两相望。',
'过去终是过去,那人,那事,那情,任你留恋,都是云烟。',
'如果学到的知识,却不能修养自己,帮助别人,那么学的知识又有什么用呢?',
'人与人之间的距离,要保持好,太近了会扎人,太远了会伤人。',
'不管见不见面,总有朋友把你惦念。早安,朋友!愿幸福一直在你身边!',
'锲而不舍你能发觉,喜欢上的便是运动自身!',
'如果我们一直消极的活着,那么这辈子就一点指望也没有了。',
'男人要有地位,但不能高得吓人。稻微一努力,自己就有可能站到他们的身边,这样的男人,才最令女人期待!',
'倒霉的时候要默默熬过去,不要声张否则留下的都是笑柄。',
'十六岁的时候看六十岁还很遥远,六十岁的时候看十六岁仿佛就在昨天。',
'赚别人的钱,越多越好,贫穷才是命运的最大的敌人。',
'一个人,经得起挫折,受得起委屈,方能为自己赢得美好的人生。',
'你没挽留,我没回头,如此余生各自安好,也没有谁不好,也许只是时间不凑巧吧。',
'只有自己足够强大,才会有用的话语权。',
'你不能控制他人,但你可以掌握自己。'
],
nowDateTimeZone: getCurrentTimeZone(),
timeIndexInterval: null
}
},
computed: {
...mapGetters(['buttonPermissions', 'name', 'loginName'])
...mapGetters(['language'])
},
created() {
const that = this
that.textKey = this.getDailyText()
if (that.timeIndexInterval) {
clearInterval(that.timeIndexInterval)
if (this.timeIndexInterval) {
clearInterval(this.timeIndexInterval)
}
that.timeIndexInterval = setInterval(() => {
that.nowDateTime = new Date()
this.timeIndexInterval = setInterval(() => {
this.nowDateTime = new Date()
}, 1000)
},
methods: {
getDailyText() {
try {
const text = Cookies.get(this.textKey)
if (!text) {
const randomText = this.copywriting[Math.floor((Math.random() * this.copywriting.length))] || this.copywriting[0]
const date = new Date()
Cookies.set(this.textKey, `${date.getFullYear()}${date.getMonth()}${date.getDate()}#${randomText}`)
return randomText
}
return text.split('#')[1]
} catch (er) {
return this.copywriting[Math.floor((Math.random() * this.copywriting.length))] || this.copywriting[0]
}
beforeDestroy() {
if (this.timeIndexInterval) {
clearInterval(this.timeIndexInterval)
this.timeIndexInterval = null
}
}
}
</script>
<style lang="scss">
</style>
<style scoped lang="scss">
.waiting-processing {
.content {
font-size: 12px;
line-height: 18px;
.tag {
width: 100%;
line-height: 32px;
padding: 5px 12px;
margin-bottom: 8px;
font-size: 12px;
overflow: hidden;
-webkit-border-radius: 2px;
-moz-border-radius: 2px;
border-radius: 2px;
cursor: pointer;
white-space: nowrap;
text-decoration: none;
.quantity {
font-weight: bold;
font-size: 24px;
}
}
}
}
</style>

View File

@ -1,9 +1,9 @@
<template>
<div class="quick-link">
<div class="quick-link" :data-language="language">
<el-card>
<div class="content">
<el-row :gutter="12">
<el-col v-for="(item,index) in links" :key="index" class="item-card" :sm="6" @click.native="clickCard(item)">
<el-col v-for="(item, index) in links" :key="index" class="item-card" :sm="6" @click.native="clickCard(item)">
<el-card shadow="hover">
{{ item.title }}
</el-card>
@ -11,10 +11,10 @@
</el-row>
</div>
<el-divider />
<div class="dashboard-text">开发使用</div>
<div class="dashboard-text">{{ $t('pages.dashboard.quickLink.devUsage') }}</div>
<div class="content">
<el-row :gutter="12">
<el-col v-for="(item,index) in devLinks" :key="index" class="item-card" :sm="6" @click.native="clickCard(item)">
<el-col v-for="(item, index) in devLinks" :key="index" class="item-card" :sm="6" @click.native="clickCard(item)">
<el-card shadow="hover">
{{ item.title }}
</el-card>
@ -22,30 +22,36 @@
</el-row>
</div>
</el-card>
</div>
</template>
<script>
import { mapGetters } from 'vuex'
export default {
name: 'QuickLink',
data() {
return {
links: [
{ title: '测试后台管理', link: 'http://test.back.sugartimeapp.com/' },
{ title: '测试经纪人后台', link: 'http://test.broker.sugartimeapp.com/' },
{ title: '线上后台', link: 'http://admin.sugartimeapp.com/' },
{ title: '线上经纪人后台', link: 'http://broker.sugartimeapp.com/' },
{ title: '线上IM后台', link: 'http://im.sugartimeapp.com/' }
],
devLinks: [
{ title: '开发经纪人后台', link: 'http://dev.broker.sugartimeapp.com/' },
{ title: '开发环境jenkins', link: 'http://dev.jenkins.sugartimeapp.com/' },
{ title: '测试环境jenkins', link: 'http://test.jenkins.sugartimeapp.com/' },
{ title: '开发环境supervisor', link: 'http://119.23.230.191:9001/' },
{ title: '测试环境supervisor', link: 'http://47.254.89.114:9001/' },
{ title: '线上环境supervisor', link: 'http://47.251.15.209:9001/' },
{ title: '图谱API文档', link: 'http://cloud.doc.tuputech.com/' }
computed: {
...mapGetters(['language']),
links() {
const language = this.language
return [
{ title: this.$t('pages.dashboard.quickLink.testAdmin'), link: 'http://test.back.sugartimeapp.com/' },
{ title: this.$t('pages.dashboard.quickLink.testBroker'), link: 'http://test.broker.sugartimeapp.com/' },
{ title: this.$t('pages.dashboard.quickLink.productionAdmin'), link: 'http://admin.sugartimeapp.com/' },
{ title: this.$t('pages.dashboard.quickLink.productionBroker'), link: 'http://broker.sugartimeapp.com/' },
{ title: this.$t('pages.dashboard.quickLink.productionIm'), link: 'http://im.sugartimeapp.com/' }
]
},
devLinks() {
const language = this.language
return [
{ title: this.$t('pages.dashboard.quickLink.devBroker'), link: 'http://dev.broker.sugartimeapp.com/' },
{ title: this.$t('pages.dashboard.quickLink.devJenkins'), link: 'http://dev.jenkins.sugartimeapp.com/' },
{ title: this.$t('pages.dashboard.quickLink.testJenkins'), link: 'http://test.jenkins.sugartimeapp.com/' },
{ title: this.$t('pages.dashboard.quickLink.devSupervisor'), link: 'http://119.23.230.191:9001/' },
{ title: this.$t('pages.dashboard.quickLink.testSupervisor'), link: 'http://47.254.89.114:9001/' },
{ title: this.$t('pages.dashboard.quickLink.productionSupervisor'), link: 'http://47.251.15.209:9001/' },
{ title: this.$t('pages.dashboard.quickLink.graphApiDocs'), link: 'http://cloud.doc.tuputech.com/' }
]
}
},
@ -60,6 +66,7 @@ export default {
<style lang="scss" scoped>
.content {
margin-top: 20px;
.item-card {
margin-bottom: 20px;
}

View File

@ -3,7 +3,7 @@
<div class="filter-container">
<el-select
v-model="listQuery.sysOrigin"
placeholder="系统"
:placeholder="$t('pages.eggExchangeRecord.placeholder.system')"
style="width: 120px"
class="filter-item"
@change="handleSearch"
@ -26,7 +26,7 @@
<account-input
v-model="listQuery.userId"
:sys-origin="listQuery.sysOrigin"
placeholder="用户ID"
:placeholder="$t('pages.eggExchangeRecord.placeholder.userId')"
/>
</div>
<el-button
@ -36,7 +36,7 @@
:disabled="searchDisabled"
@click="handleSearch"
>
搜索
{{ $t('pages.eggExchangeRecord.action.search') }}
</el-button>
</div>
<el-table
@ -48,7 +48,7 @@
>
<el-table-column
prop="sysOrigin"
label="来源系统"
:label="$t('pages.eggExchangeRecord.table.sourceSystem')"
align="center"
width="80"
>
@ -59,7 +59,7 @@
/>
</template>
</el-table-column>
<el-table-column label="用户" align="center" min-width="200">
<el-table-column :label="$t('pages.eggExchangeRecord.table.user')" align="center" min-width="200">
<template slot-scope="scope">
<user-table-exhibit
:user-profile="scope.row.userBaseInfo"
@ -67,8 +67,8 @@
/>
</template>
</el-table-column>
<el-table-column label="兑换数量" prop="quantity" align="center" />
<el-table-column label="奖品" align="center">
<el-table-column :label="$t('pages.eggExchangeRecord.table.exchangeQuantity')" prop="quantity" align="center" />
<el-table-column :label="$t('pages.eggExchangeRecord.table.prize')" align="center">
<template slot-scope="scope">
<img
v-if="scope.row.type === 'GOLD'"
@ -94,7 +94,7 @@
</el-table-column>
<el-table-column
prop="createTime"
label="创建时间"
:label="$t('pages.eggExchangeRecord.table.createdAt')"
width="200"
align="center"
>

View File

@ -1,9 +1,9 @@
<template>
<template>
<div class="egg-lottery-cnf">
<div class="filter-container">
<el-select
v-model="listQuery.sysOrigin"
placeholder="归属系统"
:placeholder="$t('pages.eggLotteryCnf.placeholder.system')"
style="width: 120px"
class="filter-item"
@change="changeSysOrigin"
@ -18,16 +18,22 @@
<span style="float: left;margin-left:10px">{{ item.label }}</span>
</el-option>
</el-select>
<el-button class="filter-item" type="primary" icon="el-icon-search" @click="handleSearch"> 搜索 </el-button>
<el-button type="primary" class="filter-item" @click="clickAdd"> <i class="el-icon-circle-plus" /> 添加</el-button>
<el-button type="primary" class="filter-item" :loading="submitLoading" :disabled="submitDisabled" @click="clickSave"><i class="el-icon-success" /> 保存</el-button>
<!-- <el-button type="text" @click="clickSetEggExtractRatio">奖金池:{{ prizePool }}</el-button>
<el-button type="text" :loading="extractRatioLoading" :disabled="extractRatioLoading" @click="clickSetEggExtractRatio">抽取率:{{ extractRatio || '-' }}</el-button> -->
<el-button class="filter-item" type="primary" icon="el-icon-search" @click="handleSearch">
{{ $t('pages.eggLotteryCnf.action.search') }}
</el-button>
<el-button type="primary" class="filter-item" @click="clickAdd">
<i class="el-icon-circle-plus" /> {{ $t('pages.eggLotteryCnf.action.add') }}
</el-button>
<el-button type="primary" class="filter-item" :loading="submitLoading" :disabled="submitDisabled" @click="clickSave">
<i class="el-icon-success" /> {{ $t('pages.eggLotteryCnf.action.save') }}
</el-button>
<!-- <el-button type="text" @click="clickSetEggExtractRatio">{{ $t('pages.eggLotteryCnf.action.prizePool') }}{{ prizePool }}</el-button>
<el-button type="text" :loading="extractRatioLoading" :disabled="extractRatioLoading" @click="clickSetEggExtractRatio">{{ $t('pages.eggLotteryCnf.action.extractRatio') }}{{ extractRatio || '-' }}</el-button> -->
</div>
<el-alert
type="info"
:closable="false"
description="注意: 不要随意对记录数据进行删除操作, 如果用户已拥有碎片将会被同步删除"
:description="$t('pages.eggLotteryCnf.message.notice')"
/>
<div class="conf-row">
<el-table
@ -38,22 +44,26 @@
highlight-current-row
>
<el-table-column
label="序号"
:label="$t('pages.eggLotteryCnf.table.sort')"
min-width="80"
align="center"
>
<template scope="scope">
<el-input v-model="scope.row.sort" size="small" placeholder="请输入序号" @input="submitDisabled=false" />
<el-input
v-model="scope.row.sort"
size="small"
:placeholder="$t('pages.eggLotteryCnf.placeholder.sort')"
@input="submitDisabled = false"
/>
</template>
</el-table-column>
<el-table-column
label="奖品"
:label="$t('pages.eggLotteryCnf.table.prize')"
min-width="80"
align="center"
>
<template scope="scope">
<el-dropdown>
<span class="el-dropdown-link">
<div v-if="scope.row.sourceCover" class="preview-img flex-c">
@ -71,76 +81,100 @@
<img v-else-if="scope.row.sourceType === 'GAME_COUPON'" src="@/assets/game_coupon.png" width="35" height="35">
<img v-else-if="scope.row.sourceType === 'SPECIAL_ID'" src="@/assets/special_id.png" width="35" height="35">
</div>
<div v-else>设置<i class="el-icon-arrow-down el-icon--right" /></div>
<div v-else>
{{ $t('pages.eggLotteryCnf.action.set') }}<i class="el-icon-arrow-down el-icon--right" />
</div>
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item @click.native="clickSelectReward('PROPS',scope.row)">道具</el-dropdown-item>
<el-dropdown-item @click.native="clickAddReward('SPECIAL_ID',scope.row)">靓号</el-dropdown-item>
<el-dropdown-item @click.native="clickAddReward('GOLD',scope.row)">金币</el-dropdown-item>
<el-dropdown-item @click.native="clickAddReward('DIAMOND',scope.row)">钻石</el-dropdown-item>
<el-dropdown-item @click.native="clickAddReward('GAME_COUPON',scope.row)">游戏券</el-dropdown-item>
<el-dropdown-item @click.native="clickSelectReward('PROPS', scope.row)">{{ $t('pages.eggLotteryCnf.rewardType.props') }}</el-dropdown-item>
<el-dropdown-item @click.native="clickAddReward('SPECIAL_ID', scope.row)">{{ $t('pages.eggLotteryCnf.rewardType.specialId') }}</el-dropdown-item>
<el-dropdown-item @click.native="clickAddReward('GOLD', scope.row)">{{ $t('pages.eggLotteryCnf.rewardType.gold') }}</el-dropdown-item>
<el-dropdown-item @click.native="clickAddReward('DIAMOND', scope.row)">{{ $t('pages.eggLotteryCnf.rewardType.diamond') }}</el-dropdown-item>
<el-dropdown-item @click.native="clickAddReward('GAME_COUPON', scope.row)">{{ $t('pages.eggLotteryCnf.rewardType.gameCoupon') }}</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</template>
</el-table-column>
<el-table-column
label="奖品量"
:label="$t('pages.eggLotteryCnf.table.prizeQuantity')"
min-width="80"
align="center"
>
<template scope="scope">
<el-input v-model="scope.row.prizeQuantity" size="small" placeholder="请输入奖品数量" @input="submitDisabled=false" />
<el-input
v-model="scope.row.prizeQuantity"
size="small"
:placeholder="$t('pages.eggLotteryCnf.placeholder.prizeQuantity')"
@input="submitDisabled = false"
/>
</template>
</el-table-column>
<el-table-column
label="碎片"
:label="$t('pages.eggLotteryCnf.table.fragmentsSize')"
min-width="80"
align="center"
>
<template scope="scope">
<el-input v-model="scope.row.fragmentsSize" size="small" placeholder="请输入碎片兑换数量" @input="submitDisabled=false" />
<el-input
v-model="scope.row.fragmentsSize"
size="small"
:placeholder="$t('pages.eggLotteryCnf.placeholder.fragmentsSize')"
@input="submitDisabled = false"
/>
</template>
</el-table-column>
<el-table-column
label="碎片价值"
:label="$t('pages.eggLotteryCnf.table.fragmentsAmount')"
min-width="80"
align="center"
>
<template scope="scope">
<el-input v-model="scope.row.fragmentsAmount" size="small" placeholder="请输入碎片价值" @input="submitDisabled=false" />
<el-input
v-model="scope.row.fragmentsAmount"
size="small"
:placeholder="$t('pages.eggLotteryCnf.placeholder.fragmentsAmount')"
@input="submitDisabled = false"
/>
</template>
</el-table-column>
<el-table-column
label="库存"
:label="$t('pages.eggLotteryCnf.table.stockQuantity')"
min-width="80"
align="center"
>
<template scope="scope">
<el-input v-model.number="scope.row.stockQuantity" size="small" placeholder="请输入库存" @input="submitDisabled=false" />
<el-input
v-model.number="scope.row.stockQuantity"
size="small"
:placeholder="$t('pages.eggLotteryCnf.placeholder.stockQuantity')"
@input="submitDisabled = false"
/>
</template>
</el-table-column>
<el-table-column
label="已消耗"
:label="$t('pages.eggLotteryCnf.table.consumeStockQuantity')"
min-width="80"
align="center"
>
<template scope="scope">
{{ scope.row.consumeStockQuantity||0 }}
{{ scope.row.consumeStockQuantity || 0 }}
<el-button type="text" class="el-icon-refresh-right" @click="clickResetConsumeStockQuantity(scope.row)" />
</template>
</el-table-column>
<el-table-column
label="操作"
:label="$t('pages.eggLotteryCnf.table.action')"
width="80"
align="center"
>
<template scope="scope">
<el-button type="text" class="filter-item" @click="clickDel(scope.row, scope.$index)"> 删除</el-button>
<el-button type="text" class="filter-item" @click="clickDel(scope.row, scope.$index)">
{{ $t('pages.eggLotteryCnf.action.delete') }}
</el-button>
</template>
</el-table-column>
</el-table>
@ -148,14 +182,13 @@
<props-source-select-drawer
v-if="propsSourceSelectDrawerVisible"
:sys-origin="listQuery.sysOrigin"
@close="propsSourceSelectDrawerVisible=false"
@close="propsSourceSelectDrawerVisible = false"
@select="selectPropsSource"
/>
</div>
</template>
<script>
import PropsSourceSelectDrawer from '@/components/data/PropsSourceSelectDrawer'
import { getEggConfig, updateEggConfig, resetEggConsumeStockQuantity, getEggExtractRatio, setEggExtractRatio, getEggPrizePool } from '@/api/game'
import { pickerOptions } from '@/constant/el-const'
@ -272,37 +305,40 @@ export default {
this.list.push(this.createConfItem())
this.submitDisabled = false
},
getValidationMessage(type, index) {
return this.$t(`pages.eggLotteryCnf.validation.${type}`, { index: index + 1 })
},
clickSave() {
const that = this
if (that.submitDisabled) {
that.$opsMessage.warn('当前没有任何操作~')
that.$opsMessage.warn(that.$t('pages.eggLotteryCnf.message.noChanges'))
return
}
const errors = []
for (let index = 0; index < that.list.length; index++) {
const item = that.list[index]
if (!that.validNumber(item.sort, 0, 99999999)) {
errors.push(`[${index + 1}行]序号, 错误请输入阿拉伯数字>=0 && <= 99999999`)
errors.push(that.getValidationMessage('sort', index))
}
if (!item.sourceType) {
errors.push(`[${index + 1}行]奖品, 为空`)
errors.push(that.getValidationMessage('prize', index))
}
if (!that.validNumber(item.prizeQuantity, 0, 99999999)) {
errors.push(`[${index + 1}行]奖品数量, 错误请输入阿拉伯数字>=1 && <= 99999999`)
errors.push(that.getValidationMessage('prizeQuantity', index))
}
if (!that.validNumber(item.stockQuantity, -1, 99999999)) {
errors.push(`[${index + 1}行]库存数量, 错误请输入阿拉伯数字>=-1 && <= 99999999`)
errors.push(that.getValidationMessage('stockQuantity', index))
}
if (!that.validNumber(item.fragmentsSize, 1, 99999999)) {
errors.push(`[${index + 1}行]碎片数量, 错误请输入阿拉伯数字>=1 && <= 99999999`)
errors.push(that.getValidationMessage('fragmentsSize', index))
}
if (!that.validNumber(item.fragmentsAmount, 1, 99999999)) {
errors.push(`[${index + 1}行]碎片价值, 错误请输入阿拉伯数字>=1 && <= 99999999`)
errors.push(that.getValidationMessage('fragmentsAmount', index))
}
if (errors.length > 0) {
errors.push('请更正后重新提交!!!')
that.$confirm(errors.join('<br>'), '提示', {
confirmButtonText: '确定',
errors.push(that.$t('pages.eggLotteryCnf.validation.fixAndSubmit'))
that.$confirm(errors.join('<br>'), that.$t('pages.eggLotteryCnf.dialog.title'), {
confirmButtonText: that.$t('pages.eggLotteryCnf.dialog.confirm'),
showCancelButton: false,
type: 'warning',
dangerouslyUseHTMLString: true
@ -335,7 +371,9 @@ export default {
},
clickDel(row, index) {
const that = this
that.$confirm('用户已拥有对应碎片数据将会同步删除, 是否继续?', '提示', {
that.$confirm(that.$t('pages.eggLotteryCnf.dialog.deleteConfirm'), that.$t('pages.eggLotteryCnf.dialog.title'), {
confirmButtonText: that.$t('pages.eggLotteryCnf.dialog.confirm'),
cancelButtonText: that.$t('pages.eggLotteryCnf.dialog.cancel'),
type: 'warning'
}).then(() => {
that.list.splice(index, 1)
@ -348,7 +386,9 @@ export default {
},
clickResetConsumeStockQuantity(row) {
const that = this
that.$confirm('是否确定重置库存消耗', '提示', {
that.$confirm(that.$t('pages.eggLotteryCnf.dialog.resetConfirm'), that.$t('pages.eggLotteryCnf.dialog.title'), {
confirmButtonText: that.$t('pages.eggLotteryCnf.dialog.confirm'),
cancelButtonText: that.$t('pages.eggLotteryCnf.dialog.cancel'),
type: 'warning'
}).then(() => {
resetEggConsumeStockQuantity(row.id).then(res => {
@ -380,11 +420,11 @@ export default {
},
clickSetEggExtractRatio() {
const that = this
that.$prompt('计算方式:门票- (门票 * 概率)=抽取', '门票抽取', {
confirmButtonText: '确定',
cancelButtonText: '取消',
that.$prompt(that.$t('pages.eggLotteryCnf.dialog.extractFormula'), that.$t('pages.eggLotteryCnf.dialog.extractTitle'), {
confirmButtonText: that.$t('pages.eggLotteryCnf.dialog.confirm'),
cancelButtonText: that.$t('pages.eggLotteryCnf.dialog.cancel'),
inputPattern: /^\d{1,5}(\.\d{0,5})?$/,
inputErrorMessage: '范围0~99999小数最多5位'
inputErrorMessage: that.$t('pages.eggLotteryCnf.dialog.extractInputError')
}).then(({ value }) => {
that.extractRatioLoading = true
setEggExtractRatio(that.listQuery.sysOrigin, value).then(res => {
@ -400,13 +440,15 @@ export default {
}
}
</script>
<style scoped lang="scss">
.egg-lottery-cnf {
.preview-img {
position: relative;
.player {
position: absolute;
right: 0px;
right: 0;
top: -10px;
}
}

View File

@ -3,7 +3,7 @@
<div class="filter-container">
<el-select
v-model="listQuery.sysOrigin"
placeholder="系统"
:placeholder="$t('pages.eggLotteryRecord.placeholder.system')"
style="width: 120px"
class="filter-item"
@change="handleSearch"
@ -26,7 +26,7 @@
<account-input
v-model="listQuery.userId"
:sys-origin="listQuery.sysOrigin"
placeholder="用户ID"
:placeholder="$t('pages.eggLotteryRecord.placeholder.userId')"
/>
</div>
<el-button
@ -36,7 +36,7 @@
:disabled="searchDisabled"
@click="handleSearch"
>
搜索
{{ $t('pages.eggLotteryRecord.action.search') }}
</el-button>
</div>
<el-table
@ -48,7 +48,7 @@
>
<el-table-column
prop="sysOrigin"
label="来源系统"
:label="$t('pages.eggLotteryRecord.table.sourceSystem')"
align="center"
width="80"
>
@ -59,7 +59,7 @@
/>
</template>
</el-table-column>
<el-table-column label="用户" align="center" min-width="200">
<el-table-column :label="$t('pages.eggLotteryRecord.table.user')" align="center" min-width="200">
<template slot-scope="scope">
<user-table-exhibit
:user-profile="scope.row.userBaseInfo"
@ -67,19 +67,19 @@
/>
</template>
</el-table-column>
<el-table-column label="付费 / 抽次数" align="center">
<el-table-column :label="$t('pages.eggLotteryRecord.table.paidDrawCount')" align="center">
<template slot-scope="scope">
{{ scope.row.lotteryFee }} / {{ scope.row.quantity }}
</template>
</el-table-column>
<el-table-column label="中奖碎片" align="center">
<el-table-column :label="$t('pages.eggLotteryRecord.table.winningFragments')" align="center">
<template slot-scope="scope">
<props-row :list="scope.row.winningDetailsList" />
</template>
</el-table-column>
<el-table-column
prop="createTime"
label="创建时间"
:label="$t('pages.eggLotteryRecord.table.createdAt')"
width="200"
align="center"
>

View File

@ -15,17 +15,21 @@ export default {
components: { EggExchangeRecord, EggLotteryRecord, EggLotteryCnf },
data() {
return {
activeName: 'EggExchangeRecord',
tables: [
activeName: 'EggExchangeRecord'
}
},
computed: {
tables() {
return [
{
title: '碎片兑换',
title: this.$t('pages.gameEgg.tabs.exchangeRecord'),
component: 'EggExchangeRecord'
}, {
title: '抽奖记录',
title: this.$t('pages.gameEgg.tabs.lotteryRecord'),
component: 'EggLotteryRecord'
},
{
title: '配置',
title: this.$t('pages.gameEgg.tabs.config'),
component: 'EggLotteryCnf'
}
]

View File

@ -10,16 +10,16 @@
>
<div class="form-edit">
<el-form ref="form" :model="form" :rules="formRules" label-width="110px" style="margin-right:50px;">
<el-form-item prop="gameType" label="游戏类型">
<el-form-item prop="gameType" :label="$t('pages.gameLotteryConfigForm.form.gameType')">
<el-select
v-model="form.gameType"
placeholder="游戏类型"
:placeholder="$t('pages.gameLotteryConfigForm.placeholder.gameType')"
style="width:100%;"
class="filter-item"
>
<el-option v-for="(item, index) in (lotteryGameTypes || [])" :key="index" :label="item.name" :value="item.value">
<el-option v-for="(item, index) in (lotteryGameTypes || [])" :key="index" :label="getLotteryGameTypeLabel(item)" :value="item.value">
<div style="float: left;margin-left:10px">
{{ item.name }}
{{ getLotteryGameTypeLabel(item) }}
</div>
</el-option>
</el-select>
@ -64,24 +64,24 @@
<el-col :span="14" style="text-align: right;">
<span style="font-weight:bold;"> {{ getTypeName(item) }}</span>
<span v-if="item.type === 'SPECIAL_ID'" style="margin-left: 0.3rem;">
<span style="margin-right: 0.3rem;">类型:{{ item.content }}</span>数量:<el-input v-model="item.quantity" v-number :value="item.quantity" style="width: 10%;" placeholder="数量" />
<span style="margin-right: 0.3rem;">{{ $t('pages.gameLotteryConfigForm.label.type') }}:{{ item.content }}</span>{{ $t('pages.gameLotteryConfigForm.label.quantity') }}:<el-input v-model="item.quantity" v-number :value="item.quantity" style="width: 10%;" :placeholder="$t('pages.gameLotteryConfigForm.placeholder.quantity')" />
</span>
<span v-else-if="item.type === 'GOLD' || item.type === 'DIAMOND' || isGameCoupon(item.type)" style="margin-left: 0.3rem;">
数量:<el-input v-model="item.content" v-number :value="item.content" style="width: 10%;" placeholder="数量" />
{{ $t('pages.gameLotteryConfigForm.label.quantity') }}:<el-input v-model="item.content" v-number :value="item.content" style="width: 10%;" :placeholder="$t('pages.gameLotteryConfigForm.placeholder.quantity')" />
</span>
<span v-else-if="item.type === 'BADGE'" style="margin-left: 0.3rem;">
<span style="margin-right: 0.3rem;">{{ item.badgeName }}</span>天数:<el-input v-model="item.quantity" v-number :value="item.quantity" style="width: 10%;" placeholder="天数" /></span>
<span style="margin-right: 0.3rem;">{{ item.badgeName }}</span>{{ $t('pages.gameLotteryConfigForm.label.days') }}:<el-input v-model="item.quantity" v-number :value="item.quantity" style="width: 10%;" :placeholder="$t('pages.gameLotteryConfigForm.placeholder.days')" /></span>
<span v-else>
<span v-if="item.type === 'GIFT'" style="margin-left: 0.3rem;">数量:</span>
<span v-else>天数:</span>
<el-input v-model="item.quantity" v-number :value="item.quantity" style="width: 10%;" :placeholder="item.type === 'GIFT' ? '数量' : '天数'" />
<span v-if="item.type === 'GIFT'" style="margin-left: 0.3rem;">{{ $t('pages.gameLotteryConfigForm.label.quantity') }}:</span>
<span v-else>{{ $t('pages.gameLotteryConfigForm.label.days') }}:</span>
<el-input v-model="item.quantity" v-number :value="item.quantity" style="width: 10%;" :placeholder="item.type === 'GIFT' ? $t('pages.gameLotteryConfigForm.placeholder.quantity') : $t('pages.gameLotteryConfigForm.placeholder.days')" />
</span>
<span style="margin-left: 0.3rem;">库存:<el-input v-model="item.inventory" v-number :value="item.inventory" style="width: 10%;" placeholder="所需碎片" /></span>
<span style="margin-left: 0.3rem;">{{ $t('pages.gameLotteryConfigForm.label.inventory') }}:<el-input v-model="item.inventory" v-number :value="item.inventory" style="width: 10%;" :placeholder="$t('pages.gameLotteryConfigForm.placeholder.inventory')" /></span>
<span v-if="form.gameType === 'EGG'" style="color:#d8ae36;margin-left: 0.3rem;">
所需碎片:<el-input v-model="item.dataHelp" v-number :value="item.dataHelp" style="width: 7%;" placeholder="所需碎片" />
{{ $t('pages.gameLotteryConfigForm.label.requiredFragments') }}:<el-input v-model="item.dataHelp" v-number :value="item.dataHelp" style="width: 7%;" :placeholder="$t('pages.gameLotteryConfigForm.placeholder.requiredFragments')" />
</span>
<span style="color:#f78e8e;margin-left: 0.3rem;">
概率:<el-input v-model="item.probability" oninput="value=value.replace(/[^0-9.]/g,'')" :value="item.probability" style="width: 10%;" placeholder="概率" />
{{ $t('pages.gameLotteryConfigForm.label.probability') }}:<el-input v-model="item.probability" oninput="value=value.replace(/[^0-9.]/g,'')" :value="item.probability" style="width: 10%;" :placeholder="$t('pages.gameLotteryConfigForm.placeholder.probability')" />
</span>
</el-col>
<el-col :span="2" style="text-align: right;">
@ -90,7 +90,7 @@
</el-row>
</div>
</div>
<el-form-item label="添加配置">
<el-form-item :label="$t('pages.gameLotteryConfigForm.form.addConfig')">
<div class="content-list">
<el-row v-for="(item, index) in form.tmpConfigList" :key="index">
<div class="content-box">
@ -101,11 +101,11 @@
<el-col :span="4">
<el-form-item
:prop="'tmpConfigList.' + index + '.content'"
:rules="{required: true, message: '必填字段不可为空', trigger: 'blur'}"
:rules="getRequiredFieldRule()"
>
<el-select
v-model="item.content"
placeholder="请选择"
:placeholder="$t('pages.gameLotteryConfigForm.placeholder.select')"
style="width:100%"
filterable
>
@ -116,33 +116,33 @@
<el-col :span="3">
<el-form-item
:prop="'tmpConfigList.' + index + '.quantity'"
:rules="{ required: true, message: '不可为空', trigger: 'blur'}"
:rules="getNotEmptyRule()"
>
<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.gameLotteryConfigForm.placeholder.quantity')" /></span>
</el-form-item>
</el-col>
<el-col :span="3">
<el-form-item
:prop="'tmpConfigList.' + index + '.inventory'"
:rules="{ required: true, message: '不可为空', trigger: 'blur'}"
:rules="getNotEmptyRule()"
>
<span><el-input v-model="item.inventory" v-number style="width: 100%;" placeholder="库存数" /></span>
<span><el-input v-model="item.inventory" v-number style="width: 100%;" :placeholder="$t('pages.gameLotteryConfigForm.placeholder.inventory')" /></span>
</el-form-item>
</el-col>
<el-col v-if="form.gameType === 'EGG'" :span="3">
<el-form-item
:prop="'tmpConfigList.' + index + '.dataHelp'"
:rules="{ required: true, message: '不可为空', trigger: 'blur'}"
:rules="getNotEmptyRule()"
>
<span><el-input v-model="item.dataHelp" v-number style="width: 100%;" placeholder="碎片数" /></span>
<span><el-input v-model="item.dataHelp" v-number style="width: 100%;" :placeholder="$t('pages.gameLotteryConfigForm.placeholder.requiredFragments')" /></span>
</el-form-item>
</el-col>
<el-col :span="4">
<el-form-item
:prop="'tmpConfigList.' + index + '.probability'"
:rules="{ required: true, message: '不可为空', trigger: 'blur'}"
:rules="getNotEmptyRule()"
>
<span><el-input v-model="item.probability" oninput="value=value.replace(/[^0-9.]/g,'')" style="width: 100%;" placeholder="概率(0.1就是10%)" /></span>
<span><el-input v-model="item.probability" oninput="value=value.replace(/[^0-9.]/g,'')" style="width: 100%;" :placeholder="$t('pages.gameLotteryConfigForm.placeholder.probabilityExample')" /></span>
</el-form-item>
</el-col>
<el-col :span="5">
@ -154,33 +154,33 @@
<el-col :span="5">
<el-form-item
:prop="'tmpConfigList.' + index + '.content'"
:rules="{ required: true, message: '必填字段不可为空', trigger: 'blur'}"
:rules="getRequiredFieldRule()"
>
<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.gameLotteryConfigForm.placeholder.inputContent')" />
</el-form-item>
</el-col>
<el-col :span="3">
<el-form-item
:prop="'tmpConfigList.' + index + '.inventory'"
:rules="{ required: true, message: '不可为空', trigger: 'blur'}"
:rules="getNotEmptyRule()"
>
<span><el-input v-model="item.inventory" v-number style="width: 100%;" placeholder="库存数" /></span>
<span><el-input v-model="item.inventory" v-number style="width: 100%;" :placeholder="$t('pages.gameLotteryConfigForm.placeholder.inventory')" /></span>
</el-form-item>
</el-col>
<el-col v-if="form.gameType === 'EGG'" :span="3">
<el-form-item
:prop="'tmpConfigList.' + index + '.dataHelp'"
:rules="{ required: true, message: '不可为空', trigger: 'blur'}"
:rules="getNotEmptyRule()"
>
<span><el-input v-model="item.dataHelp" v-number style="width: 100%;" placeholder="碎片数" /></span>
<span><el-input v-model="item.dataHelp" v-number style="width: 100%;" :placeholder="$t('pages.gameLotteryConfigForm.placeholder.requiredFragments')" /></span>
</el-form-item>
</el-col>
<el-col :span="4">
<el-form-item
:prop="'tmpConfigList.' + index + '.probability'"
:rules="{ required: true, message: '不可为空', trigger: 'blur'}"
:rules="getNotEmptyRule()"
>
<span><el-input v-model="item.probability" oninput="value=value.replace(/[^0-9.]/g,'')" style="width: 100%;" placeholder="概率(0.1就是10%)" /></span>
<span><el-input v-model="item.probability" oninput="value=value.replace(/[^0-9.]/g,'')" style="width: 100%;" :placeholder="$t('pages.gameLotteryConfigForm.placeholder.probabilityExample')" /></span>
</el-form-item>
</el-col>
<el-col :span="5">
@ -192,11 +192,11 @@
<el-col :span="3">
<el-form-item
:prop="'tmpConfigList.' + index + '.content'"
:rules="{required: true, message: '必填字段不可为空', trigger: 'blur'}"
:rules="getRequiredFieldRule()"
>
<el-select
v-model="item.content"
placeholder="请选择"
:placeholder="$t('pages.gameLotteryConfigForm.placeholder.select')"
style="width:100%"
filterable
@change="(val)=> changeContent(item, val)"
@ -215,16 +215,16 @@
<el-col :span="5">
<el-form-item
:prop="'tmpConfigList.' + index + '.quantity'"
:rules="{ required: true, message: '不可为空', trigger: 'blur'}"
:rules="getNotEmptyRule()"
>
<!-- <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.gameLotteryConfigForm.placeholder.days')" /></span> -->
<el-autocomplete
v-model="item.quantity"
v-number
popper-class="my-autocomplete"
:fetch-suggestions="querySearchBadgeRestaurants"
placeholder="天数"
:placeholder="$t('pages.gameLotteryConfigForm.placeholder.days')"
>
<template slot-scope="{ item }">
<div class="name">{{ item.value }}</div>
@ -237,25 +237,25 @@
<el-col :span="3">
<el-form-item
:prop="'tmpConfigList.' + index + '.inventory'"
:rules="{ required: true, message: '不可为空', trigger: 'blur'}"
:rules="getNotEmptyRule()"
>
<span><el-input v-model="item.inventory" v-number style="width: 100%;" placeholder="库存数" /></span>
<span><el-input v-model="item.inventory" v-number style="width: 100%;" :placeholder="$t('pages.gameLotteryConfigForm.placeholder.inventory')" /></span>
</el-form-item>
</el-col>
<el-col v-if="form.gameType === 'EGG'" :span="3">
<el-form-item
:prop="'tmpConfigList.' + index + '.dataHelp'"
:rules="{ required: true, message: '不可为空', trigger: 'blur'}"
:rules="getNotEmptyRule()"
>
<span><el-input v-model="item.dataHelp" v-number style="width: 100%;" placeholder="碎片数" /></span>
<span><el-input v-model="item.dataHelp" v-number style="width: 100%;" :placeholder="$t('pages.gameLotteryConfigForm.placeholder.requiredFragments')" /></span>
</el-form-item>
</el-col>
<el-col :span="4">
<el-form-item
:prop="'tmpConfigList.' + index + '.probability'"
:rules="{ required: true, message: '不可为空', trigger: 'blur'}"
:rules="getNotEmptyRule()"
>
<span><el-input v-model="item.probability" oninput="value=value.replace(/[^0-9.]/g,'')" style="width: 100%;" placeholder="概率(0.1就是10%)" /></span>
<span><el-input v-model="item.probability" oninput="value=value.replace(/[^0-9.]/g,'')" style="width: 100%;" :placeholder="$t('pages.gameLotteryConfigForm.placeholder.probabilityExample')" /></span>
</el-form-item>
</el-col>
<el-col :span="5">
@ -286,11 +286,11 @@
<el-col :span="3">
<el-form-item
:prop="'tmpConfigList.' + index + '.content'"
:rules="{required: true, message: '必填字段不可为空', trigger: 'blur'}"
:rules="getRequiredFieldRule()"
>
<el-select
v-model="item.content"
placeholder="请选择"
:placeholder="$t('pages.gameLotteryConfigForm.placeholder.select')"
style="width:100%"
filterable
@change="(val)=> changeContent(item, val)"
@ -309,33 +309,33 @@
<el-col :span="3">
<el-form-item
:prop="'tmpConfigList.' + index + '.quantity'"
:rules="{ required: true, message: '不可为空', trigger: 'blur'}"
:rules="getNotEmptyRule()"
>
<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.gameLotteryConfigForm.placeholder.quantityOrDays')" /></div>
</el-form-item>
</el-col>
<el-col :span="3">
<el-form-item
:prop="'tmpConfigList.' + index + '.inventory'"
:rules="{ required: true, message: '不可为空', trigger: 'blur'}"
:rules="getNotEmptyRule()"
>
<span><el-input v-model="item.inventory" v-number style="width: 100%;" placeholder="库存数" /></span>
<span><el-input v-model="item.inventory" v-number style="width: 100%;" :placeholder="$t('pages.gameLotteryConfigForm.placeholder.inventory')" /></span>
</el-form-item>
</el-col>
<el-col v-if="form.gameType === 'EGG'" :span="3">
<el-form-item
:prop="'tmpConfigList.' + index + '.dataHelp'"
:rules="{ required: true, message: '不可为空', trigger: 'blur'}"
:rules="getNotEmptyRule()"
>
<span><el-input v-model="item.dataHelp" v-number style="width: 100%;" placeholder="碎片数" /></span>
<span><el-input v-model="item.dataHelp" v-number style="width: 100%;" :placeholder="$t('pages.gameLotteryConfigForm.placeholder.requiredFragments')" /></span>
</el-form-item>
</el-col>
<el-col :span="4">
<el-form-item
:prop="'tmpConfigList.' + index + '.probability'"
:rules="{ required: true, message: '不可为空', trigger: 'blur'}"
:rules="getNotEmptyRule()"
>
<span><el-input v-model="item.probability" oninput="value=value.replace(/[^0-9.]/g,'')" style="width: 100%;" placeholder="概率(0.1就是10%)" /></span>
<span><el-input v-model="item.probability" oninput="value=value.replace(/[^0-9.]/g,'')" style="width: 100%;" :placeholder="$t('pages.gameLotteryConfigForm.placeholder.probabilityExample')" /></span>
</el-form-item>
</el-col>
<el-col :span="3">
@ -348,34 +348,34 @@
</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['FLOAT_PICTURE'].loading" @click="addContent('FLOAT_PICTURE')"><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['FRAGMENTS'].loading" @click="addContent('FRAGMENTS')"><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" />{{ getAddButtonLabel('AVATAR_FRAME') }}</el-button>
<el-button type="text" :disabled="disableAddContent" :loading="propsTypeData['RIDE'].loading" @click="addContent('RIDE')"><i class="el-icon-circle-plus" />{{ getAddButtonLabel('RIDE') }}</el-button>
<el-button type="text" :disabled="disableAddContent" :loading="propsTypeData['NOBLE_VIP'].loading" @click="addContent('NOBLE_VIP')"><i class="el-icon-circle-plus" />{{ getAddButtonLabel('NOBLE_VIP') }}</el-button>
<el-button type="text" :disabled="disableAddContent" :loading="propsTypeData['THEME'].loading" @click="addContent('THEME')"><i class="el-icon-circle-plus" />{{ getAddButtonLabel('THEME') }}</el-button>
<el-button type="text" :disabled="disableAddContent" :loading="propsTypeData['GIFT'].loading" @click="addContent('GIFT')"><i class="el-icon-circle-plus" />{{ getAddButtonLabel('GIFT') }}</el-button>
<el-button type="text" :disabled="disableAddContent" @click="addContent('SPECIAL_ID')"><i class="el-icon-circle-plus" />{{ getAddButtonLabel('SPECIAL_ID') }}</el-button>
<el-button type="text" :disabled="disableAddContent" @click="addContent('GOLD')"><i class="el-icon-circle-plus" />{{ getAddButtonLabel('GOLD') }}</el-button>
<el-button type="text" :disabled="disableAddContent" @click="addContent('DIAMOND')"><i class="el-icon-circle-plus" />{{ getAddButtonLabel('DIAMOND') }}</el-button>
<el-button type="text" :disabled="disableAddContent" @click="addContent('GAME_COUPON')"><i class="el-icon-circle-plus" />{{ getAddButtonLabel('GAME_COUPON') }}</el-button>
<el-button type="text" :disabled="disableAddContent" :loading="propsTypeData['BADGE'].loading" @click="addContent('BADGE')"><i class="el-icon-circle-plus" />{{ getAddButtonLabel('BADGE') }}</el-button>
<el-button type="text" :disabled="disableAddContent" :loading="propsTypeData['ROOM_BADGE'].loading" @click="addContent('ROOM_BADGE')"><i class="el-icon-circle-plus" />{{ getAddButtonLabel('ROOM_BADGE') }}</el-button>
<el-button type="text" :disabled="disableAddContent" :loading="propsTypeData['EMOJI'].loading" @click="addContent('EMOJI')"><i class="el-icon-circle-plus" />{{ getAddButtonLabel('EMOJI') }}</el-button>
<el-button type="text" :disabled="disableAddContent" :loading="propsTypeData['FLOAT_PICTURE'].loading" @click="addContent('FLOAT_PICTURE')"><i class="el-icon-circle-plus" />{{ getAddButtonLabel('FLOAT_PICTURE') }}</el-button>
<el-button type="text" :disabled="disableAddContent" :loading="propsTypeData['CHAT_BUBBLE'].loading" @click="addContent('CHAT_BUBBLE')"><i class="el-icon-circle-plus" />{{ getAddButtonLabel('CHAT_BUBBLE') }}</el-button>
<el-button type="text" :disabled="disableAddContent" :loading="propsTypeData['FRAGMENTS'].loading" @click="addContent('FRAGMENTS')"><i class="el-icon-circle-plus" />{{ getAddButtonLabel('FRAGMENTS') }}</el-button>
</div>
</el-form-item>
<el-form-item>
<span v-if="1 - sumProbability > 0">概率还差 <span style="color:red;">{{ (1 - sumProbability).toFixed(3) }}</span> 凑满1; <span style="color: #b3b1b1;">完整概率为"1"小数最多三位数</span></span>
<span v-else-if="1 == sumProbability" style="color:#38c95e;">概率已拼凑完整; </span>
<span v-else style="color:#ebbe40;">概率已超过{{ (sumProbability - 1).toFixed(3) }}请调整 <span style="color: #b3b1b1;">完整概率为"1"小数最多三位数</span></span>
<span v-if="1 - sumProbability > 0">{{ $t('pages.gameLotteryConfigForm.message.probabilityRemaining', { value: (1 - sumProbability).toFixed(3) }) }} <span style="color: #b3b1b1;">{{ $t('pages.gameLotteryConfigForm.message.totalProbabilityHint') }}</span></span>
<span v-else-if="1 == sumProbability" style="color:#38c95e;">{{ $t('pages.gameLotteryConfigForm.message.probabilityComplete') }} </span>
<span v-else style="color:#ebbe40;">{{ $t('pages.gameLotteryConfigForm.message.probabilityExceeded', { value: (sumProbability - 1).toFixed(3) }) }} <span style="color: #b3b1b1;">{{ $t('pages.gameLotteryConfigForm.message.totalProbabilityHint') }}</span></span>
</el-form-item>
</el-form>
</div>
<div slot="footer">
<el-button @click="handleClose()">取消</el-button>
<el-button type="primary" :loading="submitLoading" @click="submitForm()">保存</el-button>
<el-button @click="handleClose()">{{ $t('pages.gameLotteryConfigForm.action.cancel') }}</el-button>
<el-button type="primary" :loading="submitLoading" @click="submitForm()">{{ $t('pages.gameLotteryConfigForm.action.save') }}</el-button>
</div>
</el-dialog>
@ -406,7 +406,7 @@ export default {
},
data() {
const commonRules = [
{ required: true, message: '必填字段不可为空', trigger: 'blur' }
{ required: true, message: this.$t('pages.propsSourceGroupForm.validation.requiredField'), trigger: 'blur' }
]
return {
specialIdTypes,
@ -468,68 +468,6 @@ export default {
list: []
}
},
propsTypeMap: {
'EMOJI': {
value: 'EMOJI',
name: '用户-表情包'
},
'BADGE': {
value: 'BADGE',
name: '用户-徽章'
},
'ROOM_BADGE': {
value: 'BADGE',
name: '房间-徽章'
},
'AVATAR_FRAME': {
value: 'PROPS',
name: '头像框'
},
'CHAT_BUBBLE': {
value: 'PROPS',
name: '聊天气泡'
},
'FRAGMENTS': {
value: 'PROPS',
name: '碎片'
},
'RIDE': {
value: 'PROPS',
name: '座驾'
},
'NOBLE_VIP': {
value: 'PROPS',
name: '贵族'
},
'GIFT': {
value: 'GIFT',
name: '礼物'
},
'GOLD': {
value: 'GOLD',
name: '金币'
},
'DIAMOND': {
value: 'DIAMOND',
name: '钻石'
},
'SPECIAL_ID': {
value: 'SPECIAL_ID',
name: '靓号'
},
'THEME': {
value: 'PROPS',
name: '房间背景主题'
},
'GAME_COUPON': {
value: 'GAME_COUPON',
name: '游戏券'
},
'FLOAT_PICTURE': {
value: 'PROPS',
name: '飘窗'
}
},
isChangeSource: false,
propsValidDays,
propsCurrencyType,
@ -558,11 +496,15 @@ export default {
sourceTypeList: [],
selectType: {},
isLoadSourceTypeList: false,
listTypeLoading: false,
badgeRestaurants: [{ 'value': '0', 'label': '永久' }]
listTypeLoading: false
}
},
computed: {
badgeRestaurants() {
const locale = this.$i18n.locale
void locale
return [{ value: '0', label: this.$t('pages.gameLotteryConfigForm.word.permanent') }]
},
rowCover() {
return this.row ? this.row.propsSourceRecordDTO.cover : ''
},
@ -576,7 +518,10 @@ export default {
return !this.form.sourceUrl
},
textOptTitle() {
return (this.row && this.row.id ? '修改' : '添加') + '(' + this.sysOrigin + ')'
return this.$t('pages.gameLotteryConfigForm.dialog.title', {
action: this.isUpdate ? this.$t('pages.gameLotteryConfigForm.action.edit') : this.$t('pages.gameLotteryConfigForm.action.add'),
system: this.sysOrigin
})
},
isUpdate() {
return this.row && this.row.id
@ -631,9 +576,60 @@ export default {
})
},
methods: {
getRequiredFieldRule() {
return { required: true, message: this.$t('pages.propsSourceGroupForm.validation.requiredField'), trigger: 'blur' }
},
getNotEmptyRule() {
return { required: true, message: this.$t('pages.propsSourceGroupForm.validation.notEmpty'), trigger: 'blur' }
},
getLotteryGameTypeLabel(item) {
if (!item) {
return ''
}
if (item.value === 'EGG') {
return this.$t('pages.gameLotteryConfigForm.gameType.egg')
}
return item.name
},
getPropsTypeValue(type) {
if (type === 'EMOJI') {
return 'EMOJI'
}
if (type === 'BADGE' || type === 'ROOM_BADGE') {
return 'BADGE'
}
if (type === 'GIFT' || type === 'GOLD' || type === 'DIAMOND' || type === 'SPECIAL_ID' || type === 'GAME_COUPON') {
return type
}
return 'PROPS'
},
getResourceTypeName(type) {
const keyMap = {
AVATAR_FRAME: 'avatarFrame',
RIDE: 'ride',
NOBLE_VIP: 'nobleVip',
THEME: 'theme',
GIFT: 'gift',
SPECIAL_ID: 'specialId',
GOLD: 'gold',
DIAMOND: 'diamond',
GAME_COUPON: 'gameCoupon',
BADGE: 'userBadge',
ROOM_BADGE: 'roomBadge',
EMOJI: 'emoji',
FLOAT_PICTURE: 'floatPicture',
CHAT_BUBBLE: 'chatBubble',
FRAGMENTS: 'fragments'
}
const key = keyMap[type]
return key ? this.$t(`pages.propsSourceGroupForm.resourceType.${key}`) : ''
},
getAddButtonLabel(type) {
return this.getResourceTypeName(type)
},
querySearchBadgeRestaurants(queryString, cb) {
var badgeRestaurants = this.badgeRestaurants
var results = queryString ? badgeRestaurants.filter(restaurant => {
const badgeRestaurants = this.badgeRestaurants
const results = queryString ? badgeRestaurants.filter(restaurant => {
return (restaurant.value.toLowerCase().indexOf(queryString.toLowerCase()) === 0)
}) : badgeRestaurants
cb(results)
@ -686,11 +682,7 @@ export default {
})
},
getTypeName(item) {
const typeData = this.propsTypeMap[item.detailType]
if (typeData) {
return typeData.name
}
return ''
return this.getResourceTypeName(item.detailType)
},
deleteUpdateItem(index) {
this.form.rewardConfigList.splice(index, 1)
@ -856,7 +848,7 @@ export default {
addContentData(type) {
const that = this
that.form.tmpConfigList.push({
type: that.propsTypeMap[type].value,
type: that.getPropsTypeValue(type),
clickType: type,
content: '',
quantity: '',
@ -864,7 +856,7 @@ export default {
inventory: '',
dataHelp: '',
ext: {
title: that.propsTypeMap[type].name,
title: that.getResourceTypeName(type),
selectVal: { id: '', cover: '', sourceUrl: '' }
}
})
@ -923,13 +915,13 @@ export default {
return
}
if (!that.form.rewardConfigList || that.form.rewardConfigList.length < 1) {
that.$opsMessage.fail('请资源组配置内容.')
that.$opsMessage.fail(that.$t('pages.gameLotteryConfigForm.message.configRequired'))
return
}
that.submitLoading = true
const form = deepClone(that.form)
form.rewardConfigList = []
// 1
// The combined probability must equal 1 before submit.
that.sumProbability = 0.000
that.form.rewardConfigList.forEach((item, index) => {
form.rewardConfigList.push({
@ -946,7 +938,7 @@ export default {
})
if (that.sumProbability.toFixed(3) > 1 || that.sumProbability.toFixed(3) < 1) {
that.submitLoading = false
that.$opsMessage.fail('总概率不等于‘ 1 ,请参考底部提示!')
that.$opsMessage.fail(that.$t('pages.gameLotteryConfigForm.message.totalProbabilityInvalid'))
return
}
saveOrUpdateGameLotteryGroup(form).then(res => {

View File

@ -4,7 +4,7 @@
<div class="filter-container">
<el-select
v-model="listQuery.sysOrigin"
placeholder="归属系统"
:placeholder="$t('pages.gameLotteryConfig.placeholder.system')"
style="width: 120px"
class="filter-item"
@change="handleSearch"
@ -40,13 +40,13 @@
/>
<el-select
v-model="listQuery.gameType"
placeholder="游戏类型"
:placeholder="$t('pages.gameLotteryConfig.placeholder.gameType')"
style="width: 120px"
class="filter-item"
clearable
@change="handleSearch"
>
<el-option v-for="item in lotteryGameTypes" :key="item.value" :label="item.name" :value="item.value" />
<el-option v-for="item in lotteryGameTypes" :key="item.value" :label="getLotteryGameTypeLabel(item)" :value="item.value" />
</el-select>
<el-button
class="filter-item"
@ -54,7 +54,7 @@
icon="el-icon-search"
@click="handleSearch"
>
搜索
{{ $t('pages.gameLotteryConfig.action.search') }}
</el-button>
<el-button
class="filter-item"
@ -62,7 +62,7 @@
icon="el-icon-edit"
@click="handleCreate"
>
添加
{{ $t('pages.gameLotteryConfig.action.add') }}
</el-button>
</div>
<el-table
@ -74,10 +74,10 @@
@cell-mouse-enter="handleMouseEnter"
>
<el-table-column prop="id" width="200" label="ID" align="center" />
<el-table-column width="100" prop="gameType" label="类型名称" align="center">
<el-table-column width="100" prop="gameType" :label="$t('pages.gameLotteryConfig.table.typeName')" align="center">
<template slot-scope="scope">
<span v-for="(item, index) in lotteryGameTypes" :key="index">
<span v-if="item.value === scope.row.gameType">{{ item.name }}</span>
<span v-if="item.value === scope.row.gameType">{{ getLotteryGameTypeLabel(item) }}</span>
</span>
</template>
</el-table-column>
@ -91,20 +91,20 @@
/>
</template>
</el-table-column> -->
<el-table-column label="道具" align="center">
<el-table-column :label="$t('pages.gameLotteryConfig.table.props')" align="center">
<template slot-scope="scope">
<props-row :list="scope.row.rewardConfigList" />
</template>
</el-table-column>
<el-table-column label="时间" align="center">
<el-table-column :label="$t('pages.gameLotteryConfig.table.time')" align="center">
<template slot-scope="scope">
<div>创建时间{{ scope.row.createTime | dateFormat }}</div>
<div>修改时间{{ scope.row.updateTime | dateFormat }}</div>
<div>{{ $t('pages.gameLotteryConfig.time.createdAt') }}: {{ scope.row.createTime | dateFormat }}</div>
<div>{{ $t('pages.gameLotteryConfig.time.updatedAt') }}: {{ scope.row.updateTime | dateFormat }}</div>
</template>
</el-table-column>
<el-table-column fixed="right" label="操作" align="center" width="150">
<el-table-column fixed="right" :label="$t('pages.gameLotteryConfig.table.actions')" align="center" width="150">
<template slot-scope="scope">
<el-button type="text" @click.native="handleUpdate(scope.row)">编辑</el-button>
<el-button type="text" @click.native="handleUpdate(scope.row)">{{ $t('pages.gameLotteryConfig.action.edit') }}</el-button>
</template>
</el-table-column>
</el-table>
@ -142,10 +142,6 @@ export default {
editSysOrigin: '',
sysOriginPlatforms,
lotteryGameTypes,
showcaseTypes: [
{ value: false, name: '下架' },
{ value: true, name: '上架' }
],
formEditVisable: false,
thisRow: {},
thatSelectedUserId: {},
@ -164,7 +160,13 @@ export default {
}
},
computed: {
...mapGetters(['permissionsSysOriginPlatforms'])
...mapGetters(['permissionsSysOriginPlatforms']),
showcaseTypes() {
return [
{ value: false, name: this.$t('pages.propsSourceGroupForm.word.offShelf') },
{ value: true, name: this.$t('pages.propsSourceGroupForm.word.onShelf') }
]
}
},
created() {
const that = this
@ -176,6 +178,15 @@ export default {
that.renderData()
},
methods: {
getLotteryGameTypeLabel(item) {
if (!item) {
return ''
}
if (item.value === 'EGG') {
return this.$t('pages.gameLotteryConfigForm.gameType.egg')
}
return item.name
},
renderData(isClean) {
const that = this
that.listLoading = true

View File

@ -2,7 +2,7 @@
<el-popover
placement="bottom"
title="选中道具"
:title="$t('pages.gameLotteryConfigForm.popover.selectedProps')"
width="500"
trigger="click"
>
@ -29,12 +29,12 @@
</div>
<div class="opetion">
<el-form ref="form" :model="form" :rules="formRules" label-width="110px" style="margin:30px; 50px 0px 0px">
<el-form-item label="类型名称" prop="name">
<el-form-item :label="$t('pages.gameLotteryConfigForm.label.typeName')" prop="name">
<el-input v-model.trim="form.name" type="text" />
</el-form-item>
</el-form>
</div>
<el-button slot="reference">click</el-button>
<el-button slot="reference">{{ $t('pages.gameLotteryConfigForm.action.select') }}</el-button>
</el-popover>
</template>

View File

@ -1,7 +1,7 @@
<template>
<div class="app-container">
<el-dialog
title="抽奖奖励配置列表"
:title="$t('pages.luckyBoxAwardConfig.dialog.detailsListTitle')"
:visible.sync="isDialog"
:before-close="handleClose"
:close-on-click-modal="false"
@ -17,7 +17,7 @@
:disabled="isAdd"
@click="handleAdd"
>
添加
{{ $t('pages.luckyBoxAwardConfig.action.add') }}
</el-button>
</div>
<el-table
@ -28,9 +28,13 @@
highlight-current-row
>
<el-table-column type="index" width="50" label="No" />
<el-table-column prop="sysOrigin" label="平台" align="center" width="280" />
<el-table-column prop="propsType" width="280" label="类型" align="center" />
<el-table-column label="资源" width="280" align="center">
<el-table-column prop="sysOrigin" :label="$t('pages.luckyBoxAwardConfig.table.platform')" align="center" width="280" />
<el-table-column width="280" :label="$t('pages.luckyBoxAwardConfig.table.type')" align="center">
<template slot-scope="scope">
{{ getPropsTypeLabel(scope.row.propsType) }}
</template>
</el-table-column>
<el-table-column :label="$t('pages.luckyBoxAwardConfig.table.resource')" width="280" align="center">
<template slot-scope="scope">
<div class="preview-img">
<el-image
@ -47,23 +51,23 @@
</div>
</template>
</el-table-column>
<el-table-column prop="lotteryNumber" label="目标次数" align="center" width="280" />
<el-table-column prop="validDays" label="有效天数" align="center" width="280" />
<el-table-column prop="createTime" label="创建时间" align="center" width="300">
<el-table-column prop="lotteryNumber" :label="$t('pages.luckyBoxAwardConfig.table.targetCount')" align="center" width="280" />
<el-table-column prop="validDays" :label="$t('pages.luckyBoxAwardConfig.table.validDays')" align="center" width="280" />
<el-table-column prop="createTime" :label="$t('pages.luckyBoxAwardConfig.table.createdAt')" align="center" width="300">
<template slot-scope="scope">
{{ scope.row.createTime | dateFormat }}
</template>
</el-table-column>
<el-table-column fixed="right" label="操作" align="center">
<el-table-column fixed="right" :label="$t('pages.luckyBoxAwardConfig.table.actions')" align="center">
<template slot-scope="scope">
<el-button type="text" @click.native="hanldeEidt(scope.row)">编辑</el-button>
<el-button type="text" @click.native="hanldeEidt(scope.row)">{{ $t('pages.luckyBoxAwardConfig.action.edit') }}</el-button>
</template>
</el-table-column>
</el-table>
<div>
<el-dialog
title="道具配置"
:title="$t('pages.luckyBoxAwardConfig.dialog.detailsFormTitle')"
:visible.sync="innerEditVisible"
width="550px"
top="50px"
@ -74,14 +78,14 @@
<div style="height: 500px;overflow: auto;">
<el-form
ref="dataForm"
:rules="formRules"
:rules="dialogFormRules"
:model="formData"
style="width: 400px; margin-left:50px;"
>
<el-form-item label="系统" prop="sysOrigin">
<el-form-item :label="$t('pages.luckyBoxAwardConfig.form.system')" prop="sysOrigin">
<el-select
v-model="formData.sysOrigin"
placeholder="选择系统"
:placeholder="$t('pages.luckyBoxAwardConfig.placeholder.selectSystem')"
style="width:100%;"
class="filter-item"
@change="changeSysOrigin"
@ -97,18 +101,23 @@
</el-option>
</el-select>
</el-form-item>
<el-form-item label="选择类型" prop="propsType">
<el-form-item :label="$t('pages.luckyBoxAwardConfig.form.propsType')" prop="propsType">
<el-select
v-model="formData.propsType"
placeholder="类型"
:placeholder="$t('pages.luckyBoxAwardConfig.placeholder.propsType')"
style="width:100%;"
class="filter-item"
@change="changePropsType"
>
<el-option v-for="item in propsTypes" :key="item.value" :label="item.name" :value="item.value" />
<el-option
v-for="item in localizedPropsTypes"
:key="item.value"
:label="item.name"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item label="选择资源" prop="propsId">
<el-form-item :label="$t('pages.luckyBoxAwardConfig.form.propsId')" prop="propsId">
<div v-if="selectProps.cover" class="payer-source">
<div class="paler-icon">
<svgaplayer
@ -127,24 +136,24 @@
@select="selectPropsSourcePopover"
/>
</el-form-item>
<el-form-item label="目标次数" prop="lotteryNumber">
<el-input v-model="formData.lotteryNumber" v-number placeholder="目标次数" />
<el-form-item :label="$t('pages.luckyBoxAwardConfig.form.targetCount')" prop="lotteryNumber">
<el-input v-model="formData.lotteryNumber" v-number :placeholder="$t('pages.luckyBoxAwardConfig.placeholder.targetCount')" />
</el-form-item>
<el-form-item label="有效天数" prop="validDays">
<el-input v-model="formData.validDays" v-number placeholder="有效天数" />
<el-form-item :label="$t('pages.luckyBoxAwardConfig.form.validDays')" prop="validDays">
<el-input v-model="formData.validDays" v-number :placeholder="$t('pages.luckyBoxAwardConfig.placeholder.validDays')" />
</el-form-item>
</el-form>
</div>
<div slot="footer" class="dialog-footer">
<el-button @click="innerEditVisible = false">
取消
{{ $t('common.cancel') }}
</el-button>
<el-button
:loading="submitLoading"
type="primary"
@click="handleSubmit"
>
提交
{{ $t('common.submit') }}
</el-button>
</div>
</el-dialog>
@ -173,9 +182,6 @@ export default {
}
},
data() {
const commonRules = [
{ required: true, message: '必填字段不可为空', trigger: 'blur' }
]
const propsTypeMap = {}
propsTypes.forEach(item => {
propsTypeMap[item.value] = {
@ -212,19 +218,29 @@ export default {
validDays: '',
sysOrigin: 'HALAR'
},
formRules: {
propsType: commonRules,
propsId: commonRules,
lotteryNumber: commonRules,
validDays: commonRules,
sysOrigin: commonRules
},
submitLoading: false,
isAdd: true,
listLoading: true
}
},
computed: {
localizedPropsTypes() {
return this.propsTypes.map(item => ({
...item,
name: this.getPropsTypeLabel(item.value)
}))
},
dialogFormRules() {
const requiredMessage = this.$t('pages.luckyBoxAwardConfig.validation.requiredField')
const commonRules = [{ required: true, message: requiredMessage, trigger: 'blur' }]
return {
propsType: commonRules,
propsId: commonRules,
lotteryNumber: commonRules,
validDays: commonRules,
sysOrigin: commonRules
}
},
...mapGetters(['permissionsSysOriginPlatforms'])
},
watch: {
@ -253,6 +269,9 @@ export default {
this.propsTypes = this.propsTypes.filter(item => !(item.value === 'CUSTOMIZE' || item.value === 'FRAGMENTS'))
},
methods: {
getPropsTypeLabel(type) {
return this.$t(`pages.luckyBoxAwardConfig.propsType.${type}`)
},
changeSysOrigin(val) {
const that = this
that.formData.propsType = ''

View File

@ -12,14 +12,14 @@
<div style="height: 500px;overflow: auto;">
<el-form
ref="dataForm"
:rules="rules"
:rules="formRules"
:model="formData"
style="width: 400px; margin-left:50px;"
>
<el-form-item label="系统" prop="sysOrigin">
<el-form-item :label="$t('pages.luckyBoxAwardConfig.form.system')" prop="sysOrigin">
<el-select
v-model="formData.sysOrigin"
placeholder="选择系统"
:placeholder="$t('pages.luckyBoxAwardConfig.placeholder.selectSystem')"
style="width:100%;"
class="filter-item"
>
@ -34,33 +34,33 @@
</el-option>
</el-select>
</el-form-item>
<el-form-item label="关联类型" prop="lotteryType">
<el-form-item :label="$t('pages.luckyBoxAwardConfig.form.lotteryType')" prop="lotteryType">
<el-select
v-model="formData.lotteryType"
placeholder="类型"
:placeholder="$t('pages.luckyBoxAwardConfig.placeholder.type')"
clearable
style="width:100%;"
class="filter-item"
>
<el-option label="经典" :value="'CLASSICS'" />
<el-option label="星座" :value="'CONSTELLATION'" />
<el-option :label="$t('pages.luckyBoxAwardConfig.lotteryType.CLASSICS')" :value="'CLASSICS'" />
<el-option :label="$t('pages.luckyBoxAwardConfig.lotteryType.CONSTELLATION')" :value="'CONSTELLATION'" />
</el-select>
</el-form-item>
<el-form-item label="名称" prop="name">
<el-input v-model.trim="formData.name" type="text" placeholder="名称" />
<el-form-item :label="$t('pages.luckyBoxAwardConfig.form.name')" prop="name">
<el-input v-model.trim="formData.name" type="text" :placeholder="$t('pages.luckyBoxAwardConfig.placeholder.name')" />
</el-form-item>
</el-form>
</div>
<div slot="footer" class="dialog-footer">
<el-button @click="handleClose">
取消
{{ $t('common.cancel') }}
</el-button>
<el-button
:loading="submitLoading"
type="primary"
@click="handleSubmit"
>
提交
{{ $t('common.submit') }}
</el-button>
</div>
</el-dialog>
@ -90,11 +90,6 @@ export default {
name: '',
lotteryType: ''
},
rules: {
sysOrigin: [{ required: true, message: '必填项不可为空', trigger: 'blur' }],
name: [{ required: true, message: '必填项不可为空', trigger: 'blur' }],
lotteryType: [{ required: true, message: '必填项不可为空', trigger: 'blur' }]
},
submitLoading: false
}
},
@ -103,7 +98,19 @@ export default {
return this.updateData === null
},
title() {
return this.isAdd ? `添加(${this.sysOrigin})` : `修改(${this.updateData.sysOrigin})`
const system = this.isAdd ? this.sysOrigin : this.updateData.sysOrigin
const action = this.isAdd
? this.$t('pages.luckyBoxAwardConfig.action.add')
: this.$t('pages.luckyBoxAwardConfig.action.edit')
return this.$t('pages.luckyBoxAwardConfig.dialog.formTitle', { action, system })
},
formRules() {
const requiredMessage = this.$t('pages.luckyBoxAwardConfig.validation.requiredField')
return {
sysOrigin: [{ required: true, message: requiredMessage, trigger: 'blur' }],
name: [{ required: true, message: requiredMessage, trigger: 'blur' }],
lotteryType: [{ required: true, message: requiredMessage, trigger: 'blur' }]
}
},
...mapGetters(['permissionsSysOriginPlatforms'])
},

View File

@ -4,7 +4,7 @@
<div class="filter-container">
<el-select
v-model="listQuery.sysOrigin"
placeholder="系统"
:placeholder="$t('pages.luckyBoxAwardConfig.filter.system')"
style="width: 120px"
class="filter-item"
@change="handleSearch"
@ -21,14 +21,14 @@
</el-select>
<el-select
v-model="listQuery.lotteryType"
placeholder="关联类型"
:placeholder="$t('pages.luckyBoxAwardConfig.filter.lotteryType')"
style="width:120px;"
class="filter-item"
clearable
@change="handleSearch"
>
<el-option label="经典" :value="'CLASSICS'" />
<el-option label="星座" :value="'CONSTELLATION'" />
<el-option :label="$t('pages.luckyBoxAwardConfig.lotteryType.CLASSICS')" :value="'CLASSICS'" />
<el-option :label="$t('pages.luckyBoxAwardConfig.lotteryType.CONSTELLATION')" :value="'CONSTELLATION'" />
</el-select>
<el-button
:loading="searchLoading"
@ -37,7 +37,7 @@
icon="el-icon-search"
@click="handleSearch"
>
搜索
{{ $t('common.search') }}
</el-button>
<el-button
class="filter-item"
@ -46,7 +46,7 @@
icon="el-icon-edit"
@click="handleAdd"
>
添加
{{ $t('pages.luckyBoxAwardConfig.action.add') }}
</el-button>
</div>
<el-table
@ -56,26 +56,26 @@
fit
highlight-current-row
>
<el-table-column prop="sysOrigin" label="平台" align="center" />
<el-table-column label="类型" align="center">
<el-table-column prop="sysOrigin" :label="$t('pages.luckyBoxAwardConfig.table.platform')" align="center" />
<el-table-column :label="$t('pages.luckyBoxAwardConfig.table.type')" align="center">
<template slot-scope="scope">
<div>
<el-tag v-if="scope.row.lotteryType === 'CLASSICS'">经典</el-tag>
<el-tag v-else-if="scope.row.lotteryType === 'CONSTELLATION'">星座</el-tag>
<el-tag v-if="scope.row.lotteryType === 'CLASSICS'">{{ $t('pages.luckyBoxAwardConfig.lotteryType.CLASSICS') }}</el-tag>
<el-tag v-else-if="scope.row.lotteryType === 'CONSTELLATION'">{{ $t('pages.luckyBoxAwardConfig.lotteryType.CONSTELLATION') }}</el-tag>
</div>
</template>
</el-table-column>
<el-table-column prop="name" label="名称" align="center" width="200" />
<el-table-column prop="createTime" label="创建时间" align="center" width="200">
<el-table-column prop="name" :label="$t('pages.luckyBoxAwardConfig.table.name')" align="center" width="200" />
<el-table-column prop="createTime" :label="$t('pages.luckyBoxAwardConfig.table.createdAt')" 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="380">
<el-table-column fixed="right" :label="$t('pages.luckyBoxAwardConfig.table.actions')" align="center" width="380">
<template slot-scope="scope">
<el-button type="text" @click.native="hanldeEidt(scope.row)">编辑</el-button>
<el-button type="text" @click.native="handleAwardConfigList(scope.row)">奖励配置</el-button>
<el-button type="text" @click.native="handlDel(scope.row)">删除</el-button>
<el-button type="text" @click.native="hanldeEidt(scope.row)">{{ $t('pages.luckyBoxAwardConfig.action.edit') }}</el-button>
<el-button type="text" @click.native="handleAwardConfigList(scope.row)">{{ $t('pages.luckyBoxAwardConfig.action.rewardConfig') }}</el-button>
<el-button type="text" @click.native="handlDel(scope.row)">{{ $t('pages.luckyBoxAwardConfig.action.delete') }}</el-button>
</template>
</el-table-column>
</el-table>
@ -187,14 +187,14 @@ export default {
},
//
handlDel(row) {
this.$confirm('确认删除吗?', '提示', {
this.$confirm(this.$t('pages.luckyBoxAwardConfig.confirm.deleteMessage'), this.$t('pages.luckyBoxAwardConfig.confirm.title'), {
type: 'warning'
}).then(() => {
this.listLoading = true
deleteAwardConfig(row.id).then((res) => {
this.listLoading = false
this.$message({
message: '删除成功',
message: this.$t('pages.luckyBoxAwardConfig.message.deleteSuccess'),
type: 'success'
})
this.renderData()

View File

@ -1,7 +1,7 @@
<template>
<div class="app-container">
<el-dialog
title="道具配置列表"
:title="$t('pages.luckyBoxBountyConfig.dialog.detailsListTitle')"
:visible.sync="isDialog"
:before-close="handleClose"
:close-on-click-modal="false"
@ -16,7 +16,7 @@
icon="el-icon-edit"
@click="handleAdd"
>
添加
{{ $t('pages.luckyBoxBountyConfig.action.add') }}
</el-button>
</div>
<el-table
@ -26,8 +26,8 @@
fit
highlight-current-row
>
<el-table-column prop="sysOrigin" label="平台" align="center" width="280" />
<el-table-column label="资源" width="280" align="center">
<el-table-column prop="sysOrigin" :label="$t('pages.luckyBoxBountyConfig.table.platform')" align="center" width="280" />
<el-table-column :label="$t('pages.luckyBoxBountyConfig.table.resource')" width="280" align="center">
<template slot-scope="scope">
<div class="preview-img">
<el-image
@ -44,23 +44,23 @@
</div>
</template>
</el-table-column>
<el-table-column prop="targetQuantity" label="目标数量" align="center" width="280" />
<el-table-column prop="validDays" label="有效天数" align="center" width="280" />
<el-table-column prop="createTime" label="创建时间" align="center" width="300">
<el-table-column prop="targetQuantity" :label="$t('pages.luckyBoxBountyConfig.table.targetQuantity')" align="center" width="280" />
<el-table-column prop="validDays" :label="$t('pages.luckyBoxBountyConfig.table.validDays')" align="center" width="280" />
<el-table-column prop="createTime" :label="$t('pages.luckyBoxBountyConfig.table.createdAt')" align="center" width="300">
<template slot-scope="scope">
{{ scope.row.createTime | dateFormat }}
</template>
</el-table-column>
<el-table-column fixed="right" label="操作" align="center">
<el-table-column fixed="right" :label="$t('pages.luckyBoxBountyConfig.table.actions')" align="center">
<template slot-scope="scope">
<el-button type="text" @click.native="hanldeDelete(scope.row)">删除</el-button>
<el-button type="text" @click.native="hanldeDelete(scope.row)">{{ $t('pages.luckyBoxBountyConfig.action.delete') }}</el-button>
</template>
</el-table-column>
</el-table>
<div>
<el-dialog
title="道具配置"
:title="$t('pages.luckyBoxBountyConfig.dialog.detailsFormTitle')"
:visible.sync="innerEditVisible"
width="550px"
top="50px"
@ -71,14 +71,14 @@
<div style="height: 500px;overflow: auto;">
<el-form
ref="dataForm"
:rules="formRules"
:rules="dialogFormRules"
:model="formData"
style="width: 400px; margin-left:50px;"
>
<el-form-item label="系统" prop="sysOrigin">
<el-form-item :label="$t('pages.luckyBoxBountyConfig.form.system')" prop="sysOrigin">
<el-select
v-model="formData.sysOrigin"
placeholder="选择系统"
:placeholder="$t('pages.luckyBoxBountyConfig.placeholder.selectSystem')"
style="width:100%;"
class="filter-item"
@change="changeSysOrigin"
@ -94,18 +94,18 @@
</el-option>
</el-select>
</el-form-item>
<el-form-item label="选择类型" prop="propsType">
<el-form-item :label="$t('pages.luckyBoxBountyConfig.form.propsType')" prop="propsType">
<el-select
v-model="formData.propsType"
placeholder="类型"
:placeholder="$t('pages.luckyBoxBountyConfig.placeholder.propsType')"
style="width:100%;"
class="filter-item"
@change="changePropsType"
>
<el-option v-for="item in propsTypes" :key="item.value" :label="item.name" :value="item.value" />
<el-option v-for="item in localizedPropsTypes" :key="item.value" :label="item.name" :value="item.value" />
</el-select>
</el-form-item>
<el-form-item label="选择资源" prop="propsId">
<el-form-item :label="$t('pages.luckyBoxBountyConfig.form.propsId')" prop="propsId">
<div v-if="selectProps.cover" class="payer-source">
<div class="paler-icon">
<svgaplayer
@ -124,24 +124,24 @@
@select="selectPropsSourcePopover"
/>
</el-form-item>
<el-form-item label="目标数量" prop="targetQuantity">
<el-input v-model="formData.targetQuantity" v-number placeholder="目标数量" />
<el-form-item :label="$t('pages.luckyBoxBountyConfig.form.targetQuantity')" prop="targetQuantity">
<el-input v-model="formData.targetQuantity" v-number :placeholder="$t('pages.luckyBoxBountyConfig.placeholder.targetQuantity')" />
</el-form-item>
<el-form-item label="有效天数" prop="validDays">
<el-input v-model="formData.validDays" v-number placeholder="有效天数" />
<el-form-item :label="$t('pages.luckyBoxBountyConfig.form.validDays')" prop="validDays">
<el-input v-model="formData.validDays" v-number :placeholder="$t('pages.luckyBoxBountyConfig.placeholder.validDays')" />
</el-form-item>
</el-form>
</div>
<div slot="footer" class="dialog-footer">
<el-button @click="innerEditVisible = false">
取消
{{ $t('common.cancel') }}
</el-button>
<el-button
:loading="submitLoading"
type="primary"
@click="handleSubmit"
>
提交
{{ $t('common.submit') }}
</el-button>
</div>
</el-dialog>
@ -166,9 +166,6 @@ export default {
}
},
data() {
const commonRules = [
{ required: true, message: '必填字段不可为空', trigger: 'blur' }
]
const propsTypeMap = {}
propsTypes.forEach(item => {
propsTypeMap[item.value] = {
@ -205,18 +202,28 @@ export default {
validDays: '',
sysOrigin: 'HALAR'
},
formRules: {
propsType: commonRules,
propsId: commonRules,
targetQuantity: commonRules,
validDays: commonRules,
sysOrigin: commonRules
},
submitLoading: false,
listLoading: true
}
},
computed: {
dialogFormRules() {
const requiredMessage = this.$t('pages.luckyBoxBountyConfig.validation.requiredField')
const commonRules = [{ required: true, message: requiredMessage, trigger: 'blur' }]
return {
propsType: commonRules,
propsId: commonRules,
targetQuantity: commonRules,
validDays: commonRules,
sysOrigin: commonRules
}
},
localizedPropsTypes() {
return this.propsTypes.map(item => ({
...item,
name: this.getPropsTypeLabel(item.value)
}))
},
...mapGetters(['permissionsSysOriginPlatforms'])
},
watch: {
@ -237,6 +244,9 @@ export default {
this.propsTypes = this.propsTypes.filter(item => !(item.value === 'CUSTOMIZE' || item.value === 'FRAGMENTS'))
},
methods: {
getPropsTypeLabel(type) {
return this.$t(`pages.luckyBoxBountyConfig.propsType.${type}`)
},
changeSysOrigin(val) {
const that = this
that.formData.propsType = ''
@ -282,16 +292,15 @@ export default {
this.formData = {}
this.selectProps = {}
},
//
hanldeDelete(row) {
this.$confirm('确认删除吗?', '提示', {
this.$confirm(this.$t('pages.luckyBoxBountyConfig.confirm.deleteMessage'), this.$t('pages.luckyBoxBountyConfig.confirm.title'), {
type: 'warning'
}).then(() => {
this.listLoading = true
deleteBountyDetailsConfig(row.id).then((res) => {
this.listLoading = false
this.$message({
message: '删除成功',
message: this.$t('pages.luckyBoxBountyConfig.message.deleteSuccess'),
type: 'success'
})
this.renderData()

View File

@ -12,14 +12,14 @@
<div style="height: 500px;overflow: auto;">
<el-form
ref="dataForm"
:rules="rules"
:rules="formRules"
:model="formData"
style="width: 400px; margin-left:50px;"
>
<el-form-item label="系统" prop="sysOrigin">
<el-form-item :label="$t('pages.luckyBoxBountyConfig.form.system')" prop="sysOrigin">
<el-select
v-model="formData.sysOrigin"
placeholder="选择系统"
:placeholder="$t('pages.luckyBoxBountyConfig.placeholder.selectSystem')"
style="width:100%;"
class="filter-item"
>
@ -34,31 +34,31 @@
</el-option>
</el-select>
</el-form-item>
<el-form-item label="类型" prop="type">
<el-form-item :label="$t('pages.luckyBoxBountyConfig.form.type')" prop="type">
<el-select
v-model="formData.type"
placeholder="类型"
:placeholder="$t('pages.luckyBoxBountyConfig.placeholder.type')"
clearable
style="width:100%;"
class="filter-item"
>
<el-option label="每日" :value="'DAILY'" />
<el-option label="每周" :value="'WEEKLY'" />
<el-option :label="$t('pages.luckyBoxBountyConfig.type.DAILY')" :value="'DAILY'" />
<el-option :label="$t('pages.luckyBoxBountyConfig.type.WEEKLY')" :value="'WEEKLY'" />
</el-select>
</el-form-item>
<el-form-item label="礼物类型">
<el-form-item :label="$t('pages.luckyBoxBountyConfig.form.giftType')">
<el-select
v-model="formData.giftType"
placeholder="礼物类型"
:placeholder="$t('pages.luckyBoxBountyConfig.placeholder.giftType')"
style="width:100%;"
class="filter-item"
@change="getGiftListInfo()"
>
<el-option v-for="(item) in giftConfigTabs" :key="item.value" :label="item.name" :value="item.value" />
<el-option v-for="(item) in localizedGiftConfigTabs" :key="item.value" :label="item.name" :value="item.value" />
</el-select>
</el-form-item>
<el-form-item prop="giftId" label="资源">
<el-select v-model="formData.giftId" v-loading="giftLoading" placeholder="请选择" style="width:100%;" @change="showPhoto">
<el-form-item prop="giftId" :label="$t('pages.luckyBoxBountyConfig.form.resource')">
<el-select v-model="formData.giftId" v-loading="giftLoading" :placeholder="$t('pages.luckyBoxBountyConfig.placeholder.select')" style="width:100%;" @change="showPhoto">
<el-option
v-for="(item, index) in gifts"
:key="index"
@ -79,14 +79,14 @@
</div>
<div slot="footer" class="dialog-footer">
<el-button @click="handleClose">
取消
{{ $t('common.cancel') }}
</el-button>
<el-button
:loading="submitLoading"
type="primary"
@click="handleSubmit"
>
提交
{{ $t('common.submit') }}
</el-button>
</div>
</el-dialog>
@ -129,12 +129,6 @@ export default {
giftPhoto: ''
},
rules: {
sysOrigin: [{ required: true, message: '必填项不可为空', trigger: 'blur' }],
giftType: [{ required: true, message: '必填项不可为空', trigger: 'blur' }],
giftId: [{ required: true, message: '必填项不可为空', trigger: 'blur' }],
type: [{ required: true, message: '必填项不可为空', trigger: 'blur' }]
},
submitLoading: false
}
},
@ -142,8 +136,27 @@ export default {
isAdd() {
return this.updateData === null
},
localizedGiftConfigTabs() {
return this.giftConfigTabs.map(item => ({
...item,
name: this.$t(`pages.luckyBoxBountyConfig.giftType.${item.value}`)
}))
},
title() {
return this.isAdd ? `添加(${this.sysOrigin})` : `修改(${this.updateData.sysOrigin})`
const system = this.isAdd ? this.sysOrigin : this.updateData.sysOrigin
const action = this.isAdd
? this.$t('pages.luckyBoxBountyConfig.action.add')
: this.$t('pages.luckyBoxBountyConfig.action.edit')
return this.$t('pages.luckyBoxBountyConfig.dialog.formTitle', { action, system })
},
formRules() {
const requiredMessage = this.$t('pages.luckyBoxBountyConfig.validation.requiredField')
return {
sysOrigin: [{ required: true, message: requiredMessage, trigger: 'blur' }],
giftType: [{ required: true, message: requiredMessage, trigger: 'blur' }],
giftId: [{ required: true, message: requiredMessage, trigger: 'blur' }],
type: [{ required: true, message: requiredMessage, trigger: 'blur' }]
}
},
...mapGetters(['permissionsSysOriginPlatforms'])
},

View File

@ -4,7 +4,7 @@
<div class="filter-container">
<el-select
v-model="listQuery.sysOrigin"
placeholder="系统"
:placeholder="$t('pages.luckyBoxBountyConfig.filter.system')"
style="width: 120px"
class="filter-item"
@change="handleSearch"
@ -21,14 +21,14 @@
</el-select>
<el-select
v-model="listQuery.type"
placeholder="类型"
:placeholder="$t('pages.luckyBoxBountyConfig.filter.type')"
style="width:120px;"
class="filter-item"
clearable
@change="handleSearch"
>
<el-option label="每日" :value="'DAILY'" />
<el-option label="每周" :value="'WEEKLY'" />
<el-option :label="$t('pages.luckyBoxBountyConfig.type.DAILY')" :value="'DAILY'" />
<el-option :label="$t('pages.luckyBoxBountyConfig.type.WEEKLY')" :value="'WEEKLY'" />
</el-select>
<el-button
:loading="searchLoading"
@ -37,7 +37,7 @@
icon="el-icon-search"
@click="handleSearch"
>
搜索
{{ $t('common.search') }}
</el-button>
<el-button
class="filter-item"
@ -46,7 +46,7 @@
icon="el-icon-edit"
@click="handleAdd"
>
添加
{{ $t('pages.luckyBoxBountyConfig.action.add') }}
</el-button>
</div>
<el-table
@ -56,16 +56,16 @@
fit
highlight-current-row
>
<el-table-column prop="sysOrigin" label="平台" align="center" />
<el-table-column label="类型" align="center">
<el-table-column prop="sysOrigin" :label="$t('pages.luckyBoxBountyConfig.table.platform')" align="center" />
<el-table-column :label="$t('pages.luckyBoxBountyConfig.table.type')" align="center">
<template slot-scope="scope">
<div>
<el-tag v-if="scope.row.type === 'DAILY'" type="danger">每日</el-tag>
<el-tag v-else-if="scope.row.type === 'WEEKLY'">每周</el-tag>
<el-tag v-if="scope.row.type === 'DAILY'" type="danger">{{ $t('pages.luckyBoxBountyConfig.type.DAILY') }}</el-tag>
<el-tag v-else-if="scope.row.type === 'WEEKLY'">{{ $t('pages.luckyBoxBountyConfig.type.WEEKLY') }}</el-tag>
</div>
</template>
</el-table-column>
<el-table-column label="封面" align="center">
<el-table-column :label="$t('pages.luckyBoxBountyConfig.table.cover')" align="center">
<template slot-scope="scope">
<el-image
style="width: 50px; height: 50px"
@ -74,16 +74,16 @@
/>
</template>
</el-table-column>
<el-table-column prop="createTime" label="创建时间" align="center" width="200">
<el-table-column prop="createTime" :label="$t('pages.luckyBoxBountyConfig.table.createdAt')" 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="380">
<el-table-column fixed="right" :label="$t('pages.luckyBoxBountyConfig.table.actions')" align="center" width="380">
<template slot-scope="scope">
<el-button type="text" @click.native="hanldeEidt(scope.row)">编辑</el-button>
<el-button type="text" @click.native="handleProbabilityConfigList(scope.row)">道具配置</el-button>
<el-button type="text" @click.native="handlDel(scope.row)">删除</el-button>
<el-button type="text" @click.native="hanldeEidt(scope.row)">{{ $t('pages.luckyBoxBountyConfig.action.edit') }}</el-button>
<el-button type="text" @click.native="handleProbabilityConfigList(scope.row)">{{ $t('pages.luckyBoxBountyConfig.action.propsConfig') }}</el-button>
<el-button type="text" @click.native="handlDel(scope.row)">{{ $t('pages.luckyBoxBountyConfig.action.delete') }}</el-button>
</template>
</el-table-column>
</el-table>
@ -193,16 +193,15 @@ export default {
this.bountyConfigEditVisible = false
this.renderData(event === 'create')
},
//
handlDel(row) {
this.$confirm('确认删除吗?', '提示', {
this.$confirm(this.$t('pages.luckyBoxBountyConfig.confirm.deleteMessage'), this.$t('pages.luckyBoxBountyConfig.confirm.title'), {
type: 'warning'
}).then(() => {
this.listLoading = true
deleteBountyConfig(row.id).then((res) => {
this.listLoading = false
this.$message({
message: '删除成功',
message: this.$t('pages.luckyBoxBountyConfig.message.deleteSuccess'),
type: 'success'
})
this.renderData()

View File

@ -3,67 +3,67 @@
<div v-loading="loading" class="fortune-config">
<el-form ref="form" :model="form" :rules="rules" label-width="130px">
<el-alert
title="单抽获取以下幸运值概率(单位%)"
:title="$t('pages.luckyBoxFortuneConfig.alert.singleDraw')"
type="info"
:closable="false"
style="margin-bottom: 20px;"
/>
<el-form-item prop="oneDot" label="1点">
<el-form-item prop="oneDot" :label="$t('pages.luckyBoxFortuneConfig.label.oneDot')">
<el-input v-model="form.oneDot" v-number />
</el-form-item>
<el-form-item prop="twoDot" label="2点">
<el-form-item prop="twoDot" :label="$t('pages.luckyBoxFortuneConfig.label.twoDot')">
<el-input v-model.trim="form.twoDot" v-number />
</el-form-item>
<el-form-item label="3点" prop="threeDot">
<el-form-item :label="$t('pages.luckyBoxFortuneConfig.label.threeDot')" prop="threeDot">
<el-input v-model="form.threeDot" v-number />
</el-form-item>
<el-alert
title="10抽获取以下幸运值概率(单位%)"
:title="$t('pages.luckyBoxFortuneConfig.alert.tenDraws')"
type="info"
:closable="false"
style="margin-bottom: 20px;"
/>
<el-form-item prop="aroundElevenDot" label="11-15点">
<el-form-item prop="aroundElevenDot" :label="$t('pages.luckyBoxFortuneConfig.label.aroundElevenDot')">
<el-input v-model="form.aroundElevenDot" v-number />
</el-form-item>
<el-form-item prop="aroundSixteenDot" label="16-20点">
<el-form-item prop="aroundSixteenDot" :label="$t('pages.luckyBoxFortuneConfig.label.aroundSixteenDot')">
<el-input v-model.trim="form.aroundSixteenDot" v-number />
</el-form-item>
<el-form-item label="21-25点" prop="aroundTwentyOneDot">
<el-form-item :label="$t('pages.luckyBoxFortuneConfig.label.aroundTwentyOneDot')" prop="aroundTwentyOneDot">
<el-input v-model="form.aroundTwentyOneDot" v-number />
</el-form-item>
<el-form-item label="26-30点" prop="aroundTwentySixDot">
<el-form-item :label="$t('pages.luckyBoxFortuneConfig.label.aroundTwentySixDot')" prop="aroundTwentySixDot">
<el-input v-model="form.aroundTwentySixDot" v-number />
</el-form-item>
<el-alert
title="50抽获取以下幸运值概率(单位%)"
:title="$t('pages.luckyBoxFortuneConfig.alert.fiftyDraws')"
type="info"
:closable="false"
style="margin-bottom: 20px;"
/>
<el-form-item prop="aroundFiftyOneDot" label="51-100点">
<el-form-item prop="aroundFiftyOneDot" :label="$t('pages.luckyBoxFortuneConfig.label.aroundFiftyOneDot')">
<el-input v-model="form.aroundFiftyOneDot" v-number />
</el-form-item>
<el-form-item prop="aroundOneHundredDot" label="101-150点">
<el-form-item prop="aroundOneHundredDot" :label="$t('pages.luckyBoxFortuneConfig.label.aroundOneHundredDot')">
<el-input v-model.trim="form.aroundOneHundredDot" v-number />
</el-form-item>
<el-alert
title="幸运时刻配置"
:title="$t('pages.luckyBoxFortuneConfig.alert.luckyMoment')"
type="info"
:closable="false"
style="margin-bottom: 20px;"
/>
<el-form-item label="幸运时刻1" prop="luckyTimeOne">
<el-form-item :label="$t('pages.luckyBoxFortuneConfig.label.luckyTimeOne')" prop="luckyTimeOne">
<el-input v-model="form.luckyTimeOne" v-number />
</el-form-item>
<el-form-item label="幸运时刻2" prop="luckyTimeTwo">
<el-form-item :label="$t('pages.luckyBoxFortuneConfig.label.luckyTimeTwo')" prop="luckyTimeTwo">
<el-input v-model="form.luckyTimeTwo" v-number />
</el-form-item>
<el-form-item label="幸运时刻时长(S)" prop="luckyTimeDuration">
<el-form-item :label="$t('pages.luckyBoxFortuneConfig.label.luckyTimeDuration')" prop="luckyTimeDuration">
<el-input v-model="form.luckyTimeDuration" v-number />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="onSubmit">提交</el-button>
<el-button type="primary" @click="onSubmit">{{ $t('pages.luckyBoxFortuneConfig.action.submit') }}</el-button>
</el-form-item>
</el-form>
</div>
@ -76,7 +76,6 @@ import { mapGetters } from 'vuex'
export default {
name: 'FortuneConfig',
data() {
const commonValid = [{ required: true, message: '必填字段不可为空', trigger: 'blur' }]
return {
loading: false,
form: {
@ -97,8 +96,14 @@ export default {
},
fortuneConfigQuery: {
sysOrigin: 'HALAR'
},
rules: {
}
}
},
computed: {
...mapGetters(['permissionsSysOriginPlatforms']),
rules() {
const commonValid = [{ required: true, message: this.$t('pages.luckyBoxFortuneConfig.validation.requiredField'), trigger: 'blur' }]
return {
oneDot: commonValid,
twoDot: commonValid,
threeDot: commonValid,
@ -115,9 +120,6 @@ export default {
}
}
},
computed: {
...mapGetters(['permissionsSysOriginPlatforms'])
},
created() {
const that = this
const querySystem = this.permissionsSysOriginPlatforms[0]
@ -151,12 +153,12 @@ export default {
parseInt(that.form.aroundElevenDot) === 0 || parseInt(that.form.aroundSixteenDot) === 0 ||
parseInt(that.form.aroundTwentyOneDot) === 0 || parseInt(that.form.aroundTwentySixDot) === 0 ||
parseInt(that.form.aroundFiftyOneDot) === 0 || parseInt(that.form.aroundOneHundredDot) === 0) {
that.$opsMessage.fail('概率必须大于0')
that.$opsMessage.fail(that.$t('pages.luckyBoxFortuneConfig.message.probabilityGreaterThanZero'))
return
}
if (probability_1 !== 100 || probability_2 !== 100 || probability_3 !== 100) {
that.$opsMessage.fail('概率必须等于100')
that.$opsMessage.fail(that.$t('pages.luckyBoxFortuneConfig.message.probabilityMustEqualHundred'))
return
}
that.$refs.form.validate((valid) => {
@ -165,9 +167,9 @@ export default {
return false
}
that.$confirm('是否确认提交?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
that.$confirm(that.$t('pages.luckyBoxFortuneConfig.confirm.submitMessage'), that.$t('pages.luckyBoxFortuneConfig.confirm.title'), {
confirmButtonText: that.$t('pages.luckyBoxFortuneConfig.confirm.confirmButtonText'),
cancelButtonText: that.$t('pages.luckyBoxFortuneConfig.confirm.cancelButtonText'),
type: 'warning'
}).then(() => {
that.loading = true

View File

@ -12,14 +12,14 @@
<div style="height: 500px;overflow: auto;">
<el-form
ref="dataForm"
:rules="rules"
:rules="formRules"
:model="formData"
style="width: 400px; margin-left:50px;"
>
<el-form-item label="系统" prop="sysOrigin">
<el-form-item :label="$t('pages.luckyBoxStandardConfig.form.system')" prop="sysOrigin">
<el-select
v-model="formData.sysOrigin"
placeholder="选择系统"
:placeholder="$t('pages.luckyBoxStandardConfig.placeholder.selectSystem')"
style="width:100%;"
class="filter-item"
>
@ -34,36 +34,36 @@
</el-option>
</el-select>
</el-form-item>
<el-form-item label="类型" prop="lotteryType">
<el-form-item :label="$t('pages.luckyBoxStandardConfig.form.type')" prop="lotteryType">
<el-select
v-model="formData.lotteryType"
placeholder="类型"
:placeholder="$t('pages.luckyBoxStandardConfig.placeholder.type')"
clearable
style="width:100%;"
class="filter-item"
>
<el-option label="经典" :value="'CLASSICS'" />
<el-option label="星座" :value="'CONSTELLATION'" />
<el-option :label="$t('pages.luckyBoxStandardConfig.lotteryType.CLASSICS')" :value="'CLASSICS'" />
<el-option :label="$t('pages.luckyBoxStandardConfig.lotteryType.CONSTELLATION')" :value="'CONSTELLATION'" />
</el-select>
</el-form-item>
<el-form-item label="名称" prop="name">
<el-input v-model.trim="formData.name" type="text" placeholder="名称" />
<el-form-item :label="$t('pages.luckyBoxStandardConfig.form.name')" prop="name">
<el-input v-model.trim="formData.name" type="text" :placeholder="$t('pages.luckyBoxStandardConfig.placeholder.name')" />
</el-form-item>
<el-form-item label="展示礼物数量" prop="giftQuantity">
<el-input v-model.trim="formData.giftQuantity" type="text" placeholder="展示礼物数量" />
<el-form-item :label="$t('pages.luckyBoxStandardConfig.form.giftQuantity')" prop="giftQuantity">
<el-input v-model.trim="formData.giftQuantity" type="text" :placeholder="$t('pages.luckyBoxStandardConfig.placeholder.giftQuantity')" />
</el-form-item>
</el-form>
</div>
<div slot="footer" class="dialog-footer">
<el-button @click="handleClose">
取消
{{ $t('common.cancel') }}
</el-button>
<el-button
:loading="submitLoading"
type="primary"
@click="handleSubmit"
>
提交
{{ $t('common.submit') }}
</el-button>
</div>
</el-dialog>
@ -94,11 +94,6 @@ export default {
lotteryType: '',
giftQuantity: ''
},
rules: {
sysOrigin: [{ required: true, message: '必填项不可为空', trigger: 'blur' }],
lotteryType: [{ required: true, message: '必填项不可为空', trigger: 'blur' }],
giftQuantity: [{ required: true, message: '必填项不可为空', trigger: 'blur' }]
},
submitLoading: false
}
},
@ -107,7 +102,19 @@ export default {
return this.updateData === null
},
title() {
return this.isAdd ? `添加(${this.sysOrigin})` : `修改(${this.updateData.sysOrigin})`
const system = this.isAdd ? this.sysOrigin : this.updateData.sysOrigin
const action = this.isAdd
? this.$t('pages.luckyBoxStandardConfig.action.add')
: this.$t('pages.luckyBoxStandardConfig.action.edit')
return this.$t('pages.luckyBoxStandardConfig.dialog.formTitle', { action, system })
},
formRules() {
const requiredMessage = this.$t('pages.luckyBoxStandardConfig.validation.requiredField')
return {
sysOrigin: [{ required: true, message: requiredMessage, trigger: 'blur' }],
lotteryType: [{ required: true, message: requiredMessage, trigger: 'blur' }],
giftQuantity: [{ required: true, message: requiredMessage, trigger: 'blur' }]
}
},
...mapGetters(['permissionsSysOriginPlatforms'])
},

View File

@ -1,7 +1,7 @@
<template>
<div class="app-container">
<el-dialog
title="礼物配置列表"
:title="$t('pages.luckyBoxGiftDetails.dialog.listTitle')"
:visible.sync="isDialog"
:before-close="handleClose"
:close-on-click-modal="false"
@ -17,10 +17,10 @@
:disabled="isAdd"
@click="handleAdd"
>
添加
{{ $t("pages.luckyBoxGiftDetails.action.add") }}
</el-button>
<el-alert
title="luckyBox礼物配置-必读"
:title="$t('pages.luckyBoxGiftDetails.alert.title')"
type="warning"
:description="description"
show-icon
@ -35,9 +35,22 @@
highlight-current-row
>
<el-table-column type="index" width="50" label="No" />
<el-table-column prop="sysOrigin" label="平台" align="center" width="280" />
<el-table-column prop="name" label="名称" align="center" width="280" />
<el-table-column label="封面" align="center">
<el-table-column
prop="sysOrigin"
:label="$t('pages.luckyBoxGiftDetails.table.platform')"
align="center"
width="280"
/>
<el-table-column
prop="name"
:label="$t('pages.luckyBoxGiftDetails.table.name')"
align="center"
width="280"
/>
<el-table-column
:label="$t('pages.luckyBoxGiftDetails.table.cover')"
align="center"
>
<template slot-scope="scope">
<el-image
style="width: 50px; height: 50px"
@ -46,22 +59,38 @@
/>
</template>
</el-table-column>
<el-table-column prop="giftCandy" label="金币" align="center" width="280" />
<el-table-column prop="createTime" label="创建时间" align="center" width="300">
<el-table-column
prop="giftCandy"
:label="$t('pages.luckyBoxGiftDetails.table.gold')"
align="center"
width="280"
/>
<el-table-column
prop="createTime"
:label="$t('pages.luckyBoxGiftDetails.table.createdAt')"
align="center"
width="300"
>
<template slot-scope="scope">
{{ scope.row.createTime | dateFormat }}
</template>
</el-table-column>
<el-table-column fixed="right" label="操作" align="center">
<el-table-column
fixed="right"
:label="$t('pages.luckyBoxGiftDetails.table.actions')"
align="center"
>
<template slot-scope="scope">
<el-button type="text" @click.native="hanldeEidt(scope.row)">编辑</el-button>
<el-button type="text" @click.native="hanldeEidt(scope.row)">{{
$t("pages.luckyBoxGiftDetails.action.edit")
}}</el-button>
</template>
</el-table-column>
</el-table>
<div>
<el-dialog
title="礼物配置"
:title="$t('pages.luckyBoxGiftDetails.dialog.formTitle')"
width="550px"
top="50px"
:visible.sync="innerEditVisible"
@ -74,12 +103,16 @@
ref="dataForm"
:rules="formRules"
:model="formData"
class="i18n-form"
style="width: 400px; margin-left:50px;"
>
<el-form-item label="系统" prop="sysOrigin">
<el-form-item
:label="$t('pages.luckyBoxGiftDetails.form.system')"
prop="sysOrigin"
>
<el-select
v-model="formData.sysOrigin"
placeholder="选择系统"
:placeholder="$t('pages.luckyBoxGiftDetails.placeholder.selectSystem')"
style="width:100%;"
class="filter-item"
>
@ -94,25 +127,51 @@
</el-option>
</el-select>
</el-form-item>
<el-form-item label="名称" prop="name">
<el-input v-model="formData.name" placeholder="名称" />
<el-form-item
:label="$t('pages.luckyBoxGiftDetails.form.name')"
prop="name"
>
<el-input
v-model="formData.name"
:placeholder="$t('pages.luckyBoxGiftDetails.placeholder.name')"
/>
</el-form-item>
<el-form-item label="序号" prop="sort">
<el-input v-model="formData.sort" placeholder="序号" />
<el-form-item
:label="$t('pages.luckyBoxGiftDetails.form.sort')"
prop="sort"
>
<el-input
v-model="formData.sort"
:placeholder="$t('pages.luckyBoxGiftDetails.placeholder.sort')"
/>
</el-form-item>
<el-form-item label="礼物类型">
<el-form-item :label="$t('pages.luckyBoxGiftDetails.form.giftType')">
<el-select
v-model="formData.giftType"
placeholder="礼物类型"
:placeholder="$t('pages.luckyBoxGiftDetails.placeholder.giftType')"
style="width:100%;"
class="filter-item"
@change="getGiftListInfo()"
>
<el-option v-for="(item) in giftConfigTabs" :key="item.value" :label="item.name" :value="item.value" />
<el-option
v-for="item in localizedGiftConfigTabs"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item prop="giftId" label="资源">
<el-select v-model="formData.giftId" v-loading="giftLoading" placeholder="请选择" style="width:100%;" @change="showPhoto">
<el-form-item
prop="giftId"
:label="$t('pages.luckyBoxGiftDetails.form.resource')"
>
<el-select
v-model="formData.giftId"
v-loading="giftLoading"
:placeholder="$t('pages.luckyBoxGiftDetails.placeholder.select')"
style="width:100%;"
@change="showPhoto"
>
<el-option
v-for="(item, index) in gifts"
:key="index"
@ -133,14 +192,14 @@
</div>
<div slot="footer" class="dialog-footer">
<el-button @click="innerEditVisible = false">
取消
{{ $t("common.cancel") }}
</el-button>
<el-button
:loading="submitLoading"
type="primary"
@click="handleSubmit"
>
提交
{{ $t("common.submit") }}
</el-button>
</div>
</el-dialog>
@ -168,9 +227,6 @@ export default {
}
},
data() {
const commonRules = [
{ required: true, message: '必填字段不可为空', trigger: 'blur' }
]
return {
standardConfig: {
id: '',
@ -198,11 +254,6 @@ export default {
giftId: '',
sort: ''
},
formRules: {
sysOrigin: commonRules,
sort: commonRules,
giftType: commonRules
},
gift: {
id: '',
giftPhoto: ''
@ -214,7 +265,29 @@ export default {
},
computed: {
description() {
return `礼物必须配置(${this.standardConfig.giftQuantity})个,游戏才可进行!`
return this.$t('pages.luckyBoxGiftDetails.alert.description', {
count: this.standardConfig.giftQuantity
})
},
localizedGiftConfigTabs() {
return this.giftConfigTabs.map(item => ({
...item,
label: this.$t(`pages.luckyBoxGiftDetails.giftType.${item.value}`)
}))
},
formRules() {
const commonRules = [
{
required: true,
message: this.$t('pages.luckyBoxGiftDetails.validation.requiredField'),
trigger: 'blur'
}
]
return {
sysOrigin: commonRules,
sort: commonRules,
giftType: commonRules
}
},
...mapGetters(['permissionsSysOriginPlatforms'])
},

Some files were not shown because too many files have changed in this diff Show More