new:新增技能导出
This commit is contained in:
@@ -18,6 +18,7 @@
|
|||||||
"@vueuse/core": "^12.8.2",
|
"@vueuse/core": "^12.8.2",
|
||||||
"axios": "^1.8.2",
|
"axios": "^1.8.2",
|
||||||
"hoci": "^0.6.0",
|
"hoci": "^0.6.0",
|
||||||
|
"html-to-image": "^1.11.13",
|
||||||
"pako": "^2.1.0",
|
"pako": "^2.1.0",
|
||||||
"pinia": "^3.0.1",
|
"pinia": "^3.0.1",
|
||||||
"query-string": "^9.1.1",
|
"query-string": "^9.1.1",
|
||||||
|
|||||||
Generated
+8
@@ -17,6 +17,9 @@ importers:
|
|||||||
hoci:
|
hoci:
|
||||||
specifier: ^0.6.0
|
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))
|
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:
|
pako:
|
||||||
specifier: ^2.1.0
|
specifier: ^2.1.0
|
||||||
version: 2.1.0
|
version: 2.1.0
|
||||||
@@ -1547,6 +1550,9 @@ packages:
|
|||||||
hookable@5.5.3:
|
hookable@5.5.3:
|
||||||
resolution: {integrity: sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==}
|
resolution: {integrity: sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==}
|
||||||
|
|
||||||
|
html-to-image@1.11.13:
|
||||||
|
resolution: {integrity: sha512-cuOPoI7WApyhBElTTb9oqsawRvZ0rHhaHwghRLlTuffoD1B2aDemlCruLeZrUIIdvG7gs9xeELEPm6PhuASqrg==}
|
||||||
|
|
||||||
human-signals@8.0.0:
|
human-signals@8.0.0:
|
||||||
resolution: {integrity: sha512-/1/GPCpDUCCYwlERiYjxoczfP0zfvZMU/OWgQPMya9AbAE24vseigFdhAMObpc8Q4lc/kjutPfUddDYyAmejnA==}
|
resolution: {integrity: sha512-/1/GPCpDUCCYwlERiYjxoczfP0zfvZMU/OWgQPMya9AbAE24vseigFdhAMObpc8Q4lc/kjutPfUddDYyAmejnA==}
|
||||||
engines: {node: '>=18.18.0'}
|
engines: {node: '>=18.18.0'}
|
||||||
@@ -3877,6 +3883,8 @@ snapshots:
|
|||||||
|
|
||||||
hookable@5.5.3: {}
|
hookable@5.5.3: {}
|
||||||
|
|
||||||
|
html-to-image@1.11.13: {}
|
||||||
|
|
||||||
human-signals@8.0.0: {}
|
human-signals@8.0.0: {}
|
||||||
|
|
||||||
ignore@5.3.2: {}
|
ignore@5.3.2: {}
|
||||||
|
|||||||
@@ -28,6 +28,13 @@
|
|||||||
class="ml-1"
|
class="ml-1"
|
||||||
icon="exchange"
|
icon="exchange"
|
||||||
></calc-button>
|
></calc-button>
|
||||||
|
<calc-button
|
||||||
|
title="技能加点导出"
|
||||||
|
@click="exportSkillTree"
|
||||||
|
:disabled="exportingSkillTree || !infoStore.infos"
|
||||||
|
class="ml-1"
|
||||||
|
icon="share"
|
||||||
|
></calc-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -51,8 +58,16 @@ import ImportVue from './Config/import.vue'
|
|||||||
import ImportSkillVue from './Config/importSkill.vue'
|
import ImportSkillVue from './Config/importSkill.vue'
|
||||||
import ImportDnfhelper from './Config/importDnfhelper.vue'
|
import ImportDnfhelper from './Config/importDnfhelper.vue'
|
||||||
import Result from './Result/Index.vue'
|
import Result from './Result/Index.vue'
|
||||||
|
import SkillTree from './Skill/components/SkillTree'
|
||||||
import { useDnfHelperStore } from '@/stores/dnfhelper.ts'
|
import { useDnfHelperStore } from '@/stores/dnfhelper.ts'
|
||||||
import { type IHelperRoleInfo } from '@/api/info/type'
|
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<{
|
const props = defineProps<{
|
||||||
alter: string
|
alter: string
|
||||||
}>()
|
}>()
|
||||||
@@ -63,6 +78,8 @@ const importVisible = ref(false)
|
|||||||
const importSkillVisible = ref(false)
|
const importSkillVisible = ref(false)
|
||||||
const helperStore = useDnfHelperStore()
|
const helperStore = useDnfHelperStore()
|
||||||
const toImport = ref<IHelperRoleInfo>()
|
const toImport = ref<IHelperRoleInfo>()
|
||||||
|
const exportingSkillTree = ref(false)
|
||||||
|
const { alert } = useDialog()
|
||||||
|
|
||||||
// const shouldWatch = ref(true)
|
// const shouldWatch = ref(true)
|
||||||
|
|
||||||
@@ -92,6 +109,79 @@ const showImportSkill = () => {
|
|||||||
importSkillVisible.value = true
|
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()
|
const route = useRoute()
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
@@ -163,4 +253,19 @@ onUnmounted(() => {
|
|||||||
left: 0;
|
left: 0;
|
||||||
background-color: rgba(50, 50, 50, 0.75);
|
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>
|
</style>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { useVModel } from '@vueuse/core'
|
|||||||
import CalcButton from '@/components/calc/button/index.vue'
|
import CalcButton from '@/components/calc/button/index.vue'
|
||||||
import './style.scss'
|
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 defaultColumnCount = 11
|
||||||
const altersWithoutRowPrefix = new Set(['creator', 'dark_knight'])
|
const altersWithoutRowPrefix = new Set(['creator', 'dark_knight'])
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user