feat(数据大屏、退款记录): 页面翻译+样式调整

This commit is contained in:
hzj 2026-05-29 15:15:44 +08:00
parent 5e64e00f75
commit 6950aa1b40
15 changed files with 574 additions and 116 deletions

View File

@ -237,6 +237,21 @@
- 直接公共组件状态:已复核,中文扫描无命中
- 验证结果:`npm run build:prod` 通过,仍保留项目既有 17 个 warning
- 状态已完成线程一2026-05-08
- 退款记录 / Refund Records
- 路由别名:`orderReimburse`
- 路径:`order/reimburse/index`
- 已完成文件:
- `src/views/order/reimburse/index.vue`
- 页面私有组件状态:无页面私有组件
- 直接公共组件状态:
- `src/components/Pagination/index.vue`:历史已完成,本轮复核中文扫描无命中
- `src/components/SysOriginIcon/index.vue`:历史已完成,本轮复核中文扫描无命中
- `src/components/data/AccountInput/index.vue`:历史已完成,本轮复核中文扫描无命中
- `src/components/data/UserDeatilsDrawer/index.vue`:历史已完成,本轮复核中文扫描无命中
- `src/components/PlatformSvgIcon/index.vue`:本轮复核中文扫描无命中
- 保留项:`expandTypeName``productDescription``payPlatform`、订单状态等接口返回动态数据保留原样,仅对已知 `REIMBURSE` 状态补展示映射;接口参数、路由路径与权限码保持原样
- 验证结果:目标文件与直接公共组件中文扫描无命中;语言文件语法检查通过;`NODE_OPTIONS=--openssl-legacy-provider npm run build:prod` 通过,仍保留项目既有 17 个 warning
- 状态已完成线程一2026-05-29
- 数据配置 / Data Configuration
- 路由别名:`data:conf`
- 状态:已完成
@ -776,6 +791,23 @@
- `src/components/data/AccountInput/index.vue`:复核通过
- `src/components/data/UserInfo/UserTableExhibit/index.vue`:复核通过
- 状态已完成线程一2026-05-07
- 数据大屏 / Data Dashboard
- 路由别名:`Datav`
- 路径:`datav/index`
- 已完成文件:
- `src/views/datav/index.vue`
- `src/views/datav/latest-purchase.vue`
- `src/views/datav/online-user-count-origin.vue`
- `src/views/datav/purchase-count.vue`
- 页面私有组件状态:
- `latest-purchase.vue`:复核通过,当前无静态中文展示文案
- `online-user-count-origin.vue``purchase-count.vue`:已随入口页同步完成
- 直接公共组件状态:
- `src/components/data/Avatar/index.vue`:本轮复核中文扫描无命中
- `src/components/FlagIcon/index.vue`:本轮已将复制成功 / 失败提示接入 `common.copySuccess` / `common.copyFail`
- 保留项:接口返回的用户昵称、商品描述、金额、国家码、系统来源与统计数据保持原样;`online-user-map.vue``latest-purchase-timchat.vue``online-user-count.vue` 等未被入口页直接渲染的文件不纳入本轮
- 验证结果:入口页、直接私有组件与直接公共组件中文扫描无命中;语言文件语法检查通过;`NODE_OPTIONS=--openssl-legacy-provider npm run build:prod` 通过,仍保留项目既有 17 个 warning
- 状态已完成线程一2026-05-29
- 语音房间 / Voice Rooms
- 路由别名:`VoiceRoom`
@ -1156,6 +1188,7 @@
- 积分收支记录 / Points Transaction Records
- 金币代理流水 / Gold Agent Transactions
- 钻石余额列表 / Diamond Balance List
- 退款记录 / Refund Records
- 数据配置 / Data Configuration
- 参数配置 / Parameter Settings
- 幸运礼物规则配置 / Lucky Gift Rule Configuration
@ -1208,6 +1241,7 @@
- 现金提现申请 / Cash Withdrawal Applications
- 自动支付工资凭据 / Automatic Salary Payment Vouchers
- 用户工资钻石 / User Salary Diamonds
- 数据大屏 / Data Dashboard
- 语音房间 / Voice Rooms
- 在线房间 / Online Rooms
- 房间资料列表 / Room Profile List
@ -1257,7 +1291,7 @@
## 主要路由
{
"time": "1776839315693",
"time": "1780035478289",
"status": true,
"errorCode": 0,
"body": [
@ -1457,6 +1491,17 @@
"icon": "gridding",
"alias": "User:Diamond:Balance"
},
{
"id": -1648943103,
"parentId": 177,
"childrens": [],
"menuName": "退款记录",
"menuType": 2,
"router": "order/reimburse",
"path": "order/reimburse/index",
"icon": "bank_card",
"alias": "orderReimburse"
},
{
"id": 528,
"parentId": 177,
@ -2102,6 +2147,17 @@
"path": "",
"icon": "revenue",
"alias": "UserSalaryDiamondBalance"
},
{
"id": 1123491842,
"parentId": 184,
"childrens": [],
"menuName": "数据大屏",
"menuType": 2,
"router": "datav",
"path": "datav/index",
"icon": "data_overview",
"alias": "Datav"
}
],
"menuName": "运营管理",
@ -2323,17 +2379,6 @@
"id": 305,
"parentId": 0,
"childrens": [
{
"id": 307,
"parentId": 305,
"childrens": [],
"menuName": "资源配置",
"menuType": 2,
"router": "props/config",
"path": "props/props-resource-config/index",
"icon": "sys_manager_2",
"alias": "props:config"
},
{
"id": 312,
"parentId": 305,
@ -2366,6 +2411,17 @@
"path": "props/props-store/index",
"icon": "device_maintain",
"alias": "props:store"
},
{
"id": 1056382977,
"parentId": 305,
"childrens": [],
"menuName": "资源配置",
"menuType": 2,
"router": "props/config",
"path": "props/props-resource-config/index",
"icon": "sys_manager_2",
"alias": "props:config"
}
],
"menuName": "道具管理",

