7 changed files with 565 additions and 75 deletions
@ -0,0 +1,48 @@ |
|||||||
|
// 在 Vue 组件的 setup 或普通方法中使用
|
||||||
|
import { ElMessageBox } from 'element-plus'; |
||||||
|
import { nextTick } from 'vue'; |
||||||
|
|
||||||
|
export function showCountdownConfirm(message, title = '提示', confirmText = '确定', cancelText = '取消', timeout = 5) { |
||||||
|
return new Promise((resolve) => { |
||||||
|
let countdown = timeout; |
||||||
|
let timer = null; |
||||||
|
|
||||||
|
// 自定义消息内容(包含确认和取消按钮)
|
||||||
|
ElMessageBox.confirm(message, title, { |
||||||
|
confirmButtonText: countdown + 's后' + confirmText, |
||||||
|
cancelButtonText: cancelText, |
||||||
|
type: 'warning', |
||||||
|
confirmButtonClass: 'countdown-confirm', |
||||||
|
showClose: false, |
||||||
|
dangerouslyUseHTMLString: true, |
||||||
|
// 关闭前的回调,会阻止关闭操作
|
||||||
|
beforeClose: (action, instance, done) => { |
||||||
|
// 当提交操作时执行后面的步骤
|
||||||
|
if (action !== 'confirm') return done(); |
||||||
|
if (!timer) done(); |
||||||
|
}, |
||||||
|
}) |
||||||
|
.then((result) => { |
||||||
|
console.log('then: ', result); |
||||||
|
resolve(true); |
||||||
|
}) |
||||||
|
.catch((err) => { |
||||||
|
clearInterval(timer); |
||||||
|
console.error('err: ', err); |
||||||
|
resolve(false); |
||||||
|
}); |
||||||
|
|
||||||
|
nextTick(() => { |
||||||
|
timer = setInterval(() => { |
||||||
|
countdown--; |
||||||
|
const countdownNum = document.getElementsByClassName('countdown-confirm'); |
||||||
|
if (countdownNum.length > 0) countdownNum[0].textContent = countdown + 's后' + confirmText; |
||||||
|
if (countdown <= 0) { |
||||||
|
clearInterval(timer); |
||||||
|
timer = null; |
||||||
|
if (countdownNum.length > 0) countdownNum[0].textContent = confirmText; |
||||||
|
} |
||||||
|
}, 1000); |
||||||
|
}); |
||||||
|
}); |
||||||
|
} |
@ -0,0 +1,257 @@ |
|||||||
|
<template> |
||||||
|
<div class="system-edit-post-container"> |
||||||
|
<el-form size="default" label-width="80px"> |
||||||
|
<el-card shadow="hover" header="战令历史"> |
||||||
|
<template #header> |
||||||
|
战令历史 |
||||||
|
<el-button v-show="item.show === false" @click="item.show = true" class="button-caret" type="text"> |
||||||
|
<el-icon> |
||||||
|
<ele-CaretBottom /> |
||||||
|
</el-icon> |
||||||
|
</el-button> |
||||||
|
<el-button v-show="item.show === true" @click="item.show = false" class="button-caret" type="text"> |
||||||
|
<el-icon> |
||||||
|
<ele-CaretTop /> |
||||||
|
</el-icon> |
||||||
|
</el-button> |
||||||
|
</template> |
||||||
|
<div v-show="item.show"> |
||||||
|
<div class="flex-warp"> |
||||||
|
<el-form :inline="true"> |
||||||
|
<!-- <el-form-item label="服务器">--> |
||||||
|
<!-- <el-select v-model="serverSwitch" class="m-2" placeholder="选择服务器" size="large" style="width: 80px">--> |
||||||
|
<!-- <el-option v-for="item in serverCategorize" :key="item.value" :label="item.label" :value="item.value" />--> |
||||||
|
<!-- </el-select>--> |
||||||
|
<!-- <el-select v-model="query.serverId" placeholder="选择服务器" @change="getProp" filterable>--> |
||||||
|
<!-- <el-option v-for="item in switchServer()" :key="item.id" :label="item.name" :value="item.id" />--> |
||||||
|
<!-- </el-select>--> |
||||||
|
<!-- </el-form-item>--> |
||||||
|
<el-form-item prop="periodId" label="显示期数"> |
||||||
|
<el-select size="default" placeholder="请选择显示期数" filterable v-model="query.periodId"> |
||||||
|
<el-option v-for="item in unique()" :key="item.PeriodId" :label="item.PeriodId + '期'" :value="item.PeriodId" /> |
||||||
|
</el-select> |
||||||
|
</el-form-item> |
||||||
|
|
||||||
|
<el-form-item> |
||||||
|
<el-button type="primary" @click="getProp">确认</el-button> |
||||||
|
</el-form-item> |
||||||
|
</el-form> |
||||||
|
</div> |
||||||
|
<div class="flex-warp"> |
||||||
|
<el-form :inline="true"> |
||||||
|
<el-form-item prop="total" label="总数">{{ tableData.total }}</el-form-item> |
||||||
|
<el-form-item prop="total" label="总比例"> |
||||||
|
<div v-if="tableData.total && tableData.rechargeTotal">{{ (tableData.total / tableData.rechargeTotal) * 100 }}%</div> |
||||||
|
<div v-else>0%</div> |
||||||
|
</el-form-item> |
||||||
|
</el-form> |
||||||
|
</div> |
||||||
|
<div class="flex-warp"> |
||||||
|
<el-table :data="tableData.list" style="width: 100%" :span-method="objectSpanMethod" border> |
||||||
|
<el-table-column prop="platform" label="渠道" width="80"> |
||||||
|
<template #default="scope"> |
||||||
|
<div v-if="scope.row.sort == 0">其他</div> |
||||||
|
<div v-else-if="scope.row.sort == 1">ios</div> |
||||||
|
<div v-else-if="scope.row.sort == 2">官服</div> |
||||||
|
<div v-else-if="scope.row.sort == 3">渠道</div> |
||||||
|
<div v-else>其他</div> |
||||||
|
</template> |
||||||
|
</el-table-column> |
||||||
|
<el-table-column prop="area" label="区服编号" width="85"> |
||||||
|
<template #default="scope"> {{ scope.row.area }}区</template> |
||||||
|
</el-table-column> |
||||||
|
<el-table-column prop="name" label="服务器名称" width="200"> |
||||||
|
<template #default="scope"> |
||||||
|
{{ scope.row.name }} |
||||||
|
</template> |
||||||
|
</el-table-column> |
||||||
|
<el-table-column prop="State" label="购买数量" width="100"> |
||||||
|
<template #default="scope"> |
||||||
|
{{ scope.row.num }} |
||||||
|
</template> |
||||||
|
</el-table-column> |
||||||
|
<el-table-column prop="State" label="购买比例" width="100"> |
||||||
|
<template #default="scope"> |
||||||
|
{{ scope.row.num / tableData.total }} |
||||||
|
</template> |
||||||
|
</el-table-column> |
||||||
|
</el-table> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</el-card> |
||||||
|
</el-form> |
||||||
|
</div> |
||||||
|
</template> |
||||||
|
<script lang="ts"> |
||||||
|
import { defineComponent, onMounted, reactive, ref, toRefs } from 'vue'; |
||||||
|
import { gameGMGetBattlePassLog } from '/@/api/game/gameConfig'; |
||||||
|
import BattlePassConfigCategory from '/@/api/config/BattlePassConfigCategory.json'; |
||||||
|
import { ElLoading } from 'element-plus'; |
||||||
|
import { ServerCategorize, serverList } from '/@/utils/game'; |
||||||
|
import { gSwitchServer, uniquePeriodId } from '/@/utils/utils'; |
||||||
|
|
||||||
|
interface TableDataState { |
||||||
|
query: object; |
||||||
|
tableData: { list: object[]; total: number; rechargeTotal: number }; |
||||||
|
battlePassConfig: object; |
||||||
|
servers: object; |
||||||
|
serverCategorize: object[]; |
||||||
|
serverSwitch: number; |
||||||
|
} |
||||||
|
|
||||||
|
interface SpanMethodProps { |
||||||
|
row: object; |
||||||
|
column: object; |
||||||
|
rowIndex: number; |
||||||
|
columnIndex: number; |
||||||
|
} |
||||||
|
|
||||||
|
export default defineComponent({ |
||||||
|
name: 'apiV1ServerBattlePassShopLog', |
||||||
|
setup: function () { |
||||||
|
const tableRef = ref(); |
||||||
|
const state = reactive<TableDataState>({ |
||||||
|
item: { topShow: true, show: true }, |
||||||
|
query: { |
||||||
|
periodId: BattlePassConfigCategory[0].PeriodId, |
||||||
|
}, |
||||||
|
tableData: { list: [], total: 0 }, |
||||||
|
battlePassConfig: BattlePassConfigCategory, |
||||||
|
servers: [], |
||||||
|
serverCategorize: ServerCategorize, |
||||||
|
serverSwitch: 0, |
||||||
|
}); |
||||||
|
|
||||||
|
onMounted(() => { |
||||||
|
serverList().then((res) => { |
||||||
|
state.servers = res; |
||||||
|
}); |
||||||
|
}); |
||||||
|
const getProp = () => { |
||||||
|
const loading = ElLoading.service({ |
||||||
|
lock: true, |
||||||
|
text: 'Loading', |
||||||
|
background: 'rgba(0, 0, 0, 0.7)', |
||||||
|
}); |
||||||
|
state.tableData.list = []; |
||||||
|
gameGMGetBattlePassLog(state.query) |
||||||
|
.then((res) => { |
||||||
|
console.log('gameGMGetBattlePass: ', res); |
||||||
|
if (res.code != 0 || !res.data || !res.data.list) { |
||||||
|
return; |
||||||
|
} |
||||||
|
// state.tableData.list = res.data.list; |
||||||
|
state.tableData.total = res.data.total; |
||||||
|
state.tableData.rechargeTotal = res.data.rechargeTotal; |
||||||
|
let now = new Date().getTime() / 1000; |
||||||
|
console.log('gameGMGetBattlePass: time ', now); |
||||||
|
// let BattlePassConfig = unique(); |
||||||
|
console.log('gameGMGetBattlePass: state.servers ', state.servers); |
||||||
|
for (let i in state.servers) { |
||||||
|
let data = { |
||||||
|
id: state.servers[i].id, |
||||||
|
name: state.servers[i].name, |
||||||
|
area: state.servers[i].area, |
||||||
|
sort: state.servers[i].sort, |
||||||
|
num: 0, |
||||||
|
}; |
||||||
|
let battlePass = res.data.list.find((r) => r.server == state.servers[i].id); |
||||||
|
if (battlePass) { |
||||||
|
data.num = battlePass.uid; |
||||||
|
} |
||||||
|
state.tableData.list.push(data); |
||||||
|
} |
||||||
|
|
||||||
|
state.tableData.list.sort((a, b) => a.sort * 100000 + a.id - (b.sort * 100000 + b.id)); |
||||||
|
state.rows = {}; |
||||||
|
for (let i in state.tableData.list) { |
||||||
|
let server = state.tableData.list[i]; |
||||||
|
if (!state.rows[server.sort]) { |
||||||
|
state.rows[server.sort] = { num: 0, id: server.id }; |
||||||
|
} |
||||||
|
state.rows[server.sort].num++; |
||||||
|
if (state.rows[server.sort].id > server.id) { |
||||||
|
state.rows[server.sort].id = server.id; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
console.log('gameGMGetBattlePass: state.tableData.list', state.tableData.list); |
||||||
|
}) |
||||||
|
.finally(() => { |
||||||
|
loading.close(); |
||||||
|
}); |
||||||
|
}; |
||||||
|
|
||||||
|
const unique = () => { |
||||||
|
return uniquePeriodId(BattlePassConfigCategory); |
||||||
|
}; |
||||||
|
const switchServer = () => { |
||||||
|
return gSwitchServer(state.serverSwitch, state.servers); |
||||||
|
}; |
||||||
|
const objectSpanMethod = ({ row, column, rowIndex, columnIndex }: SpanMethodProps) => { |
||||||
|
if (columnIndex === 0) { |
||||||
|
if (state.rows[row.sort].id == row.id) { |
||||||
|
return { |
||||||
|
rowspan: state.rows[row.sort].num, |
||||||
|
colspan: 1, |
||||||
|
}; |
||||||
|
} else { |
||||||
|
return { |
||||||
|
rowspan: 0, |
||||||
|
colspan: 0, |
||||||
|
}; |
||||||
|
} |
||||||
|
} |
||||||
|
}; |
||||||
|
return { |
||||||
|
objectSpanMethod, |
||||||
|
unique, |
||||||
|
switchServer, |
||||||
|
tableRef, |
||||||
|
getProp, |
||||||
|
...toRefs(state), |
||||||
|
}; |
||||||
|
}, |
||||||
|
}); |
||||||
|
</script> |
||||||
|
|
||||||
|
<style scoped lang="scss"> |
||||||
|
.el-table { |
||||||
|
--el-table-border-color: var(--el-border-color-lighter); |
||||||
|
--el-table-border: 2px solid var(--el-table-border-color); |
||||||
|
} |
||||||
|
|
||||||
|
.table-item { |
||||||
|
margin-top: 10px; |
||||||
|
margin-bottom: -10px; |
||||||
|
} |
||||||
|
|
||||||
|
.box-card .el-card__header { |
||||||
|
background-color: #062ce6; |
||||||
|
color: #fff; |
||||||
|
} |
||||||
|
|
||||||
|
::v-deep .el-table--striped .el-table__body tr.el-table__row--striped td { |
||||||
|
background: #f0ffd5; |
||||||
|
} |
||||||
|
|
||||||
|
.warning-row { |
||||||
|
background: #f0ffd5ff; |
||||||
|
} |
||||||
|
|
||||||
|
.white-row { |
||||||
|
background: #ffffff; |
||||||
|
} |
||||||
|
|
||||||
|
::v-deep .warning-row .el-table__body tr { |
||||||
|
background-color: #f0ffd5ff; |
||||||
|
} |
||||||
|
|
||||||
|
::v-deep .warning-row .el-table--enable-row-hover .el-table__body tr:hover > td { |
||||||
|
background-color: inherit; |
||||||
|
} |
||||||
|
|
||||||
|
::v-deep .warning-row .el-table__cell { |
||||||
|
background-color: #f0ffd5ff; |
||||||
|
} |
||||||
|
</style> |
@ -0,0 +1,127 @@ |
|||||||
|
<template> |
||||||
|
<div> |
||||||
|
<el-dialog title="战令历史" v-model="isShowDialog" width="769px"> |
||||||
|
<div class="flex-warp"> |
||||||
|
<el-table ref="tableRef" :data="tableData.list" style="width: 100%" border> |
||||||
|
<el-table-column prop="PeriodId" label="当前期数" width="85"> |
||||||
|
<template #default="scope"> {{ scope.row.PeriodId }}期</template> |
||||||
|
</el-table-column> |
||||||
|
<el-table-column prop="State" label="状态" width="100"> |
||||||
|
<template #default="scope"> |
||||||
|
<div v-if="scope.row.State == 1" style="color: green">开启</div> |
||||||
|
<div v-else-if="scope.row.State == 2" style="color: red">关闭</div> |
||||||
|
<div v-else-if="scope.row.State == 3">永久关闭</div> |
||||||
|
<div v-else-if="scope.row.State == 4" style="color: #66b7cf">未开启</div> |
||||||
|
<div v-else>{{ scope.row.State }}</div> |
||||||
|
</template> |
||||||
|
</el-table-column> |
||||||
|
<el-table-column prop="BeginTime" label="开始时间" width="100" /> |
||||||
|
<el-table-column prop="EndTime" label="结束时间" width="100" /> |
||||||
|
</el-table> |
||||||
|
</div> |
||||||
|
</el-dialog> |
||||||
|
</div> |
||||||
|
</template> |
||||||
|
|
||||||
|
<script lang="ts"> |
||||||
|
import { reactive, toRefs, defineComponent } from 'vue'; |
||||||
|
import { ElLoading } from 'element-plus'; |
||||||
|
import {gameGMGetBattlePass} from "/@/api/game/gameConfig"; |
||||||
|
import {uniquePeriodId} from "/@/utils/utils"; |
||||||
|
import BattlePassConfigCategory from "/@/api/config/BattlePassConfigCategory.json"; |
||||||
|
|
||||||
|
interface TableDataState { |
||||||
|
isShowDialog: boolean; |
||||||
|
isNew: boolean; |
||||||
|
query: object; |
||||||
|
channels: object[]; |
||||||
|
tableData: { list: object[] }; |
||||||
|
} |
||||||
|
|
||||||
|
export default defineComponent({ |
||||||
|
name: 'serverBattlePassLog', |
||||||
|
setup(prop, { emit }) { |
||||||
|
const state = reactive<TableDataState>({ |
||||||
|
isShowDialog: false, |
||||||
|
isNew: false, |
||||||
|
query: {}, |
||||||
|
channels: [], |
||||||
|
tableData: { list: [] }, |
||||||
|
}); |
||||||
|
// 打开弹窗 |
||||||
|
const openDialog = (server: number ) => { |
||||||
|
if (!server) { |
||||||
|
return; |
||||||
|
} |
||||||
|
state.isShowDialog = true; |
||||||
|
state.query = { serverId: server }; |
||||||
|
getProp(); |
||||||
|
}; |
||||||
|
// 关闭弹窗 |
||||||
|
const closeDialog = () => { |
||||||
|
state.isShowDialog = false; |
||||||
|
}; |
||||||
|
// 取消 |
||||||
|
const onCancel = () => { |
||||||
|
closeDialog(); |
||||||
|
}; |
||||||
|
|
||||||
|
const getProp = () => { |
||||||
|
const loading = ElLoading.service({ |
||||||
|
lock: true, |
||||||
|
text: 'Loading', |
||||||
|
background: 'rgba(0, 0, 0, 0.7)', |
||||||
|
}); |
||||||
|
state.tableData.list = []; |
||||||
|
gameGMGetBattlePass({ serverId: state.query.serverId }) |
||||||
|
.then((res) => { |
||||||
|
console.log('gameGMGetBattlePass: ', res); |
||||||
|
if (res.code != 0 || !res.data || !res.data.list) { |
||||||
|
return; |
||||||
|
} |
||||||
|
state.tableData.list = res.data.list; |
||||||
|
let now = new Date().getTime() / 1000; |
||||||
|
console.log('gameGMGetBattlePass: time ', now); |
||||||
|
let battlePassConfig = unique(); |
||||||
|
console.log('gameGMGetBattlePass: battlePassConfig ', battlePassConfig); |
||||||
|
battlePassConfig.forEach((data) => { |
||||||
|
let item = state.tableData.list.find((r) => r.PeriodId === data.PeriodId); |
||||||
|
if (!item) { |
||||||
|
item = { PeriodId: data.PeriodId }; |
||||||
|
let bt = state.tableData.list.find((r) => r.PeriodId > data.PeriodId && r.BeginTime <= now); |
||||||
|
if (bt) { |
||||||
|
item.State = 3; |
||||||
|
} |
||||||
|
state.tableData.list.push(item); |
||||||
|
return; |
||||||
|
} |
||||||
|
if (item.BeginTime >= now) { |
||||||
|
item.State = 4; |
||||||
|
} |
||||||
|
}); |
||||||
|
state.tableData.list.sort((a, b) => { |
||||||
|
return a.PeriodId - b.PeriodId; |
||||||
|
}); |
||||||
|
state.tableData.list.forEach((item) => { |
||||||
|
if (item.BeginTime) item.BeginTime = new Date(item.BeginTime * 1000).toLocaleDateString(); |
||||||
|
if (item.EndTime) item.EndTime = new Date(item.EndTime * 1000).toLocaleDateString(); |
||||||
|
}); |
||||||
|
console.log('gameGMGetBattlePass: state.tableData.data', state.tableData.list); |
||||||
|
}) |
||||||
|
.finally(() => { |
||||||
|
loading.close(); |
||||||
|
}); |
||||||
|
}; |
||||||
|
|
||||||
|
const unique = () => { |
||||||
|
return uniquePeriodId(BattlePassConfigCategory); |
||||||
|
}; |
||||||
|
return { |
||||||
|
openDialog, |
||||||
|
closeDialog, |
||||||
|
onCancel, |
||||||
|
...toRefs(state), |
||||||
|
}; |
||||||
|
}, |
||||||
|
}); |
||||||
|
</script> |
Loading…
Reference in new issue