Merge pull request #71 from JeremyYu-cn/feat-upgrade-vue3

Fix: convert userWrap to composition API and fix panel component problems
This commit is contained in:
Jeremy Yu 2024-03-07 12:25:59 +00:00 committed by GitHub
commit c5b35f4e7d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 356 additions and 299 deletions

View File

@ -29,6 +29,12 @@ export type IGetTempListData = {
top: number
left: number
data?: string
listWidth?: number
gap?: number
thumb?: string
url: string
model?: string
color?: string
}
type IGetTempListResult = TPageRequestResult<IGetTempListData[]>
@ -73,5 +79,12 @@ export const saveMyTemp = (params: Type.Object = {}) => fetch('design/user/temp'
// 获取作品
export const getWorks = (params: Type.Object = {}) => fetch('design/poster', params, 'get')
type TGetMyDesignParams = {
page: number
pageSize: number
}
type TGetMyDesignResult = TPageRequestResult<IGetTempListData[]>
// 作品列表
export const getMyDesign = (params: Type.Object = {}) => fetch('design/my', params, 'get')
export const getMyDesign = (params: TGetMyDesignParams) => fetch<TGetMyDesignResult>('design/my', params, 'get')

View File

@ -6,6 +6,7 @@
* @LastEditTime: 2023-12-11 11:40:47
*/
import fetch from '@/utils/axios'
import { IGetTempListData } from './home'
// 获取素材分类:
export const getKinds = (params: Type.Object = {}) => fetch('design/cate', params)
@ -75,7 +76,7 @@ export type TGetImageListResult = {
user_id: number
id: string
thumb: string
}
} & IGetTempListData
// 图库列表
export const getImagesList = (params: TGetImageListParams) => fetch<TPageRequestResult<TGetImageListResult[]>>('design/imgs', params, 'get')

View File

