支持水印排列

御魂2,4,6属性简称
修正截图大小
式神,御魂信息更新
This commit is contained in:
rookie4show 2025-03-09 21:30:56 +08:00
parent b869d5263c
commit 9250faaf92
30 changed files with 1685 additions and 1255 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View File

@ -1,16 +1,25 @@
<script setup>
import Yys from './components/Yys.vue'
import Watermark from './components/Watermark.vue' // Watermark
import Toolbar from './components/Toolbar.vue'
import {ref} from "vue";
const yysRef = ref(null);
const onHandleInport = (file) => {
yysRef.value.importGroups(file);
};
const onHandleExport = () => {
yysRef.value.exportGroups();
};
</script>
<template>
<main id="main-container">
<!-- 添加工具栏 -->
<Toolbar title="我的应用" username="示例用户" data-html2canvas-ignore="true"/>
<Toolbar title="yys-editor" username="示例用户" data-html2canvas-ignore="true" @handleExport="onHandleExport" @handleImport="onHandleInport"/>
<!-- 添加 Watermark 组件 -->
<Yys/>
<Yys ref="yysRef"/>
</main>
</template>
@ -20,9 +29,11 @@ import Toolbar from './components/Toolbar.vue'
#main-container {
margin-top: 48px; /* 与工具栏高度相同 */
position: relative;
width: 100%;
min-height: 100vh; /* 允许容器扩展 */
//position: relative;
display: inline-block;
max-width: 100%;
}
/* 如果 Yys 组件需要特定的高度或布局,可以根据需要调整 */

View File

