Files
aggregate_translate_website/userinfo.html

864 lines
30 KiB
HTML
Raw Normal View History

2025-06-29 00:40:46 +08:00
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<meta name="baidu-site-verification" content="codeva-bhaFvYlfgd" />
<meta
name="keywords"
content="deepl、deepl翻译、百度翻译、谷歌翻译、腾讯翻译君"
/>
<meta
name="description"
content="翻译之家提供即时免费的中文、英语、日语、韩语、法语、德语、俄语、西班牙语、葡萄牙语、越南语、印尼语、意大利语、荷兰语、泰语全文翻译等服务。"
/>
<meta name="baidu-site-verification" content="codeva-mXz3BqI9VN" />
<link rel="stylesheet" href="static/css/public.css" />
<!-- <link rel="stylesheet" href="static/translate/css/public.css?v=4"> -->
<link rel="stylesheet" href="static/css/layui.css" />
<script src="static/js/jquery-1.11.0.min.js"></script>
<script src="static/js/axios.min.js"></script>
<script src="static/translate/js/echarts.min.js"></script>
<script src="static/js/countUp.min.js"></script>
<script src="static/js/vue.js"></script>
<script src="static/js/lodash.min.js"></script>
<script src="static/js/layui.all.js"></script>
<script src="static/js/common.js"></script>
<script src="https://cdn.jsdelivr.net/npm/qrcode@1.4.4/build/qrcode.min.js"></script>
<title>翻译之家</title>
<script>
var _hmt = _hmt || [];
(function () {
var hm = document.createElement("script");
hm.src = "https://hm.baidu.com/hm.js?c4e0dd6add63dd71fa52870120ca22cf";
var s = document.getElementsByTagName("script")[0];
s.parentNode.insertBefore(hm, s);
})();
</script>
</head>
<body>
<div id="main" class="public-header normal">
<div class="container clearfix">
<div class="left">
<img
src="https://codeai.oss-cn-hangzhou.aliyuncs.com/img/logo.png"
alt=""
class="logo default"
/><img
src="https://codeai.oss-cn-hangzhou.aliyuncs.com/img/logo1.png"
alt=""
class="logo1 default"
/><img
src="https://codeai.oss-cn-hangzhou.aliyuncs.com/img/logow.png"
alt=""
class="logo light"
/><img
src="https://codeai.oss-cn-hangzhou.aliyuncs.com/img/logo1w.png"
alt=""
class="logo1 light"
/>
<div class="nav">
<a class="" href="index.html"><span>首页</span></a
><a class="" href="onlineTranslation.html"><span>文本翻译</span></a
><a class="" href="voice.html"><span>语音翻译</span></a
><a class="" href="api.html"><span>API文档</span></a
><a class="" href="commonProblems.html"><span>常见问题</span></a>
</div>
</div>
<div v-if="userInfo!=undefined&&userInfo.userId>0" class="right">
<a href="userInfo.html" class="console">控制台</a>
<div class="user-info">
<div class="phone">{{userInfo.name}}</div>
<i></i>
<div class="log-out" @click="logOut">退出</div>
</div>
</div>
<div v-else class="right">
<a class="unlogin" href="login.html">登录</a>
</div>
</div>
<div class="bg"></div>
</div>
<script type="text/javascript">
var vm = new Vue({
el: "#main",
data() {
return {
userInfo: {},
token: "",
};
},
created() {},
mounted() {
let token = localStorage.getItem("token");
if (token) {
this.token = token;
this.getUserInfo(token);
} else {
location.href = "login.html";
}
},
methods: {
logOut() {
axios
.post("/logout", {
headers: {
Authorization: `Bearer ${this.token}`,
},
})
.then((response) => {
localStorage.removeItem("token");
location.reload();
});
},
getUserInfo(token) {
let that = this;
axios
.get("/getinfo", {
headers: {
Authorization: `Bearer ${this.token}`,
},
})
.then((response) => {
if (response.data.code === 200) {
this.userInfo = response.data.data;
console.log("userinfo", this.userInfo);
} else {
localStorage.removeItem("token");
location.href = "login.html";
}
});
},
},
});
</script>
<style>
.page-console {
height: auto !important;
padding-bottom: 16px;
}
.range-picker {
display: flex;
align-items: center;
justify-content: flex-end;
margin: 40px auto 0;
}
.range-picker .col {
margin-right: 16px;
font-size: 16px;
color: #333;
cursor: pointer;
}
.range-picker .col.on {
color: #2578ff;
}
</style>
<div id="app" class="page-console" style="padding-top: 90px">
<div class="id-container">
<div class="user-id-container">
<img
src="https://codeai.oss-cn-hangzhou.aliyuncs.com/img/pay.png"
class="user-id-icon"
/><span class="user-id-text">用户ID : {{userInfo.userId}}</span>
</div>
</div>
<div class="tab-container">
<!-- <div class="tabs">
<div class="tab on">文本翻译</div>
<div class="tab">语音翻译</div>
</div> -->
<div id="text-translation" class="w1200 transUserinfo">
<h5>使用情况</h5>
<div class="line"></div>
<div class="table-row">
<div class="col">
<div class="cell"><span>翻译引擎</span></div>
2025-06-30 17:49:55 +08:00
<div class="cell"><span>已使用字符数</span></div>
2025-06-29 00:40:46 +08:00
<div class="cell"><span>剩余字符数</span></div>
<div class="cell"><span>价格U/百万字符)</span></div>
<div class="cell"><span>操作</span></div>
</div>
<div
class="col"
v-for="(item,index) in translateInfos"
:key="'platform_'+index"
>
<div class="cell"><span>{{item.name}}</span></div>
2025-06-30 17:49:55 +08:00
<div class="cell"><span>{{item.useChars}}</span></div>
2025-06-29 00:40:46 +08:00
<div class="cell"><span>{{item.remainChars}}</span></div>
<div class="cell"><span>{{item.price}}</span></div>
<div class="cell">
<div class="act">
<span class="recharge" @click="show">充值</span
><span class="view" @click="look(item.apiKey,index)"
>查看API密钥</span
>
</div>
</div>
</div>
</div>
<div class="warn">备注:如需开取增值税发票,请联系客服。</div>
<div class="clearfix" style="height: 361px">
<div class="range-picker">
<!-- <div class="col on">近7天</div> -->
<div class="col">近15天</div>
<!-- <div class="col">近30天</div> -->
</div>
<div class="chart-container-wrapper">
<!-- ECharts 图表将渲染到这个 div 中 -->
<div id="mainEchart" class="echarts-chart-direct"></div>
</div>
</div>
</div>
<div
id="voice-translation"
class="w1200 transUserinfo"
style="display: none"
>
<div class="header-container">
<div class="title-section">
<h5>使用情况</h5>
<div class="api-key">
<span>密钥:</span
><button class="copy-btn" style="display: none">复制</button>
</div>
</div>
<div class="balance-section">
<div class="balance">剩余金额0元</div>
<button class="recharge-btn">充值</button>
</div>
</div>
<div class="line"></div>
<div class="table-container table-row">
<div class="grid-header col">
<div class="cell" style="width: 180px">翻译引擎</div>
<div class="cell">服务类型</div>
<div class="cell">已消耗时常/用量</div>
<div class="cell">已消费金额(元)</div>
<div class="cell">价格(元/小时)</div>
</div>
<div class="grid-body">
<div class="vendor-cell" style="grid-row: span 3">
<span>微软</span>
</div>
</div>
</div>
<div class="warn">备注:如需开取增值税发票,请联系客服。</div>
<div class="clearfix" style="height: 361px">
<div class="range-picker">
<div class="col on">近7天</div>
<div class="col">近15天</div>
<div class="col">近30天</div>
</div>
<!-- <div
id="chartDoms"
style="width: 1200px; height: 300px; margin: 30px auto"
></div> -->
</div>
</div>
</div>
<div class="public-customer">
<div class="ctr">
<div class="item">
<img
src="https://codeai.oss-cn-hangzhou.aliyuncs.com/img/p25.png"
alt=""
/>
<div>微信咨询</div>
<div class="detail wechat">
<h5>微信咨询</h5>
<img
src="https://codeai.oss-cn-hangzhou.aliyuncs.com/img/p23.jpg"
alt=""
/>
<p>手机扫码加我微信</p>
</div>
</div>
<div class="line"></div>
<div class="item">
<img
src="https://codeai.oss-cn-hangzhou.aliyuncs.com/img/p26.png"
alt=""
/>
<div>电话咨询</div>
<div class="detail">
<h5>电话咨询</h5>
<div class="phone">13327906119</div>
</div>
</div>
</div>
</div>
<div class="modal-overlay" style="display: none">
<div class="service-modal">
<div class="content">
<div class="close" @click="close"></div>
<div class="contact">TRX BLOCK</div>
<div class="contact-user">
<div class="contact-user-text">用户ID : {{userInfo.id}}</div>
</div>
<div
id="qrcode"
style="text-align: center;height: 214px;"
class="border border-gray-200 rounded-lg p-2 flex justify-center items-center"
>
<!-- 二维码将在这里生成 -->
<!-- <p class="text-gray-500">点击按钮生成二维码</p> -->
</div>
<!-- <img
src="https://codeai.oss-cn-hangzhou.aliyuncs.com/img/p23.jpg"
alt=""
class="qrcode-img"
/> -->
<p>{{receiveAddress}}</p>
</div>
</div>
</div>
</div>
<script type="text/javascript">
// 工具函数:将十六进制颜色转换为 RGBA 格式
function hexToRgba(hex, alpha) {
const r = parseInt(hex.slice(1, 3), 16);
const g = parseInt(hex.slice(3, 5), 16);
const b = parseInt(hex.slice(5, 7), 16);
return `rgba(${r}, ${g}, ${b}, ${alpha})`;
}
var vm = new Vue({
el: "#app",
data() {
return {
translateInfo: [],
translateInfos: [],
isShow: false,
dateType: 0,
pageChart: null,
tabShow: "text",
token: "",
userMoney: 0,
userInfo: {},
// ECharts 图表数据
myChart: null, // 用于存储 ECharts 实例
xAxisData: [],
seriesData: [],
receiveAddress: "",
};
},
created() {},
mounted() {
let token = localStorage.getItem("token");
if (token) {
this.token = token;
this.getUserInfo(token);
} else {
location.href = "login.html";
}
// === 直接在主 Vue 实例的 mounted 钩子中初始化 ECharts ===
// 确保在 DOM 更新周期之后再初始化 ECharts
this.$nextTick(() => {
setTimeout(() => {
this.initECharts();
}, 100); // 增加一个短暂的延迟
});
// 监听窗口大小变化,使图表自适应
window.addEventListener("resize", this.resizeECharts);
this.getUserPlatforms();
this.getStatistics();
this.getReceiveAddress();
},
beforeDestroy() {
// 销毁 ECharts 实例并移除事件监听器
if (this.myChart) {
this.myChart.dispose();
}
window.removeEventListener("resize", this.resizeECharts);
},
watch: {
dateType() {
if (this.tabShow == "voice") {
this.getVoicestatistics();
} else {
this.getStatistics();
}
},
// 监听数据变化,重新渲染图表
seriesData: {
deep: true,
handler() {
this.updateECharts();
},
},
xAxisData: {
deep: true,
handler() {
this.updateECharts();
},
},
},
methods: {
getUserPlatforms() {
axios
.get("/tm-member/platforms", {
headers: { Authorization: `Bearer ${this.token}` },
})
.then((res) => {
if (res.data.code == 200) {
let result = res.data.data;
this.translateInfos = result;
} else {
layer.msg(response.data.msg);
}
});
},
getReceiveAddress() {
axios
.get("/configKey/trx_receive_address", {
headers: { Authorization: `Bearer ${this.token}` },
})
.then((res) => {
if (res.data.code == 200) {
this.receiveAddress = res.data.data.configValue;
this.generateQRCode();
} else {
layer.msg(response.data.msg);
}
});
},
getUserInfo(token) {
console.log("token", token);
let that = this;
axios
.get("/getinfo", {
headers: {
Authorization: `Bearer ${token}`,
},
})
.then((response) => {
console.log(response.data);
console.log("code", response.data.code);
if (response.data.code === 200) {
that.userInfo = response.data.data;
console.log("userinfo", that.userInfo);
} else {
localStorage.removeItem("token");
location.href = "login.html";
}
});
},
getStatistics() {
const that = this;
axios
.get("/tm-member-platform/statistics", {
headers: { Authorization: `Bearer ${this.token}` },
})
.then((response) => {
if (response.data.code == 200) {
this.xAxisData = response.data.data.xAxis;
this.seriesData = response.data.data.data || [];
} else {
layer.msg(response.data.msg);
}
});
},
look(apiKey, index) {
var layer = layui.layer;
// layer.open({
// content: token,
// // shade: 0.8
// shade: ["0.4"]
// // shade: false
// })
layer.open({
type: 1,
title: "API密钥",
closeBtn: 1,
shadeClose: true,
skin: "11",
content:
'<div class="secret-key"><div class="secret"><span>' +
apiKey +
'</span><i class="copy" data="' +
apiKey +
'" >复制</i></div></div>',
});
},
show() {
$(".modal-overlay").fadeIn();
},
close() {
$(".modal-overlay").fadeOut();
},
tabClick(val) {
this.tabShow = val;
this.pageChart = null;
if (val == "voice") {
this.getVoicestatistics();
this.getuserinfo();
this.getUsersDuration();
}
},
copy() {
const apiKey = this.token;
navigator.clipboard.writeText(apiKey).then(() => {
console.log("已复制");
layer.msg("复制成功");
});
},
getUserInfo(token) {
let that = this;
axios
.get("/getinfo", {
headers: {
Authorization: `Bearer ${this.token}`,
},
})
.then((response) => {
if (response.data.code === 200) {
this.userInfo = response.data.data;
console.log("userinfo", this.userInfo);
} else {
location.href = "login.html";
}
});
},
// 模拟更新图表数据的方法
updateRandomChartData() {
// 直接调用 fetchChartData 来模拟数据更新
this.fetchChartData();
layer.msg("图表数据已更新!");
},
// === ECharts 初始化和更新方法 ===
initECharts() {
// 在这里添加一个检查,确保 echarts 对象是存在的
if (typeof echarts === "undefined") {
console.error("ECharts 库未加载!");
return;
}
const chartDom = document.getElementById("mainEchart");
if (!chartDom) {
console.error("ECharts 图表容器 DOM 元素 #mainEchart 未找到!");
return;
}
// 初始化 ECharts 实例
this.myChart = echarts.init(chartDom);
this.updateECharts(); // 首次加载数据
},
updateECharts() {
if (!this.myChart) {
console.error("ECharts 实例未创建!无法更新图表。");
return;
}
// 动态生成 series 配置
const series = this.seriesData.map((item, index) => {
// 使用 this.seriesData
const colors = [
"#3c8dbc",
"#00a65a",
"#f39c12",
"#724a9c",
"#e74c3c",
"#1abc9c",
];
const color = colors[index % colors.length];
return {
name: item.platformName, // 使用 platformName
type: "line",
// stack: '总量', // 如果不需要堆叠,请注释或移除此行
smooth: true,
lineStyle: {
width: 3,
},
itemStyle: {
borderColor: color,
borderWidth: 2,
},
emphasis: {
focus: "series",
},
data: item.data, // 使用 item.data
areaStyle: {
opacity: 0.8,
// 确保 echarts.graphic 存在
color:
typeof echarts !== "undefined" && echarts.graphic
? new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{
offset: 0,
color: hexToRgba(color, 0.3),
},
{
offset: 1,
color: hexToRgba(color, 0),
},
])
: color, // Fallback to solid color if graphic is not available
},
};
});
// 动态生成 legend data
const legendData = (this.seriesData || []).map(
(item) => item.platformName
); // 使用 platformName
const option = {
title: {
text: "字符消耗统计", // 标题可以直接在主实例中定义
left: "center",
textStyle: {
color: "#333",
fontSize: 18,
fontWeight: "bold",
},
},
tooltip: {
trigger: "axis",
axisPointer: {
type: "cross",
label: {
backgroundColor: "#6a7985",
},
},
formatter: function (params) {
var result = params[0].name + "<br/>";
params.forEach(function (item) {
if (
item.data !== null &&
item.seriesName &&
item.value !== undefined
) {
result +=
'<span style="display:inline-block;margin-right:4px;border-radius:10px;width:10px;height:10px;background-color:' +
item.color +
';"></span>' +
item.seriesName +
": " +
item.value +
"<br/>";
}
});
return result;
},
},
legend: {
data: legendData,
top: "bottom",
textStyle: {
color: "#666",
},
padding: [10, 0, 0, 0],
},
grid: {
left: "3%",
right: "4%",
bottom: "10%",
containLabel: true,
},
xAxis: {
type: "category",
boundaryGap: false,
data: this.xAxisData, // 直接使用 xAxisData
axisLabel: {
color: "#555",
},
axisLine: {
lineStyle: {
color: "#ccc",
},
},
},
yAxis: {
type: "value",
name: "字符数", // Y轴名称可以直接在主实例中定义
nameTextStyle: {
color: "#555",
padding: [0, 0, 10, 0],
},
axisLabel: {
formatter: "{value} ",
color: "#555",
},
axisLine: {
lineStyle: {
color: "#ccc",
},
},
splitLine: {
lineStyle: {
type: "dashed",
color: "#e0e0e0",
},
},
},
series: series,
};
this.myChart.setOption(option);
// 确保在设置选项后也调整图表大小
setTimeout(() => {
if (this.myChart) {
this.myChart.resize();
}
}, 50); // 增加一个短暂的延迟
},
resizeECharts() {
// 更改方法名以与组件内的 resizeChart 区分
if (this.myChart) {
const chartDom = document.getElementById("mainEchart");
if (chartDom) {
}
this.myChart.resize();
}
},
generateQRCode() {
const text = this.receiveAddress.trim(); // 获取输入框内容并去除首尾空格
const qrcodeContainer = document.getElementById('qrcode');
if (text) {
// 1. 清空容器内的所有内容,包括旧的二维码或提示信息
qrcodeContainer.innerHTML = '';
this.showPlaceholder = false;
this.qrError = null;
// 2. 创建一个新的 canvas 元素
const canvasElement = document.createElement('canvas');
// 可以给 canvas 设置一个 ID 或者类名,如果需要后续引用或样式控制
// canvasElement.id = 'qrCanvas';
qrcodeContainer.appendChild(canvasElement); // 将 canvas 添加到容器中
// 3. 使用 QRCode.toCanvas 在新创建的 canvas 上生成二维码
QRCode.toCanvas(canvasElement, text, { // 将 canvasElement 传递给 toCanvas
width: 214, // 设置二维码宽度
color: {
dark: '#000000', // 二维码颜色
light:'#ffffff' // 背景颜色
}
}, (error) => {
if (error) {
qrcodeContainer.innerHTML = `<p class="text-red-500">获取地址失败请刷新</p>`; // 重新显示错误信息
} else {
console.log('二维码生成成功!');
}
});
} else {
// 如果输入为空,给出提示
qrcodeContainer.innerHTML = ''; // 清空可能存在的旧二维码
}
}
}
});
function copy(val) {
var layer = layui.layer;
const jsonStr = JSON.stringify(val);
// 模拟 输入框
var cInput = document.createElement("input");
cInput.value = jsonStr;
document.body.appendChild(cInput);
cInput.select(); // 选取文本框内容
// 执行浏览器复制命令
// 复制命令会将当前选中的内容复制到剪切板中这里就是创建的input标签
// Input要在正常的编辑状态下原生复制方法才会生效
document.execCommand("copy");
layer.msg("复制成功");
// 复制成功后再将构造的标签 移除
document.body.removeChild(cInput);
}
$("body").on("click", ".copy", function () {
console.log($(this).attr("data"));
copy($(this).attr("data"));
});
// $('body .copy').click(function() {
// console.log($(this).attr('data'))
// copy($(this).attr('data'))
// })
</script>
<div class="public-footer">
<div class="w1200">
<div class="left">
<img
src="https://codeai.oss-cn-hangzhou.aliyuncs.com/img/logo.png"
alt=""
/>
</div>
<div
style="
flex-direction: column;
line-height: 23px;
padding-left: 100px;
color: #fff;
font-size: 12px;
"
>
<p style="color: #fff; font-size: 12px"><span>关于我们:</span></p>
<p style="color: #fff; font-size: 12px">
<span></span>
</p>
</div>
<div class="right" style="white-space: nowrap">
<p></p>
<p><span>联系我们:</span></p>
<p><span>地址:</span></p>
<p><span>邮箱:</span></p>
<p class=""><span>电话: </span></p>
<p></p>
</div>
</div>
<div class="copyright">
<a href="https://beian.miit.gov.cn/" target="blank"></a>
</div>
</div>
<div class="layui-layer-move" style="cursor: move; display: none"></div>
<style>
.chart-container-wrapper {
width: 100%;
max-width: 900px; /* 最大宽度 */
background-color: #ffffff;
border-radius: 12px; /* 圆角 */
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1); /* 阴影 */
padding: 20px;
box-sizing: border-box;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
margin: 20px auto; /* 居中显示 */
}
.echarts-chart-direct {
/* 更改类名以区分这里是直接渲染的div */
width: 100%;
height: 300px; /* === 移除此行,高度由内联样式直接控制 === */
/* === 调试用样式:添加边框和背景色 === */
/* ==================================== */
}
/* 响应式调整图表高度 */
@media (max-width: 768px) {
.chart-container-wrapper {
height: 350px !important; /* 响应式也添加 !important */
padding: 15px;
}
}
@media (max-width: 480px) {
.chart-container-wrapper {
height: 300px !important; /* 响应式也添加 !important */
padding: 10px;
}
}
</style>
</body>
</html>