View File

@ -6812,6 +6812,55 @@
- 权限码
- 后端依赖值
- 线程一已新认领:主要路由页面 / 运营管理 / 营收支出 / 退款记录、运营管理 / 数据大屏
- 对应文件:
- `src/views/order/reimburse/index.vue`
- `src/views/datav/index.vue`
- 领取前动作:
- 已重新读取 `docs/i18n/线程协作.md`
- 已重新读取 `docs/i18n/翻译进度.md`
- 已重新读取 `docs/i18n/翻译清单.md`
- 已重新读取 `docs/i18n/主要路由.md`
- 已重新读取 `src/lang/messages/pages/en.js`
- 已重新读取 `src/lang/messages/pages/zh.js`
- 已确认当前没有其他线程认领上述两个主要路由页面
- 已按最新规则先更新 md再开始源码改动
- 认领范围包括:
- 入口页静态展示文本翻译
- 入口页直接渲染的私有组件与公共组件复核
- 新增或复用 `src/lang/messages/pages/en.js``src/lang/messages/pages/zh.js` 对应 key
- 构建验证
- `docs/i18n/主要路由.md``docs/i18n/翻译进度.md``docs/i18n/翻译清单.md` 同步更新
- 不包含:
- 接口参数
- 路由路径
- 权限码
- 后端依赖值
- 线程一已完成:主要路由页面 / 运营管理 / 营收支出 / 退款记录、运营管理 / 数据大屏
- 完成文件:
- `src/views/order/reimburse/index.vue`
- `src/views/datav/index.vue`
- `src/views/datav/latest-purchase.vue`
- `src/views/datav/online-user-count-origin.vue`
- `src/views/datav/purchase-count.vue`
- `src/components/FlagIcon/index.vue`
- 完成范围包括:
- 退款记录筛选区、日期范围、搜索按钮、表格列名、加载文案、免费试用布尔展示与详情按钮国际化
- 数据大屏入口标题、分区标题、统计指标标题国际化
- `FlagIcon` 复制成功 / 失败提示接入 `common.copySuccess` / `common.copyFail`
- 新增 `pages.orderReimburse``pages.datav`,补充 `common.copySuccess` / `common.copyFail`
- 目标页面、直接私有组件与直接公共组件中文残留扫描
- 构建验证
- `docs/i18n/主要路由.md``docs/i18n/翻译进度.md``docs/i18n/翻译清单.md` 同步更新
- 保留项:
- 接口返回动态数据、接口参数、路由路径、权限码、系统来源、商品描述、用户昵称、金额与统计数据保持原样
- `src/views/datav/online-user-map.vue``latest-purchase-timchat.vue``online-user-count.vue` 等未被入口页直接渲染的文件不纳入本轮
- 验证结果:
- 目标页面、直接私有组件与直接公共组件中文扫描无命中
- 语言文件语法检查通过
- 直接执行 `npm run build:prod` 因 Node 22 OpenSSL 兼容问题失败;使用 `NODE_OPTIONS=--openssl-legacy-provider npm run build:prod` 构建通过
- 构建仍保留项目既有 `17` 条 warning未新增阻塞错误
## 推荐分工方式
### 方案一:按页面拆分

View File

@ -348,12 +348,27 @@
## 进行中
- 线程一:当前无进行中任务,待分配
- 上一轮已完成:主要路由页面 / 道具管理 / 资源配置 / Resource Configuration
- 上一轮已完成:主要路由页面 / 运营管理 / 营收支出 / 退款记录;运营管理 / 数据大屏
- 线程二:当前无进行中任务,待分配
- 线程三:当前无进行中任务,待分配
## 2026-05-29 最新同步
- 线程一已完成:主要路由页面 / 运营管理 / 营收支出 / 退款记录;运营管理 / 数据大屏
- 文件:
- `src/views/order/reimburse/index.vue`
- `src/views/datav/index.vue`
- `src/views/datav/latest-purchase.vue`
- `src/views/datav/online-user-count-origin.vue`
- `src/views/datav/purchase-count.vue`
- `src/components/FlagIcon/index.vue`
- 语言 key新增 `pages.orderReimburse``pages.datav`;补充 `common.copySuccess``common.copyFail`
- 直接公共组件复核:`Pagination``SysOriginIcon``AccountInput``UserDeatilsDrawer``PlatformSvgIcon``Avatar``FlagIcon` 中文扫描无命中
- 保留:接口返回动态数据、接口参数、路由路径、权限码、系统来源、商品描述、用户昵称、金额与统计数据
- 验证:目标文件中文扫描无命中;语言文件语法检查通过;`NODE_OPTIONS=--openssl-legacy-provider npm run build:prod` 通过,仍保留项目既有 17 个 warning
## 2026-05-08 最新同步
- 线程一已完成:主要路由页面 / 道具管理 / 资源配置

