mirror of
https://github.com/palxiao/poster-design.git
synced 2025-07-15 16:02:19 +08:00
Merge pull request #99 from JeremyYu-cn/feat-upgrade-vue3
Feat convert wrap components to composition API
This commit is contained in:
commit
4d4026492e
@ -73,6 +73,7 @@ export const getFontSub = (params: TGetFontSubParam, extra: TGetFontSubExtra = {
|
|||||||
|
|
||||||
type TGetImageListParams = {
|
type TGetImageListParams = {
|
||||||
page?: number
|
page?: number
|
||||||
|
pageSize?: number
|
||||||
cate?: number
|
cate?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,6 +14,7 @@ export type TStyleIconData = {
|
|||||||
tip: string
|
tip: string
|
||||||
value: string[]
|
value: string[]
|
||||||
select: boolean
|
select: boolean
|
||||||
|
extraIcon?: boolean,
|
||||||
}
|
}
|
||||||
|
|
||||||
export const styleIconList1 = [
|
export const styleIconList1 = [
|
||||||
|
@ -13,7 +13,8 @@ import wImageSetting from '@/components/modules/widgets/wImage/wImageSetting'
|
|||||||
// import wText from '@/components/modules/widgets/wText/wText.vue'
|
// import wText from '@/components/modules/widgets/wText/wText.vue'
|
||||||
import { wTextSetting } from '@/components/modules/widgets/wText/wTextSetting'
|
import { wTextSetting } from '@/components/modules/widgets/wText/wTextSetting'
|
||||||
import wImage from '@/components/modules/widgets/wImage/wImage.vue'
|
import wImage from '@/components/modules/widgets/wImage/wImage.vue'
|
||||||
import wSvg from '@/components/modules/widgets/wSvg/wSvg.vue'
|
// import wSvg from '@/components/modules/widgets/wSvg/wSvg.vue'
|
||||||
|
import { wSvgSetting } from '@/components/modules/widgets/wSvg/wSvgSetting'
|
||||||
|
|
||||||
export default async function(type: string, item: TCommonItemData, data: Record<string, any>) {
|
export default async function(type: string, item: TCommonItemData, data: Record<string, any>) {
|
||||||
let setting = data
|
let setting = data
|
||||||
@ -36,7 +37,7 @@ export default async function(type: string, item: TCommonItemData, data: Record<
|
|||||||
setting.mask = item.value.url
|
setting.mask = item.value.url
|
||||||
}
|
}
|
||||||
if (type === 'svg') {
|
if (type === 'svg') {
|
||||||
setting = JSON.parse(JSON.stringify(wSvg.setting))
|
setting = JSON.parse(JSON.stringify(wSvgSetting))
|
||||||
const img = await setImageData(item.value)
|
const img = await setImageData(item.value)
|
||||||
setting.width = img.width
|
setting.width = img.width
|
||||||
setting.height = img.height // parseInt(100 / item.value.ratio, 10)
|
setting.height = img.height // parseInt(100 / item.value.ratio, 10)
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
-->
|
-->
|
||||||
<template>
|
<template>
|
||||||
<div class="wrap">
|
<div class="wrap">
|
||||||
<search-header v-model="searchKeyword" type="none" @change="searchChange" />
|
<search-header v-model="state.searchKeyword" type="none" @change="searchChange" />
|
||||||
<div style="height: 0.5rem" />
|
<div style="height: 0.5rem" />
|
||||||
<!-- <div class="types">
|
<!-- <div class="types">
|
||||||
<div v-for="(t, ti) in types" :key="ti + 't'" :style="{ backgroundColor: colors[ti] }" :class="['types__item', { 'types--select': currentType === t.id }]" @click="selectType(t)"></div>
|
<div v-for="(t, ti) in types" :key="ti + 't'" :style="{ backgroundColor: colors[ti] }" :class="['types__item', { 'types--select': currentType === t.id }]" @click="selectType(t)"></div>
|
||||||
@ -15,183 +15,187 @@
|
|||||||
<!-- <div class="tags">
|
<!-- <div class="tags">
|
||||||
<el-check-tag v-for="(t2, t2i) in sub" :key="t2i + 't2'" :checked="t2.id === currentCheck" class="tags__item" @click="tagsChange(t2.id)">{{ t2.name }}</el-check-tag>
|
<el-check-tag v-for="(t2, t2i) in sub" :key="t2i + 't2'" :checked="t2.id === currentCheck" class="tags__item" @click="tagsChange(t2.id)">{{ t2.name }}</el-check-tag>
|
||||||
</div> -->
|
</div> -->
|
||||||
<classHeader v-show="!currentCategory" :types="types" @select="selectTypes">
|
<classHeader v-show="!state.currentCategory" :types="state.types" @select="selectTypes">
|
||||||
<template v-slot="{ index }">
|
<template v-slot="{ index }">
|
||||||
<div class="list-wrap">
|
<div class="list-wrap">
|
||||||
<div v-for="(item, i) in showList[index]" :key="i + 'sl'" draggable="false" @mousedown="dragStart($event, item)" @mousemove="mousemove" @mouseup="mouseup" @click.stop="selectItem(item)" @dragstart="dragStart($event, item)">
|
<div v-for="(item, i) in state.showList[index]" :key="i + 'sl'" draggable="false" @mousedown="dragStart($event, item)" @mousemove="mousemove" @mouseup="mouseup" @click.stop="selectItem(item)" @dragstart="dragStart($event, item)">
|
||||||
<el-image class="list__img-thumb" :src="item.thumb" fit="contain" lazy loading="lazy" />
|
<el-image class="list__img-thumb" :src="item.thumb" fit="contain" lazy loading="lazy" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</classHeader>
|
</classHeader>
|
||||||
|
|
||||||
<ul v-if="currentCategory" v-infinite-scroll="load" class="infinite-list" :infinite-scroll-distance="150" style="overflow: auto">
|
<ul v-if="state.currentCategory" v-infinite-scroll="load" class="infinite-list" :infinite-scroll-distance="150" style="overflow: auto">
|
||||||
<classHeader :is-back="true" @back="back">{{ currentCategory.name }}</classHeader>
|
<classHeader :is-back="true" @back="back">{{ state.currentCategory.name }}</classHeader>
|
||||||
<el-space fill wrap :fillRatio="30" direction="horizontal" class="list">
|
<el-space fill wrap :fillRatio="30" direction="horizontal" class="list">
|
||||||
<div v-for="(item, i) in list" :key="i + 'i'" class="list__item" draggable="false" @mousedown="dragStart($event, item)" @mousemove="mousemove" @mouseup="mouseup" @click.stop="selectItem(item)" @dragstart="dragStart($event, item)">
|
<div v-for="(item, i) in state.list" :key="i + 'i'" class="list__item" draggable="false" @mousedown="dragStart($event, item)" @mousemove="mousemove" @mouseup="mouseup" @click.stop="selectItem(item)" @dragstart="dragStart($event, item)">
|
||||||
<el-image class="list__img" :src="item.thumb" fit="contain" lazy loading="lazy" />
|
<el-image class="list__img" :src="item.thumb" fit="contain" lazy loading="lazy" />
|
||||||
</div>
|
</div>
|
||||||
</el-space>
|
</el-space>
|
||||||
<div v-show="loading" class="loading"><i class="el-icon-loading" /> 拼命加载中</div>
|
<div v-show="state.loading" class="loading"><i class="el-icon-loading" /> 拼命加载中</div>
|
||||||
<div v-show="loadDone" :style="list.length <= 0 ? 'padding-top: 4rem' : ''" class="loading">全部加载完毕</div>
|
<div v-show="state.loadDone" :style="state.list.length <= 0 ? 'padding-top: 4rem' : ''" class="loading">全部加载完毕</div>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts" setup>
|
||||||
import { defineComponent, reactive, toRefs, onMounted, watch } from 'vue'
|
import { reactive, onMounted } from 'vue'
|
||||||
import api from '@/api'
|
import api from '@/api'
|
||||||
// import wImage from '../../widgets/wImage/wImage.vue'
|
// import wImage from '../../widgets/wImage/wImage.vue'
|
||||||
import wImageSetting from '../../widgets/wImage/wImageSetting'
|
import wImageSetting from '../../widgets/wImage/wImageSetting'
|
||||||
import wSvg from '../../widgets/wSvg/wSvg.vue'
|
// import wSvg from '../../widgets/wSvg/wSvg.vue'
|
||||||
import { mapActions, mapGetters } from 'vuex'
|
import { wSvgSetting } from '../../widgets/wSvg/wSvgSetting'
|
||||||
|
import { useStore } from 'vuex'
|
||||||
import setImageData from '@/common/methods/DesignFeatures/setImage'
|
import setImageData from '@/common/methods/DesignFeatures/setImage'
|
||||||
import DragHelper from '@/common/hooks/dragHelper'
|
import DragHelper from '@/common/hooks/dragHelper'
|
||||||
|
import { useSetupMapGetters } from '@/common/hooks/mapGetters'
|
||||||
|
|
||||||
|
type TProps = {
|
||||||
|
active?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
let isDrag = false
|
let isDrag = false
|
||||||
let startPoint = { x: 99999, y: 99999 }
|
let startPoint = { x: 99999, y: 99999 }
|
||||||
const dragHelper = new DragHelper()
|
const dragHelper = new DragHelper()
|
||||||
|
|
||||||
export default defineComponent({
|
const prpos = defineProps<TProps>()
|
||||||
components: {},
|
const colors = ['#f8704b', '#5b89ff', '#2cc4cc', '#a8ba73', '#f8704b']
|
||||||
props: ['active'],
|
const state: any = reactive({
|
||||||
setup(props) {
|
loading: false,
|
||||||
const colors = ['#f8704b', '#5b89ff', '#2cc4cc', '#a8ba73', '#f8704b']
|
loadDone: false,
|
||||||
const state: any = reactive({
|
sub: [],
|
||||||
loading: false,
|
list: [],
|
||||||
loadDone: false,
|
currentType: 2, // 2
|
||||||
sub: [],
|
currentCheck: 0,
|
||||||
list: [],
|
colors,
|
||||||
currentType: 2, // 2
|
currentCategory: null,
|
||||||
currentCheck: 0,
|
types: [],
|
||||||
colors,
|
showList: [],
|
||||||
currentCategory: null,
|
searchKeyword: '',
|
||||||
types: [],
|
|
||||||
showList: [],
|
|
||||||
searchKeyword: '',
|
|
||||||
})
|
|
||||||
const pageOptions = { page: 0, pageSize: 20 }
|
|
||||||
|
|
||||||
onMounted(async () => {
|
|
||||||
if (state.types.length <= 0) {
|
|
||||||
const types = await api.material.getKinds({ type: 2 })
|
|
||||||
state.types = types
|
|
||||||
for (const iterator of types) {
|
|
||||||
const { list } = await api.material.getList({
|
|
||||||
cate: iterator.id,
|
|
||||||
pageSize: 3,
|
|
||||||
})
|
|
||||||
state.showList.push(list)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
// const dragHelper = new DragHelper()
|
|
||||||
// let isDrag = false
|
|
||||||
// let startPoint = { x: 99999, y: 99999 }
|
|
||||||
const mouseup = (e: any) => {
|
|
||||||
e.preventDefault()
|
|
||||||
setTimeout(() => {
|
|
||||||
isDrag = false
|
|
||||||
startPoint = { x: 99999, y: 99999 }
|
|
||||||
}, 10)
|
|
||||||
}
|
|
||||||
const mousemove = (e: any) => {
|
|
||||||
e.preventDefault()
|
|
||||||
if (e.x - startPoint.x > 2 || e.y - startPoint.y > 2) {
|
|
||||||
isDrag = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const load = async (init: boolean = false) => {
|
|
||||||
if (init) {
|
|
||||||
state.list = []
|
|
||||||
pageOptions.page = 0
|
|
||||||
state.loadDone = false
|
|
||||||
}
|
|
||||||
if (state.loadDone || state.loading) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
state.loading = true
|
|
||||||
pageOptions.page += 1
|
|
||||||
const list = await api.material.getList({
|
|
||||||
...{ cate: state.currentCategory?.id, search: state.searchKeyword, ...pageOptions },
|
|
||||||
})
|
|
||||||
if (init) {
|
|
||||||
state.list = list?.list
|
|
||||||
} else {
|
|
||||||
state.list = state.list.concat(list?.list)
|
|
||||||
}
|
|
||||||
list?.list.length <= 0 && (state.loadDone = true)
|
|
||||||
setTimeout(() => {
|
|
||||||
state.loading = false
|
|
||||||
}, 100)
|
|
||||||
}
|
|
||||||
|
|
||||||
const searchChange = (e: any) => {
|
|
||||||
state.currentCategory = { name: '搜索结果' }
|
|
||||||
load(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
const selectTypes = (item: any) => {
|
|
||||||
state.currentCategory = item
|
|
||||||
load(true)
|
|
||||||
}
|
|
||||||
const back = () => {
|
|
||||||
state.currentCategory = null
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
...toRefs(state),
|
|
||||||
load,
|
|
||||||
searchChange,
|
|
||||||
selectTypes,
|
|
||||||
back,
|
|
||||||
mouseup,
|
|
||||||
mousemove,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
...mapGetters(['dPage']),
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
...mapActions(['addWidget']),
|
|
||||||
async selectItem(item: any) {
|
|
||||||
if (isDrag) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
this.$store.commit('setShowMoveable', false) // 清理掉上一次的选择
|
|
||||||
let setting = item.type === 'svg' ? JSON.parse(JSON.stringify(wSvg.setting)) : JSON.parse(JSON.stringify(wImageSetting))
|
|
||||||
const img: any = await setImageData(item)
|
|
||||||
|
|
||||||
setting.width = img.width
|
|
||||||
setting.height = img.height // parseInt(100 / item.value.ratio, 10)
|
|
||||||
const { width: pW, height: pH } = this.dPage
|
|
||||||
setting.left = pW / 2 - img.width / 2
|
|
||||||
setting.top = pH / 2 - img.height / 2
|
|
||||||
setting.imgUrl = item.url
|
|
||||||
if (item.type === 'svg') {
|
|
||||||
setting.svgUrl = item.url
|
|
||||||
const models = JSON.parse(item.model)
|
|
||||||
for (const key in models) {
|
|
||||||
if (Object.hasOwnProperty.call(models, key)) {
|
|
||||||
setting[key] = models[key]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (item.type === 'mask') {
|
|
||||||
setting.mask = item.url
|
|
||||||
}
|
|
||||||
this.addWidget(setting)
|
|
||||||
},
|
|
||||||
async dragStart(e: any, item: any) {
|
|
||||||
startPoint = { x: e.x, y: e.y }
|
|
||||||
const { width, height, thumb, url } = item
|
|
||||||
const img = await setImageData({ width, height, url: thumb || url })
|
|
||||||
dragHelper.start(e, img.canvasWidth)
|
|
||||||
this.$store.commit('selectItem', { data: { value: item }, type: item.type })
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
|
const pageOptions = { page: 0, pageSize: 20 }
|
||||||
|
const store = useStore()
|
||||||
|
const {
|
||||||
|
dPage
|
||||||
|
} = useSetupMapGetters(['dPage'])
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
if (state.types.length <= 0) {
|
||||||
|
const types = await api.material.getKinds({ type: 2 })
|
||||||
|
state.types = types
|
||||||
|
for (const iterator of types) {
|
||||||
|
const { list } = await api.material.getList({
|
||||||
|
cate: iterator.id,
|
||||||
|
pageSize: 3,
|
||||||
|
})
|
||||||
|
state.showList.push(list)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
// const dragHelper = new DragHelper()
|
||||||
|
// let isDrag = false
|
||||||
|
// let startPoint = { x: 99999, y: 99999 }
|
||||||
|
const mouseup = (e: any) => {
|
||||||
|
e.preventDefault()
|
||||||
|
setTimeout(() => {
|
||||||
|
isDrag = false
|
||||||
|
startPoint = { x: 99999, y: 99999 }
|
||||||
|
}, 10)
|
||||||
|
}
|
||||||
|
const mousemove = (e: any) => {
|
||||||
|
e.preventDefault()
|
||||||
|
if (e.x - startPoint.x > 2 || e.y - startPoint.y > 2) {
|
||||||
|
isDrag = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const load = async (init: boolean = false) => {
|
||||||
|
if (init) {
|
||||||
|
state.list = []
|
||||||
|
pageOptions.page = 0
|
||||||
|
state.loadDone = false
|
||||||
|
}
|
||||||
|
if (state.loadDone || state.loading) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
state.loading = true
|
||||||
|
pageOptions.page += 1
|
||||||
|
const list = await api.material.getList({
|
||||||
|
...{ cate: state.currentCategory?.id, search: state.searchKeyword, ...pageOptions },
|
||||||
|
})
|
||||||
|
if (init) {
|
||||||
|
state.list = list?.list
|
||||||
|
} else {
|
||||||
|
state.list = state.list.concat(list?.list)
|
||||||
|
}
|
||||||
|
list?.list.length <= 0 && (state.loadDone = true)
|
||||||
|
setTimeout(() => {
|
||||||
|
state.loading = false
|
||||||
|
}, 100)
|
||||||
|
}
|
||||||
|
|
||||||
|
const searchChange = (e: any) => {
|
||||||
|
state.currentCategory = { name: '搜索结果' }
|
||||||
|
load(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
const selectTypes = (item: any) => {
|
||||||
|
state.currentCategory = item
|
||||||
|
load(true)
|
||||||
|
}
|
||||||
|
const back = () => {
|
||||||
|
state.currentCategory = null
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
load,
|
||||||
|
searchChange,
|
||||||
|
selectTypes,
|
||||||
|
back,
|
||||||
|
mouseup,
|
||||||
|
mousemove,
|
||||||
|
})
|
||||||
|
|
||||||
|
// ...mapGetters(['dPage'])
|
||||||
|
|
||||||
|
// ...mapActions(['addWidget'])
|
||||||
|
|
||||||
|
async function selectItem(item: any) {
|
||||||
|
if (isDrag) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
store.commit('setShowMoveable', false) // 清理掉上一次的选择
|
||||||
|
let setting = item.type === 'svg' ? JSON.parse(JSON.stringify(wSvgSetting)) : JSON.parse(JSON.stringify(wImageSetting))
|
||||||
|
const img: any = await setImageData(item)
|
||||||
|
|
||||||
|
setting.width = img.width
|
||||||
|
setting.height = img.height // parseInt(100 / item.value.ratio, 10)
|
||||||
|
const { width: pW, height: pH } = dPage.value
|
||||||
|
setting.left = pW / 2 - img.width / 2
|
||||||
|
setting.top = pH / 2 - img.height / 2
|
||||||
|
setting.imgUrl = item.url
|
||||||
|
if (item.type === 'svg') {
|
||||||
|
setting.svgUrl = item.url
|
||||||
|
const models = JSON.parse(item.model)
|
||||||
|
for (const key in models) {
|
||||||
|
if (Object.hasOwnProperty.call(models, key)) {
|
||||||
|
setting[key] = models[key]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (item.type === 'mask') {
|
||||||
|
setting.mask = item.url
|
||||||
|
}
|
||||||
|
store.dispatch("addWidget", setting)
|
||||||
|
// addWidget(setting)
|
||||||
|
}
|
||||||
|
async function dragStart(e: any, item: any) {
|
||||||
|
startPoint = { x: e.x, y: e.y }
|
||||||
|
const { width, height, thumb, url } = item
|
||||||
|
const img = await setImageData({ width, height, url: thumb || url })
|
||||||
|
dragHelper.start(e, img.canvasWidth)
|
||||||
|
store.commit('selectItem', { data: { value: item }, type: item.type })
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
|
@ -9,22 +9,22 @@
|
|||||||
<div class="wrap">
|
<div class="wrap">
|
||||||
<search-header type="none" @change="searchChange" />
|
<search-header type="none" @change="searchChange" />
|
||||||
<div style="height: 0.5rem" />
|
<div style="height: 0.5rem" />
|
||||||
<classHeader v-show="!currentCategory" :types="types" @select="selectTypes">
|
<classHeader v-show="!state.currentCategory" :types="state.types" @select="selectTypes">
|
||||||
<template v-slot="{ index }">
|
<template v-slot="{ index }">
|
||||||
<photo-list :isShort="true" :listData="showList[index]" @load="getDataList" @drag="dragStart($event, showList[index])" @select="selectImg($event, showList[index])" />
|
<photo-list :isShort="true" :listData="state.showList[index]" @load="getDataList" @drag="dragStart($event, state.showList[index])" @select="selectImg($event, state.showList[index])" />
|
||||||
</template>
|
</template>
|
||||||
</classHeader>
|
</classHeader>
|
||||||
<div v-if="currentCategory">
|
<div v-if="state.currentCategory">
|
||||||
<classHeader :is-back="true" @back="back">{{ currentCategory.name }}</classHeader>
|
<classHeader :is-back="true" @back="back">{{ state.currentCategory.name }}</classHeader>
|
||||||
<br /><br /><br />
|
<br /><br /><br />
|
||||||
<div style="margin: 0 1rem; height: 100vh">
|
<div style="margin: 0 1rem; height: 100vh">
|
||||||
<photo-list :isDone="loadDone" :listData="recommendImgList" @load="getDataList" @drag="dragStart" @select="selectImg" />
|
<photo-list :isDone="state.loadDone" :listData="state.recommendImgList" @load="getDataList" @drag="dragStart" @select="selectImg" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script lang="ts" setup>
|
||||||
// 图片列表
|
// 图片列表
|
||||||
const NAME = 'img-list-wrap'
|
const NAME = 'img-list-wrap'
|
||||||
import { toRefs, reactive, computed, onMounted } from 'vue'
|
import { toRefs, reactive, computed, onMounted } from 'vue'
|
||||||
@ -33,93 +33,102 @@ import wImageSetting from '../../widgets/wImage/wImageSetting'
|
|||||||
import api from '@/api'
|
import api from '@/api'
|
||||||
import { useStore } from 'vuex'
|
import { useStore } from 'vuex'
|
||||||
import setImageData from '@/common/methods/DesignFeatures/setImage'
|
import setImageData from '@/common/methods/DesignFeatures/setImage'
|
||||||
|
import { TGetImageListResult } from '@/api/material';
|
||||||
|
|
||||||
export default {
|
type TProps = {
|
||||||
name: NAME,
|
active?: boolean
|
||||||
components: {},
|
|
||||||
props: ['active'],
|
|
||||||
setup() {
|
|
||||||
const store = useStore()
|
|
||||||
const state = reactive({
|
|
||||||
recommendImgList: [],
|
|
||||||
loadDone: false,
|
|
||||||
page: 0,
|
|
||||||
currentCategory: null,
|
|
||||||
types: [],
|
|
||||||
showList: [],
|
|
||||||
})
|
|
||||||
const dPage = computed(() => store.getters.dPage)
|
|
||||||
let loading = false
|
|
||||||
|
|
||||||
onMounted(async () => {
|
|
||||||
if (state.types.length <= 0) {
|
|
||||||
const types = await api.material.getKinds({ type: 4 })
|
|
||||||
state.types = types
|
|
||||||
for (const iterator of types) {
|
|
||||||
const { list } = await api.material.getImagesList({ cate: iterator.id, pageSize: 2 })
|
|
||||||
state.showList.push(list)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const selectImg = async (index, list) => {
|
|
||||||
const item = list ? list[index] : state.recommendImgList[index]
|
|
||||||
store.commit('setShowMoveable', false) // 清理掉上一次的选择
|
|
||||||
let setting = JSON.parse(JSON.stringify(wImageSetting))
|
|
||||||
const img = await setImageData(item) // await getImage(item.url)
|
|
||||||
setting.width = img.width
|
|
||||||
setting.height = img.height // parseInt(100 / item.value.ratio, 10)
|
|
||||||
setting.imgUrl = item.url
|
|
||||||
const { width: pW, height: pH } = dPage
|
|
||||||
setting.left = pW / 2 - img.width / 2
|
|
||||||
setting.top = pH / 2 - img.height / 2
|
|
||||||
store.dispatch('addWidget', setting)
|
|
||||||
}
|
|
||||||
|
|
||||||
const getDataList = async () => {
|
|
||||||
if (state.loadDone || loading) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
loading = true
|
|
||||||
state.page += 1
|
|
||||||
let { list = [], total } = await api.material.getImagesList({ cate: state.currentCategory.id, page: state.page, pageSize: 30 })
|
|
||||||
list.length <= 0 ? (state.loadDone = true) : (state.recommendImgList = state.recommendImgList.concat(list))
|
|
||||||
setTimeout(() => {
|
|
||||||
loading = false
|
|
||||||
}, 100)
|
|
||||||
}
|
|
||||||
|
|
||||||
const dragStart = (index, list) => {
|
|
||||||
const item = list ? list[index] : state.recommendImgList[index]
|
|
||||||
store.commit('selectItem', { data: { value: item }, type: 'image' })
|
|
||||||
}
|
|
||||||
|
|
||||||
const searchChange = (e) => {
|
|
||||||
console.log(e)
|
|
||||||
}
|
|
||||||
|
|
||||||
const selectTypes = (item) => {
|
|
||||||
state.currentCategory = item
|
|
||||||
getDataList()
|
|
||||||
}
|
|
||||||
const back = () => {
|
|
||||||
state.currentCategory = null
|
|
||||||
state.page = 0
|
|
||||||
state.loadDone = false
|
|
||||||
state.recommendImgList = []
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
...toRefs(state),
|
|
||||||
selectImg,
|
|
||||||
getDataList,
|
|
||||||
dragStart,
|
|
||||||
searchChange,
|
|
||||||
selectTypes,
|
|
||||||
back,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type TState = {
|
||||||
|
recommendImgList: TGetImageListResult[],
|
||||||
|
loadDone: boolean,
|
||||||
|
page: 0,
|
||||||
|
currentCategory: null | Record<string, any>,
|
||||||
|
types: [],
|
||||||
|
showList: TGetImageListResult[][],
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = defineProps<TProps>()
|
||||||
|
|
||||||
|
const store = useStore()
|
||||||
|
const state = reactive<TState>({
|
||||||
|
recommendImgList: [],
|
||||||
|
loadDone: false,
|
||||||
|
page: 0,
|
||||||
|
currentCategory: null,
|
||||||
|
types: [],
|
||||||
|
showList: [],
|
||||||
|
})
|
||||||
|
const dPage = computed(() => store.getters.dPage)
|
||||||
|
let loading = false
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
if (state.types.length <= 0) {
|
||||||
|
const types = await api.material.getKinds({ type: 4 })
|
||||||
|
state.types = types
|
||||||
|
for (const iterator of types) {
|
||||||
|
const { list } = await api.material.getImagesList({ cate: iterator.id, pageSize: 2 })
|
||||||
|
state.showList.push(list)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const selectImg = async (index: number, list: TGetImageListResult[]) => {
|
||||||
|
const item = list ? list[index] : state.recommendImgList[index]
|
||||||
|
store.commit('setShowMoveable', false) // 清理掉上一次的选择
|
||||||
|
let setting = JSON.parse(JSON.stringify(wImageSetting))
|
||||||
|
const img = await setImageData(item) // await getImage(item.url)
|
||||||
|
setting.width = img.width
|
||||||
|
setting.height = img.height // parseInt(100 / item.value.ratio, 10)
|
||||||
|
setting.imgUrl = item.url
|
||||||
|
const { width: pW, height: pH } = dPage.value
|
||||||
|
setting.left = pW / 2 - img.width / 2
|
||||||
|
setting.top = pH / 2 - img.height / 2
|
||||||
|
store.dispatch('addWidget', setting)
|
||||||
|
}
|
||||||
|
|
||||||
|
const getDataList = async () => {
|
||||||
|
if (state.loadDone || loading) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
loading = true
|
||||||
|
state.page += 1
|
||||||
|
if (!state.currentCategory) return
|
||||||
|
let { list = [], total } = await api.material.getImagesList({ cate: state.currentCategory.id, page: state.page, pageSize: 30 })
|
||||||
|
list.length <= 0 ? (state.loadDone = true) : (state.recommendImgList = state.recommendImgList.concat(list))
|
||||||
|
setTimeout(() => {
|
||||||
|
loading = false
|
||||||
|
}, 100)
|
||||||
|
}
|
||||||
|
|
||||||
|
const dragStart = (index: number, list?: TGetImageListResult[]) => {
|
||||||
|
const item = list ? list[index] : state.recommendImgList[index]
|
||||||
|
store.commit('selectItem', { data: { value: item }, type: 'image' })
|
||||||
|
}
|
||||||
|
|
||||||
|
const searchChange = (e: Record<string, any>) => {
|
||||||
|
console.log(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
const selectTypes = (item: Record<string, any>) => {
|
||||||
|
state.currentCategory = item
|
||||||
|
getDataList()
|
||||||
|
}
|
||||||
|
const back = () => {
|
||||||
|
state.currentCategory = null
|
||||||
|
state.page = 0
|
||||||
|
state.loadDone = false
|
||||||
|
state.recommendImgList = []
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
selectImg,
|
||||||
|
getDataList,
|
||||||
|
dragStart,
|
||||||
|
searchChange,
|
||||||
|
selectTypes,
|
||||||
|
back,
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
|
@ -42,7 +42,8 @@
|
|||||||
import api from '@/api'
|
import api from '@/api'
|
||||||
import { toRefs, reactive, watch, onMounted, nextTick } from 'vue'
|
import { toRefs, reactive, watch, onMounted, nextTick } from 'vue'
|
||||||
import { ElRadioGroup, ElRadioButton } from 'element-plus'
|
import { ElRadioGroup, ElRadioButton } from 'element-plus'
|
||||||
import wSvg from '@/components/modules/widgets/wSvg/wSvg.vue'
|
// import wSvg from '@/components/modules/widgets/wSvg/wSvg.vue'
|
||||||
|
import {wSvgSetting} from '@/components/modules/widgets/wSvg/wSvgSetting'
|
||||||
import { TGetListResult } from '@/api/material';
|
import { TGetListResult } from '@/api/material';
|
||||||
|
|
||||||
type TProps = {
|
type TProps = {
|
||||||
@ -75,7 +76,7 @@ const state = reactive<TState>({
|
|||||||
|
|
||||||
const select = (value: string = '') => {
|
const select = (value: string = '') => {
|
||||||
state.visiable = false
|
state.visiable = false
|
||||||
const setting = JSON.parse(JSON.stringify(wSvg.setting))
|
const setting = JSON.parse(JSON.stringify(wSvgSetting))
|
||||||
setting.svgUrl = value
|
setting.svgUrl = value
|
||||||
emit('change', setting)
|
emit('change', setting)
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,7 @@ export type TIconItemSelectData = {
|
|||||||
extraIcon?: boolean,
|
extraIcon?: boolean,
|
||||||
tip?: string
|
tip?: string
|
||||||
icon?: string
|
icon?: string
|
||||||
value?: string | number
|
value?: string | number | number[] | string[]
|
||||||
}
|
}
|
||||||
|
|
||||||
type TProps = {
|
type TProps = {
|
||||||
|
@ -4,10 +4,10 @@
|
|||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
:id="params.uuid"
|
:id="params.uuid"
|
||||||
ref="widget"
|
ref="widgetRef"
|
||||||
class="w-svg"
|
class="w-svg"
|
||||||
:style="{
|
:style="{
|
||||||
position,
|
position: state.position,
|
||||||
left: params.left - parent.left + 'px',
|
left: params.left - parent.left + 'px',
|
||||||
top: params.top - parent.top + 'px',
|
top: params.top - parent.top + 'px',
|
||||||
width: params.width + 'px',
|
width: params.width + 'px',
|
||||||
@ -17,300 +17,336 @@
|
|||||||
></div>
|
></div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script lang="ts" setup>
|
||||||
// svg
|
// svg
|
||||||
const NAME = 'w-svg'
|
// const NAME = 'w-svg'
|
||||||
|
import { mapGetters, mapActions, useStore } from 'vuex'
|
||||||
|
import { TWSvgSetting } from './wSvgSetting'
|
||||||
|
import { CSSProperties, computed, nextTick, onBeforeMount, onMounted, onUpdated, reactive, ref, watch } from 'vue';
|
||||||
|
import { useSetupMapGetters } from '@/common/hooks/mapGetters';
|
||||||
|
|
||||||
import { mapGetters, mapActions } from 'vuex'
|
type TProps = {
|
||||||
|
params: TWSvgSetting
|
||||||
|
parent: {
|
||||||
|
left: number
|
||||||
|
top: number
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export default {
|
type TState = {
|
||||||
name: NAME,
|
position: CSSProperties['position'], // 'absolute'relative
|
||||||
setting: {
|
editBoxStyle: CSSProperties,
|
||||||
name: '矢量图形',
|
editBoxs: Record<string, any>,
|
||||||
type: NAME,
|
editingKey: string,
|
||||||
uuid: -1,
|
cropWidgetXY: Record<string, any>, // 裁剪框移动作用
|
||||||
width: 100,
|
attrRecord: Record<string, any>, // 记录可更改的属性
|
||||||
height: 100,
|
svgImg: Record<string, any> | null
|
||||||
colors: [],
|
}
|
||||||
left: 0,
|
|
||||||
top: 0,
|
const props = defineProps<TProps>()
|
||||||
// zoom: 1.5,
|
const state = reactive<TState>({
|
||||||
transform: '',
|
position: 'absolute', // 'absolute'relative
|
||||||
radius: 0,
|
editBoxStyle: {
|
||||||
opacity: 1,
|
transformOrigin: 'center',
|
||||||
parent: '-1',
|
|
||||||
svgUrl: '',
|
|
||||||
setting: [],
|
|
||||||
record: {
|
|
||||||
width: 0,
|
|
||||||
height: 0,
|
|
||||||
minWidth: 10,
|
|
||||||
minHeight: 10,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
props: ['params', 'parent'],
|
editBoxs: {},
|
||||||
data() {
|
editingKey: '',
|
||||||
return {
|
cropWidgetXY: {}, // 裁剪框移动作用
|
||||||
position: 'absolute', // 'absolute'relative
|
attrRecord: {}, // 记录可更改的属性
|
||||||
editBoxStyle: {
|
svgImg: null
|
||||||
transformOrigin: 'center',
|
})
|
||||||
},
|
const store = useStore()
|
||||||
editBoxs: {},
|
// ...mapGetters(['dActiveElement', 'dZoom', 'dMouseXY']),
|
||||||
editingKey: '',
|
const {
|
||||||
cropWidgetXY: {}, // 裁剪框移动作用
|
dActiveElement, dZoom, dMouseXY
|
||||||
attrRecord: {}, // 记录可更改的属性
|
} = useSetupMapGetters(['dActiveElement', 'dZoom', 'dMouseXY'])
|
||||||
|
|
||||||
|
const widgetRef = ref<HTMLElement | null>(null)
|
||||||
|
|
||||||
|
let svgElements: Record<string, any>[] | null = null
|
||||||
|
let viewBox = { width: 0, height: 0 }
|
||||||
|
|
||||||
|
const tZoom = computed(() => {
|
||||||
|
return props.params.zoom
|
||||||
|
})
|
||||||
|
const cropEdit = computed(() => {
|
||||||
|
return props.params.cropEdit
|
||||||
|
})
|
||||||
|
const imgChange = computed(() => {
|
||||||
|
return props.params.imgUrl
|
||||||
|
})
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.params,
|
||||||
|
() => {
|
||||||
|
attrsChange()
|
||||||
|
},
|
||||||
|
{ immediate: true, deep: true }
|
||||||
|
)
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => tZoom.value,
|
||||||
|
async () => {
|
||||||
|
await nextTick()
|
||||||
|
updateRecord()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => imgChange.value,
|
||||||
|
() => {
|
||||||
|
if (!state.svgImg) return
|
||||||
|
state.svgImg.attr({
|
||||||
|
'xlink:href': props.params.imgUrl,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => cropEdit.value,
|
||||||
|
(val) => {
|
||||||
|
const el = document.getElementById(props.params.uuid)
|
||||||
|
if (val) {
|
||||||
|
el?.addEventListener('mousedown', touchstart, false)
|
||||||
|
} else {
|
||||||
|
el?.removeEventListener('mousedown', touchstart, false)
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
computed: {
|
)
|
||||||
...mapGetters(['dActiveElement', 'dZoom', 'dMouseXY']),
|
|
||||||
tZoom() {
|
|
||||||
return this.params.zoom
|
|
||||||
},
|
|
||||||
cropEdit() {
|
|
||||||
return this.params.cropEdit
|
|
||||||
},
|
|
||||||
imgChange() {
|
|
||||||
return this.params.imgUrl
|
|
||||||
},
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
params: {
|
|
||||||
async handler(nval) {
|
|
||||||
this.attrsChange()
|
|
||||||
},
|
|
||||||
immediate: true,
|
|
||||||
deep: true,
|
|
||||||
},
|
|
||||||
async tZoom() {
|
|
||||||
await this.$nextTick()
|
|
||||||
this.updateRecord()
|
|
||||||
},
|
|
||||||
imgChange() {
|
|
||||||
// TODO 更新所有图片
|
|
||||||
this.svgImg.attr({
|
|
||||||
'xlink:href': this.params.imgUrl,
|
|
||||||
})
|
|
||||||
},
|
|
||||||
cropEdit(val) {
|
|
||||||
// TODO 移动事件绑定
|
|
||||||
if (val) {
|
|
||||||
document.getElementById(this.params.uuid).addEventListener('mousedown', this.touchstart, false)
|
|
||||||
} else {
|
|
||||||
document.getElementById(this.params.uuid).removeEventListener('mousedown', this.touchstart, false)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
updated() {
|
|
||||||
this.updateRecord()
|
|
||||||
this.$store.commit('updateRect')
|
|
||||||
},
|
|
||||||
async mounted() {
|
|
||||||
await this.$nextTick()
|
|
||||||
await this.loadSvg()
|
|
||||||
this.updateRecord()
|
|
||||||
// document.getElementById(this.params.uuid).addEventListener('mousedown', this.touchstart, false)
|
|
||||||
document.addEventListener('mouseup', this.touchend, false)
|
|
||||||
this.params.transform && (this.$refs.widget.style.transform = this.params.transform)
|
|
||||||
this.params.rotate && (this.$refs.widget.style.transform += `rotate(${this.params.rotate})`)
|
|
||||||
},
|
|
||||||
beforeUnmount() {
|
|
||||||
document.removeEventListener('mouseup', this.touchend, false)
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
...mapActions(['updateWidgetData']),
|
|
||||||
touchstart(e) {
|
|
||||||
// TODO move start
|
|
||||||
// const imgKey = e.target.getAttribute('img-key')
|
|
||||||
// this.editingKey = imgKey
|
|
||||||
// this.editBoxs[this.editingKey] = {
|
|
||||||
// transformOrigin: 'center',
|
|
||||||
// }
|
|
||||||
// const editBox = this.$refs[this.params.uuid + '_ebox_' + imgKey]
|
|
||||||
const editBox = this.$refs[this.params.uuid + '_ebox']
|
|
||||||
this.cropWidgetXY = {
|
|
||||||
x: Number(editBox.style.left.replace('px', '')) || 0,
|
|
||||||
y: Number(editBox.style.top.replace('px', '')) || 0,
|
|
||||||
}
|
|
||||||
// 绑定鼠标移动事件
|
|
||||||
document.addEventListener('mousemove', this.handlemousemove, true)
|
|
||||||
},
|
|
||||||
touchend() {
|
|
||||||
// 取消鼠标移动事件
|
|
||||||
document.removeEventListener('mousemove', this.handlemousemove, true)
|
|
||||||
// document.removeEventListener('mouseup', () => {}, true)
|
|
||||||
},
|
|
||||||
handlemousemove(e) {
|
|
||||||
e.stopPropagation()
|
|
||||||
e.preventDefault()
|
|
||||||
const { left, top } = this.move(e)
|
|
||||||
// TODO
|
|
||||||
this.editBoxStyle.left = left + 'px'
|
|
||||||
this.editBoxStyle.top = top + 'px'
|
|
||||||
// this.editBoxs[this.editingKey].left = left + 'px'
|
|
||||||
// this.editBoxs[this.editingKey].top = top + 'px'
|
|
||||||
const { width, height } = this.params
|
|
||||||
const { width: vWidth, height: vHeight } = this.viewBox
|
|
||||||
const params = {
|
|
||||||
x: left / (width / vWidth) / this.params.zoom,
|
|
||||||
y: top / (height / vHeight) / this.params.zoom,
|
|
||||||
}
|
|
||||||
// this.svgImg.attr(params)
|
|
||||||
this.changeFinish('x', params.x)
|
|
||||||
this.changeFinish('y', params.y)
|
|
||||||
// console.log('-----', left / (width / vWidth) / this.params.zoom)
|
|
||||||
},
|
|
||||||
loadSvg() {
|
|
||||||
// console.log(this.params)
|
|
||||||
const _this = this
|
|
||||||
const Snap = window.Snap
|
|
||||||
return new Promise((resolve) => {
|
|
||||||
Snap.load(
|
|
||||||
this.params.svgUrl,
|
|
||||||
function (svg) {
|
|
||||||
let svg2 = Snap(svg.node)
|
|
||||||
// let item = svg2.select('circle')
|
|
||||||
// item.attr({
|
|
||||||
// fill: 'rgb(255, 0, 0)',
|
|
||||||
// })
|
|
||||||
// console.log(item.attr('fill'))
|
|
||||||
|
|
||||||
let items = svg2.node.childNodes
|
onUpdated(() => {
|
||||||
svg2.node.removeAttribute('width')
|
updateRecord()
|
||||||
svg2.node.removeAttribute('height')
|
store.commit('updateRect')
|
||||||
svg2.node.setAttribute('style', 'height: inherit;width: inherit;')
|
})
|
||||||
// svg2.node.setAttribute('height', 'inherit')
|
|
||||||
_this.svgElements = []
|
|
||||||
const colorsObj = _this.color2obj()
|
|
||||||
|
|
||||||
deepElement(items)
|
onMounted(async () => {
|
||||||
|
await nextTick()
|
||||||
|
await loadSvg()
|
||||||
|
updateRecord()
|
||||||
|
document.getElementById(props.params.uuid)?.addEventListener('mousedown', touchstart, false)
|
||||||
|
document.addEventListener('mouseup', touchend, false)
|
||||||
|
if (!widgetRef.value) return
|
||||||
|
props.params.transform && (widgetRef.value.style.transform = props.params.transform)
|
||||||
|
props.params.rotate && (widgetRef.value.style.transform += `rotate(${props.params.rotate})`)
|
||||||
|
})
|
||||||
|
|
||||||
function deepElement(els) {
|
onBeforeMount(() => {
|
||||||
// 判断是NodeList对象则继续递归,否则进入元素处理工厂
|
// document.removeEventListener('mouseup', touchend, false)
|
||||||
if (els.item) {
|
})
|
||||||
els.forEach((element) => {
|
|
||||||
elementFactory(element)
|
// ...mapActions(['updateWidgetData'])
|
||||||
if (element.childNodes.length > 0) {
|
|
||||||
element.childNodes.forEach((element) => {
|
function touchstart(e: MouseEvent) {
|
||||||
deepElement(element)
|
// TODO move start
|
||||||
})
|
// const imgKey = e.target.getAttribute('img-key')
|
||||||
}
|
// this.editingKey = imgKey
|
||||||
})
|
// this.editBoxs[this.editingKey] = {
|
||||||
} else {
|
// transformOrigin: 'center',
|
||||||
elementFactory(els)
|
// }
|
||||||
}
|
// const editBox = this.$refs[this.params.uuid + '_ebox_' + imgKey]
|
||||||
}
|
// const editBox = this.$refs[this.params.uuid + '_ebox']
|
||||||
// 元素工厂: 遍历元素中是否存在可自定义的颜色属性
|
const editBox = document.getElementById(props.params.uuid + '_ebox')
|
||||||
function elementFactory(element) {
|
if (editBox) {
|
||||||
const attrsColor = {}
|
state.cropWidgetXY = {
|
||||||
try {
|
x: Number(editBox.style.left.replace('px', '')) || 0,
|
||||||
element.attributes.forEach((attr) => {
|
y: Number(editBox.style.top.replace('px', '')) || 0,
|
||||||
if (colorsObj[attr.value]) {
|
}
|
||||||
// console.log(attr.name, colorsObj[attr.value])
|
}
|
||||||
attr.value = colorsObj[attr.value]
|
// 绑定鼠标移动事件
|
||||||
attrsColor[attr.name] = _this.params.colors.findIndex((x) => x == attr.value)
|
document.addEventListener('mousemove', handlemousemove, true)
|
||||||
}
|
}
|
||||||
})
|
|
||||||
} catch (e) {}
|
function touchend() {
|
||||||
if (JSON.stringify(attrsColor) !== '{}') {
|
// 取消鼠标移动事件
|
||||||
_this.svgElements.push({
|
document.removeEventListener('mousemove', handlemousemove, true)
|
||||||
item: element,
|
// document.removeEventListener('mouseup', () => {}, true)
|
||||||
attrsColor,
|
}
|
||||||
|
|
||||||
|
function handlemousemove(e: MouseEvent) {
|
||||||
|
e.stopPropagation()
|
||||||
|
e.preventDefault()
|
||||||
|
const { left, top } = move(e)
|
||||||
|
// TODO
|
||||||
|
state.editBoxStyle.left = left + 'px'
|
||||||
|
state.editBoxStyle.top = top + 'px'
|
||||||
|
// this.editBoxs[this.editingKey].left = left + 'px'
|
||||||
|
// this.editBoxs[this.editingKey].top = top + 'px'
|
||||||
|
const { width, height } = props.params
|
||||||
|
const { width: vWidth, height: vHeight } = viewBox
|
||||||
|
const params = {
|
||||||
|
x: left / (width / vWidth) / (props.params.zoom || 0),
|
||||||
|
y: top / (height / vHeight) / (props.params.zoom || 0),
|
||||||
|
}
|
||||||
|
// this.svgImg.attr(params)
|
||||||
|
changeFinish('x', params.x)
|
||||||
|
changeFinish('y', params.y)
|
||||||
|
// console.log('-----', left / (width / vWidth) / this.params.zoom)
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadSvg() {
|
||||||
|
// console.log(this.params)
|
||||||
|
const Snap = (window as any).Snap
|
||||||
|
return new Promise<void>((resolve) => {
|
||||||
|
Snap.load(
|
||||||
|
props.params.svgUrl,
|
||||||
|
function (svg: Record<string, any>) {
|
||||||
|
let svg2 = Snap(svg.node)
|
||||||
|
// let item = svg2.select('circle')
|
||||||
|
// item.attr({
|
||||||
|
// fill: 'rgb(255, 0, 0)',
|
||||||
|
// })
|
||||||
|
// console.log(item.attr('fill'))
|
||||||
|
|
||||||
|
let items = svg2.node.childNodes
|
||||||
|
svg2.node.removeAttribute('width')
|
||||||
|
svg2.node.removeAttribute('height')
|
||||||
|
svg2.node.setAttribute('style', 'height: inherit;width: inherit;')
|
||||||
|
// svg2.node.setAttribute('height', 'inherit')
|
||||||
|
svgElements = []
|
||||||
|
const colorsObj = color2obj()
|
||||||
|
|
||||||
|
deepElement(items)
|
||||||
|
|
||||||
|
function deepElement(els: Record<string, any>) {
|
||||||
|
// 判断是NodeList对象则继续递归,否则进入元素处理工厂
|
||||||
|
if (els.item) {
|
||||||
|
els.forEach((element: Record<string, any>) => {
|
||||||
|
elementFactory(element)
|
||||||
|
if (element.childNodes.length > 0) {
|
||||||
|
element.childNodes.forEach((element: Record<string, any>) => {
|
||||||
|
deepElement(element)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
// console.log(element.attributes, element.getAttribute('fill'), _this.params.colors)
|
})
|
||||||
}
|
} else {
|
||||||
|
elementFactory(els)
|
||||||
// _this.viewBox = svg2.node.viewBox.baseVal
|
|
||||||
// _this.svgImg = img
|
|
||||||
|
|
||||||
// img.attr({
|
|
||||||
// width: '100%',
|
|
||||||
// height: '100%',
|
|
||||||
// transform: '',
|
|
||||||
// 'xlink:href': _this.params.imgUrl || '',
|
|
||||||
// })
|
|
||||||
const el = this || _this.$refs.widget
|
|
||||||
// svg.node.classList.add('svg__box')
|
|
||||||
el.appendChild(svg.node)
|
|
||||||
resolve()
|
|
||||||
},
|
|
||||||
document.getElementById(this.params.uuid),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
},
|
|
||||||
color2obj() {
|
|
||||||
const obj = {}
|
|
||||||
for (let i = 0; i < this.params.colors.length; i++) {
|
|
||||||
obj[`{{colors[${i}]}}`] = this.params.colors[i]
|
|
||||||
}
|
|
||||||
return obj
|
|
||||||
},
|
|
||||||
updateRecord() {
|
|
||||||
if (this.dActiveElement.uuid === this.params.uuid) {
|
|
||||||
let record = this.dActiveElement.record
|
|
||||||
record.width = this.$refs.widget.offsetWidth
|
|
||||||
record.height = this.$refs.widget.offsetHeight
|
|
||||||
}
|
|
||||||
this.updateZoom()
|
|
||||||
},
|
|
||||||
updateZoom() {
|
|
||||||
// TODO
|
|
||||||
this.editBoxStyle.transform = `scale(${this.params.zoom})`
|
|
||||||
// this.editingKey && (this.editBoxs[this.editingKey].transform = `scale(${this.params.zoom})`)
|
|
||||||
if (this.svgImg) {
|
|
||||||
const { x, y } = this.params
|
|
||||||
this.svgImg.attr({
|
|
||||||
x: x || 0,
|
|
||||||
y: y || 0,
|
|
||||||
style: `transform-origin: center;transform: scale(${this.params.zoom})`,
|
|
||||||
})
|
|
||||||
// 根据图片位置设置回editBox的位置
|
|
||||||
const { width, height } = this.params
|
|
||||||
const { width: vWidth, height: vHeight } = this.viewBox
|
|
||||||
const params = {
|
|
||||||
left: x * (width / vWidth) * this.params.zoom,
|
|
||||||
top: y * (height / vHeight) * this.params.zoom,
|
|
||||||
}
|
|
||||||
// TODO
|
|
||||||
this.editBoxStyle.left = params.left + 'px'
|
|
||||||
this.editBoxStyle.top = params.top + 'px'
|
|
||||||
// if (this.editingKey) {
|
|
||||||
// this.editBoxs[this.editingKey].left = params.left + 'px'
|
|
||||||
// this.editBoxs[this.editingKey].top = params.top + 'px'
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
},
|
|
||||||
changeFinish(key, value) {
|
|
||||||
this.updateWidgetData({
|
|
||||||
uuid: this.params.uuid,
|
|
||||||
key: key,
|
|
||||||
value: value,
|
|
||||||
pushHistory: true,
|
|
||||||
})
|
|
||||||
},
|
|
||||||
move(payload) {
|
|
||||||
// const widgetXY = { x: this.cropWidgetXY.x / this.dZoom, y: this.cropWidgetXY.y / this.dZoom }
|
|
||||||
const widgetXY = { x: this.cropWidgetXY.x, y: this.cropWidgetXY.y }
|
|
||||||
const dx = Number(payload.pageX) - this.dMouseXY.x
|
|
||||||
const dy = Number(payload.pageY) - this.dMouseXY.y
|
|
||||||
let left = Number(widgetXY.x) + Math.floor((dx * 100) / this.dZoom)
|
|
||||||
let top = Number(widgetXY.y) + Math.floor((dy * 100) / this.dZoom)
|
|
||||||
return { left, top }
|
|
||||||
},
|
|
||||||
attrsChange() {
|
|
||||||
if (this.dActiveElement.uuid === this.params.uuid && this.svgElements) {
|
|
||||||
for (const element of this.svgElements) {
|
|
||||||
const { item, attrsColor } = element
|
|
||||||
for (const key in attrsColor) {
|
|
||||||
if (Object.hasOwnProperty.call(attrsColor, key)) {
|
|
||||||
const color = this.params.colors[attrsColor[key]]
|
|
||||||
item.setAttribute(key, color)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// 元素工厂: 遍历元素中是否存在可自定义的颜色属性
|
||||||
|
function elementFactory(element: Record<string, any>) {
|
||||||
|
const attrsColor: Record<string, any> = {}
|
||||||
|
try {
|
||||||
|
element.attributes.forEach((attr: Record<string, any>) => {
|
||||||
|
if (colorsObj[attr.value]) {
|
||||||
|
// console.log(attr.name, colorsObj[attr.value])
|
||||||
|
attr.value = colorsObj[attr.value]
|
||||||
|
attrsColor[attr.name] = props.params.colors.findIndex((x) => x == attr.value)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} catch (e) {}
|
||||||
|
if (JSON.stringify(attrsColor) !== '{}' && svgElements) {
|
||||||
|
svgElements.push({
|
||||||
|
item: element,
|
||||||
|
attrsColor,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// console.log(element.attributes, element.getAttribute('fill'), _this.params.colors)
|
||||||
|
}
|
||||||
|
|
||||||
|
// _this.viewBox = svg2.node.viewBox.baseVal
|
||||||
|
// _this.svgImg = img
|
||||||
|
|
||||||
|
// img.attr({
|
||||||
|
// width: '100%',
|
||||||
|
// height: '100%',
|
||||||
|
// transform: '',
|
||||||
|
// 'xlink:href': _this.params.imgUrl || '',
|
||||||
|
// })
|
||||||
|
if (widgetRef.value) {
|
||||||
|
// svg.node.classList.add('svg__box')
|
||||||
|
widgetRef.value.appendChild(svg.node)
|
||||||
|
}
|
||||||
|
resolve()
|
||||||
|
},
|
||||||
|
document.getElementById(props.params.uuid),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function color2obj() {
|
||||||
|
const obj: Record<string, any> = {}
|
||||||
|
for (let i = 0; i < props.params.colors.length; i++) {
|
||||||
|
obj[`{{colors[${i}]}}`] = props.params.colors[i]
|
||||||
|
}
|
||||||
|
return obj
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateRecord() {
|
||||||
|
if (dActiveElement.value.uuid === props.params.uuid) {
|
||||||
|
let record = dActiveElement.value.record
|
||||||
|
if (widgetRef.value) {
|
||||||
|
record.width = widgetRef.value.offsetWidth
|
||||||
|
record.height = widgetRef.value.offsetHeight
|
||||||
|
}
|
||||||
|
}
|
||||||
|
updateZoom()
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateZoom() {
|
||||||
|
// TODO
|
||||||
|
state.editBoxStyle.transform = `scale(${props.params.zoom})`
|
||||||
|
// this.editingKey && (this.editBoxs[this.editingKey].transform = `scale(${this.params.zoom})`)
|
||||||
|
if (state.svgImg) {
|
||||||
|
const { x = 0, y = 0 } = props.params
|
||||||
|
state.svgImg.attr({
|
||||||
|
x: x ?? 0,
|
||||||
|
y: y ?? 0,
|
||||||
|
style: `transform-origin: center;transform: scale(${props.params.zoom})`,
|
||||||
|
})
|
||||||
|
// 根据图片位置设置回editBox的位置
|
||||||
|
const { width, height } = props.params
|
||||||
|
const { width: vWidth, height: vHeight } = viewBox
|
||||||
|
const params = {
|
||||||
|
left: x * (width / vWidth) * (props.params.zoom || 0),
|
||||||
|
top: y * (height / vHeight) * (props.params.zoom || 0),
|
||||||
|
}
|
||||||
|
// TODO
|
||||||
|
state.editBoxStyle.left = params.left + 'px'
|
||||||
|
state.editBoxStyle.top = params.top + 'px'
|
||||||
|
// if (this.editingKey) {
|
||||||
|
// this.editBoxs[this.editingKey].left = params.left + 'px'
|
||||||
|
// this.editBoxs[this.editingKey].top = params.top + 'px'
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function changeFinish(key: string, value: number) {
|
||||||
|
store.dispatch("updateWidgetData", {
|
||||||
|
uuid: props.params.uuid,
|
||||||
|
key: key,
|
||||||
|
value: value,
|
||||||
|
pushHistory: true,
|
||||||
|
})
|
||||||
|
// this.updateWidgetData({
|
||||||
|
// uuid: this.params.uuid,
|
||||||
|
// key: key,
|
||||||
|
// value: value,
|
||||||
|
// pushHistory: true,
|
||||||
|
// })
|
||||||
|
}
|
||||||
|
|
||||||
|
function move(payload: Record<string, any>) {
|
||||||
|
// const widgetXY = { x: this.cropWidgetXY.x / this.dZoom, y: this.cropWidgetXY.y / this.dZoom }
|
||||||
|
const widgetXY = { x: state.cropWidgetXY.x, y: state.cropWidgetXY.y }
|
||||||
|
const dx = Number(payload.pageX) - dMouseXY.value.x
|
||||||
|
const dy = Number(payload.pageY) - dMouseXY.value.y
|
||||||
|
let left = Number(widgetXY.x) + Math.floor((dx * 100) / dZoom.value)
|
||||||
|
let top = Number(widgetXY.y) + Math.floor((dy * 100) / dZoom.value)
|
||||||
|
return { left, top }
|
||||||
|
}
|
||||||
|
|
||||||
|
function attrsChange() {
|
||||||
|
if (dActiveElement.value.uuid === props.params.uuid && svgElements) {
|
||||||
|
for (const element of svgElements) {
|
||||||
|
const { item, attrsColor } = element
|
||||||
|
for (const key in attrsColor) {
|
||||||
|
if (Object.hasOwnProperty.call(attrsColor, key)) {
|
||||||
|
const color = props.params.colors[attrsColor[key]]
|
||||||
|
item.setAttribute(key, color)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
54
src/components/modules/widgets/wSvg/wSvgSetting.ts
Normal file
54
src/components/modules/widgets/wSvg/wSvgSetting.ts
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
|
||||||
|
export type TWSvgSetting = {
|
||||||
|
name: string,
|
||||||
|
type: string,
|
||||||
|
uuid: string
|
||||||
|
width: number
|
||||||
|
height: number
|
||||||
|
colors: [],
|
||||||
|
left: number
|
||||||
|
top: number
|
||||||
|
// zoom: 1.5,
|
||||||
|
transform: string
|
||||||
|
radius: number
|
||||||
|
opacity: number
|
||||||
|
parent: string
|
||||||
|
svgUrl: string
|
||||||
|
setting: [],
|
||||||
|
record: {
|
||||||
|
width: number
|
||||||
|
height: number
|
||||||
|
minWidth: number
|
||||||
|
minHeight: number
|
||||||
|
},
|
||||||
|
zoom?: number
|
||||||
|
cropEdit?: boolean
|
||||||
|
imgUrl?: string
|
||||||
|
rotate?: number
|
||||||
|
x?: number
|
||||||
|
y?: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export const wSvgSetting = {
|
||||||
|
name: '矢量图形',
|
||||||
|
type: "w-svg",
|
||||||
|
uuid: `-1`,
|
||||||
|
width: 100,
|
||||||
|
height: 100,
|
||||||
|
colors: [],
|
||||||
|
left: 0,
|
||||||
|
top: 0,
|
||||||
|
// zoom: 1.5,
|
||||||
|
transform: '',
|
||||||
|
radius: 0,
|
||||||
|
opacity: 1,
|
||||||
|
parent: '-1',
|
||||||
|
svgUrl: '',
|
||||||
|
setting: [],
|
||||||
|
record: {
|
||||||
|
width: 0,
|
||||||
|
height: 0,
|
||||||
|
minWidth: 10,
|
||||||
|
minHeight: 10,
|
||||||
|
},
|
||||||
|
}
|
@ -7,22 +7,22 @@
|
|||||||
-->
|
-->
|
||||||
<template>
|
<template>
|
||||||
<div id="w-image-style">
|
<div id="w-image-style">
|
||||||
<el-collapse v-model="activeNames">
|
<el-collapse v-model="state.activeNames">
|
||||||
<el-collapse-item title="位置尺寸" name="1">
|
<el-collapse-item title="位置尺寸" name="1">
|
||||||
<div class="line-layout">
|
<div class="line-layout">
|
||||||
<number-input v-model="innerElement.left" label="X" @finish="(value) => finish('left', value)" />
|
<number-input v-model="state.innerElement.left" label="X" @finish="(value) => finish('left', value)" />
|
||||||
<number-input v-model="innerElement.top" label="Y" @finish="(value) => finish('top', value)" />
|
<number-input v-model="state.innerElement.top" label="Y" @finish="(value) => finish('top', value)" />
|
||||||
<number-input v-model="innerElement.width" style="margin-top: 0.5rem" label="宽" @finish="(value) => finish('width', value)" />
|
<number-input v-model="state.innerElement.width" style="margin-top: 0.5rem" label="宽" @finish="(value) => finish('width', value)" />
|
||||||
<number-input v-model="innerElement.height" style="margin-top: 0.5rem" label="高" @finish="(value) => finish('height', value)" />
|
<number-input v-model="state.innerElement.height" style="margin-top: 0.5rem" label="高" @finish="(value) => finish('height', value)" />
|
||||||
</div>
|
</div>
|
||||||
</el-collapse-item>
|
</el-collapse-item>
|
||||||
<el-collapse-item title="设置颜色" name="2">
|
<el-collapse-item title="设置颜色" name="2">
|
||||||
<div v-for="(c, ci) in innerElement.colors" :key="ci + 'c'">
|
<div v-for="(c, ci) in state.innerElement.colors" :key="ci + 'c'">
|
||||||
<color-select v-model="innerElement.colors[ci]" @finish="(value) => colorFinish('colors')" />
|
<color-select v-model="state.innerElement.colors[ci]" @finish="(value) => colorFinish('colors')" />
|
||||||
</div>
|
</div>
|
||||||
<br />
|
<br />
|
||||||
<div class="slide-wrap">
|
<div class="slide-wrap">
|
||||||
<number-slider v-model="innerElement.opacity" label="不透明" :step="0.01" :maxValue="1" @finish="(value) => finish('opacity', value)" />
|
<number-slider v-model="state.innerElement.opacity" label="不透明" :step="0.01" :maxValue="1" @finish="(value) => finish('opacity', value)" />
|
||||||
</div>
|
</div>
|
||||||
</el-collapse-item>
|
</el-collapse-item>
|
||||||
<br />
|
<br />
|
||||||
@ -33,100 +33,139 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script lang="ts" setup>
|
||||||
// 图片组件样式
|
// 图片组件样式
|
||||||
const NAME = 'w-image-style'
|
// const NAME = 'w-image-style'
|
||||||
import { mapGetters, mapActions } from 'vuex'
|
import { reactive, watch } from 'vue'
|
||||||
|
import { mapGetters, mapActions, useStore } from 'vuex'
|
||||||
import numberInput from '../../settings/numberInput.vue'
|
import numberInput from '../../settings/numberInput.vue'
|
||||||
import iconItemSelect from '../../settings/iconItemSelect.vue'
|
import iconItemSelect, { TIconItemSelectData } from '../../settings/iconItemSelect.vue'
|
||||||
import numberSlider from '../../settings/numberSlider.vue'
|
import numberSlider from '../../settings/numberSlider.vue'
|
||||||
import colorSelect from '../../settings/colorSelect.vue'
|
import colorSelect from '../../settings/colorSelect.vue'
|
||||||
import layerIconList from '@/assets/data/LayerIconList'
|
import layerIconList from '@/assets/data/LayerIconList'
|
||||||
import alignIconList from '@/assets/data/AlignListData'
|
import alignIconList from '@/assets/data/AlignListData'
|
||||||
|
import { TWSvgSetting, wSvgSetting } from './wSvgSetting'
|
||||||
|
import { useSetupMapGetters } from '@/common/hooks/mapGetters'
|
||||||
|
|
||||||
export default {
|
type TState = {
|
||||||
name: NAME,
|
activeNames: string[]
|
||||||
components: { numberInput, numberSlider, iconItemSelect, colorSelect },
|
innerElement: TWSvgSetting
|
||||||
data() {
|
tag: boolean
|
||||||
return {
|
ingoreKeys: string[]
|
||||||
activeNames: ['2', '3', '4'],
|
layerIconList: TIconItemSelectData[]
|
||||||
innerElement: {},
|
alignIconList: TIconItemSelectData[]
|
||||||
tag: false,
|
}
|
||||||
ingoreKeys: ['left', 'top', 'name', 'width', 'height', 'radiusTopLeft', 'radiusTopRight', 'radiusBottomLeft', 'radiusBottomRight'],
|
|
||||||
layerIconList,
|
const state = reactive<TState>({
|
||||||
alignIconList,
|
activeNames: ['2', '3', '4'],
|
||||||
}
|
innerElement: JSON.parse(JSON.stringify(wSvgSetting)),
|
||||||
|
tag: false,
|
||||||
|
ingoreKeys: ['left', 'top', 'name', 'width', 'height', 'radiusTopLeft', 'radiusTopRight', 'radiusBottomLeft', 'radiusBottomRight'],
|
||||||
|
layerIconList,
|
||||||
|
alignIconList,
|
||||||
|
})
|
||||||
|
const store = useStore()
|
||||||
|
const {
|
||||||
|
dActiveElement, dMoving
|
||||||
|
} = useSetupMapGetters(['dActiveElement', 'dMoving'])
|
||||||
|
|
||||||
|
// ...mapGetters(['dActiveElement', 'dMoving']),
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => dActiveElement.value,
|
||||||
|
() => {
|
||||||
|
change()
|
||||||
},
|
},
|
||||||
computed: {
|
{ deep: true }
|
||||||
...mapGetters(['dActiveElement', 'dMoving']),
|
)
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => state.innerElement,
|
||||||
|
() => {
|
||||||
|
changeValue()
|
||||||
},
|
},
|
||||||
watch: {
|
{ deep: true }
|
||||||
dActiveElement: {
|
)
|
||||||
handler(newValue, oldValue) {
|
|
||||||
this.change()
|
function created() {
|
||||||
},
|
change()
|
||||||
deep: true,
|
}
|
||||||
},
|
|
||||||
innerElement: {
|
created()
|
||||||
handler(newValue, oldValue) {
|
|
||||||
this.changeValue()
|
// ...mapActions(['updateWidgetData', 'updateAlign', 'updateLayerIndex']),
|
||||||
},
|
|
||||||
deep: true,
|
function change() {
|
||||||
},
|
state.tag = true
|
||||||
},
|
state.innerElement = JSON.parse(JSON.stringify(dActiveElement.value))
|
||||||
created() {
|
}
|
||||||
this.change()
|
|
||||||
},
|
function changeValue() {
|
||||||
methods: {
|
if (state.tag) {
|
||||||
...mapActions(['updateWidgetData', 'updateAlign', 'updateLayerIndex']),
|
state.tag = false
|
||||||
change() {
|
return
|
||||||
this.tag = true
|
}
|
||||||
this.innerElement = JSON.parse(JSON.stringify(this.dActiveElement))
|
if (dMoving.value) {
|
||||||
},
|
return
|
||||||
changeValue() {
|
}
|
||||||
if (this.tag) {
|
for (let key in state.innerElement) {
|
||||||
this.tag = false
|
const itemKey = key as keyof TWSvgSetting
|
||||||
return
|
if (state.ingoreKeys.indexOf(itemKey) !== -1) {
|
||||||
}
|
dActiveElement.value[key] = state.innerElement[itemKey]
|
||||||
if (this.dMoving) {
|
} else if (itemKey !== 'setting' && itemKey !== 'record' && state.innerElement[itemKey] !== dActiveElement.value[itemKey]) {
|
||||||
return
|
store.dispatch("updateWidgetData", {
|
||||||
}
|
uuid: dActiveElement.value.uuid,
|
||||||
for (let key in this.innerElement) {
|
|
||||||
if (this.ingoreKeys.indexOf(key) !== -1) {
|
|
||||||
this.dActiveElement[key] = this.innerElement[key]
|
|
||||||
} else if (key !== 'setting' && key !== 'record' && this.innerElement[key] !== this.dActiveElement[key]) {
|
|
||||||
this.updateWidgetData({
|
|
||||||
uuid: this.dActiveElement.uuid,
|
|
||||||
key: key,
|
|
||||||
value: this.innerElement[key],
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
colorFinish(key) {
|
|
||||||
this.finish(key, this.innerElement[key])
|
|
||||||
},
|
|
||||||
finish(key, value) {
|
|
||||||
this.updateWidgetData({
|
|
||||||
uuid: this.dActiveElement.uuid,
|
|
||||||
key: key,
|
key: key,
|
||||||
value: value,
|
value: state.innerElement[itemKey],
|
||||||
pushHistory: true,
|
|
||||||
})
|
})
|
||||||
},
|
// this.updateWidgetData({
|
||||||
layerAction(item) {
|
// uuid: this.dActiveElement.uuid,
|
||||||
this.updateLayerIndex({
|
// key: key,
|
||||||
uuid: this.dActiveElement.uuid,
|
// value: this.innerElement[key],
|
||||||
value: item.value,
|
// })
|
||||||
})
|
}
|
||||||
},
|
}
|
||||||
alignAction(item) {
|
}
|
||||||
this.updateAlign({
|
|
||||||
align: item.value,
|
function colorFinish(key: keyof TWSvgSetting) {
|
||||||
uuid: this.dActiveElement.uuid,
|
finish(key, state.innerElement[key])
|
||||||
})
|
}
|
||||||
},
|
|
||||||
},
|
function finish(key: string, value: any) {
|
||||||
|
store.dispatch("updateWidgetData", {
|
||||||
|
uuid: dActiveElement.value.uuid,
|
||||||
|
key: key,
|
||||||
|
value: value,
|
||||||
|
pushHistory: true,
|
||||||
|
})
|
||||||
|
// this.updateWidgetData({
|
||||||
|
// uuid: this.dActiveElement.uuid,
|
||||||
|
// key: key,
|
||||||
|
// value: value,
|
||||||
|
// pushHistory: true,
|
||||||
|
// })
|
||||||
|
}
|
||||||
|
|
||||||
|
function layerAction(item: TIconItemSelectData) {
|
||||||
|
store.dispatch("updateLayerIndex", {
|
||||||
|
uuid: dActiveElement.value.uuid,
|
||||||
|
value: item.value,
|
||||||
|
})
|
||||||
|
// this.updateLayerIndex({
|
||||||
|
// uuid: this.dActiveElement.uuid,
|
||||||
|
// value: item.value,
|
||||||
|
// })
|
||||||
|
}
|
||||||
|
|
||||||
|
function alignAction(item: TIconItemSelectData) {
|
||||||
|
store.dispatch("updateAlign", {
|
||||||
|
align: item.value,
|
||||||
|
uuid: dActiveElement.value.uuid,
|
||||||
|
})
|
||||||
|
// this.updateAlign({
|
||||||
|
// align: item.value,
|
||||||
|
// uuid: this.dActiveElement.uuid,
|
||||||
|
// })
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -8,8 +8,8 @@
|
|||||||
import store from '@/store'
|
import store from '@/store'
|
||||||
import { toRaw } from 'vue'
|
import { toRaw } from 'vue'
|
||||||
export default () => {
|
export default () => {
|
||||||
const collector = new Set()
|
const collector = new Set<string>()
|
||||||
const fonts: any = {}
|
const fonts: Record<string, any> = {}
|
||||||
const { dWidgets: widgets } = store.getters
|
const { dWidgets: widgets } = store.getters
|
||||||
for (let i = 0; i < widgets.length; i++) {
|
for (let i = 0; i < widgets.length; i++) {
|
||||||
const { type, fontClass } = widgets[i]
|
const { type, fontClass } = widgets[i]
|
||||||
@ -18,5 +18,5 @@ export default () => {
|
|||||||
fonts[fontClass.id] = toRaw(fontClass)
|
fonts[fontClass.id] = toRaw(fontClass)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Array.from(collector).map((id: any) => fonts[id])
|
return Array.from(collector).map((id: string) => fonts[id])
|
||||||
}
|
}
|
||||||
|
@ -60,6 +60,9 @@ export type TwTextData = {
|
|||||||
y: number
|
y: number
|
||||||
}
|
}
|
||||||
}[]
|
}[]
|
||||||
|
width?: number
|
||||||
|
height?: number
|
||||||
|
degree?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export const wTextSetting: TwTextData = {
|
export const wTextSetting: TwTextData = {
|
||||||
|
@ -1,18 +1,18 @@
|
|||||||
<template>
|
<template>
|
||||||
<div id="w-text-style">
|
<div id="w-text-style">
|
||||||
<el-collapse v-model="activeNames">
|
<el-collapse v-model="state.activeNames">
|
||||||
<el-collapse-item title="位置尺寸" name="1">
|
<el-collapse-item title="位置尺寸" name="1">
|
||||||
<div class="line-layout">
|
<div class="line-layout">
|
||||||
<number-input v-model="innerElement.left" label="X" @finish="(value) => finish('left', value)" />
|
<number-input v-model="state.innerElement.left" label="X" @finish="(value) => finish('left', value)" />
|
||||||
<number-input v-model="innerElement.top" label="Y" @finish="(value) => finish('top', value)" />
|
<number-input v-model="state.innerElement.top" label="Y" @finish="(value) => finish('top', value)" />
|
||||||
<number-input v-model="innerElement.width" style="margin-top: 0.5rem" label="宽" :editable="true" @finish="(value) => finish('width', value)" />
|
<number-input v-model="state.innerElement.width" style="margin-top: 0.5rem" label="宽" :editable="true" @finish="(value) => finish('width', value)" />
|
||||||
<number-input v-model="innerElement.height" style="margin-top: 0.5rem" label="高" :editable="true" @finish="(value) => finish('height', value)" />
|
<number-input v-model="state.innerElement.height" style="margin-top: 0.5rem" label="高" :editable="true" @finish="(value) => finish('height', value)" />
|
||||||
</div>
|
</div>
|
||||||
</el-collapse-item>
|
</el-collapse-item>
|
||||||
<!-- <el-collapse-item title="样式设置" name="2"> -->
|
<!-- <el-collapse-item title="样式设置" name="2"> -->
|
||||||
<div class="line-layout style-item">
|
<div class="line-layout style-item">
|
||||||
<value-select v-model="innerElement.fontClass" label="文字" :data="fontClassList" inputWidth="152px" :readonly="true" @finish="(font) => finish('fontClass', font)" />
|
<value-select v-model="state.innerElement.fontClass" label="文字" :data="state.fontClassList" inputWidth="152px" :readonly="true" @finish="(font) => finish('fontClass', font)" />
|
||||||
<value-select v-model="innerElement.fontSize" label="大小" suffix="px" :data="fontSizeList" @finish="(value) => finish('fontSize', value)" />
|
<value-select v-model="state.innerElement.fontSize" label="大小" suffix="px" :data="state.fontSizeList" @finish="(value) => finish('fontSize', value)" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<icon-item-select class="style-item" :data="styleIconList1" @finish="textStyleAction" />
|
<icon-item-select class="style-item" :data="styleIconList1" @finish="textStyleAction" />
|
||||||
@ -24,232 +24,242 @@
|
|||||||
</div> -->
|
</div> -->
|
||||||
<!-- <el-collapse-item title="位置尺寸" name="1"> -->
|
<!-- <el-collapse-item title="位置尺寸" name="1"> -->
|
||||||
<div class="style-item slide-wrap">
|
<div class="style-item slide-wrap">
|
||||||
<number-slider v-model="innerElement.letterSpacing" style="font-size: 14px" label="字距" labelWidth="40px" :step="0.05" :minValue="-innerElement.fontSize" :maxValue="innerElement.fontSize * 2" @finish="(value) => finish('letterSpacing', value)" />
|
<number-slider v-model="state.innerElement.letterSpacing" style="font-size: 14px" label="字距" labelWidth="40px" :step="0.05" :minValue="-state.innerElement.fontSize" :maxValue="state.innerElement.fontSize * 2" @finish="(value) => finish('letterSpacing', value)" />
|
||||||
<number-slider v-model="innerElement.lineHeight" style="font-size: 14px" label="行距" labelWidth="40px" :step="0.05" :minValue="0" :maxValue="2.5" @finish="(value) => finish('lineHeight', value)" />
|
<number-slider v-model="state.innerElement.lineHeight" style="font-size: 14px" label="行距" labelWidth="40px" :step="0.05" :minValue="0" :maxValue="2.5" @finish="(value) => finish('lineHeight', value)" />
|
||||||
</div>
|
</div>
|
||||||
<!-- </el-collapse-item> -->
|
<!-- </el-collapse-item> -->
|
||||||
|
|
||||||
<div style="flex-wrap: nowrap" class="line-layout style-item">
|
<div style="flex-wrap: nowrap" class="line-layout style-item">
|
||||||
<color-select v-model="innerElement.color" label="颜色" @finish="(value) => finish('color', value)" />
|
<color-select v-model="state.innerElement.color" label="颜色" @finish="(value) => finish('color', value)" />
|
||||||
<!-- <color-select v-model="innerElement.backgroundColor" label="背景颜色" @finish="(value) => finish('backgroundColor', value)" /> -->
|
<!-- <color-select v-model="innerElement.backgroundColor" label="背景颜色" @finish="(value) => finish('backgroundColor', value)" /> -->
|
||||||
</div>
|
</div>
|
||||||
<div class="line-layout style-item">
|
<div class="line-layout style-item">
|
||||||
<effect-wrap v-model="innerElement.textEffects" :data="innerElement" :degree="innerElement.degree" @select="selectTextEffect" />
|
<effect-wrap v-model="state.innerElement.textEffects" :data="state.innerElement" :degree="state.innerElement.degree" @select="selectTextEffect" />
|
||||||
</div>
|
</div>
|
||||||
<icon-item-select class="style-item" :data="layerIconList" @finish="layerAction" />
|
<icon-item-select class="style-item" :data="layerIconList" @finish="layerAction" />
|
||||||
<icon-item-select class="style-item" :data="alignIconList" @finish="alignAction" />
|
<icon-item-select class="style-item" :data="alignIconList" @finish="alignAction" />
|
||||||
|
|
||||||
<!-- v-show="!innerElement.editable" -->
|
<!-- v-show="!innerElement.editable" -->
|
||||||
<div style="margin-top: 10px" class="line-layout style-item">
|
<div style="margin-top: 10px" class="line-layout style-item">
|
||||||
<text-input-area v-model="innerElement.text" @finish="(value) => finish('text', value)" />
|
<text-input-area v-model="state.innerElement.text" @finish="(value) => finish('text', value)" />
|
||||||
</div>
|
</div>
|
||||||
<!-- </el-collapse-item> -->
|
<!-- </el-collapse-item> -->
|
||||||
</el-collapse>
|
</el-collapse>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts" setup>
|
||||||
// 文本组件样式
|
// 文本组件样式
|
||||||
const NAME = 'w-text-style'
|
const NAME = 'w-text-style'
|
||||||
import { defineComponent, reactive, toRefs, computed, watch, nextTick, onMounted } from 'vue'
|
import { defineComponent, reactive, toRefs, computed, watch, nextTick, onMounted } from 'vue'
|
||||||
import { useStore } from 'vuex'
|
import { useStore } from 'vuex'
|
||||||
import { useRoute } from 'vue-router'
|
import { useRoute } from 'vue-router'
|
||||||
import { styleIconList1, styleIconList2, alignIconList } from '@/assets/data/TextIconsData'
|
import { styleIconList1, styleIconList2, alignIconList, TStyleIconData, TStyleIconData2 } from '@/assets/data/TextIconsData'
|
||||||
import layerIconList from '@/assets/data/LayerIconList'
|
import layerIconList from '@/assets/data/LayerIconList'
|
||||||
import numberInput from '../../settings/numberInput.vue'
|
import numberInput from '../../settings/numberInput.vue'
|
||||||
import numberSlider from '../../settings/numberSlider.vue'
|
import numberSlider from '../../settings/numberSlider.vue'
|
||||||
import colorSelect from '../../settings/colorSelect.vue'
|
import colorSelect from '../../settings/colorSelect.vue'
|
||||||
import iconItemSelect from '../../settings/iconItemSelect.vue'
|
import iconItemSelect, { TIconItemSelectData } from '../../settings/iconItemSelect.vue'
|
||||||
import textInputArea from '../../settings/textInputArea.vue'
|
import textInputArea from '../../settings/textInputArea.vue'
|
||||||
import valueSelect from '../../settings/valueSelect.vue'
|
import valueSelect from '../../settings/valueSelect.vue'
|
||||||
import effectWrap from '../../settings/EffectSelect/TextWrap.vue'
|
import effectWrap from '../../settings/EffectSelect/TextWrap.vue'
|
||||||
import { useFontStore } from '@/common/methods/fonts'
|
import { useFontStore } from '@/common/methods/fonts'
|
||||||
import usePageFontsFilter from './pageFontsFilter.ts'
|
import usePageFontsFilter from './pageFontsFilter'
|
||||||
|
import { wTextSetting ,TwTextData } from './wTextSetting';
|
||||||
|
|
||||||
export default defineComponent({
|
type TState = {
|
||||||
name: NAME,
|
activeNames: string[],
|
||||||
components: { numberInput, colorSelect, iconItemSelect, textInputArea, valueSelect, effectWrap, numberSlider },
|
innerElement: TwTextData,
|
||||||
setup() {
|
tag: boolean,
|
||||||
const store = useStore()
|
ingoreKeys: string[],
|
||||||
const route = useRoute()
|
fontSizeList: number[],
|
||||||
const state = reactive({
|
fontClassList: Record<string, any>, // 不能设空字体系统默认字体,因为截图服务的默认字体无法保证一致
|
||||||
activeNames: [],
|
lineHeightList: number[],
|
||||||
innerElement: {},
|
letterSpacingList: number[],
|
||||||
tag: false,
|
layerIconList: TIconItemSelectData[],
|
||||||
ingoreKeys: ['left', 'top', 'name', 'width', 'height', 'text', 'color', 'backgroundColor'],
|
styleIconList1: TStyleIconData[],
|
||||||
fontSizeList: [12, 14, 24, 26, 28, 30, 36, 48, 60, 72, 96, 108, 120, 140, 180, 200, 250, 300, 400, 500],
|
styleIconList2: TStyleIconData2[],
|
||||||
fontClassList: [], // 不能设空字体系统默认字体,因为截图服务的默认字体无法保证一致
|
alignIconList: TIconItemSelectData[],
|
||||||
lineHeightList: [1, 1.5, 2],
|
}
|
||||||
letterSpacingList: [0, 10, 25, 50, 75, 100, 200],
|
|
||||||
layerIconList,
|
const store = useStore()
|
||||||
styleIconList1,
|
const route = useRoute()
|
||||||
styleIconList2,
|
const state = reactive<TState>({
|
||||||
alignIconList,
|
activeNames: [],
|
||||||
|
innerElement: JSON.parse(JSON.stringify(wTextSetting)),
|
||||||
|
tag: false,
|
||||||
|
ingoreKeys: ['left', 'top', 'name', 'width', 'height', 'text', 'color', 'backgroundColor'],
|
||||||
|
fontSizeList: [12, 14, 24, 26, 28, 30, 36, 48, 60, 72, 96, 108, 120, 140, 180, 200, 250, 300, 400, 500],
|
||||||
|
fontClassList: {}, // 不能设空字体系统默认字体,因为截图服务的默认字体无法保证一致
|
||||||
|
lineHeightList: [1, 1.5, 2],
|
||||||
|
letterSpacingList: [0, 10, 25, 50, 75, 100, 200],
|
||||||
|
layerIconList,
|
||||||
|
styleIconList1,
|
||||||
|
styleIconList2,
|
||||||
|
alignIconList,
|
||||||
|
})
|
||||||
|
const dActiveElement = computed(() => store.getters.dActiveElement)
|
||||||
|
const dMoving = computed(() => store.getters.dMoving)
|
||||||
|
// const isDraw = computed(() => route.name === 'Draw')
|
||||||
|
|
||||||
|
watch(() => dActiveElement.value, () => {
|
||||||
|
change()
|
||||||
|
}, { deep: true })
|
||||||
|
|
||||||
|
watch(() => state.innerElement, () => {
|
||||||
|
changeValue()
|
||||||
|
}, { deep: true })
|
||||||
|
|
||||||
|
let timer: boolean | null = null
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
change()
|
||||||
|
setTimeout(() => {
|
||||||
|
loadFonts()
|
||||||
|
}, 100)
|
||||||
|
})
|
||||||
|
|
||||||
|
function change() {
|
||||||
|
if (timer) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
timer = true
|
||||||
|
setTimeout(() => {
|
||||||
|
timer = null
|
||||||
|
}, 300)
|
||||||
|
state.tag = true
|
||||||
|
state.innerElement = JSON.parse(JSON.stringify(dActiveElement.value))
|
||||||
|
changeStyleIconList()
|
||||||
|
}
|
||||||
|
|
||||||
|
function changeValue() {
|
||||||
|
if (state.tag) {
|
||||||
|
state.tag = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (dMoving.value) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// TODO 修改数值
|
||||||
|
for (let key in state.innerElement) {
|
||||||
|
const itemKey = key as keyof TwTextData
|
||||||
|
if (state.ingoreKeys.indexOf(itemKey) !== -1) {
|
||||||
|
dActiveElement.value[itemKey] = state.innerElement[itemKey]
|
||||||
|
} else if (key !== 'setting' && key !== 'record' && state.innerElement[itemKey] !== dActiveElement.value[itemKey]) {
|
||||||
|
// const pushHistory = !['textEffects', 'transformData', 'fontClass'].includes(key)
|
||||||
|
store.dispatch('updateWidgetData', {
|
||||||
|
uuid: dActiveElement.value.uuid,
|
||||||
|
key,
|
||||||
|
value: state.innerElement[itemKey],
|
||||||
|
pushHistory: false,
|
||||||
})
|
})
|
||||||
const dActiveElement = computed(() => store.getters.dActiveElement)
|
|
||||||
const dMoving = computed(() => store.getters.dMoving)
|
|
||||||
// const isDraw = computed(() => route.name === 'Draw')
|
|
||||||
|
|
||||||
watch(() => dActiveElement.value, () => {
|
|
||||||
change()
|
|
||||||
}, { deep: true })
|
|
||||||
|
|
||||||
watch(() => state.innerElement, () => {
|
|
||||||
changeValue()
|
|
||||||
}, { deep: true })
|
|
||||||
|
|
||||||
let timer: any = null
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
change()
|
|
||||||
setTimeout(() => {
|
|
||||||
loadFonts()
|
|
||||||
}, 100)
|
|
||||||
})
|
|
||||||
|
|
||||||
function change() {
|
|
||||||
if (timer) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
timer = true
|
|
||||||
setTimeout(() => {
|
|
||||||
timer = null
|
|
||||||
}, 300)
|
|
||||||
state.tag = true
|
|
||||||
state.innerElement = JSON.parse(JSON.stringify(dActiveElement.value))
|
|
||||||
changeStyleIconList()
|
|
||||||
}
|
|
||||||
|
|
||||||
function changeValue() {
|
|
||||||
if (state.tag) {
|
|
||||||
state.tag = false
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (dMoving.value) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// TODO 修改数值
|
|
||||||
for (let key in state.innerElement) {
|
|
||||||
if (state.ingoreKeys.indexOf(key) !== -1) {
|
|
||||||
dActiveElement.value[key] = state.innerElement[key]
|
|
||||||
} else if (key !== 'setting' && key !== 'record' && state.innerElement[key] !== dActiveElement.value[key]) {
|
|
||||||
// const pushHistory = !['textEffects', 'transformData', 'fontClass'].includes(key)
|
|
||||||
store.dispatch('updateWidgetData', {
|
|
||||||
uuid: dActiveElement.value.uuid,
|
|
||||||
key,
|
|
||||||
value: state.innerElement[key],
|
|
||||||
pushHistory: false,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function selectTextEffect({ key, value, style }: any) {
|
|
||||||
const uuid = dActiveElement.value.uuid
|
|
||||||
store.commit('setWidgetStyle', { uuid, key, value })
|
|
||||||
if (style) {
|
|
||||||
finish('color', style.color || '')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function loadFonts() {
|
|
||||||
const localFonts = useFontStore.list
|
|
||||||
const fontLists = { 当前页面: [], 中文: [], 英文: [] }
|
|
||||||
for (const font of localFonts) {
|
|
||||||
const { id, oid, value, url, alias, preview, lang } = font
|
|
||||||
const item = { id, oid, value, url, alias, preview }
|
|
||||||
lang === 'zh' ? fontLists['中文'].unshift(item) : fontLists['英文'].unshift(item)
|
|
||||||
}
|
|
||||||
fontLists['当前页面'] = usePageFontsFilter()
|
|
||||||
state.fontClassList = fontLists
|
|
||||||
}
|
|
||||||
|
|
||||||
function finish(key, value) {
|
|
||||||
store.dispatch('updateWidgetData', {
|
|
||||||
uuid: dActiveElement.value.uuid,
|
|
||||||
key,
|
|
||||||
value,
|
|
||||||
pushHistory: false,
|
|
||||||
})
|
|
||||||
setTimeout(() => {
|
|
||||||
key === 'fontClass' && (state.fontClassList['当前页面'] = usePageFontsFilter())
|
|
||||||
}, 300)
|
|
||||||
}
|
|
||||||
|
|
||||||
function layerAction(item) {
|
|
||||||
store.dispatch('updateLayerIndex', {
|
|
||||||
uuid: dActiveElement.value.uuid,
|
|
||||||
value: item.value,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
async function textStyleAction(item) {
|
|
||||||
let value = item.key === 'textAlign' ? item.value : item.value[item.select ? 1 : 0]
|
|
||||||
state.innerElement[item.key] = value
|
|
||||||
// TODO: 对竖版文字的特殊处理
|
|
||||||
item.key === 'writingMode' && relationChange()
|
|
||||||
await nextTick()
|
|
||||||
store.commit('updateRect')
|
|
||||||
}
|
|
||||||
|
|
||||||
async function alignAction(item) {
|
|
||||||
store.dispatch('updateAlign', {
|
|
||||||
align: item.value,
|
|
||||||
uuid: dActiveElement.value.uuid,
|
|
||||||
})
|
|
||||||
await nextTick()
|
|
||||||
store.commit('updateRect')
|
|
||||||
}
|
|
||||||
|
|
||||||
function changeStyleIconList() {
|
|
||||||
for (let i = 0; i < state.styleIconList1.length; ++i) {
|
|
||||||
let key = state.styleIconList1[i].key
|
|
||||||
state.styleIconList1[i].select = false
|
|
||||||
const [unchecked, checked] = state.styleIconList1[i].value
|
|
||||||
switch (key) {
|
|
||||||
case 'fontWeight':
|
|
||||||
case 'textDecoration':
|
|
||||||
case 'fontStyle':
|
|
||||||
if (state.innerElement[key] !== unchecked && state.innerElement[key] == checked) {
|
|
||||||
state.styleIconList1[i].select = !state.styleIconList1[i].select
|
|
||||||
}
|
|
||||||
break
|
|
||||||
case 'writingMode':
|
|
||||||
if (state.innerElement[key] !== unchecked) {
|
|
||||||
state.styleIconList1[i].select = true
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (let i = 0; i < state.styleIconList2.length; i++) {
|
|
||||||
let key = state.styleIconList2[i].key
|
|
||||||
state.styleIconList2[i].select = false
|
|
||||||
if (key === 'textAlign' && state.innerElement[key] === state.styleIconList2[i].value) {
|
|
||||||
state.styleIconList2[i].select = true
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function relationChange() {
|
|
||||||
setTimeout(() => {
|
|
||||||
if (dActiveElement.value.writingMode) {
|
|
||||||
const w_record = dActiveElement.value.width
|
|
||||||
state.innerElement.width = dActiveElement.value.height
|
|
||||||
state.innerElement.height = w_record
|
|
||||||
}
|
|
||||||
}, 10)
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
...toRefs(state),
|
|
||||||
selectTextEffect,
|
|
||||||
textStyleAction,
|
|
||||||
finish,
|
|
||||||
layerAction,
|
|
||||||
alignAction
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function selectTextEffect({ key, value, style }: any) {
|
||||||
|
const uuid = dActiveElement.value.uuid
|
||||||
|
store.commit('setWidgetStyle', { uuid, key, value })
|
||||||
|
if (style) {
|
||||||
|
finish('color', style.color || '')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadFonts() {
|
||||||
|
const localFonts = useFontStore.list
|
||||||
|
const fontLists: Record<string, any> = { 当前页面: [], 中文: [], 英文: [] }
|
||||||
|
for (const font of localFonts) {
|
||||||
|
const { id, oid, value, url, alias, preview, lang } = font
|
||||||
|
const item = { id, oid, value, url, alias, preview }
|
||||||
|
lang === 'zh' ? fontLists['中文'].unshift(item) : fontLists['英文'].unshift(item)
|
||||||
|
}
|
||||||
|
fontLists['当前页面'] = usePageFontsFilter()
|
||||||
|
state.fontClassList = fontLists
|
||||||
|
}
|
||||||
|
|
||||||
|
function finish(key: string, value: number | Record<string, any> | string) {
|
||||||
|
store.dispatch('updateWidgetData', {
|
||||||
|
uuid: dActiveElement.value.uuid,
|
||||||
|
key,
|
||||||
|
value,
|
||||||
|
pushHistory: false,
|
||||||
|
})
|
||||||
|
setTimeout(() => {
|
||||||
|
key === 'fontClass' && (state.fontClassList['当前页面'] = usePageFontsFilter())
|
||||||
|
}, 300)
|
||||||
|
}
|
||||||
|
|
||||||
|
function layerAction(item: TIconItemSelectData) {
|
||||||
|
store.dispatch('updateLayerIndex', {
|
||||||
|
uuid: dActiveElement.value.uuid,
|
||||||
|
value: item.value,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async function textStyleAction(item: TIconItemSelectData) {
|
||||||
|
let value = item.key === 'textAlign' ? item.value : (item.value as number[])[item.select ? 1 : 0];
|
||||||
|
(state.innerElement as Record<string, any>)[item.key || ""] = value
|
||||||
|
// TODO: 对竖版文字的特殊处理
|
||||||
|
item.key === 'writingMode' && relationChange()
|
||||||
|
await nextTick()
|
||||||
|
store.commit('updateRect')
|
||||||
|
}
|
||||||
|
|
||||||
|
async function alignAction(item: TIconItemSelectData) {
|
||||||
|
store.dispatch('updateAlign', {
|
||||||
|
align: item.value,
|
||||||
|
uuid: dActiveElement.value.uuid,
|
||||||
|
})
|
||||||
|
await nextTick()
|
||||||
|
store.commit('updateRect')
|
||||||
|
}
|
||||||
|
|
||||||
|
function changeStyleIconList() {
|
||||||
|
for (let i = 0; i < state.styleIconList1.length; ++i) {
|
||||||
|
let key = state.styleIconList1[i].key
|
||||||
|
state.styleIconList1[i].select = false
|
||||||
|
const [unchecked, checked] = state.styleIconList1[i].value
|
||||||
|
switch (key) {
|
||||||
|
case 'fontWeight':
|
||||||
|
case 'textDecoration':
|
||||||
|
case 'fontStyle':
|
||||||
|
if (state.innerElement[key] !== unchecked && state.innerElement[key] == checked) {
|
||||||
|
state.styleIconList1[i].select = !state.styleIconList1[i].select
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case 'writingMode':
|
||||||
|
if (state.innerElement[key] !== unchecked) {
|
||||||
|
state.styleIconList1[i].select = true
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (let i = 0; i < state.styleIconList2.length; i++) {
|
||||||
|
let key = state.styleIconList2[i].key
|
||||||
|
state.styleIconList2[i].select = false
|
||||||
|
if (key === 'textAlign' && state.innerElement[key] === state.styleIconList2[i].value) {
|
||||||
|
state.styleIconList2[i].select = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function relationChange() {
|
||||||
|
setTimeout(() => {
|
||||||
|
if (dActiveElement.value.writingMode) {
|
||||||
|
const w_record = dActiveElement.value.width
|
||||||
|
state.innerElement.width = dActiveElement.value.height
|
||||||
|
state.innerElement.height = w_record
|
||||||
|
}
|
||||||
|
}, 10)
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
selectTextEffect,
|
||||||
|
textStyleAction,
|
||||||
|
finish,
|
||||||
|
layerAction,
|
||||||
|
alignAction
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user