mirror of
https://github.com/Powerful-517/yys-editor.git
synced 2025-07-08 05:11:52 +00:00
水印调整,增加工具栏
This commit is contained in:
12
src/App.vue
12
src/App.vue
@ -1,24 +1,28 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import Yys from './components/Yys.vue'
|
import Yys from './components/Yys.vue'
|
||||||
import Watermark from './components/Watermark.vue' // 引入 Watermark 组件
|
import Watermark from './components/Watermark.vue' // 引入 Watermark 组件
|
||||||
|
import Toolbar from './components/Toolbar.vue'
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<main id="main-container">
|
<main id="main-container">
|
||||||
|
<!-- 添加工具栏 -->
|
||||||
|
<Toolbar title="我的应用" username="示例用户" data-html2canvas-ignore="true"/>
|
||||||
<!-- 添加 Watermark 组件 -->
|
<!-- 添加 Watermark 组件 -->
|
||||||
<Watermark text="示例水印" font="30px Arial" color="rgba(184, 184, 184, 0.3)" angle=-20 >
|
<Yys/>
|
||||||
<Yys />
|
|
||||||
</Watermark>
|
|
||||||
</main>
|
</main>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
/* 确保 #main-container 具有相对定位 */
|
/* 确保 #main-container 具有相对定位 */
|
||||||
#main-container {
|
#main-container {
|
||||||
|
margin-top: 48px; /* 与工具栏高度相同 */
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
min-height: 100vh; /* 允许容器扩展 */
|
min-height: 100vh; /* 允许容器扩展 */
|
||||||
//position: relative;
|
//position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 如果 Yys 组件需要特定的高度或布局,可以根据需要调整 */
|
/* 如果 Yys 组件需要特定的高度或布局,可以根据需要调整 */
|
||||||
|
@ -157,7 +157,7 @@ let shikigami = ref({
|
|||||||
edit: false,
|
edit: false,
|
||||||
yuhun: {
|
yuhun: {
|
||||||
yuhunSetEffect: [],
|
yuhunSetEffect: [],
|
||||||
target: "伤害输出",
|
target: "1",
|
||||||
property2: ["Attack"],
|
property2: ["Attack"],
|
||||||
property4: ["Attack"],
|
property4: ["Attack"],
|
||||||
property6: ["Crit", "CritDamage"],
|
property6: ["Crit", "CritDamage"],
|
||||||
@ -175,7 +175,9 @@ watch(() => props.currentShikigami, (newVal) => {
|
|||||||
if (newVal.properties != undefined && newVal.properties.edit == true) {
|
if (newVal.properties != undefined && newVal.properties.edit == true) {
|
||||||
shikigami.value = newVal.properties
|
shikigami.value = newVal.properties
|
||||||
}
|
}
|
||||||
|
console.log("ShikigamiProperty.vue" + current.value.name)
|
||||||
current.value = newVal
|
current.value = newVal
|
||||||
|
console.log("ShikigamiProperty.vue" + current.value.name)
|
||||||
}, {deep: true})
|
}, {deep: true})
|
||||||
|
|
||||||
watch(() => props.showProperty, (newVal) => {
|
watch(() => props.showProperty, (newVal) => {
|
||||||
|
@ -1,6 +1,11 @@
|
|||||||
<template>
|
<template>
|
||||||
<el-dialog v-model="show" :visable.sync="show" title="请选择式神" @cancel="cancel" :before-close="cancel">
|
<el-dialog
|
||||||
<span>当前选择:{{current.name}}</span>
|
v-model="show"
|
||||||
|
title="请选择式神"
|
||||||
|
@close="cancel"
|
||||||
|
:before-close="cancel"
|
||||||
|
>
|
||||||
|
<span>当前选择式神:{{ current.name }}</span>
|
||||||
<el-tabs
|
<el-tabs
|
||||||
v-model="activeName"
|
v-model="activeName"
|
||||||
type="card"
|
type="card"
|
||||||
@ -13,127 +18,91 @@
|
|||||||
:label="rarity.label"
|
:label="rarity.label"
|
||||||
:name="rarity.name"
|
:name="rarity.name"
|
||||||
>
|
>
|
||||||
<div v-if="rarityLevels.includes(rarity)"> <!-- 只在这些级别中显示内容 -->
|
<div style="max-height: 600px; overflow-y: auto;">
|
||||||
<el-space wrap size="large">
|
<el-space wrap size="large">
|
||||||
<div v-for="i in filterShikigamiByRarity(rarity.name)" :key="i.name">
|
<div v-for="i in filterShikigamiByRarity(rarity.name)" :key="i.name">
|
||||||
<el-button style="width: 100px; height: 100px;" @click.stop="confirm(i)">
|
<el-button
|
||||||
|
style="width: 100px; height: 100px;"
|
||||||
|
@click.stop="confirm(i)"
|
||||||
|
>
|
||||||
<img :src="i.avatar" style="width: 99px; height: 99px;">
|
<img :src="i.avatar" style="width: 99px; height: 99px;">
|
||||||
</el-button>
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
</el-space>
|
</el-space>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
</el-tabs>
|
</el-tabs>
|
||||||
|
|
||||||
<!-- <template #footer>-->
|
|
||||||
<!-- <span class="dialog-footer">-->
|
|
||||||
<!-- <el-button @click.stop="cancel">Cancel</el-button>-->
|
|
||||||
<!-- <el-button type="primary" @click.stop="confirm"> Confirm </el-button>-->
|
|
||||||
<!-- </span>-->
|
|
||||||
<!-- </template>-->
|
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script setup lang="ts">
|
||||||
import shikigamiData from "../data/Shikigami.json";
|
import { ref, watch, computed } from 'vue'
|
||||||
import { ref } from "vue";
|
|
||||||
import type { TabsPaneContext } from 'element-plus'
|
import type { TabsPaneContext } from 'element-plus'
|
||||||
|
import shikigamiData from "../data/Shikigami.json"
|
||||||
|
|
||||||
|
interface Shikigami {
|
||||||
const activeName = ref('ALL')
|
name: string
|
||||||
|
avatar: string
|
||||||
const handleClick = (tab: TabsPaneContext, event: Event) => {
|
rarity: string
|
||||||
console.log(tab, event)
|
|
||||||
}
|
}
|
||||||
export default {
|
|
||||||
props: {
|
const props = defineProps({
|
||||||
currentShikigami: {
|
currentShikigami: {
|
||||||
type: Object,
|
type: Object as () => Shikigami,
|
||||||
default: {},
|
default: () => ({ name: '' })
|
||||||
},
|
},
|
||||||
showSelectShikigami: {
|
showSelectShikigami: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
},
|
default: false
|
||||||
},
|
}
|
||||||
|
})
|
||||||
|
|
||||||
data() {
|
const emit = defineEmits(['closeSelectShikigami', 'updateShikigami'])
|
||||||
return {
|
|
||||||
activeName:activeName,
|
|
||||||
shikigamiData: shikigamiData,
|
|
||||||
selected: null,
|
|
||||||
current: {},
|
|
||||||
show: false,
|
|
||||||
rarityLevels: [
|
|
||||||
{
|
|
||||||
"label":"全部",
|
|
||||||
"name":"ALL"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"label":"SP",
|
|
||||||
"name":"SP"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"label":"SSR",
|
|
||||||
"name":"SSR"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"label":"SR",
|
|
||||||
"name":"SR"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"label":"R",
|
|
||||||
"name":"R"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"label":"N",
|
|
||||||
"name":"N"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"label":"联动",
|
|
||||||
"name":"L"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"label":"呱太",
|
|
||||||
"name":"G"
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
showSelectShikigami(newVal, oldVal) {
|
|
||||||
console.log("=======>>>> ", newVal, oldVal);
|
|
||||||
this.show = newVal;
|
|
||||||
},
|
|
||||||
currentShikigami(newVal, oldVal) {
|
|
||||||
console.log("===item====>>>> ", newVal, oldVal);
|
|
||||||
this.current = newVal;
|
|
||||||
},
|
|
||||||
|
|
||||||
},
|
const activeName = ref('ALL')
|
||||||
methods: {
|
let current = ref({name:''})
|
||||||
select(item) {
|
const show = ref(false)
|
||||||
this.current = item;
|
|
||||||
},
|
const rarityLevels = [
|
||||||
cancel() {
|
{ label: "全部", name: "ALL" },
|
||||||
console.log("cancel====");
|
{ label: "SP", name: "SP" },
|
||||||
this.$emit("closeSelectShikigami");
|
{ label: "SSR", name: "SSR" },
|
||||||
},
|
{ label: "SR", name: "SR" },
|
||||||
confirm(i) {
|
{ label: "R", name: "R" },
|
||||||
console.log("confirm====");
|
{ label: "N", name: "N" },
|
||||||
this.$emit("updateShikigami", JSON.parse(JSON.stringify(i)))
|
{ label: "联动", name: "L" },
|
||||||
// this.current = {};
|
{ label: "呱太", name: "G" },
|
||||||
},
|
]
|
||||||
filterShikigamiByRarity(rarity) {
|
|
||||||
if(rarity.toLowerCase() == "all")
|
watch(() => props.showSelectShikigami, (newVal) => {
|
||||||
return this.shikigamiData
|
show.value = newVal
|
||||||
// 将传入的rarity参数转换为小写
|
})
|
||||||
const lowerCaseRarity = rarity.toLowerCase();
|
|
||||||
return this.shikigamiData.filter(shikigami =>
|
watch(() => props.currentShikigami, (newVal) => {
|
||||||
// 将shikigami对象的rarity属性也转换为小写进行比较
|
console.log("ShikigamiSelect.vue" + current.value.name)
|
||||||
shikigami.rarity.toLowerCase() === lowerCaseRarity
|
current.value = newVal
|
||||||
);
|
console.log("ShikigamiSelect.vue" + current.value.name)
|
||||||
},
|
}, {deep: true})
|
||||||
},
|
|
||||||
};
|
|
||||||
|
const handleClick = (tab: TabsPaneContext) => {
|
||||||
|
console.log('Tab clicked:', tab)
|
||||||
|
}
|
||||||
|
|
||||||
|
const cancel = () => {
|
||||||
|
emit('closeSelectShikigami')
|
||||||
|
show.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
const confirm = (shikigami: Shikigami) => {
|
||||||
|
emit('updateShikigami', shikigami)
|
||||||
|
// cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
const filterShikigamiByRarity = (rarity: string) => {
|
||||||
|
if (rarity.toLowerCase() === 'all') return shikigamiData
|
||||||
|
return shikigamiData.filter(item =>
|
||||||
|
item.rarity.toLowerCase() === rarity.toLowerCase()
|
||||||
|
)
|
||||||
|
}
|
||||||
</script>
|
</script>
|
139
src/components/Toolbar.vue
Normal file
139
src/components/Toolbar.vue
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
<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>
|
||||||
|
<el-button icon="Share" type="primary" @click="prepareCapture">{{ t('prepareCapture') }}</el-button>
|
||||||
|
<el-button icon="Setting" type="primary" @click="prepareCapture">{{ t('setWatermark') }}</el-button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 预览弹窗 -->
|
||||||
|
<el-dialog id="preview-container" v-model="state.previewVisible" width="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">
|
||||||
|
<el-button @click="state.previewVisible = false">取 消</el-button>
|
||||||
|
<el-button type="primary" @click="downloadImage">下 载</el-button>
|
||||||
|
</span>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import {inject, nextTick} from 'vue';
|
||||||
|
import html2canvas from "html2canvas";
|
||||||
|
import {useI18n} from 'vue-i18n'
|
||||||
|
import Watermark from './Watermark.vue' // 引入 Watermark 组件
|
||||||
|
|
||||||
|
// 获取当前的 i18n 实例
|
||||||
|
const {t} = useI18n()
|
||||||
|
|
||||||
|
// 注入水印控制方法
|
||||||
|
const watermarkControl = inject('watermarkControl');
|
||||||
|
|
||||||
|
defineProps({
|
||||||
|
title: {
|
||||||
|
type: String,
|
||||||
|
default: '默认标题'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
import {ref, reactive, toRefs} from 'vue';
|
||||||
|
|
||||||
|
// 定义响应式数据
|
||||||
|
const state = reactive({
|
||||||
|
previewImage: null, // 用于存储预览图像的数据URL
|
||||||
|
previewVisible: false, // 控制预览弹窗的显示状态
|
||||||
|
});
|
||||||
|
|
||||||
|
const ignoreElements = (element) => {
|
||||||
|
return element.classList.contains('ql-toolbar');
|
||||||
|
}
|
||||||
|
const prepareCapture = async () => {
|
||||||
|
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)
|
||||||
|
// 捕获页面元素并生成图片
|
||||||
|
try {
|
||||||
|
const element = document.querySelector('#main-container'); // 替换为要捕获的元素选择器
|
||||||
|
if (!element) {
|
||||||
|
console.error('Element with ID "main-container" not found.');
|
||||||
|
state.previewVisible = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const canvas = await html2canvas(element, {
|
||||||
|
ignoreElements: ignoreElements,
|
||||||
|
height: element.scrollHeight,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
state.previewImage = canvas.toDataURL();
|
||||||
|
if (!state.previewImage) {
|
||||||
|
console.error('Failed to generate image data URL.');
|
||||||
|
state.previewVisible = false;
|
||||||
|
state.previewVisible = false;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to capture screenshot', error);
|
||||||
|
state.previewVisible = false;
|
||||||
|
} finally {
|
||||||
|
// 清除临时样式
|
||||||
|
document.getElementById('capture-style')?.remove()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const downloadImage = () => {
|
||||||
|
if (state.previewImage) {
|
||||||
|
const link = document.createElement('a');
|
||||||
|
link.href = state.previewImage;
|
||||||
|
link.download = 'screenshot.png'; // 设置下载的文件名
|
||||||
|
link.click();
|
||||||
|
state.previewVisible = false; // 关闭预览弹窗
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleClose = (done) => {
|
||||||
|
state.previewImage = null; // 清除预览图像
|
||||||
|
done(); // 关闭弹窗
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.toolbar {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
height: 48px;
|
||||||
|
background: #f8f8f8;
|
||||||
|
//border-bottom: 1px solid #eee; display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0 8px;
|
||||||
|
z-index: 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
flex-grow: 1;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.left, .right {
|
||||||
|
flex-basis: 120px;
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
</style>
|
@ -12,11 +12,7 @@
|
|||||||
@closeProperty="closeProperty"
|
@closeProperty="closeProperty"
|
||||||
@updateProperty="updateProperty"
|
@updateProperty="updateProperty"
|
||||||
/>
|
/>
|
||||||
<!-- 现有的代码 -->
|
|
||||||
<div style="margin: 20px" data-html2canvas-ignore="true">
|
|
||||||
<!-- 触发截图的按钮 -->
|
|
||||||
<el-button type="primary" @click="prepareCapture">{{ t('prepareCapture') }}</el-button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<draggable :list="state.groups" item-key="group" style="display: flex; flex-direction: column; width: 100%;"
|
<draggable :list="state.groups" item-key="group" style="display: flex; flex-direction: column; width: 100%;"
|
||||||
handle=".drag-handle">
|
handle=".drag-handle">
|
||||||
@ -125,17 +121,7 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<!-- 预览弹窗 -->
|
|
||||||
<el-dialog id="preview-container" v-model="state.previewVisible" width="80%" :before-close="handleClose">
|
|
||||||
<div style="max-height: 500px; overflow-y: auto;">
|
|
||||||
<img v-if="state.previewImage" :src="state.previewImage" alt="Preview" style="width: 100%; display: block;"/>
|
|
||||||
</div>
|
|
||||||
<!-- <img v-if="state.previewImage" :src="state.previewImage" alt="Preview" style="width: 100%; height: auto;" />-->
|
|
||||||
<span slot="footer" class="dialog-footer">
|
|
||||||
<el-button @click="state.previewVisible = false">取 消</el-button>
|
|
||||||
<el-button type="primary" @click="downloadImage">下 载</el-button>
|
|
||||||
</span>
|
|
||||||
</el-dialog>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -224,6 +210,7 @@ const editShikigami = (groupIndex, positionIndex) => {
|
|||||||
state.showSelectShikigami = true;
|
state.showSelectShikigami = true;
|
||||||
state.groupIndex = groupIndex;
|
state.groupIndex = groupIndex;
|
||||||
state.positionIndex = positionIndex;
|
state.positionIndex = positionIndex;
|
||||||
|
state.currentShikigami = state.groups[groupIndex].groupInfo[positionIndex];
|
||||||
};
|
};
|
||||||
|
|
||||||
const updateShikigami = (shikigami) => {
|
const updateShikigami = (shikigami) => {
|
||||||
@ -274,65 +261,11 @@ const addGroupElement = (groupIndex) => {
|
|||||||
state.groups[groupIndex].groupInfo.push({});
|
state.groups[groupIndex].groupInfo.push({});
|
||||||
};
|
};
|
||||||
|
|
||||||
const ignoreElements = (element) => {
|
|
||||||
return element.classList.contains('ql-toolbar');
|
|
||||||
}
|
|
||||||
|
|
||||||
const prepareCapture = async () => {
|
|
||||||
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)
|
|
||||||
// 捕获页面元素并生成图片
|
|
||||||
try {
|
|
||||||
const element = document.querySelector('#main-container'); // 替换为要捕获的元素选择器
|
|
||||||
if (!element) {
|
|
||||||
console.error('Element with ID "main-container" not found.');
|
|
||||||
state.previewVisible = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const canvas = await html2canvas(element, {
|
|
||||||
ignoreElements: ignoreElements,
|
|
||||||
height: element.scrollHeight,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
state.previewImage = canvas.toDataURL();
|
|
||||||
if (!state.previewImage) {
|
|
||||||
console.error('Failed to generate image data URL.');
|
|
||||||
state.previewVisible = false;
|
|
||||||
state.previewVisible = false;
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Failed to capture screenshot', error);
|
|
||||||
state.previewVisible = false;
|
|
||||||
}finally {
|
|
||||||
// 清除临时样式
|
|
||||||
document.getElementById('capture-style')?.remove()
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const downloadImage = () => {
|
|
||||||
if (state.previewImage) {
|
|
||||||
const link = document.createElement('a');
|
|
||||||
link.href = state.previewImage;
|
|
||||||
link.download = 'screenshot.png'; // 设置下载的文件名
|
|
||||||
link.click();
|
|
||||||
state.previewVisible = false; // 关闭预览弹窗
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleClose = (done) => {
|
|
||||||
state.previewImage = null; // 清除预览图像
|
|
||||||
done(); // 关闭弹窗
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
{
|
{
|
||||||
|
"import": "导入",
|
||||||
|
"export": "导出",
|
||||||
|
"setWatermark": "设置水印",
|
||||||
"Copy": "复制",
|
"Copy": "复制",
|
||||||
"Paste": "粘贴",
|
"Paste": "粘贴",
|
||||||
"editProperties": "配置属性",
|
"editProperties": "配置属性",
|
||||||
|
Reference in New Issue
Block a user