View File

@ -20,10 +20,37 @@
## 当前认领线程
- 线程一:待分配,上一轮已完成 `src/views/props/props-resource-config/**`,对应主要路由 `资源配置 / Resource Configuration`
- 线程一:待分配,上一轮已完成 `src/views/order/reimburse/index.vue` 与 `src/views/datav/**` 入口渲染链路,对应主要路由 `退款记录 / Refund Records``数据大屏 / Data Dashboard`
- 线程二:待分配,上一轮已完成 `src/views/approval/room-cover-wall-approval/index.vue`,对应主要路由 `房间封面 / Room Covers`
- 线程三:待分配,上一轮已完成 `src/views/user/bank-balance/table/**` 用户银行账户页面入口、同目录私有组件、直接公共组件复核、语言文件校验与构建验证
## 2026-05-29 最新同步
- 2026-05-29 线程一已完成:
- `src/views/order/reimburse/index.vue`
- `src/views/datav/index.vue`
- `src/views/datav/latest-purchase.vue`
- `src/views/datav/online-user-count-origin.vue`
- `src/views/datav/purchase-count.vue`
- `src/components/FlagIcon/index.vue`
- 对应主要路由:
- 运营管理 / Operations Management -> 营收支出 / Revenue and Expenses -> 退款记录 / Refund Records
- 运营管理 / Operations Management -> 数据大屏 / Data Dashboard
- 本轮完成:
- 退款记录筛选区、日期范围、搜索按钮、表格列名、加载文案、免费试用布尔展示与详情按钮国际化
- 数据大屏入口标题、分区标题、统计指标标题国际化
- `FlagIcon` 复制成功 / 失败提示接入 `common.copySuccess` / `common.copyFail`
- 新增 `pages.orderReimburse``pages.datav` 中英文语言 key补充 `common.copySuccess` / `common.copyFail`
- 直接公共组件复核:`Pagination``SysOriginIcon``AccountInput``UserDeatilsDrawer``PlatformSvgIcon``Avatar``FlagIcon` 中文扫描无命中
- 保留项:
- 接口返回动态数据、接口参数、路由路径、权限码、系统来源、商品描述、用户昵称、金额、统计数据保持原样
- `src/views/datav/online-user-map.vue``latest-purchase-timchat.vue``online-user-count.vue` 等未被入口页直接渲染的文件不纳入本轮
- 验证结果:
- 目标页面、直接私有组件与直接公共组件中文扫描无命中
- `src/lang/messages/pages/en.js``src/lang/messages/pages/zh.js``src/lang/messages/base/en.js``src/lang/messages/base/zh.js` 语法检查通过
- 直接执行 `npm run build:prod` 因当前 Node 22 OpenSSL 兼容问题失败;使用项目历史惯例 `NODE_OPTIONS=--openssl-legacy-provider npm run build:prod` 构建通过
- 构建仍保留项目既有 `17` 条 warning未新增阻塞错误
## 2026-05-08 最新同步
- 2026-05-08 线程一已完成:

View File