@ -31,7 +31,7 @@
<div v-if="shikigami.skillRequiredMode === 'custom'" style="display: flex; flex-direction: row; width: 100%;">
<el-select v-for="(value, key, index) in shikigami.skillRequired" :placeholder="value"
style="margin-bottom: 10px;" @change="updateSkillRequired(key, $event)">
<el-option label="*" value="*"/>
<el-option label="*" value="X"/>
<el-option label="1" value="1"/>
<el-option label="2" value="2"/>
<el-option label="3" value="3"/>
@ -67,20 +67,9 @@
</el-form-item>
<el-form-item label="效果指标">
<el-select placeholder="1" v-model="shikigami.yuhun.target">
<el-option label="伤害输出" value="1"/>
<el-option label="效果命中" value="2"/>
<el-option label="效果抵抗" value="3"/>
<el-option label="生命" value="4"/>
<el-option label="攻击" value="5"/>
<el-option label="防御" value="6"/>
<el-option label="速度" value="7"/>
<el-option label="暴击" value="8"/>
<el-option label="暴击伤害" value="9"/>
<el-option label="治疗量" value="10"/>
<el-option label="命抗双修" value="11"/>
<el-option label="防御输出" value="12"/>
<el-form-item :label="t('yuhunTarget')">
<el-select v-model="yuhunTarget">
<el-option v-for="option in yuhunTargetOptions" :key="option.value" :label="t(option.label)" :value="option.value"/>
</el-select>
</el-form-item>
<el-form-item label="2号位主属性">
@ -122,7 +111,7 @@
</el-form-item>
</div>
</div>
<el-form-item label="Activity form">
<el-form-item label="额外描述">
<el-input v-model="shikigami.desc" type="textarea"/>
</el-form-item>
<el-form-item>
@ -138,8 +127,12 @@ import propertyData from "../data/property.json";
import {ref, watch, computed} from 'vue'
import ShikigamiSelect from "@/components/ShikigamiSelect.vue";
import YuhunSelect from "@/components/YuhunSelect.vue";
import {useI18n} from 'vue-i18n'
// import YuhunSelect from "./YuhunSelect.vue";
// i18n
const {t} = useI18n()
const props = defineProps({
currentShikigami: {
type: Object,
@ -152,8 +145,9 @@ const props = defineProps({
const emit = defineEmits(['closeProperty', 'updateProperty'])
let showYuhunSelect = ref(false)
let shikigami = ref({
const showYuhunSelect = ref(false)
const yuhunTarget = ref('1')
const shikigami = ref({
edit: false,
yuhun: {
yuhunSetEffect: [],
@ -165,11 +159,27 @@ let shikigami = ref({
levelRequired: "40",
speed: "",
skillRequiredMode: "all",
skillRequired: ["技能一", "技能二", "技能三"]
skillRequired: ["5", "5", "5"]
})
let yuhunIndex = ref(-1)
let current = ref({})
let show = ref(props.showProperty)
const yuhunIndex = ref(-1)
const current = ref({})
const show = ref(props.showProperty)
const yuhunTargetOptions = [
{ label: 'yuhun_target.fullName.0', value: '0' },
{ label: 'yuhun_target.fullName.1', value: '1' },
{ label: 'yuhun_target.fullName.2', value: '2' },
{ label: 'yuhun_target.fullName.3', value: '3' },
{ label: 'yuhun_target.fullName.4', value: '4' },
{ label: 'yuhun_target.fullName.5', value: '5' },
{ label: 'yuhun_target.fullName.6', value: '6' },
{ label: 'yuhun_target.fullName.7', value: '7' },
{ label: 'yuhun_target.fullName.8', value: '8' },
{ label: 'yuhun_target.fullName.9', value: '9' },
{ label: 'yuhun_target.fullName.10', value: '10' },
{ label: 'yuhun_target.fullName.11', value: '11' },
{ label: 'yuhun_target.fullName.12', value: '12' },
]
watch(() => props.currentShikigami, (newVal) => {
if (newVal.properties != undefined && newVal.properties.edit == true) {
@ -184,10 +194,20 @@ watch(() => props.showProperty, (newVal) => {
show.value = newVal;
})
watch(() => shikigami.value.yuhun.target, (newVal) => {
watch(() => shikigami.value.skillRequiredMode, (newVal) => {
if(newVal == "all") {
shikigami.value.skillRequired = ["5", "5", "5"]
}
else if (newVal == "111") {
shikigami.value.skillRequired = ["1", "1", "1"]
}
})
watch(() => yuhunTarget.value, (newVal) => {
switch (newVal) {
//<el-option label="" value="1"/>
case "1": {
shikigami.value.yuhun.target = 1
shikigami.value.yuhun.property2 = ["Attack"];
shikigami.value.yuhun.property4 = ["Attack"];
shikigami.value.yuhun.property6 = ["Crit", "CritDamage"];
@ -195,6 +215,7 @@ watch(() => shikigami.value.yuhun.target, (newVal) => {
}
//<el-option label="" value="2"/>
case "2": {
shikigami.value.yuhun.target = 2
shikigami.value.yuhun.property2 = ["Speed"];
shikigami.value.yuhun.property4 = ["ControlHit"];
shikigami.value.yuhun.property6 = ["Attack", "Defense", "Health", "Crit", "CritDamage"];
@ -202,6 +223,7 @@ watch(() => shikigami.value.yuhun.target, (newVal) => {
}
//<el-option label="" value="3"/>
case "3": {
shikigami.value.yuhun.target = 3
shikigami.value.yuhun.property2 = ["Speed"];
shikigami.value.yuhun.property4 = ["ControlMiss"];
shikigami.value.yuhun.property6 = ["Attack", "Defense", "Health", "Crit", "CritDamage"];
@ -209,6 +231,7 @@ watch(() => shikigami.value.yuhun.target, (newVal) => {
}
//<el-option label="" value="4"/>
case "4": {
shikigami.value.yuhun.target = 4
shikigami.value.yuhun.property2 = ["Health"];
shikigami.value.yuhun.property4 = ["Health"];
shikigami.value.yuhun.property6 = ["Health"];
@ -216,6 +239,7 @@ watch(() => shikigami.value.yuhun.target, (newVal) => {
}
//<el-option label="" value="5"/>
case "5": {
shikigami.value.yuhun.target = 5
shikigami.value.yuhun.property2 = ["Attack"];
shikigami.value.yuhun.property4 = ["Attack"];
shikigami.value.yuhun.property6 = ["Attack"];
@ -223,6 +247,7 @@ watch(() => shikigami.value.yuhun.target, (newVal) => {
}
//<el-option label="" value="6"/>
case "6": {
shikigami.value.yuhun.target = 6
shikigami.value.yuhun.property2 = ["Defense"];
shikigami.value.yuhun.property4 = ["Defense"];
shikigami.value.yuhun.property6 = ["Defense"];
@ -230,6 +255,7 @@ watch(() => shikigami.value.yuhun.target, (newVal) => {
}
//<el-option label="" value="7"/>
case "7": {
shikigami.value.yuhun.target = 7
shikigami.value.yuhun.property2 = ["Speed"];
shikigami.value.yuhun.property4 = ["Attack", "Defense", "Health", "ControlHit", "ControlMiss"];
shikigami.value.yuhun.property6 = ["Attack", "Defense", "Health", "Crit", "CritDamage"];
@ -237,6 +263,7 @@ watch(() => shikigami.value.yuhun.target, (newVal) => {
}
//<el-option label="" value="8"/>
case "8": {
shikigami.value.yuhun.target = 8
shikigami.value.yuhun.property2 = ["Attack", "Defense", "Health", "Speed"];
shikigami.value.yuhun.property4 = ["Attack", "Defense", "Health", "ControlHit", "ControlMiss"];
shikigami.value.yuhun.property6 = ["Crit"];
@ -244,6 +271,7 @@ watch(() => shikigami.value.yuhun.target, (newVal) => {
}
//<el-option label="" value="9"/>
case "9": {
shikigami.value.yuhun.target = 9
shikigami.value.yuhun.property2 = ["Attack", "Defense", "Health", "Speed"];
shikigami.value.yuhun.property4 = ["Attack", "Defense", "Health", "ControlHit", "ControlMiss"];
shikigami.value.yuhun.property6 = ["CritDamage"];
@ -251,6 +279,7 @@ watch(() => shikigami.value.yuhun.target, (newVal) => {
}
//<el-option label="" value="10"/>
case "10": {
shikigami.value.yuhun.target = 10
shikigami.value.yuhun.property2 = ["Speed"];
shikigami.value.yuhun.property4 = ["Health"];
shikigami.value.yuhun.property6 = ["Crit", "CritDamage"];
@ -258,6 +287,7 @@ watch(() => shikigami.value.yuhun.target, (newVal) => {
}
//<el-option label="" value="11"/>
case "11": {
shikigami.value.yuhun.target = 11
shikigami.value.yuhun.property2 = ["Speed"];
shikigami.value.yuhun.property4 = ["ControlHit", "ControlMiss"];
shikigami.value.yuhun.property6 = ["Attack", "Defense", "Health", "Crit", "CritDamage"];
@ -265,6 +295,7 @@ watch(() => shikigami.value.yuhun.target, (newVal) => {
}
//<el-option label="" value="12"/>
case "12": {
shikigami.value.yuhun.target = 12
shikigami.value.yuhun.property2 = ["Defense"];
shikigami.value.yuhun.property4 = ["Defense"];
shikigami.value.yuhun.property6 = ["Crit", "CritDamage"];
@ -313,11 +344,12 @@ const confirm = () => {
}
const resetData = () => {
yuhunTarget.value = '1'
shikigami.value = {
edit: false,
yuhun: {
yuhunSetEffect: [],
target: "伤害输出",
target: "0",
property2: ["Attack"],
property4: ["Attack"],
property6: ["Crit", "CritDamage"],
@ -325,7 +357,11 @@ const resetData = () => {
levelRequired: "40",
speed: "",
skillRequiredMode: "all",
skillRequired: ["技能一", "技能二", "技能三"]
skillRequired: ["5", "5", "5"]
}
}
const updateSkillRequired = (index, value) => {
shikigami.value.skillRequired[index] = value;
}
</script>

View File

@ -1,19 +1,19 @@
<template>
<div class="toolbar">
<div data-html2canvas-ignore="true">
<el-button icon="Upload" type="primary" @click="prepareCapture">{{ t('import') }}</el-button>
<el-button icon="Download" type="primary" @click="prepareCapture">{{ t('export') }}</el-button>
<div>
<el-button icon="Upload" type="primary" @click="handleImport">{{ t('import') }}</el-button>
<el-button icon="Download" type="primary" @click="handleExport">{{ t('export') }}</el-button>
<el-button icon="Share" type="primary" @click="prepareCapture">{{ t('prepareCapture') }}</el-button>
<el-button icon="Setting" type="primary" @click="prepareCapture">{{ t('setWatermark') }}</el-button>
<el-button icon="Setting" type="primary" @click="state.showWatermarkDialog = true">{{
t('setWatermark')
}}
</el-button>
</div>
<!-- 预览弹窗 -->
<el-dialog id="preview-container" v-model="state.previewVisible" width="80%" :before-close="handleClose">
<el-dialog id="preview-container" v-model="state.previewVisible" width="80%" height="80%" :before-close="handleClose">
<div style="max-height: 500px; overflow-y: auto;">
<Watermark text="示例水印" font="30px Arial" color="rgba(184, 184, 184, 0.3)" angle=-20>
<img v-if="state.previewImage" :src="state.previewImage" alt="Preview" style="width: 100%; display: block;"/>
</Watermark>
</div>
<!-- <img v-if="state.previewImage" :src="state.previewImage" alt="Preview" style="width: 100%; height: auto;" />-->
<span slot="footer" class="dialog-footer">
@ -21,77 +21,212 @@
<el-button type="primary" @click="downloadImage"> </el-button>
</span>
</el-dialog>
<!-- 水印设置弹窗 -->
<el-dialog v-model="state.showWatermarkDialog" title="设置水印" width="30%">
<el-form>
<el-form-item label="水印文字">
<el-input v-model="watermark.text"></el-input>
</el-form-item>
<el-form-item label="字体大小">
<el-input-number v-model="watermark.fontSize" :min="10" :max="100"></el-input-number>
</el-form-item>
<el-form-item label="颜色">
<el-color-picker v-model="watermark.color"></el-color-picker>
</el-form-item>
<el-form-item label="水印行数">
<el-input-number v-model="watermark.rows" :min="1" :max="10"></el-input-number>
</el-form-item>
<el-form-item label="水印列数">
<el-input-number v-model="watermark.cols" :min="1" :max="10"></el-input-number>
</el-form-item>
<el-form-item label="角度">
<el-input-number v-model="watermark.angle" :min="-90" :max="90"></el-input-number>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="state.showWatermarkDialog = false">取消</el-button>
<el-button type="primary" @click="applyWatermarkSettings">确认</el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script setup>
import {inject, nextTick} from 'vue';
import {ref, reactive} from 'vue';
import html2canvas from "html2canvas";
import {useI18n} from 'vue-i18n'
import Watermark from './Watermark.vue' // Watermark
import {useI18n} from 'vue-i18n';
// i18n
const {t} = useI18n()
//
const watermarkControl = inject('watermarkControl');
defineProps({
title: {
type: String,
default: '默认标题'
}
})
import {ref, reactive, toRefs} from 'vue';
const {t} = useI18n();
const emit = defineEmits(['handleExport', 'handleImport'])
//
const state = reactive({
previewImage: null, // URL
previewVisible: false, //
showWatermarkDialog: false, //
});
const handleExport = () => {
emit('handleExport');
};
const handleImport = () => {
const input = document.createElement('input');
input.type = 'file';
input.accept = '.json';
input.onchange = (e) => {
const file = e.target.files[0];
if (file) emit('handleImport', file);
};
input.click();
};
const watermark = reactive({
text: '示例水印',
fontSize: 30,
color: 'rgba(184, 184, 184, 0.3)',
angle: -20,
rows: 1, //
cols: 1 //
});
const applyWatermarkSettings = () => {
state.showWatermarkDialog = false;
};
//
function calculateVisualHeight(selector) {
// 1.
const elements = Array.from(document.querySelectorAll(selector));
// 2.
const rects = elements.map(el => {
const rect = el.getBoundingClientRect();
return {
el,
top: rect.top,
bottom: rect.bottom,
height: rect.height
};
}).sort((a, b) => a.top - b.top); //
// 3.
const rows = [];
rects.forEach(rect => {
let placed = false;
//
for (const row of rows) {
if (
rect.top < row.bottom && //
rect.bottom > row.top //
) {
row.elements.push(rect);
row.bottom = Math.max(row.bottom, rect.bottom); //
row.maxHeight = Math.max(row.maxHeight, rect.height);
placed = true;
break;
}
}
//
if (!placed) {
rows.push({
elements: [rect],
top: rect.top,
bottom: rect.bottom,
maxHeight: rect.height
});
}
});
// 4.
return rows.reduce((sum, row) => sum + row.maxHeight, 0);
}
const ignoreElements = (element) => {
return element.classList.contains('ql-toolbar');
}
};
const prepareCapture = async () => {
state.previewVisible = true; //
state.previewVisible = true;
//
const style = document.createElement('style')
style.id = 'capture-style'
style.textContent = `
.ql-container.ql-snow {
border: none !important;
}
`
document.head.appendChild(style)
//
const style = document.createElement('style');
style.textContent = `.ql-container.ql-snow { border: none !important; }`;
document.head.appendChild(style);
try {
const element = document.querySelector('#main-container'); //
const element = document.querySelector('#main-container');
let totalHeight = calculateVisualHeight('[data-html2canvas-ignore]') + calculateVisualHeight('.ql-toolbar');
console.log('所有携带指定属性的元素高度之和:', totalHeight);
if (!element) {
console.error('Element with ID "main-container" not found.');
state.previewVisible = false;
console.error('Element not found');
return;
}
// 1.
const canvas = await html2canvas(element, {
ignoreElements: ignoreElements,
height: element.scrollHeight,
height: element.scrollHeight - totalHeight
});
// 2. Canvas
const watermarkedCanvas = document.createElement('canvas');
const ctx = watermarkedCanvas.getContext('2d');
// Canvas
watermarkedCanvas.width = canvas.width;
watermarkedCanvas.height = canvas.height;
//
ctx.drawImage(canvas, 0, 0);
//
ctx.font = `${watermark.fontSize}px Arial`;
ctx.fillStyle = watermark.color;
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
//
const colSpace = watermarkedCanvas.width / (watermark.cols + 1);
const rowSpace = watermarkedCanvas.height / (watermark.rows + 1);
//
ctx.save();
//
for (let row = 1; row <= watermark.rows; row++) {
for (let col = 1; col <= watermark.cols; col++) {
ctx.save(); //
const x = col * colSpace;
const y = row * rowSpace;
//
ctx.translate(x, y);
ctx.rotate((watermark.angle * Math.PI) / 180);
//
ctx.fillText(watermark.text, 0, 0);
ctx.restore(); //
}
);
state.previewImage = canvas.toDataURL();
if (!state.previewImage) {
console.error('Failed to generate image data URL.');
state.previewVisible = false;
state.previewVisible = false;
}
ctx.restore(); //
// 3.
state.previewImage = watermarkedCanvas.toDataURL();
} catch (error) {
console.error('Failed to capture screenshot', error);
state.previewVisible = false;
console.error('Capture failed', error);
} finally {
//
document.getElementById('capture-style')?.remove()
document.head.removeChild(style);
}
};
@ -119,7 +254,7 @@ const handleClose = (done) => {
right: 0;
height: 48px;
background: #f8f8f8;
//border-bottom: 1px solid #eee; display: flex;
//border-bottom: 1px solid #eee; display: flex;
align-items: center;
padding: 0 8px;
z-index: 100;

View File

@ -6,7 +6,7 @@
</template>
<script setup>
import { onMounted, ref } from 'vue';
import { onMounted,watch, ref } from 'vue';
const props = defineProps({
text: {
@ -31,6 +31,8 @@ onMounted(() => {
createWatermark();
});
const createWatermark = () => {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
@ -78,6 +80,9 @@ const createWatermark = () => {
// });
// observer.observe(container);
};
// props
watch(() => [props.text, props.font, props.color, props.angle], createWatermark);
</script>
<style scoped>

View File

@ -6,13 +6,15 @@
</div>
<el-tabs v-model="activeName" type="card" class="demo-tabs" @tab-click="handleTabClick">
<el-tab-pane v-for="type in yuhunTypes" :key="type.name" :label="type.label" :name="type.name">
<el-space wrap size="large">
<div style="max-height: 500px; overflow-y: auto;">
<el-space wrap size="large" style="">
<div v-for="yuhun in filterYuhunByType(activeName)" :key="yuhun.name">
<el-button style="width: 100px; height: 100px;" @click="confirm(yuhun)">
<img :src="yuhun.avatar" style="width: 99px; height: 99px;">
</el-button>
</div>
</el-space>
</div>
</el-tab-pane>
</el-tabs>
</el-dialog>
@ -48,6 +50,7 @@ const yuhunTypes = [
{label: '防御加成', name: 'Defense'},
{label: '效果命中', name: 'ControlHit'},
{label: '效果抵抗', name: 'ControlMiss'},
{label: '暴击伤害', name: 'CritDamage'},
{label: '首领御魂', name: 'PVE'}
];

View File

@ -40,11 +40,6 @@
<QuillEditor v-model:content="group.shortDescription" contentType="html" theme="snow"
:toolbar="toolbarOptions"/>
</div>
<!-- <div style="display: flex; justify-content: flex-end;" data-html2canvas-ignore="true">-->
<!-- <el-button class="drag-handle" type="primary" icon="Rank" circle></el-button>-->
<!-- <el-button type="danger" icon="Delete" circle @click="removeGroup(groupIndex)"></el-button>-->
<!-- <el-button type="primary" icon="Plus" circle @click="addGroup"></el-button>-->
<!-- </div>-->
<div>
<draggable :list="group.groupInfo" item-key="name" style="display: flex; flex-direction: row; width: 20%;">
<template #item="{element : position, index:positionIndex}">
@ -53,41 +48,49 @@
<el-card shadow="never"
:body-style="{ display: 'flex', 'flex-direction': 'column', 'justify-content': 'center', 'align-items': 'center' }">
<div>
<div data-html2canvas-ignore="true">
<!-- Add delete button here -->
<el-button type="danger" icon="Delete" circle
@click="removeGroupElement(groupIndex, positionIndex)"
data-html2canvas-ignore="true"></el-button>
<el-button type="primary" icon="Plus" circle @click="addGroupElement(groupIndex)"
data-html2canvas-ignore="true"></el-button>
<el-button type="danger" icon="Delete" circle @click="removeGroupElement(groupIndex, positionIndex)"/>
<el-button type="primary" icon="Plus" circle @click="addGroupElement(groupIndex)"/>
</div>
<div class="avatar-wrapper">
<div style="position: relative; display: inline-block;">
<!-- 头像图片 -->
<img :src="position.avatar || '/assets/Shikigami/default.png'"
style="cursor: pointer; vertical-align: bottom;"
class="avatar-image"
@click="editShikigami(groupIndex,positionIndex)"/>
</div>
<div style="padding: 14px; width: 95px">
@click="editShikigami(groupIndex, positionIndex)"/>
<!-- 文字图层 -->
<span v-if="position.properties"
style="position: absolute; bottom: 0; left: 50%; transform: translateX(-50%) translateY(50%);
font-size: 24px; color: white; text-shadow: -1px -1px 0 black, 1px -1px 0 black, -1px 1px 0 black, 1px 1px 0 black;
white-space: nowrap; padding: 0 8px; margin: 0; display: flex; align-items: center; justify-content: center;">
{{ position.properties.levelRequired }} {{ position.properties.skillRequired.join('') }}
</span>
</div>
<div style="padding: 14px; width: 120px;">
<div style="display: flex; justify-content: center;" data-html2canvas-ignore="true">
<span>{{ position.name || "" }}</span>
<div class="bottom" data-html2canvas-ignore="true">
</div>
<div style="display: flex; justify-content: center;" class="bottom" data-html2canvas-ignore="true">
<el-button @click="editProperty(groupIndex,positionIndex)">{{ t('editProperties') }}
</el-button>
</div>
<!-- properties-->
<!-- {"edit":true,"yuhun":{"yuhunSetEffect":[{"name":"狰","type":"attack","avatar":"/assets/Yuhun/狰.png"}],"target":"伤害输出","property2":["Attack"],"property4":["Attack"],"property6":["Crit","CritDamage"]},"levelRequired":"40","speed":"","skillRequiredMode":"all","skillRequired":["技能一","技能二","技能三"]}-->
<div v-if="position.properties">
<div>
<div style="display: flex; justify-content: center;">
<span
style="display: inline-block; width: 100px; height: 25px; background-color: #666; border-top-left-radius: 5px; border-top-right-radius: 5px; margin-right: 5px; color: white; text-align: center; white-space: pre-wrap ">
{{ position.properties.yuhun.yuhunSetEffect.map(item => item.name).join(' ') }}
</span>
<span
style="display: inline-block; width: 100px; height: 25px; background-color: #666; border-bottom-left-radius: 5px; border-bottom-right-radius: 5px; margin-right: 5px; color: white; text-align: center; white-space: pre-wrap ">
{{ t('yuhun_target.' + position.properties.yuhun.target) }}·
style="width: 100px;height: 50px;background-color: #666;
border-radius: 5px; margin-right: 5px; color: white;
text-align: center; white-space: pre-wrap; display: flex; align-items: center; justify-content: center; flex-direction: column ">
{{getYuhunNames(position.properties.yuhun.yuhunSetEffect)}}<br/>{{ t('yuhun_target.shortName.' + position.properties.yuhun.target) }}·{{ getYuhunPropertyNames(position.properties.yuhun) }}
</span>
</div>
<div v-for="(value, key, index) in position.properties" data-html2canvas-ignore="true">
<span>{{ key }}</span> : <span>{{ value || '-' }}</span>
<div>
<span
style="display: inline-block; width: 100px; height: 30px; border-radius: 5px; margin-right: 5px; color: red; text-align: center; white-space: pre-wrap; display: flex; align-items: center; justify-content: center; flex-direction: column ">
{{ position.properties.desc }}
</span>
</div>
</div>
</div>
@ -120,9 +123,6 @@
<div style="margin: 20px">
</div>
</template>
@ -227,7 +227,6 @@ const editProperty = (groupIndex, positionIndex) => {
state.groupIndex = groupIndex;
state.positionIndex = positionIndex;
state.currentShikigami = state.groups[groupIndex].groupInfo[positionIndex];
console.log("currentShikigami", JSON.stringify(state.currentShikigami));
};
const closeProperty = () => {
@ -262,9 +261,72 @@ const addGroupElement = (groupIndex) => {
};
const exportGroups = () => {
const dataStr = JSON.stringify(state.groups, null, 2);
const blob = new Blob([dataStr], {type: 'application/json'});
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = `yys-export-${Date.now()}.json`;
link.click();
URL.revokeObjectURL(url);
};
const getYuhunNames =(yuhunSetEffect) =>{
const names = yuhunSetEffect.map(item => item.name).join('');
if (names.length <= 6) {
return names;
} else {
return yuhunSetEffect.map(item => item.shortName || item.name).join('');
}
}
const getYuhunPropertyNames = (yuhun) =>{
// yuhun.property2
let property2Value,property4Value,property6Value;
if (yuhun.property2.length >= 4) {
property2Value = 'X';
} else {
property2Value = t('yuhun_property.shortName.' + yuhun.property2[0]);
}
if (yuhun.property4.length >= 5) {
property4Value = 'X';
} else {
property4Value = t('yuhun_property.shortName.' + yuhun.property4[0]);
}
if (yuhun.property6.length >= 5) {
property6Value = 'X';
} else {
property6Value = t('yuhun_property.shortName.' + yuhun.property6[0]);
}
// propertyNames
const propertyNames =
property2Value + property4Value + property6Value
return propertyNames;
}
const importGroups = (file) => {
const reader = new FileReader();
reader.onload = (e) => {
try {
const importedData = JSON.parse(e.target.result);
state.groups = importedData;
ElMessage.success('导入成功');
} catch (error) {
ElMessage.error('文件格式错误');
}
};
reader.readAsText(file);
};
//
defineExpose({
exportGroups,
importGroups
});
</script>
@ -288,7 +350,7 @@ const addGroupElement = (groupIndex) => {
position: relative;
overflow: hidden; /* 隐藏超出部分 */
border-radius: 50%; /* 圆形裁剪 */
//border: 2px solid #fff; /* */ //box-shadow: 0 2px 8px rgba(0,0,0,0.1); /* */
//border: 2px solid #fff; /* */ //box-shadow: 0 2px 8px rgba(0,0,0,0.1); /* */
}
/* 图片样式 */
@ -302,8 +364,6 @@ const addGroupElement = (groupIndex) => {
}
.el-card {
border: 0;
}

File diff suppressed because it is too large Load Diff

View File

@ -1,111 +1,139 @@
[
{
"name": "散件",
"shortName": "散件",
"type": "attack",
"avatar": "/assets/Yuhun/散件.png"
},
{
"name": "隐念",
"shortName": "隐",
"type": "attack",
"avatar": "/assets/Yuhun/隐念.png"
},
{
"name": "贝吹坊",
"shortName": "贝",
"type": "attack",
"avatar": "/assets/Yuhun/贝吹坊.png"
},
{
"name": "兵主部",
"shortName": "兵",
"type": "attack",
"avatar": "/assets/Yuhun/兵主部.png"
},
{
"name": "狂骨",
"shortName": "狂",
"type": "attack",
"avatar": "/assets/Yuhun/狂骨.png"
},
{
"name": "阴摩罗",
"shortName": "阴",
"type": "attack",
"avatar": "/assets/Yuhun/阴摩罗.png"
},
{
"name": "心眼",
"shortName": "心",
"type": "attack",
"avatar": "/assets/Yuhun/心眼.png"
},
{
"name": "鸣屋",
"shortName": "鸣",
"type": "attack",
"avatar": "/assets/Yuhun/鸣屋.png"
},
{
"name": "狰",
"shortName": "狰",
"type": "attack",
"avatar": "/assets/Yuhun/狰.png"
},
{
"name": "轮入道",
"shortName": "轮",
"type": "attack",
"avatar": "/assets/Yuhun/轮入道.png"
},
{
"name": "蝠翼",
"shortName": "蝠",
"type": "attack",
"avatar": "/assets/Yuhun/蝠翼.png"
},
{
"name": "应声虫",
"shortName": "应",
"type": "Crit",
"avatar": "/assets/Yuhun/应声虫.png"
},
{
"name": "海月火玉",
"shortName": "海",
"type": "Crit",
"avatar": "/assets/Yuhun/海月火玉.png"
},
{
"name": "青女房",
"shortName": "房",
"type": "Crit",
"avatar": "/assets/Yuhun/青女房.png"
},
{
"name": "针女",
"shortName": "针",
"type": "Crit",
"avatar": "/assets/Yuhun/针女.png"
},
{
"name": "镇墓兽",
"shortName": "镇",
"type": "Crit",
"avatar": "/assets/Yuhun/镇墓兽.png"
},
{
"name": "破势",
"shortName": "破",
"type": "Crit",
"avatar": "/assets/Yuhun/破势.png"
},
{
"name": "伤魂鸟",
"shortName": "伤",
"type": "Crit",
"avatar": "/assets/Yuhun/伤魂鸟.png"
},
{
"name": "网切",
"shortName": "网",
"type": "Crit",
"avatar": "/assets/Yuhun/网切.png"
},
{
"name": "三味",
"shortName": "三",
"type": "Crit",
"avatar": "/assets/Yuhun/三味.png"
},
{
"name": "叠叩",
"shortName": "叠",
"type": "Health",
"avatar": "/assets/Yuhun/叠叩.png"
},
{
"name": "恶楼",
"shortName": "恶",
"type": "Health",
"avatar": "/assets/Yuhun/恶楼.png"
},
{
"name": "涂佛",
"shortName": "涂",
"type": "Health",
"avatar": "/assets/Yuhun/涂佛.png"
},
@ -136,11 +164,13 @@
},
{
"name": "涅槃之火",
"shortName": "涅槃火",
"type": "Health",
"avatar": "/assets/Yuhun/涅槃之火.png"
},
{
"name": "地藏像",
"shortName": "地藏",
"type": "Health",
"avatar": "/assets/Yuhun/地藏像.png"
},
@ -171,6 +201,7 @@
},
{
"name": "日女巳时",
"shortName": "日女",
"type": "Defense",
"avatar": "/assets/Yuhun/日女巳时.png"
},
@ -181,6 +212,7 @@
},
{
"name": "招财猫",
"shortName": "招财",
"type": "Defense",
"avatar": "/assets/Yuhun/招财猫.png"
},
@ -241,16 +273,19 @@
},
{
"name": "魍魉之匣",
"shortName": "魍魉",
"type": "ControlMiss",
"avatar": "/assets/Yuhun/魍魉之匣.png"
},
{
"name": "鬼灵歌伎",
"shortName": "歌伎",
"type": "PVE",
"avatar": "/assets/Yuhun/鬼灵歌伎.png"
},
{
"name": "蜃气楼",
"shortName": "蜃",
"type": "PVE",
"avatar": "/assets/Yuhun/蜃气楼.png"
},
@ -261,16 +296,35 @@
},
{
"name": "荒骷髅",
"shortName": "荒",
"type": "PVE",
"avatar": "/assets/Yuhun/荒骷髅.png"
},
{
"name": "胧车",
"shortName": "胧",
"type": "PVE",
"avatar": "/assets/Yuhun/胧车.png"
},{
},
{
"name": "土蜘蛛",
"shortName": "土",
"type": "PVE",
"avatar": "/assets/Yuhun/土蜘蛛.png"
}
},
{
"name": "夜荒魂",
"type": "PVE",
"avatar": "/assets/Yuhun/夜荒魂.png"
},
{
"name": "奉海图",
"type": "Defense",
"avatar": "/assets/Yuhun/奉海图.png"
},
{
"name": "无刀取",
"type": "CritDamage",
"avatar": "/assets/Yuhun/无刀取.png"
}
]

View File

@ -6,7 +6,11 @@
"Paste": "粘贴",
"editProperties": "配置属性",
"prepareCapture": "生成截图",
"yuhunSelect": "请选择御魂",
"yuhunTarget": "效果指标",
"yuhun_target": {
"shortName": {
"0": "任意",
"1": "伤害",
"2": "命中",
"3": "抵抗",
@ -20,5 +24,42 @@
"11": "命抗",
"12": "防御输出"
},
"yuhunSelect": "请选择御魂"
"fullName": {
"0": "任意",
"1": "伤害输出",
"2": "效果命中",
"3": "效果抵抗",
"4": "生命",
"5": "攻击",
"6": "防御",
"7": "速度",
"8": "暴击",
"9": "暴击伤害",
"10": "治疗量",
"11": "命抗双修",
"12": "防御输出"
}
},
"yuhun_property": {
"shortName": {
"Attack": "攻",
"Defense": "防",
"Health": "生",
"Speed": "速",
"ControlHit": "命",
"ControlMiss": "抵",
"Crit": "暴",
"CritDamage": "爆"
},
"fullName": {
"Attack": "攻击加成",
"Defense": "防御加成",
"Health": "生命加成",
"Speed": "速度",
"ControlHit": "效果命中",
"ControlMiss": "效果抵抗",
"Crit": "暴击",
"CritDamage": "暴击伤害"
}
}
}