@ -9,6 +9,7 @@ import store from '@/store'
import { getImage } from '../getImgDetail'
export type TItem2DataParam = {
id?: string | number
width: number
height: number
url: string

View File

@ -26,7 +26,7 @@ type TModelData = {
ratio?: string
}
type TUploadDoneData = {
export type TUploadDoneData = {
width: number
height: number
url: string

View File

@ -7,7 +7,7 @@
<div v-show="activeTab === 0" class="style-wrap">
<div v-show="showGroupCombined" style="padding: 2rem 0">
<el-button plain type="primary" class="gounp__btn" @click="handleCombine">成组</el-button>
<icon-item-select label="" :data="alignIconList" @finish="alignAction" />
<icon-item-select label="" :data="iconList" @finish="alignAction" />
</div>
<component :is="dActiveElement.type + '-style'" v-show="!showGroupCombined" v-if="dActiveElement.type" />
</div>

View File

@ -22,78 +22,74 @@
</div>
</template>
<script lang="ts">
const NAME = 'text-list-wrap'
<script lang="ts" setup>
// const NAME = 'text-list-wrap'
import wText from '../../widgets/wText/wText.vue'
import { mapActions, useStore } from 'vuex'
import { getCurrentInstance, ComponentInternalInstance } from 'vue'
import { useStore } from 'vuex'
export default {
name: NAME,
setup() {
const store: any = useStore()
const { proxy } = getCurrentInstance() as ComponentInternalInstance
const selectBasicText = (item: any) => {
store.commit('setShowMoveable', false) //
let setting = JSON.parse(JSON.stringify(wText.setting))
setting.text = '双击编辑文字' // item.text
setting.width = item.fontSize * setting.text.length
setting.fontSize = item.fontSize
setting.fontWeight = item.fontWeight
const { width: pW, height: pH } = store.getters.dPage
setting.left = pW / 2 - item.fontSize * 3
setting.top = pH / 2 - item.fontSize / 2
;(proxy as any).addWidget(setting)
}
const dragStart = (e: Element, item: any) => {
store.commit('setDraging', true)
store.commit('selectItem', { data: { value: item }, type: 'text' })
}
return {
selectBasicText,
dragStart,
}
},
data() {
return {
basicTextList: [
// {
// text: '',
// fontSize: 96,
// fontWeight: 'bold',
// },
{
text: '+ 添加文字',
fontSize: 60,
fontWeight: 'normal',
},
// {
// text: '+ ',
// fontSize: 40,
// fontWeight: 'normal',
// },
// {
// text: '',
// fontSize: 36,
// fontWeight: 'normal',
// },
// {
// text: '',
// fontSize: 28,
// fontWeight: 'normal',
// },
],
}
},
methods: {
...mapActions(['addWidget']),
},
type TBasicTextData = {
text: string
fontSize: number
fontWeight: string
}
const store = useStore()
const selectBasicText = (item: TBasicTextData) => {
store.commit('setShowMoveable', false) //
let setting = JSON.parse(JSON.stringify(wText.setting))
setting.text = '双击编辑文字' // item.text
setting.width = item.fontSize * setting.text.length
setting.fontSize = item.fontSize
setting.fontWeight = item.fontWeight
const { width: pW, height: pH } = store.getters.dPage
setting.left = pW / 2 - item.fontSize * 3
setting.top = pH / 2 - item.fontSize / 2
store.dispatch('addWidget', setting)
// addWidget(setting)
}
const dragStart = (_: MouseEvent, item: any) => {
store.commit('setDraging', true)
store.commit('selectItem', { data: { value: item }, type: 'text' })
}
const basicTextList: TBasicTextData[] = [
// {
// text: '',
// fontSize: 96,
// fontWeight: 'bold',
// },
{
text: '+ 添加文字',
fontSize: 60,
fontWeight: 'normal',
},
// {
// text: '+ ',
// fontSize: 40,
// fontWeight: 'normal',
// },
// {
// text: '',
// fontSize: 36,
// fontWeight: 'normal',
// },
// {
// text: '',
// fontSize: 28,
// fontWeight: 'normal',
// },
]
defineExpose({
selectBasicText,
dragStart,
})
// ...mapActions(['addWidget'])
</script>
<style lang="less" scoped>

View File

@ -21,54 +21,55 @@
</div>
</template>
<script>
<script lang="ts" setup>
//
const NAME = 'tool-list-wrap'
// const NAME = 'tool-list-wrap'
// import api from '@/api'
import { mapActions, mapGetters } from 'vuex'
import { ref, onMounted } from 'vue'
import { useStore } from 'vuex'
import { useRoute } from 'vue-router'
import wQrcode from '../../widgets/wQrcode/wQrcode.vue'
import imageCutout from '@/components/business/image-cutout'
import { useSetupMapGetters } from '@/common/hooks/mapGetters'
export default {
name: NAME,
components: { imageCutout },
data() {
return {
loadDone: false,
}
},
computed: {
...mapGetters(['dPage']),
},
const store = useStore()
const route = useRoute()
mounted() {
// this.getDataList()
setTimeout(() => {
const { koutu } = this.$route.query
koutu && this.openImageCutout()
}, 300)
},
methods: {
...mapActions(['addWidget']),
async getDataList() {
if (this.loadDone || this.loading) {
return
}
this.loading = true
this.page += 1
},
addQrcode() {
this.$store.commit('setShowMoveable', false) //
let setting = JSON.parse(JSON.stringify(wQrcode.setting))
const { width: pW, height: pH } = this.dPage
setting.left = pW / 2 - setting.width / 2
setting.top = pH / 2 - setting.height / 2
this.addWidget(setting)
},
openImageCutout() {
this.$refs.imageCutout.open()
},
},
const loadDone = ref(false)
const imageCutoutRef = ref<typeof imageCutout | null>(null)
const { dPage } = useSetupMapGetters(['dPage'])
onMounted(() => {
// this.getDataList()
setTimeout(() => {
const { koutu } = route.query
koutu && openImageCutout()
}, 300)
})
// ...mapActions(['addWidget'])
// async function getDataList() {
// if (loadDone || loading) {
// return
// }
// loading = true
// page += 1
// }
function addQrcode() {
store.commit('setShowMoveable', false) //
let setting = JSON.parse(JSON.stringify(wQrcode.setting))
const { width: pW, height: pH } = dPage.value
setting.left = pW / 2 - setting.width / 2
setting.top = pH / 2 - setting.height / 2
store.dispatch('addWidget', setting)
// addWidget(setting)
}
function openImageCutout() {
if (!imageCutoutRef.value) return
imageCutoutRef.value.open()
}
</script>

View File

@ -7,206 +7,250 @@
-->
<template>
<div class="wrap">
<el-tabs v-model="tabActiveName" :stretch="true" class="tabs" @tab-change="tabChange">
<el-tabs v-model="state.tabActiveName" :stretch="true" class="tabs" @tab-change="tabChange">
<el-tab-pane label="资源管理" name="pics"> </el-tab-pane>
<el-tab-pane label="我的作品" name="design"> </el-tab-pane>
</el-tabs>
<div v-show="tabActiveName === 'pics'">
<uploader v-model="percent" class="upload" @done="uploadDone">
<div v-show="state.tabActiveName === 'pics'">
<uploader v-model="state.percent" class="upload" @done="uploadDone">
<el-button class="upload-btn" plain>上传图片 <i class="iconfont icon-upload" /></el-button>
</uploader>
<el-button class="upload-btn upload-psd" plain type="primary" @click="openPSD">上传 PSD 模板</el-button>
<div style="margin: 1rem; height: 100vh">
<photo-list ref="imgListRef" :edit="editOptions.photo" :isDone="isDone" :listData="imgList" @load="load" @drag="dragStart" @select="selectImg" />
<photo-list
ref="imgListRef"
:edit="state.editOptions.photo" :isDone="state.isDone"
:listData="state.imgList"
@load="load" @drag="dragStart" @select="selectImg"
/>
</div>
</div>
<div v-show="tabActiveName === 'design'" class="wrap">
<div v-show="state.tabActiveName === 'design'" class="wrap">
<ul ref="listRef" v-infinite-scroll="loadDesign" class="infinite-list" :infinite-scroll-distance="150" style="overflow: auto">
<img-water-fall :edit="editOptions.works" :listData="designList" @select="selectDesign" />
<img-water-fall :edit="state.editOptions.works" :listData="state.designList" @select="selectDesign" />
<!-- <div v-show="loading" class="loading"><i class="el-icon-loading"></i>拼命加载中..</div> -->
<div v-show="isDone" class="loading">全部加载完毕</div>
<div v-show="state.isDone" class="loading">全部加载完毕</div>
</ul>
</div>
</div>
</template>
<script lang="ts">
import { defineComponent, reactive, toRefs, watch, nextTick, ref, onMounted } from 'vue'
import { ElTabPane, ElTabs } from 'element-plus'
<script lang="ts" setup>
import { reactive, toRefs, watch, nextTick, ref, onMounted, defineProps, defineExpose } from 'vue'
import { ElTabPane, ElTabs, TabPaneName } from 'element-plus'
import { useRouter } from 'vue-router'
import { useStore } from 'vuex'
import uploader from '@/components/common/Uploader'
import api from '@/api'
import wImage from '../../widgets/wImage/wImage.vue'
import setImageData from '@/common/methods/DesignFeatures/setImage'
import setImageData, { TItem2DataParam } from '@/common/methods/DesignFeatures/setImage'
import useConfirm from '@/common/methods/confirm'
import { TGetImageListResult } from '@/api/material'
import photoList from './components/photoList.vue'
import imgWaterFall from './components/imgWaterFall.vue'
import { TUploadDoneData } from '@/components/common/Uploader/index.vue'
import { IGetTempListData } from '@/api/home'
export default defineComponent({
components: { uploader, ElTabPane, ElTabs },
props: ['active'],
setup(props) {
const router = useRouter()
const store = useStore()
const state: any = reactive({
prePath: 'user',
percent: { num: 0 }, //
imgList: [],
designList: [],
isDone: false,
editOptions: [],
listRef: null,
imgListRef: null,
tabActiveName: '',
})
let loading = false
let page = 0
let listPage = 0
type TProps = {
active: number
}
const load = (init: boolean) => {
if (init) {
state.imgList = []
page = 0
state.isDone = false
}
if (state.isDone || loading) {
return
}
loading = true
page += 1
api.material.getMyPhoto({ page }).then(({ list }: any) => {
list.length <= 0 ? (state.isDone = true) : (state.imgList = state.imgList.concat(list))
setTimeout(() => {
loading = false
checkHeight(state.imgListRef.getRef(), load)
}, 100)
})
}
const loadDesign = (init: boolean = false) => {
if (init) {
state.designList = []
listPage = 0
state.isDone = false
}
if (state.isDone || loading) {
return
}
loading = true
listPage += 1
api.home.getMyDesign({ page: listPage, pageSize: 10 }).then(({ list }: any) => {
list.length <= 0
? (state.isDone = true)
: (state.designList = state.designList.concat(
list.map((x: any) => {
x.cover = x.cover + '?r=' + Math.random()
return x
}),
))
setTimeout(() => {
loading = false
checkHeight(state.listRef, loadDesign)
}, 100)
})
}
type TState = {
prePath: string,
percent: { num: number }, //
imgList: IGetTempListData[],
designList: IGetTempListData[],
isDone: boolean,
editOptions: Record<string, any>,
tabActiveName: string,
}
function checkHeight(el: any, loadFn: Function) {
//
const isLess = el.offsetHeight > el.firstElementChild.offsetHeight
isLess && loadFn()
}
const props = defineProps<TProps>()
onMounted(() => {
load(true)
nextTick(() => {
state.tabActiveName = 'pics'
})
})
const router = useRouter()
const store = useStore()
const listRef = ref<HTMLElement | null>(null)
const imgListRef = ref<typeof photoList | null>(null)
const selectImg = async (index: number) => {
const item: any = state.imgList[index]
store.commit('setShowMoveable', false) //
let setting = JSON.parse(JSON.stringify(wImage.setting))
const img: any = await setImageData(item)
setting.width = img.width
setting.height = img.height // parseInt(100 / item.value.ratio, 10)
setting.imgUrl = item.url
const { width: pW, height: pH } = store.getters.dPage
setting.left = pW / 2 - img.width / 2
setting.top = pH / 2 - img.height / 2
store.dispatch('addWidget', setting)
}
const deleteImg = async ({ i, item }: any) => {
store.commit('setShowMoveable', false) //
const isPass = await useConfirm('警告', '删除后不可找回,已引用资源将会失效,请谨慎操作', 'warning')
if (!isPass) {
return false
}
const arr = item.url.split('/')
let key = arr.splice(3, arr.length - 1).join('/')
api.material.deleteMyPhoto({ id: item.id, key })
state.imgListRef.delItem(i) //
}
const deleteWorks = async ({ i, item }: any) => {
const isPass = await useConfirm('警告', '删除后不可找回,请确认操作', 'warning')
if (isPass) {
await api.material.deleteMyWorks({ id: item.id })
setTimeout(() => {
router.push({ path: '/home', query: { }, replace: true })
loadDesign(true)
}, 300);
}
}
state.editOptions = {
photo: [
{
name: '删除',
fn: deleteImg,
},
],works: [
{
name: '删除',
fn: deleteWorks,
},
]
}
const dragStart = (index: number) => {
const item = state.imgList[index]
store.commit('selectItem', { data: { value: item }, type: 'image' })
}
const uploadDone = async (res: any) => {
await api.material.addMyPhoto(res)
state.imgList = []
load(true)
}
const state = reactive<TState>({
prePath: 'user',
percent: { num: 0 }, //
imgList: [],
designList: [],
isDone: false,
editOptions: [],
tabActiveName: '',
})
const tabChange = (tabName: string) => {
if (tabName === 'design') {
loadDesign(true)
}
}
let loading = false
let page = 0
let listPage = 0
const selectDesign = async (item: any) => {
// const { id }: any = state.designList[index]
const { id }: any = item
window.open(`${window.location.protocol + '//' + window.location.host}/home?id=${id}`)
const load = (init?: boolean) => {
if (init) {
state.imgList = []
page = 0
state.isDone = false
}
if (state.isDone || loading) {
return
}
loading = true
page += 1
api.material.getMyPhoto({ page }).then(({ list }) => {
if (list.length <= 0) {
state.isDone = true
} else {
state.imgList = state.imgList.concat(list)
}
console.log('state.imgList', state.imgList)
setTimeout(() => {
loading = false
if (!imgListRef.value) return
checkHeight(imgListRef.value.getRef(), load)
}, 100)
})
}
const openPSD = () => {
window.open(router.resolve('/psd').href, '_blank')
}
const loadDesign = (init: boolean = false) => {
if (init) {
state.designList = []
listPage = 0
state.isDone = false
}
if (state.isDone || loading) {
return
}
loading = true
listPage += 1
api.home.getMyDesign({ page: listPage, pageSize: 10 }).then(({ list }) => {
list.length <= 0
? (state.isDone = true)
: (state.designList = state.designList.concat(
list.map((x) => {
x.cover = x.cover + '?r=' + Math.random()
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)
})
}
return {
...toRefs(state),
selectDesign,
loadDesign,
load,
uploadDone,
selectImg,
deleteImg,
dragStart,
tabChange,
openPSD,
}
},
function checkHeight(el: HTMLElement, loadFn: Function) {
//
if (el.offsetHeight && el.firstElementChild) {
const isLess = el.offsetHeight > (el.firstElementChild as HTMLElement).offsetHeight
isLess && loadFn()
}
}
onMounted(() => {
load(true)
nextTick(() => {
state.tabActiveName = 'pics'
})
})
const selectImg = async (index: number) => {
const item = state.imgList[index]
store.commit('setShowMoveable', false) //
let setting = JSON.parse(JSON.stringify(wImage.setting))
const img = await setImageData(item)
setting.width = img.width
setting.height = img.height // parseInt(100 / item.value.ratio, 10)
setting.imgUrl = item.url
const { width: pW, height: pH } = store.getters.dPage
setting.left = pW / 2 - img.width / 2
setting.top = pH / 2 - img.height / 2
store.dispatch('addWidget', setting)
}
type controlImgParam = {
i: number
item: Required<TItem2DataParam>
}
const deleteImg = async ({ i, item }: controlImgParam) => {
store.commit('setShowMoveable', false) //
const isPass = await useConfirm('警告', '删除后不可找回,已引用资源将会失效,请谨慎操作', 'warning')
if (!isPass) {
return false
}
const arr = item.url.split('/')
let key = arr.splice(3, arr.length - 1).join('/')
api.material.deleteMyPhoto({ id: item.id, key })
if (!imgListRef.value) return
imgListRef.value.delItem(i) //
}
const deleteWorks = async ({ i, item }: controlImgParam) => {
const isPass = await useConfirm('警告', '删除后不可找回,请确认操作', 'warning')
if (isPass) {
await api.material.deleteMyWorks({ id: item.id })
setTimeout(() => {
router.push({ path: '/home', query: { }, replace: true })
loadDesign(true)
}, 300);
}
}
state.editOptions = {
photo: [
{
name: '删除',
fn: deleteImg,
},
],
works: [
{
name: '删除',
fn: deleteWorks,
},
]
}
const dragStart = (index: number) => {
const item = state.imgList[index]
store.commit('selectItem', { data: { value: item }, type: 'image' })
}
const uploadDone = async (res: TUploadDoneData) => {
await api.material.addMyPhoto(res)
state.imgList = []
load(true)
}
const tabChange = (tabName: TabPaneName) => {
if (tabName === 'design') {
loadDesign(true)
}
}
const selectDesign = async (item: IGetTempListData) => {
// const { id }: any = state.designList[index]
const { id } = item
window.open(`${window.location.protocol + '//' + window.location.host}/home?id=${id}`)
}
const openPSD = () => {
window.open(router.resolve('/psd').href, '_blank')
}
defineExpose({
selectDesign,
loadDesign,
load,
uploadDone,
selectImg,
deleteImg,
dragStart,
tabChange,
openPSD,
})
</script>

View File

@ -6,7 +6,7 @@
* @Date: 2024-03-06 21:16:00
-->
<template>
<ul ref="listRef" class="img-list-wrap" :style="{ paddingBottom: isShort ? '15px' : '200px' }" @scroll="scrollEvent($event)">
<ul ref="listRef" class="img-list-wrap" :style="{ paddingBottom: props.isShort ? '15px' : '200px' }" @scroll="scrollEvent($event)">
<div class="list">
<div
v-for="(item, i) in state.list" :key="i + 'i'"
@ -18,7 +18,7 @@
@click.stop="select(i)"
@dragstart="dragStart($event, i)"
>
<edit-model v-if="edit" :options="edit" :data="{ item, i }">
<edit-model v-if="props.edit" :options="props.edit" :data="{ item, i }">
<div v-if="item.isDelect" class="list__mask">已删除</div>
<el-image class="img transparent-bg" :src="item.thumb || item.url" :style="{ height: getInnerHeight(item) + 'px' }" lazy loading="lazy" />
</edit-model>
@ -33,7 +33,7 @@
</template>
</div>
</div>
<div v-if="!isDone" v-show="state.loading" class="loading"><i class="el-icon-loading" /> 拼命加载中</div>
<div v-if="!props.isDone" v-show="state.loading" class="loading"><i class="el-icon-loading" /> 拼命加载中</div>
<div v-else class="loading">全部加载完毕</div>
</ul>
</template>
@ -42,11 +42,12 @@
import { reactive, watch, nextTick, defineProps, defineExpose, defineEmits, ref } from 'vue'
import DragHelper from '@/common/hooks/dragHelper'
import setImageData, { TItem2DataParam } from '@/common/methods/DesignFeatures/setImage'
import { IGetTempListData } from '@/api/home';
type TProps = {
listData: TCommonPhotoListData[]
listData: IGetTempListData[]
edit: Record<string, any>
isDone: Record<string, any>
isDone: boolean
isShort: boolean
}
@ -59,10 +60,10 @@ type TEmits = {
type TState = {
loading: boolean
list: TCommonPhotoListData[]
list: IGetTempListData[]
}
const { listData, edit, isDone, isShort } = withDefaults(defineProps<TProps>(), {
const props = withDefaults(defineProps<TProps>(), {
isShort: false
})
const emit = defineEmits<TEmits>()
@ -92,21 +93,21 @@ const mousemove = (e: MouseEvent) => {
}
watch(
() => listData,
async (newList: TCommonPhotoListData[], oldList: TCommonPhotoListData[]) => {
() => props.listData,
async (newList: IGetTempListData[], oldList: IGetTempListData[]) => {
!oldList && (oldList = [])
if (newList.length <= 0) {
state.list.length = 0
return
}
let list = newList.filter((v: TCommonPhotoListData) => !newList.includes(v) || !oldList.includes(v)) // difference
let list = newList.filter((v: IGetTempListData) => !newList.includes(v) || !oldList.includes(v)) // difference
list = JSON.parse(JSON.stringify(list))
const marginRight = 6 //
const limitWidth = (await getFatherWidth()) - marginRight
const standardHeight = 280 //
const neatArr: TCommonPhotoListData[][] = [] //
function factory(cutArr: TCommonPhotoListData[]) {
return new Promise<{ height: number, list: TCommonPhotoListData[] }>((resolve) => {
const neatArr: IGetTempListData[][] = [] //
function factory(cutArr: IGetTempListData[]) {
return new Promise<{ height: number, list: IGetTempListData[] }>((resolve) => {
const lineup = list.shift()
if (!lineup) {
resolve({ height: calculate(cutArr), list: cutArr })
@ -121,7 +122,7 @@ watch(
}
})
}
function calculate(cutArr: TCommonPhotoListData[]) {
function calculate(cutArr: IGetTempListData[]) {
let cumulate = 0
for (const iterator of cutArr) {
const { width, height } = iterator
@ -133,9 +134,9 @@ watch(
if (list.length <= 0) {
return
}
const { list: newList, height } = await factory([(list.shift() as TCommonPhotoListData)])
const { list: newList, height } = await factory([(list.shift() as IGetTempListData)])
neatArr.push(
newList.map((x: TCommonPhotoListData, index: number) => {
newList.map((x: IGetTempListData, index: number) => {
x.listWidth = (x.width / x.height) * height
x.gap = index !== newList.length - 1 ? marginRight : 0
return x
@ -154,7 +155,7 @@ watch(
async function getFatherWidth() {
await nextTick()
if (!listRef.value) return 0
const father = listRef.value.parentElement || listRef.value.parentNode
const father = listRef.value.parentElement ?? listRef.value.parentNode
if (!father) return 0
return (father as HTMLElement).offsetWidth
}

View File

@ -36,7 +36,7 @@ import { useRoute } from 'vue-router'
import api from '@/api'
type TProps = {
type: string
type?: string
modelValue: string
}