@ -60,12 +60,12 @@ export default {
const that = this
copyText(text).then(() => {
that.$message({
message: '复制成功',
message: that.$t('common.copySuccess'),
type: 'success'
})
}).catch(() => {
that.$message({
message: '复制失败',
message: that.$t('common.copyFail'),
type: 'error'
})
})

View File

@ -20,6 +20,8 @@ export default {
globalSize: 'Global Size',
resetPassword: 'Reset Password',
logout: 'Logout',
copySuccess: 'Copied successfully',
copyFail: 'Copy failed',
cancel: 'Cancel',
submit: 'Submit'
},

View File

@ -18,6 +18,8 @@ export default {
globalSize: '\u5168\u5c40\u5c3a\u5bf8',
resetPassword: '\u4fee\u6539\u5bc6\u7801',
logout: '\u9000\u51fa\u767b\u5f55',
copySuccess: '\u590d\u5236\u6210\u529f',
copyFail: '\u590d\u5236\u5931\u8d25',
cancel: '\u53d6\u6d88',
submit: '\u63d0\u4ea4'
},

View File

@ -5011,6 +5011,71 @@ export default {
downloadFailed: 'Download failed.'
}
},
orderReimburse: {
loading: 'Loading',
filter: {
system: 'System',
sourcePlatform: 'Source Platform',
userId: 'User ID'
},
date: {
rangeSeparator: 'To',
start: 'Start Date',
end: 'End Date'
},
table: {
environment: 'Environment',
system: 'System',
event: 'Event',
nickname: 'Nickname',
description: 'Description',
platform: 'Platform',
paymentMethod: 'Payment Method',
unitPrice: 'Unit Price',
status: 'Status',
freeTrial: 'Free Trial',
expireTime: 'Expire Time',
purchaseTime: 'Purchase Time',
updateTime: 'Updated At',
actions: 'Actions'
},
action: {
search: 'Search',
details: 'Details'
},
word: {
yes: 'Yes',
no: 'No'
},
orderStatus: {
REIMBURSE: 'Refunded'
}
},
datav: {
title: 'Data Dashboard',
subtitle: 'Real-time Data Preview',
section: {
purchaseUsers: 'Purchasing Users',
todayPaymentPreview: "Today's Payment Preview",
aslanOnline: 'Aslan Online'
},
metric: {
totalAmount: 'Total Amount',
totalUsers: 'Total Users',
femaleUsers: 'Female',
maleUsers: 'Male',
rooms: 'Rooms'
},
relativeTime: {
justNow: 'Just now',
minuteAgo: '{count} minute ago',
minutesAgo: '{count} minutes ago',
hourAgo: '{count} hour ago',
hoursAgo: '{count} hours ago',
dayAgo: '{count} day ago',
daysAgo: '{count} days ago'
}
},
goldRunningWater: {
dialog: {
title: 'User Gold Transaction Records'

View File

@ -5008,6 +5008,71 @@ export default {
downloadFailed: '下载失败'
}
},
orderReimburse: {
loading: '加载中',
filter: {
system: '系统',
sourcePlatform: '来源平台',
userId: '用户ID'
},
date: {
rangeSeparator: '至',
start: '开始日期',
end: '结束日期'
},
table: {
environment: '环境',
system: '系统',
event: '事件',
nickname: '昵称',
description: '描述',
platform: '平台',
paymentMethod: '支付方式',
unitPrice: '单价',
status: '状态',
freeTrial: '免费试用',
expireTime: '过期时间',
purchaseTime: '购买时间',
updateTime: '修改时间',
actions: '操作'
},
action: {
search: '搜索',
details: '查看详情'
},
word: {
yes: '是',
no: '否'
},
orderStatus: {
REIMBURSE: '已退款'
}
},
datav: {
title: '数据大屏',
subtitle: '实时数据预览',
section: {
purchaseUsers: '购买用户',
todayPaymentPreview: '今日付费预览',
aslanOnline: 'Aslan在线'
},
metric: {
totalAmount: '总额',
totalUsers: '总人数',
femaleUsers: '女生',
maleUsers: '男生',
rooms: '房间'
},
relativeTime: {
justNow: '刚刚',
minuteAgo: '{count}分钟前',
minutesAgo: '{count}分钟前',
hourAgo: '{count}小时前',
hoursAgo: '{count}小时前',
dayAgo: '{count}天前',
daysAgo: '{count}天前'
}
},
goldRunningWater: {
dialog: {
title: '用户金币流水'

View File

@ -6,8 +6,8 @@
@dblclick="screen"
>
<div v-if="!mobile" class="office-header">
<div class="title-info">数据大屏</div>
<div class="analysis-info">实时数据预览</div>
<div class="title-info">{{ $t('pages.datav.title') }}</div>
<div class="analysis-info">{{ $t('pages.datav.subtitle') }}</div>
</div>
<el-row
v-if="buttonPermissions.includes('datav:query')"
@ -17,7 +17,7 @@
<el-row>
<el-col>
<div id="today-pay" class="col-info">
<div class="title">购买用户</div>
<div class="title">{{ $t('pages.datav.section.purchaseUsers') }}</div>
<div class="content">
<latest-purchase />
</div>
@ -39,7 +39,7 @@
:span="24"
>
<div id="online-info" class="col-info">
<div class="title">今日付费预览</div>
<div class="title">{{ $t('pages.datav.section.todayPaymentPreview') }}</div>
<div class="content">
<purchase-count />
</div>
@ -48,7 +48,7 @@
<el-col :span="24">
<div id="online-info-tim-chat" class="col-info">
<div class="title">Atyou在线</div>
<div class="title">{{ $t('pages.datav.section.aslanOnline') }}</div>
<div class="content">
<online-user-count-origin origin="ATYOU" />
</div>
@ -119,62 +119,82 @@ export default {
</script>
<style scoped lang="scss">
.app-container-datav {
overflow: hidden;
overflow: auto;
min-height: calc(100vh - 50px);
background-color: #22284a;
margin: 18px;
.office-efficiency-index {
font-family: PingFangSC-Semibold, PingFang SC;
background-color: #22284a;
min-height: calc(100vh - 86px);
font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB, Microsoft YaHei, Arial, sans-serif;
background:
radial-gradient(circle at top right, rgba(254, 178, 25, 0.18), transparent 26%),
radial-gradient(circle at left top, rgba(255, 147, 38, 0.12), transparent 22%),
linear-gradient(180deg, var(--brand-primary-pale) 0%, #ffffff 100%);
background-size: cover;
/*头部样式*/
padding: 18px;
/* header styles */
.office-header {
background: url("./../../assets/datav/office_efficiency_header_bg.png")
no-repeat center center;
background-size: 100% 100%;
display: flex;
align-items: center;
justify-content: space-between;
min-height: 72px;
padding: 0 24px;
background: linear-gradient(135deg, #fff8ef 0%, #fff1df 100%);
border: 1px solid rgba(255, 147, 38, 0.2);
border-radius: 16px;
box-shadow: 0 12px 28px rgba(148, 84, 19, 0.12);
.title-info {
color: #03c2ec;
text-align: center;
font-size: 0.26rem;
font-family: "cuhei";
font-weight: 600;
line-height: 0.6rem;
vertical-align: middle;
color: var(--brand-text-main);
text-align: left;
font-size: 24px;
font-weight: 700;
line-height: 1.3;
}
}
.analysis-info {
line-height: 0.6rem;
vertical-align: middle;
padding: 0px 0.2rem;
font-size: 0.24rem;
line-height: 1.4;
padding: 8px 14px;
font-size: 14px;
font-weight: 600;
background-image: linear-gradient(#d8ae22, #dc9546);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
-webkit-animation: hue 60s infinite linear;
color: #8c5b2a;
background: #fff9f2;
border: 1px solid rgba(255, 147, 38, 0.22);
border-radius: 999px;
}
/*数据模块样式*/
/* data module styles */
.office-content {
margin-top: 20px;
.col-info {
margin: 0px 10px 20px 10px;
border-radius: 0.1rem;
background-color: #181c41;
margin: 0 10px 20px 10px;
overflow: hidden;
border-radius: 16px;
background: rgba(255, 255, 255, 0.94);
border: 1px solid rgba(255, 147, 38, 0.18);
box-shadow: 0 14px 32px rgba(148, 84, 19, 0.12);
.title {
line-height: 0.5rem;
vertical-align: middle;
padding: 0.2rem 0.3rem;
font-size: 0.4rem;
font-weight: 600;
color: #03c2ec;
line-height: 1.35;
padding: 18px 22px 12px;
font-size: 18px;
font-weight: 700;
color: var(--brand-text-main);
text-align: left;
border-bottom: 1px solid rgba(255, 147, 38, 0.12);
}
.content {
vertical-align: middle;
padding: 0px 10px;
font-size: 0.14rem;
color: #fff;
padding: 14px 16px 18px;
font-size: 14px;
color: var(--brand-text-main);
}
}
#today-pay {
height: 100%;
overflow: hidden;
@ -191,4 +211,25 @@ export default {
}
}
}
@media (max-width: 768px) {
.app-container-datav {
margin: 10px;
.office-efficiency-index {
padding: 12px;
.office-header {
align-items: flex-start;
flex-direction: column;
gap: 10px;
padding: 16px;
}
.office-content .col-info {
margin: 0 0 14px 0;
}
}
}
}
</style>

View File

@ -4,7 +4,7 @@
<div class="pay-user-info">
<div v-for="(item,index) in list" :key="index" class="user-info">
<div class="left">
<avatar :gender="item.userBaseInfo.userSex" :url="item.userBaseInfo.userAvatar" customize-style="width: 1rem; height: 1rem;border-radius: 50%" />
<avatar :gender="item.userBaseInfo.userSex" :url="item.userBaseInfo.userAvatar" customize-style="width: 44px; height: 44px;border-radius: 50%" />
</div>
<div class="right">
<div class="right-head">
@ -12,7 +12,7 @@
<span class="nickname">{{ item.userBaseInfo.userNickname }}</span>
</div>
<div class="right-content">
<div class="right-content-item">{{ item.orderPurchase.createTime | formatTime }}</div>
<div class="right-content-item">{{ formatRelativeTime(item.orderPurchase.createTime) }}</div>
<div class="right-content-item product-name">{{ item.orderPurchase.productDescription }}</div>
<div class="right-content-item">{{ item.orderPurchase.unitPrice }}</div>
</div>
@ -56,6 +56,32 @@ export default {
that.$store.dispatch('app/pushRunTask', intervalIndex)
},
methods: {
formatRelativeTime(time) {
if (!time) {
return ''
}
if (typeof time === 'string') {
time = time.replace(/-/g, '/')
}
const date = new Date(time)
const diff = (Date.now() - date.getTime()) / 1000
if (!Number.isFinite(diff) || diff < 30) {
return this.$t('pages.datav.relativeTime.justNow')
}
if (diff < 3600) {
const count = Math.ceil(diff / 60)
const key = count === 1 ? 'pages.datav.relativeTime.minuteAgo' : 'pages.datav.relativeTime.minutesAgo'
return this.$t(key, { count })
}
if (diff < 3600 * 24) {
const count = Math.ceil(diff / 3600)
const key = count === 1 ? 'pages.datav.relativeTime.hourAgo' : 'pages.datav.relativeTime.hoursAgo'
return this.$t(key, { count })
}
const count = Math.ceil(diff / (3600 * 24))
const key = count === 1 ? 'pages.datav.relativeTime.dayAgo' : 'pages.datav.relativeTime.daysAgo'
return this.$t(key, { count })
},
renderData() {
const that = this
getPurchaseTable(that.listQuery).then(res => {
@ -67,7 +93,7 @@ export default {
</script>
<style scoped lang="scss">
.seamless-warp {
height: 8rem;
height: 420px;
overflow: hidden;
}
.pay-user-info {
@ -76,29 +102,56 @@ export default {
overflow: hidden;
text-overflow:ellipsis;
white-space: nowrap;
padding: 0.05rem;
align-items: center;
padding: 12px 6px;
border-bottom: 1px solid rgba(255, 147, 38, 0.1);
> .left {
width: 0.6rem;
width: 48px;
text-align: center;
flex: 0 0 48px;
}
> .right {
width: 100%;
padding-left: 0.1rem;
min-width: 0;
padding-left: 12px;
.right-head {
display: flex;
align-items: center;
gap: 8px;
color: var(--brand-text-main);
font-weight: 600;
.flag-icon {
display: inline-block;
vertical-align: middle;
}
.nickname {
overflow: hidden;
text-overflow: ellipsis;
}
}
.right-content {
display: flex;
color: #909399;
gap: 12px;
color: var(--brand-text-subtle);
margin-top: 6px;
> .right-content-item {
width: 100%;
padding-right: 0.01rem;
min-width: 0;
flex: 1;
font-size: 13px;
overflow: hidden;
text-overflow: ellipsis;
}
> .product-name{
color: #03c2ec;
color: #ff9326;
font-weight: 600;
}
}
}

View File

@ -4,7 +4,7 @@
<div class="pay-user-info">
<div v-for="(item,index) in list" :key="index" class="user-info">
<div class="left">
<avatar :gender="item.userBaseInfo.userSex" :url="item.userBaseInfo.userAvatar" customize-style="width: 1rem; height: 1rem;border-radius: 50%" />
<avatar :gender="item.userBaseInfo.userSex" :url="item.userBaseInfo.userAvatar" customize-style="width: 44px; height: 44px;border-radius: 50%" />
</div>
<div class="right">
<div class="right-head">
@ -12,7 +12,7 @@
<span class="nickname">{{ item.userBaseInfo.userNickname }}</span>
</div>
<div class="right-content">
<div class="right-content-item">{{ item.orderPurchase.createTime | formatTime }}</div>
<div class="right-content-item">{{ formatRelativeTime(item.orderPurchase.createTime) }}</div>
<div class="right-content-item product-name">{{ item.orderPurchase.productDescription }}</div>
<div class="right-content-item">{{ item.orderPurchase.unitPrice }}</div>
</div>
@ -63,6 +63,32 @@ export default {
that.$store.dispatch('app/pushRunTask', intervalIndex)
},
methods: {
formatRelativeTime(time) {
if (!time) {
return ''
}
if (typeof time === 'string') {
time = time.replace(/-/g, '/')
}
const date = new Date(time)
const diff = (Date.now() - date.getTime()) / 1000
if (!Number.isFinite(diff) || diff < 30) {
return this.$t('pages.datav.relativeTime.justNow')
}
if (diff < 3600) {
const count = Math.ceil(diff / 60)
const key = count === 1 ? 'pages.datav.relativeTime.minuteAgo' : 'pages.datav.relativeTime.minutesAgo'
return this.$t(key, { count })
}
if (diff < 3600 * 24) {
const count = Math.ceil(diff / 3600)
const key = count === 1 ? 'pages.datav.relativeTime.hourAgo' : 'pages.datav.relativeTime.hoursAgo'
return this.$t(key, { count })
}
const count = Math.ceil(diff / (3600 * 24))
const key = count === 1 ? 'pages.datav.relativeTime.dayAgo' : 'pages.datav.relativeTime.daysAgo'
return this.$t(key, { count })
},
renderData() {
const that = this
getPurchaseTable({ cursor: 1, limit: 60, sysOrigin: this.permissionsSysOriginCode.join(',') }).then(res => {
@ -74,11 +100,11 @@ export default {
</script>
<style scoped lang="scss">
.seamless-warp {
height: 13rem;
height: 520px;
overflow: hidden;
}
.seamless-warp-mobile {
height: 6rem;
height: 360px;
overflow: hidden;
}
.pay-user-info {
@ -87,30 +113,56 @@ export default {
overflow: hidden;
text-overflow:ellipsis;
white-space: nowrap;
padding: 0.1rem;
align-items: center;
padding: 12px 6px;
border-bottom: 1px solid rgba(255, 147, 38, 0.1);
> .left {
width: 0.6rem;
width: 48px;
text-align: center;
flex: 0 0 48px;
}
> .right {
width: 100%;
padding-left: .6rem;
min-width: 0;
padding-left: 12px;
.right-head {
display: flex;
align-items: center;
gap: 8px;
color: var(--brand-text-main);
font-weight: 600;
.flag-icon {
display: inline-block;
vertical-align: middle;
}
.nickname {
overflow: hidden;
text-overflow: ellipsis;
}
}
.right-content {
display: flex;
color: #909399;
gap: 12px;
color: var(--brand-text-subtle);
margin-top: 6px;
> .right-content-item {
width: 100%;
padding-right: 1rem;
font-size: .3rem;
min-width: 0;
flex: 1;
font-size: 13px;
overflow: hidden;
text-overflow: ellipsis;
}
> .product-name{
color: #03c2ec;
color: #ff9326;
font-weight: 600;
}
}
}

View File

@ -2,19 +2,19 @@
<div class="online-user-counts">
<el-row>
<el-col class="count" :span="6">
<div class="title">总人数</div>
<div class="title">{{ $t('pages.datav.metric.totalUsers') }}</div>
<div><countTo :start-val="0" :end-val="userTotal" :duration="3000" /></div>
</el-col>
<el-col class="count" :span="6">
<div class="title">女生</div>
<div class="title">{{ $t('pages.datav.metric.femaleUsers') }}</div>
<div><countTo :start-val="0" :end-val="femaleUserCount" :duration="3000" /></div>
</el-col>
<el-col class="count" :span="6">
<div class="title">男生</div>
<div class="title">{{ $t('pages.datav.metric.maleUsers') }}</div>
<div><countTo :start-val="0" :end-val="maleUserCount" :duration="3000" /></div>
</el-col>
<el-col class="count" :span="6">
<div class="title">房间</div>
<div class="title">{{ $t('pages.datav.metric.rooms') }}</div>
<div><countTo :start-val="0" :end-val="roomCount" :duration="3000" /></div>
</el-col>
</el-row>
@ -79,9 +79,22 @@ export default {
.online-user-counts {
.count {
text-align: center;
line-height: .6rem;
font-size: 0.4rem;
padding-bottom: .2rem;
line-height: 1.5;
padding: 18px 10px;
color: var(--brand-text-main);
.title {
margin-bottom: 8px;
color: var(--brand-text-subtle);
font-size: 13px;
font-weight: 600;
}
> div:last-child {
color: #ff9326;
font-size: 28px;
font-weight: 700;
}
}
}
</style>

View File

@ -2,11 +2,11 @@
<div class="online-user-counts">
<el-row>
<el-col class="count" :span="6">
<div class="title">总额</div>
<div class="title">{{ $t('pages.datav.metric.totalAmount') }}</div>
<div><countTo :start-val="0" :end-val="total" :duration="3000" /></div>
</el-col>
<el-col class="count" :span="6">
<div class="title">ATYOU</div>
<div class="title">ASLAN</div>
<div>
<countTo :start-val="0" :end-val="timchatTotal" :duration="3000" />
</div>
@ -91,9 +91,22 @@ export default {
.online-user-counts {
.count {
text-align: center;
line-height: 0.6rem;
font-size: 0.4rem;
padding-bottom: 0.2rem;
line-height: 1.5;
padding: 16px 10px;
color: var(--brand-text-main);
.title {
margin-bottom: 8px;
color: var(--brand-text-subtle);
font-size: 13px;
font-weight: 600;
}
> div:last-child {
color: #ff9326;
font-size: 26px;
font-weight: 700;
}
}
}
</style>

View File

@ -3,7 +3,7 @@
<div class="filter-container">
<el-select
v-model="sysOrigins"
placeholder="系统"
:placeholder="$t('pages.orderReimburse.filter.system')"
style="width: 200px"
class="filter-item"
multiple
@ -25,7 +25,7 @@
</el-select>
<el-select
v-model="listQuery.platform"
placeholder="来源平台"
:placeholder="$t('pages.orderReimburse.filter.sourcePlatform')"
clearable
style="width: 120px"
class="filter-item"
@ -44,7 +44,7 @@
:sys-origin="
sysOrigins && sysOrigins.length === 1 ? sysOrigins[0] : ''
"
placeholder="用户ID"
:placeholder="$t('pages.orderReimburse.filter.userId')"
/>
</div>
@ -54,9 +54,9 @@
value-format="timestamp"
type="datetimerange"
:picker-options="pickerOptions"
range-separator=""
start-placeholder="开始日期"
end-placeholder="结束日期"
:range-separator="$t('pages.orderReimburse.date.rangeSeparator')"
:start-placeholder="$t('pages.orderReimburse.date.start')"
:end-placeholder="$t('pages.orderReimburse.date.end')"
/>
</div>
<el-button
@ -66,18 +66,18 @@
:disabled="searchDisabled"
@click="handleSearch"
>
搜索
{{ $t('pages.orderReimburse.action.search') }}
</el-button>
</div>
<el-table
v-loading="listLoading"
:data="list"
element-loading-text="Loading"
:element-loading-text="$t('pages.orderReimburse.loading')"
fit
highlight-current-row
>
<el-table-column type="index" width="50" label="No" />
<el-table-column prop="orderPurchase.evn" label="环境" align="center" width="80">
<el-table-column prop="orderPurchase.evn" :label="$t('pages.orderReimburse.table.environment')" align="center" width="80">
<template slot-scope="scope">
<el-tag v-if="scope.row.orderPurchase.evn === 'PROD'" type="success">{{
scope.row.orderPurchase.evn
@ -88,7 +88,7 @@
<el-tag v-else type="danger">{{ scope.row.orderPurchase.evn }}</el-tag>
</template>
</el-table-column>
<el-table-column prop="orderPurchase.sysOrigin" label="系统" align="center" width="150">
<el-table-column prop="orderPurchase.sysOrigin" :label="$t('pages.orderReimburse.table.system')" align="center" width="150">
<template slot-scope="scope">
<sys-origin-icon
:icon="scope.row.orderPurchase.sysOrigin"
@ -98,11 +98,11 @@
</el-table-column>
<el-table-column
prop="expandTypeName"
label="事件"
:label="$t('pages.orderReimburse.table.event')"
align="center"
width="100"
/>
<el-table-column label="昵称" align="center">
<el-table-column :label="$t('pages.orderReimburse.table.nickname')" align="center">
<template slot-scope="scope">
<el-link
v-if="scope.row.userBaseInfo && scope.row.userBaseInfo.userNickname"
@ -114,17 +114,17 @@
</el-table-column>
<el-table-column
prop="orderPurchase.productDescription"
label="描述"
:label="$t('pages.orderReimburse.table.description')"
align="center"
width="100"
/>
<el-table-column prop="orderPurchase.platform" label="平台" width="50" align="center">
<el-table-column prop="orderPurchase.platform" :label="$t('pages.orderReimburse.table.platform')" width="50" align="center">
<template slot-scope="scope">
<platform-svg-icon :icon="scope.row.orderPurchase.platform" />
</template>
</el-table-column>
<el-table-column prop="orderPurchase.payPlatform" label="支付方式" align="center" />
<el-table-column label="单价" align="center" width="120">
<el-table-column prop="orderPurchase.payPlatform" :label="$t('pages.orderReimburse.table.paymentMethod')" align="center" />
<el-table-column :label="$t('pages.orderReimburse.table.unitPrice')" align="center" width="120">
<template slot-scope="scope">
<div>
{{ scope.row.orderPurchase.unitPrice
@ -134,45 +134,45 @@
</el-table-column>
<el-table-column
prop="statusName"
label="状态"
:label="$t('pages.orderReimburse.table.status')"
align="center"
width="120"
>
<template slot-scope="scope">
<el-tag type="danger">{{ scope.row.statusName }}</el-tag>
<el-tag type="danger">{{ getOrderStatusName(scope.row.orderPurchase.status, scope.row.statusName) }}</el-tag>
</template>
</el-table-column>
<el-table-column
prop="orderPurchase.trialPeriod"
label="免费试用"
:label="$t('pages.orderReimburse.table.freeTrial')"
align="center"
width="120"
>
<template slot-scope="scope">
<div>{{ scope.row.orderPurchase.trialPeriod === true ? "是" : "否" }}</div>
<div>{{ scope.row.orderPurchase.trialPeriod === true ? $t('pages.orderReimburse.word.yes') : $t('pages.orderReimburse.word.no') }}</div>
</template>
</el-table-column>
<el-table-column prop="orderPurchase.expiresDateMs" label="过期时间" align="center">
<el-table-column prop="orderPurchase.expiresDateMs" :label="$t('pages.orderReimburse.table.expireTime')" align="center">
<template slot-scope="scope">
{{ scope.row.orderPurchase.expiresDateMs | dateFormat }}
</template>
</el-table-column>
<el-table-column prop="orderPurchase.purchaseDateMs" label="购买时间" align="center">
<el-table-column prop="orderPurchase.purchaseDateMs" :label="$t('pages.orderReimburse.table.purchaseTime')" align="center">
<template slot-scope="scope">
{{ scope.row.orderPurchase.purchaseDateMs | dateFormat }}
</template>
</el-table-column>
<el-table-column prop="orderPurchase.updateTime" label="修改时间" align="center">
<el-table-column prop="orderPurchase.updateTime" :label="$t('pages.orderReimburse.table.updateTime')" align="center">
<template slot-scope="scope">
{{ scope.row.orderPurchase.updateTime | dateFormat }}
</template>
</el-table-column>
<el-table-column label="操作" align="center" width="100">
<el-table-column :label="$t('pages.orderReimburse.table.actions')" align="center" width="100">
<template slot-scope="scope">
<el-button
type="text"
@click.native="queryOrderDetails(scope.row)"
>查看详情</el-button>
>{{ $t('pages.orderReimburse.action.details') }}</el-button>
</template>
</el-table-column>
</el-table>
@ -290,6 +290,11 @@ export default {
},
queryOrderDetails(row) {
this.purchaseId = row.id
},
getOrderStatusName(status, fallback) {
const key = `pages.orderReimburse.orderStatus.${status}`
const text = this.$t(key)
return text === key ? fallback : text
}
}
}