mirror of
https://github.com/palxiao/poster-design.git
synced 2025-07-15 16:02:19 +08:00
Merge pull request #72 from JeremyYu-cn/feat-upgrade-vue3
Feat: Convert views and components to composition API
This commit is contained in:
commit
db962af514
@ -39,6 +39,7 @@
|
||||
"vuex": "^4.0.0-0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/fontfaceobserver": "^2.1.3",
|
||||
"@types/node": "^20.11.24",
|
||||
"@types/throttle-debounce": "^2.1.0",
|
||||
"@typescript-eslint/eslint-plugin": "^7.1.0",
|
||||
|
@ -43,6 +43,7 @@ export const getTempList = (params: IGetTempListParam) => fetch<IGetTempListResu
|
||||
|
||||
type TGetTempDetail = {
|
||||
id: number
|
||||
type?: number
|
||||
}
|
||||
|
||||
export const getTempDetail = (params: TGetTempDetail) => fetch<{data: string}>('design/temp', params, 'get')
|
||||
|
@ -32,12 +32,11 @@ export type TGetListData = {
|
||||
updated_time: string
|
||||
url: string
|
||||
width: number
|
||||
thumbUrl: string
|
||||
imgUrl: string
|
||||
}
|
||||
|
||||
type TGetListResult = TCommResResult<{
|
||||
list: TGetListData
|
||||
total: number
|
||||
}>
|
||||
export type TGetListResult = TPageRequestResult<TGetListData[]>
|
||||
|
||||
|
||||
|
||||
|
@ -20,7 +20,7 @@ type TProps = {
|
||||
title: string
|
||||
width: number
|
||||
content: string
|
||||
position: "bottom" | "auto" | "auto-start" | "auto-end" | "top" | "right" | "left" | "top-start" | "top-end" | "bottom-start" | "bottom-end" | "right-start" | "right-end" | "left-start" | "left-end"
|
||||
position?: "bottom" | "auto" | "auto-start" | "auto-end" | "top" | "right" | "left" | "top-start" | "top-end" | "bottom-start" | "bottom-end" | "right-start" | "right-end" | "left-start" | "left-end"
|
||||
offset?: number
|
||||
}
|
||||
|
||||
|
@ -24,7 +24,7 @@ type TProps = {
|
||||
percent: number
|
||||
text: string
|
||||
cancelText: string
|
||||
msg: string
|
||||
msg?: string
|
||||
}
|
||||
|
||||
type TEmits = {
|
||||
|
@ -35,9 +35,9 @@ export type TUploadDoneData = {
|
||||
type TQiNiuUploadReturn = { hash: string, key: string }
|
||||
|
||||
type TProps = {
|
||||
modelValue: TModelData
|
||||
options: { bucket: string, prePath: string }
|
||||
hold: boolean
|
||||
modelValue?: TModelData
|
||||
options?: { bucket: string, prePath: string }
|
||||
hold?: boolean
|
||||
}
|
||||
|
||||
type TEmits = {
|
||||
|
@ -39,6 +39,7 @@ const state = reactive({
|
||||
active: true,
|
||||
})
|
||||
const clickClassify = (index: number) => {
|
||||
console.log('index' ,index)
|
||||
state.activeWidgetClassify = index
|
||||
state.active = true
|
||||
}
|
||||
|
@ -17,7 +17,7 @@
|
||||
<i class="icon sd-AI_zhineng" />
|
||||
<div class="text"><span>智能抠图</span> <span class="desc">上传图像一键去除背景</span></div>
|
||||
</div>
|
||||
<imageCutout ref="imageCutout" />
|
||||
<imageCutout ref="imageCutoutRef" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -52,7 +52,7 @@ import { TUploadDoneData } from '@/components/common/Uploader/index.vue'
|
||||
import { IGetTempListData } from '@/api/home'
|
||||
|
||||
type TProps = {
|
||||
active: number
|
||||
active?: number
|
||||
}
|
||||
|
||||
type TState = {
|
||||
@ -104,7 +104,6 @@ const load = (init?: boolean) => {
|
||||
} else {
|
||||
state.imgList = state.imgList.concat(list)
|
||||
}
|
||||
console.log('state.imgList', state.imgList)
|
||||
setTimeout(() => {
|
||||
loading = false
|
||||
if (!imgListRef.value) return
|
||||
@ -133,11 +132,9 @@ const loadDesign = (init: boolean = false) => {
|
||||
return x
|
||||
}),
|
||||
))
|
||||
console.log('state.designList', state.designList)
|
||||
setTimeout(() => {
|
||||
loading = false
|
||||
if (!listRef.value) return
|
||||
console.log('listRef.value', listRef.value)
|
||||
checkHeight(listRef.value, loadDesign)
|
||||
}, 100)
|
||||
})
|
||||
|
@ -30,7 +30,7 @@ export type TClassHeaderTypeData = {
|
||||
|
||||
type TProps = {
|
||||
types: TClassHeaderTypeData[]
|
||||
isBack: boolean
|
||||
isBack?: boolean
|
||||
}
|
||||
|
||||
type TEmits = {
|
||||
|
@ -46,9 +46,9 @@ import { IGetTempListData } from '@/api/home';
|
||||
|
||||
type TProps = {
|
||||
listData: IGetTempListData[]
|
||||
edit: Record<string, any>
|
||||
isDone: boolean
|
||||
isShort: boolean
|
||||
edit?: Record<string, any>
|
||||
isDone?: boolean
|
||||
isShort?: boolean
|
||||
}
|
||||
|
||||
type TEmits = {
|
||||
@ -64,7 +64,8 @@ type TState = {
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<TProps>(), {
|
||||
isShort: false
|
||||
isShort: false,
|
||||
listData: () => ([])
|
||||
})
|
||||
const emit = defineEmits<TEmits>()
|
||||
const listRef = ref<HTMLElement | null>(null)
|
||||
|
@ -37,7 +37,7 @@ import api from '@/api'
|
||||
|
||||
type TProps = {
|
||||
type?: string
|
||||
modelValue: string
|
||||
modelValue?: string
|
||||
}
|
||||
|
||||
type TEmits = {
|
||||
|
@ -6,27 +6,27 @@
|
||||
* @LastEditTime: 2023-06-29 17:53:39
|
||||
-->
|
||||
<template>
|
||||
<el-card class="box-card" shadow="hover" :body-style="{ padding: effectSelect ? '20px' : 0 }">
|
||||
<el-card class="box-card" shadow="hover" :body-style="{ padding: state.effectSelect ? '20px' : 0 }">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<template v-if="effectSelect">
|
||||
<component :is="effectSelect" class="demo" />
|
||||
<template v-if="state.effectSelect">
|
||||
<component :is="state.effectSelect" class="demo" />
|
||||
</template>
|
||||
<div v-show="!effectSelect">无</div>
|
||||
<div v-show="!state.effectSelect">无</div>
|
||||
<span class="title">图片容器</span>
|
||||
<el-popover :visible="visiable" placement="bottom-end" :width="260" trigger="click">
|
||||
<el-popover :visible="state.visiable" placement="bottom-end" :width="260" trigger="click">
|
||||
<div class="box__header">
|
||||
<el-radio-group v-model="type" size="small">
|
||||
<el-radio-group v-model="state.type" size="small">
|
||||
<el-radio-button label="160">形状</el-radio-button>
|
||||
<el-radio-button label="166">框架</el-radio-button>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
<div class="select__box">
|
||||
<div class="select__box__select-item" @click="select(null)">无</div>
|
||||
<el-image v-for="(item, i) in list" :key="i + 'l'" class="select__box__select-item" :src="item.thumbUrl" fit="contain" @click="select(item.imgUrl)"></el-image>
|
||||
<div class="select__box__select-item" @click="select()">无</div>
|
||||
<el-image v-for="(item, i) in state.list" :key="i + 'l'" class="select__box__select-item" :src="item.thumbUrl" fit="contain" @click="select(item.imgUrl)"></el-image>
|
||||
</div>
|
||||
<template #reference>
|
||||
<el-button class="button" link @click="visiable = !visiable">{{ visiable ? '取消' : '选择' }}</el-button>
|
||||
<el-button class="button" link @click="state.visiable = !state.visiable">{{ state.visiable ? '取消' : '选择' }}</el-button>
|
||||
</template>
|
||||
</el-popover>
|
||||
</div>
|
||||
@ -38,30 +38,49 @@
|
||||
</el-card>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
<script lang="ts" setup>
|
||||
import api from '@/api'
|
||||
import { defineComponent, toRefs, reactive, watch, onMounted, nextTick } from 'vue'
|
||||
import { toRefs, reactive, watch, onMounted, nextTick, defineProps, defineEmits, defineExpose } from 'vue'
|
||||
import { ElRadioGroup, ElRadioButton } from 'element-plus'
|
||||
import wSvg from '@/components/modules/widgets/wSvg/wSvg.vue'
|
||||
import { TGetListResult } from '@/api/material';
|
||||
|
||||
export default defineComponent({
|
||||
components: { ElRadioGroup, ElRadioButton },
|
||||
props: ['modelValue', 'degree'],
|
||||
emits: ['change'],
|
||||
setup(props, context) {
|
||||
const state = reactive({
|
||||
type TProps = {
|
||||
modelValue?: string
|
||||
degree?: number
|
||||
}
|
||||
|
||||
type TEmits = {
|
||||
(event: 'change', data: Record<string, any>): void
|
||||
}
|
||||
|
||||
type TState = {
|
||||
effectSelect: string
|
||||
visiable: boolean
|
||||
type: string
|
||||
list: {thumbUrl: string, imgUrl: string}[]
|
||||
}
|
||||
|
||||
const props = defineProps<TProps>()
|
||||
|
||||
const emit = defineEmits<TEmits>()
|
||||
|
||||
const state = reactive<TState>({
|
||||
// strength: 20, // 强度
|
||||
effectSelect: '', // 选择的模板
|
||||
visiable: false, // 弹出选择层控制
|
||||
type: '166', // 类型
|
||||
list: [],
|
||||
})
|
||||
|
||||
const select = (value: string = '') => {
|
||||
state.visiable = false
|
||||
const setting = JSON.parse(JSON.stringify(wSvg.setting))
|
||||
setting.svgUrl = value
|
||||
context.emit('change', setting)
|
||||
emit('change', setting)
|
||||
}
|
||||
|
||||
|
||||
onMounted(async () => {
|
||||
await nextTick()
|
||||
state.effectSelect = props?.modelValue || ''
|
||||
@ -74,22 +93,19 @@ export default defineComponent({
|
||||
first_id: 2,
|
||||
second_id: state.type,
|
||||
})
|
||||
state.list = res.list.map(({ thumbUrl, imgUrl }: any) => {
|
||||
state.list = res.list.map(({ thumbUrl, imgUrl }) => {
|
||||
return { thumbUrl, imgUrl }
|
||||
})
|
||||
}
|
||||
|
||||
watch(
|
||||
() => state.type,
|
||||
(value) => {
|
||||
getList()
|
||||
},
|
||||
)
|
||||
return {
|
||||
...toRefs(state),
|
||||
defineExpose({
|
||||
select,
|
||||
}
|
||||
},
|
||||
methods: {},
|
||||
})
|
||||
</script>
|
||||
|
||||
|
@ -27,7 +27,7 @@
|
||||
:key="efi + 'effect'"
|
||||
:style="{
|
||||
color: ef.filling && ef.filling.enable && ef.filling.type === 0 ? ef.filling.color : 'transparent',
|
||||
webkitTextStroke: ef.stroke && ef.stroke.enable ? `${ef.stroke.width / coefficient}px ${ef.stroke.color}` : undefined,
|
||||
webkitTextStroke: ef.stroke && ef.stroke.enable ? `${ef.stroke.width / coefficient}px ${ef.stroke.color}` : '',
|
||||
textShadow: ef.shadow && ef.shadow.enable ? `${ef.shadow.offsetX / coefficient}px ${ef.shadow.offsetY / coefficient}px ${ef.shadow.blur / coefficient}px ${ef.shadow.color}` : undefined,
|
||||
backgroundImage: ef.filling && ef.filling.enable ? (ef.filling.type === 0 ? undefined : getGradientOrImg(ef)) : undefined,
|
||||
webkitBackgroundClip: ef.filling && ef.filling.enable ? (ef.filling.type === 0 ? undefined : 'text') : undefined,
|
||||
@ -39,52 +39,61 @@
|
||||
A
|
||||
</div>
|
||||
<span class="title">文字特效</span>
|
||||
<el-popover :visible="visiable" placement="left" :width="220" trigger="click">
|
||||
<el-popover :visible="state.visiable" placement="left" :width="220" trigger="click">
|
||||
<div class="select__box">
|
||||
<div class="select__box__select-item" @click="selectEffect(null)">无</div>
|
||||
<div v-for="(l, li) in list" :key="'list' + li" class="select__box__select-item" @click="selectEffect(l.id)">
|
||||
<div class="select__box__select-item" @click="selectEffect()">无</div>
|
||||
<div v-for="(l, li) in state.list" :key="'list' + li" class="select__box__select-item" @click="selectEffect(l.id)">
|
||||
<img :src="l.cover" />
|
||||
</div>
|
||||
</div>
|
||||
<template #reference>
|
||||
<el-button class="button" link @click="openSet">{{ visiable ? '取消' : '选择' }}</el-button>
|
||||
<el-button class="button" link @click="openSet">{{ state.visiable ? '取消' : '选择' }}</el-button>
|
||||
</template>
|
||||
</el-popover>
|
||||
</div>
|
||||
</template>
|
||||
<!-- filling 描边 stroke 阴影 shadow -->
|
||||
<div v-show="layers && layers.length > 0" class="text item"><span style="width: 65px">强度</span> <el-slider v-model="strength" show-input :maxValue="100" input-size="small" :show-input-controls="false" @input="strengthChange"> </el-slider></div>
|
||||
<div v-show="state.layers && state.layers.length > 0" class="text item"><span style="width: 65px">强度</span> <el-slider v-model="state.strength" show-input :maxValue="100" input-size="small" :show-input-controls="false" @input="strengthChange"> </el-slider></div>
|
||||
<el-collapse-item>
|
||||
<template #title>
|
||||
<b>高级编辑</b>
|
||||
</template>
|
||||
<div class="line"></div>
|
||||
<div style="display: flex; justify-content: space-between">
|
||||
<el-button class="add-layer" size="small" type="primary" link @click="addLayer"> + 新建特效层</el-button> <el-button v-show="layers && layers.length > 0" class="add-layer" size="small" type="primary" link @click="unfold = !unfold">{{ unfold ? '收起' : '展开' }}全部</el-button>
|
||||
<el-button
|
||||
class="add-layer" size="small" type="primary" link
|
||||
@click="addLayer">
|
||||
+ 新建特效层
|
||||
</el-button>
|
||||
<el-button
|
||||
v-show="state.layers && state.layers.length > 0" class="add-layer" size="small"
|
||||
type="primary" link @click="state.unfold = !state.unfold">
|
||||
{{ state.unfold ? '收起' : '展开' }}全部
|
||||
</el-button>
|
||||
</div>
|
||||
<div class="line"></div>
|
||||
<draggable v-model="layers" handle=".sd-yidong" item-key="uuid" v-bind="dragOptions">
|
||||
<draggable v-model="state.layers" handle=".sd-yidong" item-key="uuid" v-bind="dragOptions">
|
||||
<template #item="{ element, index }">
|
||||
<div class="feature__grab-wrap">
|
||||
<div class="layer__title">
|
||||
<i class="icon sd-yidong" /><span style="font-size: 12px"><b>特效层</b> {{ index + 1 }}</span>
|
||||
<i class="icon sd-delete" @click="removeLayer(index)" />
|
||||
</div>
|
||||
<div v-if="element.filling && [0, 2, '0', '2'].includes(element.filling.type)" v-show="unfold" class="feature__item">
|
||||
<div v-if="element.filling && [0, 2, '0', '2'].includes(element.filling.type)" v-show="state.unfold" class="feature__item">
|
||||
<el-checkbox v-model="element.filling.enable" label="填充" class="feature__header" />
|
||||
<color-select v-model="element.filling.color" width="28px" :modes="['纯色', '渐变']" label="" @change="colorChange($event, element.filling)" />
|
||||
</div>
|
||||
<div v-if="element.stroke" v-show="unfold" class="feature__item">
|
||||
<div v-if="element.stroke" v-show="state.unfold" class="feature__item">
|
||||
<el-checkbox v-model="element.stroke.enable" label="描边" class="feature__header" />
|
||||
<el-input-number v-model="element.stroke.width" style="width: 65px; margin-right: 0.5rem" :min="0" size="small" controls-position="right" />
|
||||
<color-select v-model="element.stroke.color" width="28px" label="" @finish="(value) => finish('color', value)" />
|
||||
</div>
|
||||
<div v-if="element.offset" v-show="unfold" class="feature__item">
|
||||
<div v-if="element.offset" v-show="state.unfold" class="feature__item">
|
||||
<el-checkbox v-model="element.offset.enable" label="偏移" class="feature__header" />
|
||||
<numberInput v-model="element.offset.x" style="width: 49.5px; margin-right: 2px" prepend="x" type="simple" />
|
||||
<numberInput v-model="element.offset.y" style="width: 49.5px" prepend="y" type="simple" />
|
||||
</div>
|
||||
<div v-if="element.shadow" v-show="unfold" class="feature__item">
|
||||
<div v-if="element.shadow" v-show="state.unfold" class="feature__item">
|
||||
<el-checkbox v-model="element.shadow.enable" label="阴影" class="feature__header" />
|
||||
<numberInput v-model="element.shadow.blur" prepend="blur" :minValue="0" style="width: 30px; margin-right: 2px" type="simple" />
|
||||
<numberInput v-model="element.shadow.offsetX" prepend="x" style="width: 30px; margin-right: 2px" type="simple" />
|
||||
@ -98,22 +107,46 @@
|
||||
</el-card>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, toRefs, reactive, watch, onMounted, nextTick, computed } from 'vue'
|
||||
<script lang="ts" setup>
|
||||
import {
|
||||
reactive, watch, onMounted, nextTick, computed,
|
||||
defineProps, defineEmits, defineExpose
|
||||
} from 'vue'
|
||||
import colorSelect from '../colorSelect.vue'
|
||||
import { ElInputNumber, ElCheckbox } from 'element-plus'
|
||||
import numberInput from '../numberInput.vue'
|
||||
import draggable from 'vuedraggable'
|
||||
import api from '@/api'
|
||||
import getGradientOrImg from '../../widgets/wText/getGradientOrImg'
|
||||
let froze_font_effect_list: any = []
|
||||
let froze_font_effect_list: Record<string, any>[] = []
|
||||
|
||||
export default defineComponent({
|
||||
components: { colorSelect, ElInputNumber, numberInput, ElCheckbox, draggable },
|
||||
props: ['modelValue', 'degree', 'data'],
|
||||
emits: ['update:modelValue'],
|
||||
setup(props, { emit }) {
|
||||
const state = reactive({
|
||||
type TProps = {
|
||||
modelValue?: Record<string, any>
|
||||
degree?: string | number
|
||||
data: Record<string, any>
|
||||
}
|
||||
|
||||
type TEmits = {
|
||||
(event: 'update:modelValue', data: Record<string, any>[]): void
|
||||
}
|
||||
|
||||
type TState = {
|
||||
strength: number
|
||||
visiable: boolean
|
||||
list: Record<string,any>[]
|
||||
layers: Record<string, any>[]
|
||||
draging: boolean
|
||||
unfold: boolean
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<TProps>(), {
|
||||
modelValue: () => ({}),
|
||||
data: () => ({})
|
||||
})
|
||||
|
||||
const emit = defineEmits<TEmits>()
|
||||
|
||||
const state = reactive<TState>({
|
||||
strength: 50, // 强度
|
||||
visiable: false, // 弹出选择层控制
|
||||
list: [],
|
||||
@ -121,13 +154,14 @@ export default defineComponent({
|
||||
draging: false,
|
||||
unfold: true,
|
||||
})
|
||||
|
||||
const dragOptions = {
|
||||
animation: 300,
|
||||
ghostClass: 'ghost',
|
||||
chosenClass: 'choose',
|
||||
}
|
||||
const coefficient = computed(() => Math.round(160 / 27))
|
||||
let rawData: any = [] // 初始化记录数据,用于强度修改
|
||||
let rawData: Record<string, any>[] = [] // 初始化记录数据,用于强度修改
|
||||
|
||||
onMounted(async () => {
|
||||
await nextTick()
|
||||
@ -144,6 +178,7 @@ export default defineComponent({
|
||||
.reverse()
|
||||
rawData = JSON.parse(JSON.stringify(state.layers))
|
||||
})
|
||||
|
||||
watch(
|
||||
() => state.layers,
|
||||
(v) => {
|
||||
@ -157,12 +192,12 @@ export default defineComponent({
|
||||
)
|
||||
|
||||
// 选中加载特效预设
|
||||
const selectEffect = async (id) => {
|
||||
const selectEffect = async (id?: number) => {
|
||||
state.visiable = false
|
||||
if (id) {
|
||||
const { data } = await api.home.getTempDetail({ id, type: 1 })
|
||||
state.layers = JSON.parse(data)
|
||||
.textEffects.map((x) => {
|
||||
.textEffects.map((x: Record<string, any>) => {
|
||||
x.uuid = String(Math.random())
|
||||
return x
|
||||
})
|
||||
@ -186,10 +221,10 @@ export default defineComponent({
|
||||
rawData = JSON.parse(JSON.stringify(state.layers))
|
||||
}
|
||||
|
||||
const finish = () => {}
|
||||
const finish = (type?: string, value?: string) => {}
|
||||
|
||||
const colorChange = (e: any, item: any) => {
|
||||
const modeStr: any = {
|
||||
const colorChange = (e: Record<string, any>, item: Record<string, any>) => {
|
||||
const modeStr: Record<string, number> = {
|
||||
渐变: 2,
|
||||
纯色: 0,
|
||||
}
|
||||
@ -236,8 +271,8 @@ export default defineComponent({
|
||||
froze_font_effect_list = list
|
||||
} else state.list = froze_font_effect_list
|
||||
}
|
||||
return {
|
||||
...toRefs(state),
|
||||
|
||||
defineExpose({
|
||||
selectEffect,
|
||||
finish,
|
||||
coefficient,
|
||||
@ -249,9 +284,6 @@ export default defineComponent({
|
||||
openSet,
|
||||
colorChange,
|
||||
getGradientOrImg,
|
||||
}
|
||||
},
|
||||
methods: {},
|
||||
})
|
||||
</script>
|
||||
|
||||
|
@ -46,7 +46,7 @@ export default defineComponent({
|
||||
setup(props, { emit }) {
|
||||
const store = useStore()
|
||||
const state: any = reactive({
|
||||
innerColor: '#ffffffff',
|
||||
innerColor: '',
|
||||
// colorLength: 0,
|
||||
// hasEyeDrop: 'EyeDropper' in window,
|
||||
})
|
||||
|
@ -14,6 +14,7 @@ _this.dHistoryParams = store.getters.dHistoryParams
|
||||
|
||||
import keyCodeOptions from './methods/keyCodeOptions'
|
||||
import dealWithCtrl from './methods/dealWithCtrl'
|
||||
import { useStore, Store } from 'vuex'
|
||||
|
||||
const ignoreNode = ['INPUT', 'TEXTAREA']
|
||||
|
||||
@ -36,7 +37,8 @@ let hadDown = false
|
||||
|
||||
const shortcuts = {
|
||||
methods: {
|
||||
handleKeydowm(e: any) {
|
||||
handleKeydowm(store: Store<any>, checkCtrl: number | undefined, instance: any, dealCtrl: (e: any, instance: any) => void) {
|
||||
return (e: any) => {
|
||||
const nodeName = e.target.nodeName
|
||||
if (ignoreNode.indexOf(nodeName) !== -1 || (nodeName === 'DIV' && e.target.contentEditable === 'true')) {
|
||||
return
|
||||
@ -56,14 +58,14 @@ const shortcuts = {
|
||||
// hadDown = false
|
||||
// }
|
||||
if (shift || ctrl) {
|
||||
this.$store.dispatch('updateAltDown', true)
|
||||
clearInterval(this.checkCtrl)
|
||||
this.checkCtrl = setInterval(() => {
|
||||
store.dispatch('updateAltDown', true)
|
||||
clearInterval(checkCtrl)
|
||||
checkCtrl = setInterval(() => {
|
||||
// TODO: 防止组合键导致页面失焦无法操作
|
||||
if (!document.hasFocus()) {
|
||||
clearInterval(this.checkCtrl)
|
||||
clearInterval(checkCtrl)
|
||||
hadDown = false
|
||||
this.$store.dispatch('updateAltDown', false)
|
||||
store.dispatch('updateAltDown', false)
|
||||
}
|
||||
}, 500)
|
||||
}
|
||||
@ -91,7 +93,7 @@ const shortcuts = {
|
||||
// }
|
||||
const withCtrl = e.ctrlKey || e.metaKey
|
||||
if (withCtrl && !(ctrl || alt || shift)) {
|
||||
this.dealCtrl(e)
|
||||
dealCtrl(e, instance)
|
||||
return
|
||||
}
|
||||
// const withAlt = e.altKey
|
||||
@ -113,17 +115,20 @@ const shortcuts = {
|
||||
// e.preventDefault()
|
||||
const f = withShift ? 10 : 1
|
||||
keyCodeOptions(e, { f })
|
||||
},
|
||||
handleKeyup(e) {
|
||||
console.log(e)
|
||||
clearInterval(this.checkCtrl)
|
||||
hadDown = false
|
||||
if (e.key === 'Alt' || e.key === 'Shift' || e.key === 'Control' || e.key === 'Meta') {
|
||||
this.$store.dispatch('updateAltDown', false)
|
||||
}
|
||||
},
|
||||
dealCtrl(e: any) {
|
||||
dealWithCtrl(e, this)
|
||||
handleKeyup(store: Store<any>, checkCtrl: number | undefined) {
|
||||
return (e: any) => {
|
||||
console.log(e)
|
||||
clearInterval(checkCtrl)
|
||||
hadDown = false
|
||||
if (e.key === 'Alt' || e.key === 'Shift' || e.key === 'Control' || e.key === 'Meta') {
|
||||
store.dispatch('updateAltDown', false)
|
||||
}
|
||||
}
|
||||
},
|
||||
dealCtrl(e: any, instance: any) {
|
||||
dealWithCtrl(e, instance)
|
||||
console.log(e.key, e.keyCode)
|
||||
},
|
||||
},
|
||||
|
1
src/types/global.d.ts
vendored
1
src/types/global.d.ts
vendored
@ -62,3 +62,4 @@ interface MouseEvent {
|
||||
layerX: number
|
||||
layerY: number
|
||||
}
|
||||
|
||||
|
@ -8,9 +8,9 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, reactive, toRefs } from 'vue'
|
||||
import { mapActions, mapGetters } from 'vuex'
|
||||
<script lang="ts" setup>
|
||||
import { StyleValue, onMounted, reactive, nextTick } from 'vue'
|
||||
import { useStore } from 'vuex'
|
||||
import api from '@/api'
|
||||
import wGroup from '@/components/modules/widgets/wGroup/wGroup.vue'
|
||||
import Preload from '@/utils/plugins/preload'
|
||||
@ -18,57 +18,66 @@ import FontFaceObserver from 'fontfaceobserver'
|
||||
import { fontWithDraw, font2style } from '@/utils/widgets/loadFontRule'
|
||||
import designBoard from '@/components/modules/layout/designBoard/index.vue'
|
||||
import zoomControl from '@/components/modules/layout/zoomControl/index.vue'
|
||||
import { useSetupMapGetters } from '@/common/hooks/mapGetters'
|
||||
import { useRoute } from 'vue-router'
|
||||
|
||||
type TState = {
|
||||
style: StyleValue
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
components: { designBoard, zoomControl },
|
||||
// mixins: [shortcuts],
|
||||
setup() {
|
||||
const state = reactive({
|
||||
const store = useStore()
|
||||
const route = useRoute()
|
||||
const state = reactive<TState>({
|
||||
style: {
|
||||
left: '0px',
|
||||
},
|
||||
})
|
||||
const { dPage } = useSetupMapGetters(['dPage'])
|
||||
|
||||
return {
|
||||
...toRefs(state),
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['dPage']),
|
||||
},
|
||||
mounted() {
|
||||
this.initGroupJson(JSON.stringify(wGroup.setting))
|
||||
this.$nextTick(() => {
|
||||
this.load()
|
||||
onMounted(() => {
|
||||
store.dispatch('initGroupJson', JSON.stringify(wGroup.setting))
|
||||
// initGroupJson(JSON.stringify(wGroup.setting))
|
||||
nextTick(() => {
|
||||
load()
|
||||
})
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
...mapActions(['initGroupJson', 'setTemplate', 'addGroup']),
|
||||
async load() {
|
||||
let loadFlag = false
|
||||
const { id, tempid, tempType: type } = this.$route.query
|
||||
if (id || tempid) {
|
||||
const { data, width, height } = await api.home[id ? 'getWorks' : 'getTempDetail']({ id: id || tempid, type })
|
||||
const content = JSON.parse(data)
|
||||
const widgets = type == 1 ? content : content.widgets
|
||||
|
||||
if (type == 1) {
|
||||
this.dPage.width = width
|
||||
this.dPage.height = height
|
||||
this.dPage.backgroundColor = '#ffffff00'
|
||||
this.addGroup(content)
|
||||
// ...mapActions(['initGroupJson', 'setTemplate', 'addGroup']),
|
||||
async function load() {
|
||||
let loadFlag = false
|
||||
const { id, tempid, tempType: type } = route.query
|
||||
if (id || tempid) {
|
||||
const postData = {
|
||||
id: Number(id || tempid),
|
||||
type: Number(type)
|
||||
}
|
||||
const { data, width, height } = await api.home[id ? 'getWorks' : 'getTempDetail'](postData)
|
||||
const content = JSON.parse(data)
|
||||
const widgets = Number(type) == 1 ? content : content.widgets
|
||||
|
||||
if (Number(type) == 1) {
|
||||
dPage.value.width = width
|
||||
dPage.value.height = height
|
||||
dPage.value.backgroundColor = '#ffffff00'
|
||||
store.dispatch('addGroup', content)
|
||||
// addGroup(content)
|
||||
} else {
|
||||
this.$store.commit('setDPage', content.page)
|
||||
id ? this.$store.commit('setDWidgets', widgets) : this.setTemplate(widgets)
|
||||
store.commit('setDPage', content.page)
|
||||
if (id) {
|
||||
store.commit('setDWidgets', widgets)
|
||||
} else {
|
||||
store.dispatch('setTemplate', widgets)
|
||||
}
|
||||
}
|
||||
|
||||
await this.$nextTick()
|
||||
await nextTick()
|
||||
|
||||
const imgsData: any = []
|
||||
const svgsData: any = []
|
||||
const fontLoaders: any = []
|
||||
const fontContent: any = {}
|
||||
let fontData: any = []
|
||||
const imgsData: HTMLImageElement[] = []
|
||||
const svgsData: HTMLImageElement[] = []
|
||||
const fontLoaders: Promise<void>[] = []
|
||||
const fontContent: Record<string, string> = {}
|
||||
let fontData: string[] = []
|
||||
widgets.forEach((item: any) => {
|
||||
if (item.fontClass && item.fontClass.value) {
|
||||
const loader = new FontFaceObserver(item.fontClass.value)
|
||||
@ -131,9 +140,7 @@ export default defineComponent({
|
||||
setTimeout(() => {
|
||||
!loadFlag && (window as any).loadFinishToInject('done')
|
||||
}, 60000)
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
|
@ -8,10 +8,10 @@
|
||||
-->
|
||||
<template>
|
||||
<div id="page-design-index" ref="pageDesignIndex" class="page-design-bg-color">
|
||||
<div :style="style" class="top-nav">
|
||||
<div :style="state.style" class="top-nav">
|
||||
<div class="top-nav-wrap">
|
||||
<div class="top-left">
|
||||
<div class="name" @click="jump2home">{{ APP_NAME }}</div>
|
||||
<div class="name" @click="jump2home">{{ state.APP_NAME }}</div>
|
||||
<div class="operation">
|
||||
<div :class="['operation-item', { disable: !undoable }]" @click="undoable ? handleHistory('undo') : ''"><i class="iconfont icon-undo" /></div>
|
||||
<div :class="['operation-item', { disable: !redoable }]" @click="redoable ? handleHistory('redo') : ''"><i class="iconfont icon-redo" /></div>
|
||||
@ -20,7 +20,7 @@
|
||||
<i style="font-size: 20px" class="icon sd-biaochi extra-operation" @click="changeLineGuides" />
|
||||
</el-tooltip>
|
||||
</div>
|
||||
<HeaderOptions ref="options" v-model="isContinue" @change="optionsChange" />
|
||||
<HeaderOptions ref="optionsRef" v-model="state.isContinue" @change="optionsChange" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="page-design-index-wrap">
|
||||
@ -34,28 +34,32 @@
|
||||
<style-panel></style-panel>
|
||||
</div>
|
||||
<!-- 标尺 -->
|
||||
<line-guides :show="showLineGuides" />
|
||||
<line-guides :show="state.showLineGuides" />
|
||||
<!-- 缩放控制 -->
|
||||
<zoom-control ref="zoomControl" />
|
||||
<zoom-control ref="zoomControlRef" />
|
||||
<!-- 右键菜单 -->
|
||||
<right-click-menu />
|
||||
<!-- 旋转缩放组件 -->
|
||||
<Moveable />
|
||||
<!-- 遮罩百分比进度条 -->
|
||||
<ProgressLoading
|
||||
:percent="downloadPercent"
|
||||
:text="downloadText"
|
||||
:percent="state.downloadPercent"
|
||||
:text="state.downloadText"
|
||||
cancelText="取消"
|
||||
@cancel="downloadCancel"
|
||||
@done="downloadPercent = 0"
|
||||
@done="state.downloadPercent = 0"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
<script lang="ts" setup>
|
||||
import _config from '../config'
|
||||
import { defineComponent, reactive, toRefs } from 'vue'
|
||||
import { mapActions, mapGetters } from 'vuex'
|
||||
import {
|
||||
CSSProperties, computed, nextTick,
|
||||
onBeforeUnmount, onMounted, reactive, ref,
|
||||
getCurrentInstance
|
||||
} from 'vue'
|
||||
import { useStore } from 'vuex'
|
||||
import RightClickMenu from '@/components/business/right-click-menu/RcMenu.vue'
|
||||
import Moveable from '@/components/business/moveable/Moveable.vue'
|
||||
import designBoard from '@/components/modules/layout/designBoard/index.vue'
|
||||
@ -66,28 +70,33 @@ import shortcuts from '@/mixins/shortcuts'
|
||||
import wGroup from '@/components/modules/widgets/wGroup/wGroup.vue'
|
||||
import HeaderOptions from './components/HeaderOptions.vue'
|
||||
import ProgressLoading from '@/components/common/ProgressLoading/index.vue'
|
||||
import { useSetupMapGetters } from '@/common/hooks/mapGetters'
|
||||
import { useRoute } from 'vue-router'
|
||||
|
||||
const beforeUnload = function (e: any) {
|
||||
const confirmationMessage = '系统不会自动保存您未修改的内容'
|
||||
;(e || window.event).returnValue = confirmationMessage // Gecko and Trident
|
||||
type TState = {
|
||||
style: CSSProperties
|
||||
downloadPercent: number // 下载进度
|
||||
downloadText: string
|
||||
isContinue: boolean
|
||||
APP_NAME: string
|
||||
showLineGuides: boolean
|
||||
}
|
||||
|
||||
const beforeUnload = function (e: Event): string {
|
||||
const confirmationMessage: string = '系统不会自动保存您未修改的内容';
|
||||
|
||||
(e || window.event).returnValue = (confirmationMessage as any) // Gecko and Trident
|
||||
return confirmationMessage // Gecko and WebKit
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
RightClickMenu,
|
||||
Moveable,
|
||||
HeaderOptions,
|
||||
ProgressLoading,
|
||||
designBoard,
|
||||
zoomControl,
|
||||
lineGuides,
|
||||
},
|
||||
// mixins: [shortcuts],
|
||||
setup() {
|
||||
!_config.isDev && window.addEventListener('beforeunload', beforeUnload)
|
||||
|
||||
const state = reactive({
|
||||
const {
|
||||
dActiveElement, dHistoryParams, dCopyElement, dPage, dZoom
|
||||
} = useSetupMapGetters(['dActiveElement', 'dHistoryParams', 'dCopyElement', 'dPage', 'dZoom'])
|
||||
|
||||
const state = reactive<TState>({
|
||||
style: {
|
||||
left: '0px',
|
||||
},
|
||||
@ -98,93 +107,122 @@ export default defineComponent({
|
||||
APP_NAME: _config.APP_NAME,
|
||||
showLineGuides: false,
|
||||
})
|
||||
const optionsRef = ref<typeof HeaderOptions | null>(null)
|
||||
const zoomControlRef = ref<typeof zoomControl | null>(null)
|
||||
const store = useStore()
|
||||
const route = useRoute()
|
||||
|
||||
// const draw = () => {
|
||||
// state.openDraw = true
|
||||
// }
|
||||
|
||||
function jump2home() {
|
||||
// const fullPath = window.location.href.split('/')
|
||||
// window.open(fullPath[0] + '//' + fullPath[2])
|
||||
window.open('https://xp.palxp.cn/')
|
||||
}
|
||||
return {
|
||||
...toRefs(state),
|
||||
|
||||
defineExpose({
|
||||
jump2home,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['dActiveElement', 'dHistoryParams', 'dCopyElement', 'dPage', 'dZoom']),
|
||||
undoable() {
|
||||
return !(this.dHistoryParams.index === -1 || (this.dHistoryParams === 0 && this.dHistoryParams.length === this.dHistoryParams.maxLength))
|
||||
},
|
||||
redoable() {
|
||||
return !(this.dHistoryParams.index === this.dHistoryParams.length - 1)
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
const undoable = computed(() => {
|
||||
return !(
|
||||
dHistoryParams.value.index === -1 ||
|
||||
(dHistoryParams.value === 0 && dHistoryParams.value.length === dHistoryParams.value.maxLength))
|
||||
})
|
||||
|
||||
const redoable = computed(() => {
|
||||
return !(dHistoryParams.value.index === dHistoryParams.value.length - 1)
|
||||
})
|
||||
// watch: {
|
||||
// $route() {
|
||||
// console.log('change route', this.$route.query)
|
||||
// this.loadData()
|
||||
// },
|
||||
// },
|
||||
mounted() {
|
||||
this.initGroupJson(JSON.stringify(wGroup.setting))
|
||||
window.addEventListener('scroll', this.fixTopBarScroll)
|
||||
|
||||
const { handleKeydowm, handleKeyup, dealCtrl } = shortcuts.methods
|
||||
let checkCtrl: number | undefined
|
||||
|
||||
onMounted(() => {
|
||||
store.dispatch('initGroupJson', JSON.stringify(wGroup.setting))
|
||||
// initGroupJson(JSON.stringify(wGroup.setting))
|
||||
window.addEventListener('scroll', fixTopBarScroll)
|
||||
// window.addEventListener('click', this.clickListener)
|
||||
document.addEventListener('keydown', this.handleKeydowm, false)
|
||||
document.addEventListener('keyup', this.handleKeyup, false)
|
||||
this.loadData()
|
||||
},
|
||||
beforeUnmount() {
|
||||
window.removeEventListener('scroll', this.fixTopBarScroll)
|
||||
const instance = getCurrentInstance()
|
||||
document.addEventListener('keydown', handleKeydowm(store, checkCtrl, instance, dealCtrl), false)
|
||||
document.addEventListener('keyup', handleKeyup(store, checkCtrl), false)
|
||||
loadData()
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
window.removeEventListener('scroll', fixTopBarScroll)
|
||||
const instance = getCurrentInstance()
|
||||
// window.removeEventListener('click', this.clickListener)
|
||||
document.removeEventListener('keydown', this.handleKeydowm, false)
|
||||
document.removeEventListener('keyup', this.handleKeyup, false)
|
||||
document.removeEventListener('keydown', handleKeydowm(store, checkCtrl, instance, dealCtrl), false)
|
||||
document.removeEventListener('keyup', handleKeyup(store, checkCtrl), false)
|
||||
document.oncontextmenu = null
|
||||
},
|
||||
methods: {
|
||||
...mapActions(['selectWidget', 'initGroupJson', 'handleHistory']),
|
||||
...shortcuts.methods,
|
||||
changeLineGuides() {
|
||||
this.showLineGuides = !this.showLineGuides
|
||||
},
|
||||
downloadCancel() {
|
||||
this.downloadPercent = 0
|
||||
this.isContinue = false
|
||||
},
|
||||
loadData() {
|
||||
})
|
||||
// ...mapActions(['selectWidget', 'initGroupJson', 'handleHistory']),
|
||||
|
||||
function handleHistory(data: string) {
|
||||
store.dispatch('handleHistory', data)
|
||||
}
|
||||
|
||||
function changeLineGuides() {
|
||||
state.showLineGuides = !state.showLineGuides
|
||||
}
|
||||
|
||||
function downloadCancel() {
|
||||
state.downloadPercent = 0
|
||||
state.isContinue = false
|
||||
}
|
||||
|
||||
function loadData() {
|
||||
// 初始化加载页面
|
||||
const { id, tempid, tempType } = this.$route.query
|
||||
;(this.$refs as any).options.load(id, tempid, tempType, async () => {
|
||||
;(this.$refs as any).zoomControl.screenChange()
|
||||
await this.$nextTick()
|
||||
const { id, tempid, tempType } = route.query
|
||||
if (!optionsRef.value) return
|
||||
optionsRef.value.load(id, tempid, tempType, async () => {
|
||||
if (!zoomControlRef.value) return
|
||||
zoomControlRef.value.screenChange()
|
||||
await nextTick()
|
||||
// 初始化激活的控件为page
|
||||
this.selectWidget({
|
||||
uuid: '-1',
|
||||
store.dispatch('selectWidget', { uuid: '-1' })
|
||||
// selectWidget({
|
||||
// uuid: '-1',
|
||||
// })
|
||||
})
|
||||
})
|
||||
},
|
||||
zoomSub() {
|
||||
;(this.$refs as any).zoomControl.sub()
|
||||
},
|
||||
zoomAdd() {
|
||||
;(this.$refs as any).zoomControl.add()
|
||||
},
|
||||
save() {
|
||||
;(this.$refs as any).options.save()
|
||||
},
|
||||
fixTopBarScroll() {
|
||||
}
|
||||
|
||||
function zoomSub() {
|
||||
if (!zoomControlRef.value) return
|
||||
zoomControlRef.value.sub()
|
||||
}
|
||||
|
||||
function zoomAdd() {
|
||||
if (!zoomControlRef.value) return
|
||||
zoomControlRef.value.add()
|
||||
}
|
||||
|
||||
function save() {
|
||||
if (!optionsRef.value) return
|
||||
optionsRef.value.save()
|
||||
}
|
||||
|
||||
function fixTopBarScroll() {
|
||||
const scrollLeft = document.documentElement.scrollLeft || document.body.scrollLeft
|
||||
this.style.left = `-${scrollLeft}px`
|
||||
},
|
||||
clickListener(e: Event) {
|
||||
state.style.left = `-${scrollLeft}px`
|
||||
}
|
||||
|
||||
function clickListener(e: Event) {
|
||||
console.log('click listener', e)
|
||||
},
|
||||
optionsChange({ downloadPercent, downloadText }: any) {
|
||||
this.downloadPercent = downloadPercent
|
||||
this.downloadText = downloadText
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
function optionsChange({ downloadPercent, downloadText }: { downloadPercent: number, downloadText: string }) {
|
||||
state.downloadPercent = downloadPercent
|
||||
state.downloadText = downloadText
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
|
@ -12,10 +12,12 @@
|
||||
<div class="top-left">
|
||||
<div class="name" style="font-size: 15px">在线PSD解析</div>
|
||||
</div>
|
||||
<div style="flex: 1"><el-button plain type="primary" @click="jump2word">说明文档及PSD规范</el-button></div>
|
||||
<el-button v-show="isDone" @click="clear">清空模板</el-button>
|
||||
<div style="flex: 1">
|
||||
<el-button plain type="primary" @click="jump2word">说明文档及PSD规范</el-button>
|
||||
</div>
|
||||
<el-button v-show="state.isDone" @click="clear">清空模板</el-button>
|
||||
<div class="v-tips">
|
||||
<HeaderOptions :isDone="isDone" @change="optionsChange" />
|
||||
<HeaderOptions :isDone="state.isDone" @change="optionsChange" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -23,28 +25,38 @@
|
||||
<div class="page-design-index-wrap">
|
||||
<!-- <widget-panel></widget-panel> -->
|
||||
<design-board class="page-design-wrap" pageDesignCanvasId="page-design-canvas">
|
||||
<div v-if="isDone" class="shelter" :style="{ width: (dPage.width * dZoom) / 100 + 'px', height: (dPage.height * dZoom) / 100 + 'px' }"></div>
|
||||
<div v-if="state.isDone" class="shelter" :style="{ width: (dPage.width * dZoom) / 100 + 'px', height: (dPage.height * dZoom) / 100 + 'px' }"></div>
|
||||
<uploader v-else accept=".psd" :hold="true" :drag="true" class="uploader" @load="selectFile">
|
||||
<div class="uploader__box"><img style="margin-right: 1rem" src="https://cdn.dancf.com/design/svg/icon_psdimport.37e6f23e.svg" /> 在此拖入或选择PSD文件</div>
|
||||
<div class="uploader__box">
|
||||
<img
|
||||
style="margin-right: 1rem"
|
||||
src="https://cdn.dancf.com/design/svg/icon_psdimport.37e6f23e.svg"
|
||||
alt="upload"
|
||||
/> 在此拖入或选择PSD文件
|
||||
</div>
|
||||
</uploader>
|
||||
</design-board>
|
||||
<style-panel v-show="isDone"></style-panel>
|
||||
<style-panel v-show="state.isDone"></style-panel>
|
||||
</div>
|
||||
<!-- 缩放控制 -->
|
||||
<zoom-control v-if="isDone" ref="zoomControl" />
|
||||
<zoom-control v-if="state.isDone" ref="zoomControlRef" />
|
||||
<!-- 右键菜单 -->
|
||||
<right-click-menu />
|
||||
<!-- 旋转缩放组件 -->
|
||||
<Moveable />
|
||||
<!-- 遮罩百分比进度条 -->
|
||||
<ProgressLoading :percent="downloadPercent" :text="downloadText" :cancelText="cancelText" :msg="downloadMsg" @cancel="cancel" @done="downloadPercent = 0" />
|
||||
<ProgressLoading
|
||||
:percent="state.downloadPercent" :text="state.downloadText"
|
||||
:cancelText="state.cancelText" :msg="state.downloadMsg"
|
||||
@cancel="cancel" @done="state.downloadPercent = 0"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, reactive, toRefs, getCurrentInstance, ComponentInternalInstance, onMounted, nextTick } from 'vue'
|
||||
<script lang="ts" setup>
|
||||
import { reactive, onMounted, nextTick, onBeforeMount, ref, getCurrentInstance } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { mapActions, mapGetters, useStore } from 'vuex'
|
||||
import { useStore } from 'vuex'
|
||||
import RightClickMenu from '@/components/business/right-click-menu/RcMenu.vue'
|
||||
import Moveable from '@/components/business/moveable/Moveable.vue'
|
||||
import shortcuts from '@/mixins/shortcuts'
|
||||
@ -54,16 +66,22 @@ import useLoading from '@/common/methods/loading'
|
||||
import uploader from '@/components/common/Uploader/index.vue'
|
||||
import designBoard from '@/components/modules/layout/designBoard/index.vue'
|
||||
import zoomControl from '@/components/modules/layout/zoomControl/index.vue'
|
||||
import HeaderOptions from './components/UploadTemplate.vue'
|
||||
import HeaderOptions, { TEmitChangeData } from './components/UploadTemplate.vue'
|
||||
import ProgressLoading from '@/components/common/ProgressLoading/index.vue'
|
||||
// import MyWorker from '@/utils/plugins/webWorker'
|
||||
import { processPSD2Page } from '@/utils/plugins/psd'
|
||||
import { useSetupMapGetters } from '@/common/hooks/mapGetters'
|
||||
|
||||
export default defineComponent({
|
||||
components: { RightClickMenu, Moveable, uploader, designBoard, zoomControl, HeaderOptions, ProgressLoading },
|
||||
mixins: [shortcuts],
|
||||
setup() {
|
||||
const state = reactive({
|
||||
type TState = {
|
||||
isDone: boolean
|
||||
downloadPercent: number // 下载进度
|
||||
downloadText: string
|
||||
downloadMsg: string
|
||||
cancelText: string
|
||||
}
|
||||
|
||||
// mixins: [shortcuts],
|
||||
const state = reactive<TState>({
|
||||
isDone: true,
|
||||
downloadPercent: 0, // 下载进度
|
||||
downloadText: '',
|
||||
@ -72,12 +90,18 @@ export default defineComponent({
|
||||
})
|
||||
const store = useStore()
|
||||
const route = useRoute()
|
||||
const { proxy }: any = getCurrentInstance() as ComponentInternalInstance
|
||||
let loading: any = null
|
||||
|
||||
const { dPage, dZoom } = useSetupMapGetters(['dPage', 'dZoom'])
|
||||
const zoomControlRef = ref<typeof zoomControl | null>()
|
||||
|
||||
let loading: ReturnType<typeof useLoading> | null = null
|
||||
// const myWorker = new MyWorker('loadPSD')
|
||||
|
||||
onMounted(async () => {
|
||||
await nextTick()
|
||||
if (zoomControlRef.value){
|
||||
zoomControlRef.value.screenChange()
|
||||
}
|
||||
state.isDone = false
|
||||
})
|
||||
|
||||
@ -86,13 +110,15 @@ export default defineComponent({
|
||||
link_element.setAttribute('src', '/psd.js')
|
||||
document.head.appendChild(link_element)
|
||||
}
|
||||
async function selectFile(file: any) {
|
||||
|
||||
async function selectFile(file: File) {
|
||||
loading = useLoading()
|
||||
await loadPSD(file)
|
||||
loading.close()
|
||||
state.isDone = true
|
||||
}
|
||||
async function loadPSD(file: any) {
|
||||
|
||||
async function loadPSD(file: File) {
|
||||
// const { compositeBuffer, psdFile } = await myWorker.start(file)
|
||||
const data = await processPSD2Page(file)
|
||||
|
||||
@ -111,7 +137,7 @@ export default defineComponent({
|
||||
|
||||
const { width, height, background: bg } = data
|
||||
store.commit('setDPage', Object.assign(store.getters.dPage, { width, height, backgroundColor: bg.color, backgroundImage: bg.image }))
|
||||
await proxy?.loadDone()
|
||||
await loadDone()
|
||||
}, 10)
|
||||
}
|
||||
|
||||
@ -123,56 +149,62 @@ export default defineComponent({
|
||||
state.isDone = false
|
||||
}
|
||||
|
||||
const optionsChange = ({ downloadPercent, downloadText, downloadMsg = '', cancelText = '' }: any) => {
|
||||
const optionsChange = ({ downloadPercent, downloadText, downloadMsg = '', cancelText = '' }: TEmitChangeData) => {
|
||||
typeof downloadPercent === 'number' && (state.downloadPercent = downloadPercent)
|
||||
state.downloadText = downloadText
|
||||
state.downloadMsg = downloadMsg
|
||||
state.cancelText = cancelText
|
||||
}
|
||||
|
||||
const cancel = () => {
|
||||
state.downloadPercent = 100
|
||||
window.open(`${window.location.protocol + '//' + window.location.host}/home?id=${route.query.id}`)
|
||||
}
|
||||
|
||||
return {
|
||||
...toRefs(state),
|
||||
const {handleKeydowm, handleKeyup, dealCtrl} = shortcuts.methods
|
||||
|
||||
// ...mapGetters(['dPage', 'dZoom']),
|
||||
|
||||
let checkCtrl: number | undefined
|
||||
|
||||
onMounted(() => {
|
||||
const instance = getCurrentInstance()
|
||||
document.addEventListener('keydown', handleKeydowm(store, checkCtrl, instance, dealCtrl), false)
|
||||
document.addEventListener('keyup', handleKeyup(store, checkCtrl), false)
|
||||
loadJS()
|
||||
})
|
||||
|
||||
onBeforeMount(() => {
|
||||
const instance = getCurrentInstance()
|
||||
document.removeEventListener('keydown', handleKeydowm(store, checkCtrl, instance, dealCtrl), false)
|
||||
document.removeEventListener('keyup', handleKeyup(store, checkCtrl), false)
|
||||
document.oncontextmenu = null
|
||||
})
|
||||
// ...mapActions(['selectWidget']),
|
||||
|
||||
async function loadDone() {
|
||||
await nextTick()
|
||||
if (!zoomControlRef.value) return
|
||||
zoomControlRef.value.screenChange()
|
||||
setTimeout(() => {
|
||||
store.dispatch('selectWidget', { uuid: '-1' })
|
||||
// selectWidget({
|
||||
// uuid: '-1',
|
||||
// })
|
||||
// this.$store.commit('setShowMoveable', false)
|
||||
}, 100)
|
||||
}
|
||||
function jump2word() {
|
||||
window.open('https://xp.palxp.cn/#/articles/1687855172725')
|
||||
// window.open('https://kdocs.cn/l/clmBsIkhve8d')
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
loadJS,
|
||||
selectFile,
|
||||
clear,
|
||||
cancel,
|
||||
optionsChange,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['dPage', 'dZoom']),
|
||||
},
|
||||
async mounted() {
|
||||
document.addEventListener('keydown', this.handleKeydowm, false)
|
||||
document.addEventListener('keyup', this.handleKeyup, false)
|
||||
this.loadJS()
|
||||
},
|
||||
beforeUnmount() {
|
||||
document.removeEventListener('keydown', this.handleKeydowm, false)
|
||||
document.removeEventListener('keyup', this.handleKeyup, false)
|
||||
document.oncontextmenu = null
|
||||
},
|
||||
methods: {
|
||||
...mapActions(['selectWidget']),
|
||||
async loadDone() {
|
||||
await this.$nextTick()
|
||||
;(this.$refs as any).zoomControl.screenChange()
|
||||
setTimeout(() => {
|
||||
this.selectWidget({
|
||||
uuid: '-1',
|
||||
})
|
||||
// this.$store.commit('setShowMoveable', false)
|
||||
}, 100)
|
||||
},
|
||||
jump2word() {
|
||||
window.open('https://xp.palxp.cn/#/articles/1687855172725')
|
||||
// window.open('https://kdocs.cn/l/clmBsIkhve8d')
|
||||
},
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
|
@ -13,19 +13,10 @@
|
||||
</tool-tip>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue'
|
||||
<script lang="ts" setup>
|
||||
import toolTip from '@/components/common/PopoverTip.vue'
|
||||
|
||||
export default defineComponent({
|
||||
components: { toolTip },
|
||||
setup() {
|
||||
const content = '本站为个人项目,所使用素材图片等均为网络收集而来,下载之作品仅供学习研究或欣赏目的而使用,无法提供商用授权哦。'
|
||||
return {
|
||||
content,
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
|
@ -6,10 +6,10 @@
|
||||
* @LastEditTime: 2023-12-11 12:40:59
|
||||
-->
|
||||
<template>
|
||||
<div class="top-title"><el-input v-model="title" placeholder="未命名的设计" class="input-wrap" /></div>
|
||||
<div class="top-title"><el-input v-model="state.title" placeholder="未命名的设计" class="input-wrap" /></div>
|
||||
<div class="top-icon-wrap">
|
||||
<template v-if="tempEditing">
|
||||
<span style="color: #999; font-size: 14px; margin-right: 0.5rem">{{ stateBollean ? '启用' : '停用' }}</span> <el-switch v-model="stateBollean" @change="stateChange" />
|
||||
<span style="color: #999; font-size: 14px; margin-right: 0.5rem">{{ state.stateBollean ? '启用' : '停用' }}</span> <el-switch v-model="state.stateBollean" @change="stateChange" />
|
||||
<div class="divide__line">|</div>
|
||||
<el-button plain type="primary" @click="saveTemp">保存模板</el-button>
|
||||
<el-button @click="$store.commit('managerEdit', false)">取消</el-button>
|
||||
@ -18,16 +18,16 @@
|
||||
<!-- <el-button @click="draw">绘制(测试)</el-button> -->
|
||||
<el-button size="large" class="primary-btn" :disabled="tempEditing" @click="save(false)">保存</el-button>
|
||||
<copyRight>
|
||||
<el-button :loading="loading" size="large" class="primary-btn" :disabled="tempEditing" plain type="primary" @click="download">下载作品</el-button>
|
||||
<el-button :loading="state.loading" size="large" class="primary-btn" :disabled="tempEditing" plain type="primary" @click="download">下载作品</el-button>
|
||||
</copyRight>
|
||||
</div>
|
||||
<!-- 生成图片组件 -->
|
||||
<SaveImage ref="canvasImage" />
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
<script lang="ts" setup>
|
||||
import api from '@/api'
|
||||
import { defineComponent, reactive, toRefs, getCurrentInstance, ComponentInternalInstance } from 'vue'
|
||||
import { reactive, toRefs, defineEmits, defineProps, ref } from 'vue'
|
||||
import { mapGetters, mapActions, useStore } from 'vuex'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import _dl from '@/common/methods/download'
|
||||
@ -38,17 +38,34 @@ import copyRight from './CopyRight.vue'
|
||||
import _config from '@/config'
|
||||
import useConfirm from '@/common/methods/confirm'
|
||||
import wGroup from '@/components/modules/widgets/wGroup/wGroup.vue'
|
||||
import { useSetupMapGetters } from '@/common/hooks/mapGetters'
|
||||
|
||||
export default defineComponent({
|
||||
components: { copyRight, SaveImage },
|
||||
props: ['modelValue'],
|
||||
emits: ['change', 'update:modelValue'],
|
||||
setup(props, context) {
|
||||
const { proxy }: any = getCurrentInstance() as ComponentInternalInstance
|
||||
type TProps = {
|
||||
modelValue?: boolean
|
||||
}
|
||||
|
||||
type TEmits = {
|
||||
(event: 'change', data: {downloadPercent: number, downloadText: string}): void
|
||||
(event: 'update:modelValue', data: boolean): void
|
||||
}
|
||||
|
||||
type TState= {
|
||||
stateBollean: boolean,
|
||||
title: string,
|
||||
loading: boolean,
|
||||
}
|
||||
|
||||
const props = defineProps<TProps>()
|
||||
const emit = defineEmits<TEmits>()
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const store = useStore()
|
||||
const state = reactive({
|
||||
const canvasImage = ref<typeof SaveImage | null>(null)
|
||||
const {
|
||||
dPage, dWidgets, tempEditing, dHistory, dPageHistory
|
||||
} = useSetupMapGetters(['dPage', 'dWidgets', 'tempEditing', 'dHistory', 'dPageHistory'])
|
||||
|
||||
const state = reactive<TState>({
|
||||
stateBollean: false,
|
||||
title: '',
|
||||
loading: false,
|
||||
@ -57,43 +74,45 @@ export default defineComponent({
|
||||
// 保存作品
|
||||
async function save(hasCover: boolean = false) {
|
||||
// Bugs: 历史操作有问题,且page操作未及时入栈 proxy?.dPageHistory
|
||||
if (proxy?.dHistory.length <= 0) {
|
||||
if (dHistory.value.length <= 0) {
|
||||
return
|
||||
}
|
||||
store.commit('setShowMoveable', false) // 清理掉上一次的选择框
|
||||
// console.log(proxy?.dPage, proxy?.dWidgets)
|
||||
const { id, tempid } = route.query
|
||||
const cover = hasCover ? await proxy?.draw() : undefined
|
||||
const widgets = proxy.dWidgets // reviseData()
|
||||
const { id: newId, stat, msg } = await api.home.saveWorks({ cover, id, title: proxy.title || '未命名设计', data: JSON.stringify({ page: proxy.dPage, widgets }), temp_id: tempid, width: proxy.dPage.width, height: proxy.dPage.height })
|
||||
const cover = hasCover ? await draw() : undefined
|
||||
const widgets = dWidgets.value // reviseData()
|
||||
const { id: newId, stat, msg } = await api.home.saveWorks({ cover, id, title: state.title || '未命名设计', data: JSON.stringify({ page: dPage.value, widgets }), temp_id: tempid, width: dPage.value.width, height: dPage.value.height })
|
||||
stat !== 0 ? useNotification('保存成功', '可在"我的作品"中查看') : useNotification('保存失败', msg, { type: 'error' })
|
||||
!id && router.push({ path: '/home', query: { id: newId }, replace: true })
|
||||
store.commit('setShowMoveable', true)
|
||||
}
|
||||
|
||||
// 保存模板
|
||||
async function saveTemp() {
|
||||
const { tempid, tempType: type } = route.query
|
||||
let res = null
|
||||
if (type == 1) {
|
||||
if (Number(type) == 1) {
|
||||
// 保存组件,组合元素要保证在最后一位,才能默认选中
|
||||
if (proxy.dWidgets[0].type === 'w-group') {
|
||||
const group = proxy.dWidgets.shift()
|
||||
if (dWidgets.value[0].type === 'w-group') {
|
||||
const group = dWidgets.value.shift()
|
||||
group.record.width = 0
|
||||
group.record.height = 0
|
||||
proxy.dWidgets.push(group)
|
||||
dWidgets.value.push(group)
|
||||
}
|
||||
// TODO:如果保存组件不存在组合,则添加组合。该功能待优化
|
||||
if (!proxy.dWidgets.some((x) => x.type === 'w-group')) {
|
||||
if (!dWidgets.value.some((x: Record<string, any>) => x.type === 'w-group')) {
|
||||
alert('提交组件必须为组合!')
|
||||
return
|
||||
// proxy.dWidgets.push(wGroup.setting)
|
||||
}
|
||||
res = await api.home.saveTemp({ id: tempid, type, title: proxy.title || '未命名组件', content: JSON.stringify(proxy.dWidgets), width: proxy.dPage.width, height: proxy.dPage.height })
|
||||
} else res = await api.home.saveTemp({ id: tempid, title: proxy.title || '未命名模板', content: JSON.stringify({ page: proxy.dPage, widgets: proxy.dWidgets }), width: proxy.dPage.width, height: proxy.dPage.height })
|
||||
res = await api.home.saveTemp({ id: tempid, type, title: state.title || '未命名组件', content: JSON.stringify(dWidgets.value), width: dPage.value.width, height: dPage.value.height })
|
||||
} else res = await api.home.saveTemp({ id: tempid, title: state.title || '未命名模板', content: JSON.stringify({ page: dPage.value, widgets: dWidgets.value }), width: dPage.value.width, height: dPage.value.height })
|
||||
res.stat != 0 && useNotification('保存成功', '模板内容已变更')
|
||||
}
|
||||
|
||||
// 停用启用
|
||||
async function stateChange(e: any) {
|
||||
async function stateChange(e: string | number | boolean) {
|
||||
const { tempid, tempType: type } = route.query
|
||||
const { stat } = await api.home.saveTemp({ id: tempid, type, state: e ? 1 : 0 })
|
||||
stat != 0 && useNotification('保存成功', '模板内容已变更')
|
||||
@ -103,28 +122,28 @@ export default defineComponent({
|
||||
return
|
||||
}
|
||||
// 临时提示
|
||||
if (proxy.title === '自设计模板') {
|
||||
if (state.title === '自设计模板') {
|
||||
const isPass = await useConfirm('提示', 'PSD自设计作品暂时保存在Github,下载可能失败', 'warning')
|
||||
if (!isPass) {
|
||||
return
|
||||
}
|
||||
}
|
||||
state.loading = true
|
||||
context.emit('update:modelValue', true)
|
||||
context.emit('change', { downloadPercent: 1, downloadText: '正在处理封面' })
|
||||
emit('update:modelValue', true)
|
||||
emit('change', { downloadPercent: 1, downloadText: '正在处理封面' })
|
||||
await save(true)
|
||||
setTimeout(async () => {
|
||||
const { id } = route.query
|
||||
if (id) {
|
||||
const { width, height } = proxy.dPage
|
||||
context.emit('update:modelValue', true)
|
||||
context.emit('change', { downloadPercent: 1, downloadText: '准备合成图片' })
|
||||
const { width, height } = dPage.value
|
||||
emit('update:modelValue', true)
|
||||
emit('change', { downloadPercent: 1, downloadText: '准备合成图片' })
|
||||
state.loading = false
|
||||
let timerCount = 0
|
||||
const animation = setInterval(() => {
|
||||
if (props.modelValue && timerCount < 75) {
|
||||
timerCount += RandomNumber(1, 10)
|
||||
context.emit('change', { downloadPercent: 1 + timerCount, downloadText: '正在合成图片' })
|
||||
emit('change', { downloadPercent: 1 + timerCount, downloadText: '正在合成图片' })
|
||||
} else {
|
||||
clearInterval(animation)
|
||||
}
|
||||
@ -132,12 +151,12 @@ export default defineComponent({
|
||||
await _dl.downloadImg(api.home.download({ id, width, height }) + '&r=' + Math.random(), (progress: number, xhr: any) => {
|
||||
if (props.modelValue) {
|
||||
clearInterval(animation)
|
||||
progress >= timerCount && context.emit('change', { downloadPercent: Number(progress.toFixed(0)), downloadText: '图片生成中' })
|
||||
progress >= timerCount && emit('change', { downloadPercent: Number(progress.toFixed(0)), downloadText: '图片生成中' })
|
||||
} else {
|
||||
xhr.abort()
|
||||
}
|
||||
})
|
||||
context.emit('change', { downloadPercent: 100, downloadText: '图片下载中' })
|
||||
emit('change', { downloadPercent: 100, downloadText: '图片下载中' })
|
||||
}
|
||||
}, 100)
|
||||
}
|
||||
@ -145,21 +164,10 @@ export default defineComponent({
|
||||
return Math.ceil(Math.random() * (max - min)) + min
|
||||
}
|
||||
|
||||
return {
|
||||
...toRefs(state),
|
||||
download,
|
||||
save,
|
||||
saveTemp,
|
||||
stateChange,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['dPage', 'dWidgets', 'tempEditing', 'dHistory', 'dPageHistory']),
|
||||
},
|
||||
methods: {
|
||||
...mapActions(['pushHistory', 'addGroup']),
|
||||
async load(id: any, tempId: any, type: any, cb: Function) {
|
||||
if (this.$route.name !== 'Draw') {
|
||||
// ...mapActions(['pushHistory', 'addGroup']),
|
||||
|
||||
async function load(id: number, tempId: number, type: number, cb: () => void) {
|
||||
if (route.name !== 'Draw') {
|
||||
await useFontStore.init() // 初始化加载字体
|
||||
}
|
||||
const apiName = tempId && !id ? 'getTempDetail' : 'getWorks'
|
||||
@ -170,31 +178,43 @@ export default defineComponent({
|
||||
const { data: content, title, state, width, height } = await api.home[apiName]({ id: id || tempId, type })
|
||||
if (content) {
|
||||
const data = JSON.parse(content)
|
||||
this.stateBollean = !!state
|
||||
this.title = title
|
||||
this.$store.commit('setShowMoveable', false) // 清理掉上一次的选择框
|
||||
state.stateBollean = !!state
|
||||
state.title = title
|
||||
store.commit('setShowMoveable', false) // 清理掉上一次的选择框
|
||||
// this.$store.commit('setDWidgets', [])
|
||||
if (type == 1) {
|
||||
// 加载文字组合组件
|
||||
this.dPage.width = width
|
||||
this.dPage.height = height
|
||||
this.addGroup(data)
|
||||
dPage.value.width = width
|
||||
dPage.value.height = height
|
||||
store.dispatch('addGroup', data)
|
||||
// addGroup(data)
|
||||
} else {
|
||||
this.$store.commit('setDPage', data.page)
|
||||
id ? this.$store.commit('setDWidgets', data.widgets) : this.$store.dispatch('setTemplate', data.widgets)
|
||||
store.commit('setDPage', data.page)
|
||||
id ? store.commit('setDWidgets', data.widgets) : store.dispatch('setTemplate', data.widgets)
|
||||
}
|
||||
cb()
|
||||
this.pushHistory('请求加载load')
|
||||
store.dispatch('pushHistory', '请求加载load')
|
||||
// pushHistory('请求加载load')
|
||||
}
|
||||
},
|
||||
draw() {
|
||||
}
|
||||
|
||||
function draw() {
|
||||
return new Promise((resolve) => {
|
||||
this.$refs.canvasImage.createCover(({ key }) => {
|
||||
if (!canvasImage.value) resolve('')
|
||||
else {
|
||||
canvasImage.value.createCover(({ key }: {key: string}) => {
|
||||
resolve(_config.IMG_URL + key)
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
download,
|
||||
save,
|
||||
saveTemp,
|
||||
stateChange,
|
||||
load,
|
||||
})
|
||||
</script>
|
||||
|
||||
|
@ -11,31 +11,55 @@
|
||||
<SaveImage ref="canvasImage" />
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
<script lang="ts" setup>
|
||||
import api from '@/api'
|
||||
import { defineComponent, reactive, toRefs, getCurrentInstance, ComponentInternalInstance } from 'vue'
|
||||
import { mapGetters, useStore } from 'vuex'
|
||||
import { reactive, ref } from 'vue'
|
||||
import { useStore } from 'vuex'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import useNotification from '@/common/methods/notification'
|
||||
import SaveImage from '@/components/business/save-download/CreateCover.vue'
|
||||
import { useFontStore } from '@/common/methods/fonts'
|
||||
import _config from '@/config'
|
||||
import github from '@/api/github'
|
||||
import { useSetupMapGetters } from '@/common/hooks/mapGetters'
|
||||
|
||||
export default defineComponent({
|
||||
components: { SaveImage },
|
||||
props: ['modelValue', 'isDone'],
|
||||
emits: ['change', 'update:modelValue'],
|
||||
setup(props, context) {
|
||||
const { proxy }: any = getCurrentInstance() as ComponentInternalInstance
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const store = useStore()
|
||||
const state: any = reactive({
|
||||
type TProps = {
|
||||
modelValue?: string
|
||||
isDone?: boolean
|
||||
}
|
||||
|
||||
export type TEmitChangeData = {
|
||||
downloadPercent: number | null
|
||||
downloadText: string
|
||||
downloadMsg?: string
|
||||
cancelText?: string
|
||||
}
|
||||
|
||||
type TEmits = {
|
||||
(event: 'change', data: TEmitChangeData): void
|
||||
(event: 'update:modelValue', data: string): void
|
||||
}
|
||||
|
||||
type TState = {
|
||||
stateBollean: false,
|
||||
title: '',
|
||||
loading: false,
|
||||
}
|
||||
|
||||
const { dPage, dWidgets } = useSetupMapGetters(['dPage', 'dWidgets'])
|
||||
|
||||
const props = defineProps<TProps>()
|
||||
const emit = defineEmits<TEmits>()
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const store = useStore()
|
||||
|
||||
const canvasImage = ref<typeof SaveImage | null>(null)
|
||||
const state = reactive<TState>({
|
||||
stateBollean: false,
|
||||
title: '',
|
||||
loading: false,
|
||||
canvasImage: null,
|
||||
})
|
||||
|
||||
useFontStore.init() // 初始化加载字体
|
||||
@ -43,28 +67,32 @@ export default defineComponent({
|
||||
// 生成封面
|
||||
const draw = () => {
|
||||
return new Promise((resolve) => {
|
||||
state.canvasImage.createCover(({ key }: any) => {
|
||||
if (!canvasImage.value) {
|
||||
resolve('')
|
||||
} else {
|
||||
canvasImage.value.createCover(({ key }: { key: string }) => {
|
||||
resolve(_config.IMG_URL + key)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
let addition = 0 // 累加大小
|
||||
let lenCount = 0 // 全部大小
|
||||
let lens = 0 // 任务数
|
||||
const queue: any[] = [] // 队列
|
||||
let widgets: any = []
|
||||
let page: any = {}
|
||||
const queue: { type: string, imgUrl: string }[] = [] // 队列
|
||||
let widgets: { type: string, imgUrl: string }[] = []
|
||||
let page: Record<string, any> = {}
|
||||
|
||||
async function prepare() {
|
||||
store.commit('setShowMoveable', false) // 清理掉上一次的选择框
|
||||
addition = 0
|
||||
lenCount = 0
|
||||
widgets = proxy.dWidgets
|
||||
page = proxy.dPage
|
||||
widgets = dWidgets.value
|
||||
page = dPage.value
|
||||
|
||||
if (page.backgroundImage) {
|
||||
context.emit('change', { downloadPercent: 1, downloadText: '正在准备上传', downloadMsg: '请等待..' })
|
||||
emit('change', { downloadPercent: 1, downloadText: '正在准备上传', downloadMsg: '请等待..' })
|
||||
page.backgroundImage = await github.putPic(page.backgroundImage.split(',')[1])
|
||||
}
|
||||
|
||||
@ -81,11 +109,12 @@ export default defineComponent({
|
||||
async function uploadImgs() {
|
||||
if (queue.length > 0) {
|
||||
const item = queue.pop()
|
||||
if (!item) return
|
||||
const url = await github.putPic(item.imgUrl.split(',')[1])
|
||||
addition += item.imgUrl.length
|
||||
let downloadPercent: any = (addition / lenCount) * 100
|
||||
let downloadPercent: number | null = (addition / lenCount) * 100
|
||||
downloadPercent >= 100 && (downloadPercent = null)
|
||||
context.emit('change', { downloadPercent, downloadText: '上传资源中', downloadMsg: `已完成:${lens - queue.length} / ${lens}` })
|
||||
emit('change', { downloadPercent, downloadText: '上传资源中', downloadMsg: `已完成:${lens - queue.length} / ${lens}` })
|
||||
item.imgUrl = url
|
||||
uploadImgs()
|
||||
} else {
|
||||
@ -94,22 +123,16 @@ export default defineComponent({
|
||||
}
|
||||
|
||||
const uploadTemplate = async () => {
|
||||
context.emit('change', { downloadPercent: 95, downloadText: '正在处理封面', downloadMsg: '即将结束...' })
|
||||
emit('change', { downloadPercent: 95, downloadText: '正在处理封面', downloadMsg: '即将结束...' })
|
||||
const cover = await draw()
|
||||
const { id, stat, msg } = await api.home.saveWorks({ cover, title: '自设计模板', data: JSON.stringify({ page, widgets }), width: page.width, height: page.height })
|
||||
stat !== 0 ? useNotification('保存成功', '可在"我的模板"中查看') : useNotification('保存失败', msg, { type: 'error' })
|
||||
router.push({ path: '/psd', query: { id }, replace: true })
|
||||
context.emit('change', { downloadPercent: 99.99, downloadText: '上传完成', cancelText: '查看我的作品' }) // 关闭弹窗
|
||||
emit('change', { downloadPercent: 99.99, downloadText: '上传完成', cancelText: '查看我的作品' }) // 关闭弹窗
|
||||
}
|
||||
|
||||
return {
|
||||
...toRefs(state),
|
||||
defineExpose({
|
||||
prepare,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['dPage', 'dWidgets']),
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user