new:新增技能导出

This commit is contained in:
2026-10-03 01:24:44 +08:00
parent dd85b08452
commit 9e325bcab0
4 changed files with 115 additions and 1 deletions
+1
View File
@@ -18,6 +18,7 @@
"@vueuse/core": "^12.8.2",
"axios": "^1.8.2",
"hoci": "^0.6.0",
"html-to-image": "^1.11.13",
"pako": "^2.1.0",
"pinia": "^3.0.1",
"query-string": "^9.1.1",
+8
View File
@@ -17,6 +17,9 @@ importers:
hoci:
specifier: ^0.6.0
version: 0.6.0(@babel/parser@7.26.9)(@vueuse/core@12.8.2(typescript@5.8.2))(rollup@4.35.0)(vue@3.5.13(typescript@5.8.2))
html-to-image:
specifier: ^1.11.13
version: 1.11.13
pako:
specifier: ^2.1.0
version: 2.1.0
@@ -1547,6 +1550,9 @@ packages:
hookable@5.5.3:
resolution: {integrity: sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==}
html-to-image@1.11.13:
resolution: {integrity: sha512-cuOPoI7WApyhBElTTb9oqsawRvZ0rHhaHwghRLlTuffoD1B2aDemlCruLeZrUIIdvG7gs9xeELEPm6PhuASqrg==}
human-signals@8.0.0:
resolution: {integrity: sha512-/1/GPCpDUCCYwlERiYjxoczfP0zfvZMU/OWgQPMya9AbAE24vseigFdhAMObpc8Q4lc/kjutPfUddDYyAmejnA==}
engines: {node: '>=18.18.0'}
@@ -3877,6 +3883,8 @@ snapshots:
hookable@5.5.3: {}
html-to-image@1.11.13: {}
human-signals@8.0.0: {}
ignore@5.3.2: {}
+105
View File
@@ -28,6 +28,13 @@
class="ml-1"
icon="exchange"
></calc-button>
<calc-button
title="技能加点导出"
@click="exportSkillTree"
:disabled="exportingSkillTree || !infoStore.infos"
class="ml-1"
icon="share"
></calc-button>
</div>
</div>
@@ -51,8 +58,16 @@ import ImportVue from './Config/import.vue'
import ImportSkillVue from './Config/importSkill.vue'
import ImportDnfhelper from './Config/importDnfhelper.vue'
import Result from './Result/Index.vue'
import SkillTree from './Skill/components/SkillTree'
import { useDnfHelperStore } from '@/stores/dnfhelper.ts'
import { type IHelperRoleInfo } from '@/api/info/type'
import { useDialog } from '@/components/hooks/dialog'
import { toPng } from 'html-to-image'
import { h, nextTick, render } from 'vue'
const skillTreeExportWidth = 550
const skillTreeExportPixelRatio = 2
const props = defineProps<{
alter: string
}>()
@@ -63,6 +78,8 @@ const importVisible = ref(false)
const importSkillVisible = ref(false)
const helperStore = useDnfHelperStore()
const toImport = ref<IHelperRoleInfo>()
const exportingSkillTree = ref(false)
const { alert } = useDialog()
// const shouldWatch = ref(true)
@@ -92,6 +109,79 @@ const showImportSkill = () => {
importSkillVisible.value = true
}
const waitForSkillImages = async (root: HTMLElement) => {
const pendingImages = Array.from(root.querySelectorAll('img')).filter((image) => !image.complete)
if (pendingImages.length === 0) return
await Promise.race([
Promise.all(
pendingImages.map(
(image) =>
new Promise<void>((resolve) => {
image.addEventListener('load', () => resolve(), { once: true })
image.addEventListener('error', () => resolve(), { once: true })
}),
),
),
new Promise<void>((resolve) => window.setTimeout(resolve, 3000)),
])
}
const exportSkillTree = async () => {
if (exportingSkillTree.value || !infoStore.infos) return
exportingSkillTree.value = true
const exportHost = document.createElement('div')
exportHost.className = 'skill-tree-export-host'
const exportRoot = document.createElement('div')
exportRoot.className = 'skill-tree-export-root'
exportHost.appendChild(exportRoot)
document.body.appendChild(exportHost)
try {
render(
h(SkillTree, {
skills: infoStore.skills,
lvInfo: configStore.config.skills,
bindAwake: configStore.config.bindAwake,
alter: infoStore.infos.alter,
}),
exportRoot,
)
await nextTick()
await document.fonts.ready
await waitForSkillImages(exportRoot)
const exportHeight = exportRoot.scrollHeight
const dataUrl = await toPng(exportRoot, {
width: skillTreeExportWidth,
height: exportHeight,
canvasWidth: skillTreeExportWidth,
canvasHeight: exportHeight,
pixelRatio: skillTreeExportPixelRatio,
backgroundColor: '#101012',
cacheBust: true,
})
const fileName = `${infoStore.infos.name || props.alter}-技能加点.png`.replace(
/[\\/:*?"<>|]/g,
'-',
)
const downloadLink = document.createElement('a')
downloadLink.download = fileName
downloadLink.href = dataUrl
downloadLink.click()
} catch (error) {
console.error('技能加点导出失败', error)
await alert({ content: '技能加点导出失败,请稍后重试' })
} finally {
render(null, exportRoot)
exportHost.remove()
exportingSkillTree.value = false
}
}
const route = useRoute()
onMounted(async () => {
@@ -163,4 +253,19 @@ onUnmounted(() => {
left: 0;
background-color: rgba(50, 50, 50, 0.75);
}
.skill-tree-export-host {
position: fixed;
top: 0;
left: -10000px;
pointer-events: none;
}
.skill-tree-export-root {
width: 550px;
min-width: 550px;
overflow: visible;
color: #e9c558;
background: #101012;
}
</style>
@@ -4,7 +4,7 @@ import { useVModel } from '@vueuse/core'
import CalcButton from '@/components/calc/button/index.vue'
import './style.scss'
const rowPrefixes = ['通用', 1, '', 10, '', 20, '', 30, '', 40, 50, 60, 70, 80, 90, 100]
const rowPrefixes = ['通用', 1, '', 10, '', 20, '', 30, '', 40, '', 50, 60, 70, 80, 90, 100]
const defaultColumnCount = 11
const altersWithoutRowPrefix = new Set(['creator', 'dark_knight'])