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

Feat: Convert image-cutout and picture-selector to Vue3
This commit is contained in:
Jeremy Yu 2024-03-04 12:37:28 +00:00 committed by GitHub
commit 4ff1f70e4d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 350 additions and 196 deletions

View File

@ -62,11 +62,24 @@ export type TGetFontItemData = {
export const getFonts = (params: TGetFontParam = {}) => fetch<TPageRequestResult<TGetFontItemData[]>>('design/fonts', params)
export const getFontSub = (params: Type.Object = {}, extra: any = {}) => fetch('design/font_sub', params, 'get', {}, extra)
type TGetImageListParams = {
page?: number
}
export type TGetImageListResult = {
created_time: string
height: number
width: number
url: string
user_id: number
id: string
}
// 图库列表
export const getImagesList = (params: Type.Object = {}) => fetch('design/imgs', params, 'get')
export const getImagesList = (params: TGetImageListParams) => fetch<TPageRequestResult<TGetImageListResult[]>>('design/imgs', params, 'get')
// 我的上传列表
export const getMyPhoto = (params: Type.Object = {}) => fetch('design/user/image', params)
export const getMyPhoto = (params: TGetImageListParams) => fetch<TPageRequestResult<TGetImageListResult[]>>('design/user/image', params)
export const deleteMyPhoto = (params: Type.Object = {}) => fetch('design/user/image/del', params, 'post')
export const deleteMyWorks = (params: Type.Object = {}) => fetch('design/poster/del', params, 'post')

View File

@ -46,7 +46,7 @@ import { ElProgress } from 'element-plus'
import { UploadFilled } from '@element-plus/icons-vue'
import uploader from '@/components/common/Uploader/index.vue'
import _dl from '@/common/methods/download'
import ImageExtraction from '../ImageExtraction.vue'
import ImageExtraction from '../ImageExtraction/index.vue'
import { selectImageFile, uploadCutPhotoToCloud } from './method'
export type TImageCutoutState = {
@ -100,7 +100,6 @@ defineExpose({
})
const handleUploaderLoad = (file: File) => {
console.log(file)
selectImageFile(state as TImageCutoutState, raw, file, (result, name) => {
fileName = name
const resultImage = URL.createObjectURL(result)

View File

@ -1,106 +0,0 @@
<!--
* @Author: ShawnPhang
* @Date: 2023-10-08 14:15:17
* @Description: 手动抠图 - 修补擦除
* @LastEditors: ShawnPhang <https://m.palxp.cn>
* @LastEditTime: 2023-10-09 01:28:11
-->
<template>
<div>
<el-dialog v-model="show" align-center width="90%" @close="showMatting = false">
<template #header>
<div class="tool-wrap">
<el-button type="primary" plain @click="done">确认应用</el-button>
<el-radio-group v-model="isErasing" style="margin-left: 35px">
<el-radio :label="false" size="large"> <b>修补画笔</b> <i class="icon sd-xiubu" /></el-radio>
<el-radio :label="true" size="large"> <b>擦除画笔</b> <i class="icon sd-cachu" /></el-radio>
</el-radio-group>
<number-slider v-model="radius" class="slider-wrap" label="画笔尺寸" :showInput="false" labelWidth="90px" :maxValue="constants.RADIUS_SLIDER_MAX" :minValue="constants.RADIUS_SLIDER_MIN" :step="constants.RADIUS_SLIDER_STEP" />
<number-slider v-model="hardness" class="slider-wrap" label="画笔硬度" :showInput="false" labelWidth="90px" :maxValue="constants.HARDNESS_SLIDER_MAX" :minValue="constants.HARDNESS_SLIDER_MIN" :step="constants.HARDNESS_SLIDER_STEP" />
</div>
</template>
<matting v-if="showMatting" :hasHeader="false" @register="mattingStart" />
</el-dialog>
</div>
</template>
<script lang="ts">
import { defineComponent, reactive, toRefs, nextTick } from 'vue'
import matting, { MattingType } from '@palxp/image-extraction'
import { ElRadioGroup, ElRadio } from 'element-plus'
import numberSlider from '@/components/modules/settings/numberSlider.vue'
export default defineComponent({
components: { matting, ElRadioGroup, ElRadio, numberSlider },
setup() {
const state: any = reactive({
show: false,
showMatting: false,
isErasing: false,
radius: 0, //
brushSize: '', //
hardness: 0, //
hardnessText: '', //
constants: {},
})
const params: any = { raw: '', result: '' }
let matting: MattingType | any = {}
let callback: any = null //
const mattingStart: any = (mattingOptions: MattingType) => {
mattingOptions.initLoadImages(params.raw, params.result)
state.isErasing = mattingOptions.isErasing
state.radius = mattingOptions.radius
state.hardness = mattingOptions.hardness
state.constants = mattingOptions.constants
matting = mattingOptions
}
const open = async (raw: any, result: any, cb: any) => {
state.show = true
params.raw = raw
params.result = result
await nextTick()
setTimeout(() => {
state.showMatting = true
}, 300)
callback = cb
}
const done = () => {
state.show = false
callback(matting.getResult())
}
return {
...toRefs(state),
open,
done,
mattingStart,
}
},
})
</script>
<style lang="less" scoped>
:deep(.el-dialog__body) {
padding: 0 !important;
}
:deep(.el-dialog__header) {
padding: 10px 35px;
// var(--el-dialog-padding-primary)
}
.tool-wrap {
display: flex;
align-items: center;
}
// .tool-left {
// display: inline-flex;
// flex: 1;
// }
.slider-wrap {
margin-left: 35px;
width: 240px;
}
</style>

View File

@ -0,0 +1,107 @@
<!--
* Old Component File
* @Author: ShawnPhang
* @Date: 2023-10-08 14:15:17
* @Description: 手动抠图 - 修补擦除
* @LastEditors: ShawnPhang <https://m.palxp.cn>
* @LastEditTime: 2023-10-09 01:28:11
-->
<!-- <template>
<div>
<el-dialog v-model="show" align-center width="90%" @close="showMatting = false">
<template #header>
<div class="tool-wrap">
<el-button type="primary" plain @click="done">确认应用</el-button>
<el-radio-group v-model="isErasing" style="margin-left: 35px">
<el-radio :label="false" size="large"> <b>修补画笔</b> <i class="icon sd-xiubu" /></el-radio>
<el-radio :label="true" size="large"> <b>擦除画笔</b> <i class="icon sd-cachu" /></el-radio>
</el-radio-group>
<number-slider v-model="radius" class="slider-wrap" label="画笔尺寸" :showInput="false" labelWidth="90px" :maxValue="constants.RADIUS_SLIDER_MAX" :minValue="constants.RADIUS_SLIDER_MIN" :step="constants.RADIUS_SLIDER_STEP" />
<number-slider v-model="hardness" class="slider-wrap" label="画笔硬度" :showInput="false" labelWidth="90px" :maxValue="constants.HARDNESS_SLIDER_MAX" :minValue="constants.HARDNESS_SLIDER_MIN" :step="constants.HARDNESS_SLIDER_STEP" />
</div>
</template>
<matting v-if="showMatting" :hasHeader="false" @register="mattingStart" />
</el-dialog>
</div>
</template> -->
<script lang="ts">
// import { defineComponent, reactive, toRefs, nextTick } from 'vue'
// import matting, { MattingType } from '@palxp/image-extraction'
// import { ElRadioGroup, ElRadio } from 'element-plus'
// import numberSlider from '@/components/modules/settings/numberSlider.vue'
// export default defineComponent({
// components: { matting, ElRadioGroup, ElRadio, numberSlider },
// setup() {
// const state: any = reactive({
// show: false,
// showMatting: false,
// isErasing: false,
// radius: 0, //
// brushSize: '', //
// hardness: 0, //
// hardnessText: '', //
// constants: {},
// })
// const params: any = { raw: '', result: '' }
// let matting: MattingType | any = {}
// let callback: any = null //
// const mattingStart: any = (mattingOptions: MattingType) => {
// mattingOptions.initLoadImages(params.raw, params.result)
// state.isErasing = mattingOptions.isErasing
// state.radius = mattingOptions.radius
// state.hardness = mattingOptions.hardness
// state.constants = mattingOptions.constants
// matting = mattingOptions
// }
// const open = async (raw: any, result: any, cb: any) => {
// state.show = true
// params.raw = raw
// params.result = result
// await nextTick()
// setTimeout(() => {
// state.showMatting = true
// }, 300)
// callback = cb
// }
// const done = () => {
// state.show = false
// callback(matting.getResult())
// }
// return {
// ...toRefs(state),
// open,
// done,
// mattingStart,
// }
// },
// })
</script>
<style lang="less" scoped>
:deep(.el-dialog__body) {
padding: 0 !important;
}
:deep(.el-dialog__header) {
padding: 10px 35px;
// var(--el-dialog-padding-primary)
}
.tool-wrap {
display: flex;
align-items: center;
}
// .tool-left {
// display: inline-flex;
// flex: 1;
// }
.slider-wrap {
margin-left: 35px;
width: 240px;
}
</style>

View File

@ -0,0 +1,136 @@
<!--
* @Author: ShawnPhang
* @Date: 2023-10-08 14:15:17
* @Description: 手动抠图 - 修补擦除
* @LastEditors: ShawnPhang <https://m.palxp.cn>, Jeremy Yu <https://github.com/JeremyYu-cn>
* @Date: 2024-03-04 09:50:00
-->
<template>
<div>
<el-dialog v-model="state.show" align-center width="90%" @close="state.showMatting = false">
<template #header>
<div class="tool-wrap">
<el-button type="primary" plain @click="done">确认应用</el-button>
<el-radio-group v-model="state.isErasing" style="margin-left: 35px">
<el-radio :value="false" size="large"> <b>修补画笔</b> <i class="icon sd-xiubu" /></el-radio>
<el-radio :value="true" size="large"> <b>擦除画笔</b> <i class="icon sd-cachu" /></el-radio>
</el-radio-group>
<number-slider
v-model="state.radius" class="slider-wrap"
label="画笔尺寸" :showInput="false"
labelWidth="90px"
:maxValue="state.constants?.RADIUS_SLIDER_MAX" :minValue="state.constants?.RADIUS_SLIDER_MIN"
:step="state.constants?.RADIUS_SLIDER_STEP"
/>
<number-slider
v-model="state.hardness" class="slider-wrap"
label="画笔硬度" :showInput="false"
labelWidth="90px"
:maxValue="state.constants?.HARDNESS_SLIDER_MAX" :minValue="state.constants?.HARDNESS_SLIDER_MIN"
:step="state.constants?.HARDNESS_SLIDER_STEP"
/>
</div>
</template>
<matting v-if="state.showMatting" :hasHeader="false" @register="mattingStart" />
</el-dialog>
</div>
</template>
<script setup lang="ts">
import { reactive, toRefs, nextTick, DefineComponent } from 'vue'
import matting, { MattingType } from '@palxp/image-extraction'
import { ElRadioGroup, ElRadio } from 'element-plus'
import numberSlider from '@/components/modules/settings/numberSlider.vue'
type TState = {
show: boolean;
showMatting: boolean;
isErasing: boolean;
radius: number | string;
brushSize: string;
hardness: number | string;
hardnessText: string;
constants: MattingType['constants'] | null;
}
type TParams = {
raw: string
result: string
}
type TCallback = ((base64: string) => void) | null
const props: TParams = {
raw: '',
result: ''
}
let callback: TCallback = null //
const state = reactive<TState>({
show: false,
showMatting: false,
isErasing: false,
radius: 0, //
brushSize: '', //
hardness: 0, //
hardnessText: '', //
constants: null,
})
let mattingParam: MattingType | null
const mattingStart = (mattingOptions: MattingType) => {
mattingOptions.initLoadImages(props.raw, props.result)
state.isErasing = mattingOptions.isErasing
state.radius = mattingOptions.radius
state.hardness = mattingOptions.hardness
state.constants = mattingOptions.constants
mattingParam = mattingOptions
}
const open = async (raw: string, result: string, cb: TCallback) => {
state.show = true
props.raw = raw
props.result = result
await nextTick()
setTimeout(() => {
state.showMatting = true
}, 300)
callback = cb
}
defineExpose({
open
})
const done = () => {
state.show = false
callback && callback(mattingParam?.getResult())
}
</script>
<style lang="less" scoped>
:deep(.el-dialog__body) {
padding: 0 !important;
}
:deep(.el-dialog__header) {
padding: 10px 35px;
// var(--el-dialog-padding-primary)
}
.tool-wrap {
display: flex;
align-items: center;
}
// .tool-left {
// display: inline-flex;
// flex: 1;
// }
.slider-wrap {
margin-left: 35px;
width: 240px;
}
</style>

View File

@ -6,16 +6,16 @@
* @LastEditTime: 2023-10-05 00:04:51
-->
<template>
<el-dialog v-model="dialogVisible" title="选择图片" @close="close">
<el-dialog v-model="state.dialogVisible" title="选择图片" @close="close">
<el-tabs tab-position="left" style="height: 60vh" class="demo-tabs" @tab-change="tabChange">
<el-tab-pane label="我的素材">
<div class="pic__box">
<photo-list :isDone="isDone" :listData="imgList" @load="load" @select="selectImg" />
<photo-list :isDone="state.isDone" :listData="state.imgList" @load="load" @select="selectImg" />
</div>
</el-tab-pane>
<el-tab-pane label="照片图库">
<div class="pic__box">
<photo-list :isDone="isPicsDone" :listData="recommendImgList" @load="loadPic" @select="selectImg($event, recommendImgList)" />
<photo-list :isDone="state.isPicsDone" :listData="state.recommendImgList" @load="loadPic" @select="selectImg($event, state.recommendImgList)" />
</div>
</el-tab-pane>
</el-tabs>
@ -30,96 +30,101 @@
</el-dialog>
</template>
<script lang="ts">
import { defineComponent, toRefs, reactive } from 'vue'
<script lang="ts" setup>
import { reactive, defineEmits, defineExpose } from 'vue'
import { useStore } from 'vuex'
import { ElTabPane, ElTabs } from 'element-plus'
import { ElTabPane, ElTabs, TabPaneName } from 'element-plus'
import api from '@/api'
import { TGetImageListResult } from '@/api/material'
export default defineComponent({
components: { [ElTabPane.name]: ElTabPane, [ElTabs.name]: ElTabs },
emits: ['select'],
setup(props, context) {
const store = useStore()
const state: any = reactive({
dialogVisible: false,
imgList: [],
recommendImgList: [],
isDone: false,
isPicsDone: false,
})
type TEmits = (event: 'select', data: TGetImageListResult) => void
let loading = false
let page = 0
let picPage = 0
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
}, 100)
})
}
const loadPic = (init?: boolean) => {
if (state.isPicsDone || loading) {
return
}
if (init && state.recommendImgList.length > 0) {
return
}
loading = true
picPage += 1
api.material.getImagesList({ page: picPage }).then(({ list }: any) => {
list.length <= 0 ? (state.isPicsDone = true) : (state.recommendImgList = state.recommendImgList.concat(list))
setTimeout(() => {
loading = false
}, 100)
})
}
type TState = {
dialogVisible: boolean;
imgList: TGetImageListResult[];
recommendImgList: TGetImageListResult[];
isDone: boolean;
isPicsDone: boolean;
}
const open = () => {
state.dialogVisible = true
load()
store.commit('setShowMoveable', false)
}
const emits = defineEmits<TEmits>()
const close = () => {
store.commit('setShowMoveable', true)
}
const selectImg = (index: number, list: any) => {
const item: any = list ? list[index] : state.imgList[index]
context.emit('select', item)
state.dialogVisible = false
}
const tabChange = (index: any) => {
if (index == 1) {
loadPic(true)
}
}
return {
...toRefs(state),
open,
close,
load,
loadPic,
selectImg,
tabChange,
}
},
const store = useStore()
const state = reactive<TState>({
dialogVisible: false,
imgList: [],
recommendImgList: [],
isDone: false,
isPicsDone: false,
})
let loading = false
let page = 0
let picPage = 0
const load = async (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 }) => {
list.length <= 0 ? (state.isDone = true) : (state.imgList = state.imgList.concat(list))
setTimeout(() => {
loading = false
}, 100)
})
}
const loadPic = (init?: boolean) => {
if (state.isPicsDone || loading) {
return
}
if (init && state.recommendImgList.length > 0) {
return
}
loading = true
picPage += 1
api.material.getImagesList({ page: picPage }).then(({ list }) => {
list.length <= 0 ? (state.isPicsDone = true) : (state.recommendImgList = state.recommendImgList.concat(list))
setTimeout(() => {
loading = false
}, 100)
})
}
const open = () => {
state.dialogVisible = true
load()
store.commit('setShowMoveable', false)
}
const close = () => {
store.commit('setShowMoveable', true)
}
const selectImg = (index: number, list: TGetImageListResult[]) => {
const item: TGetImageListResult = list ? list[index] : state.imgList[index]
// context.emit('select', item)
emits('select', item)
state.dialogVisible = false
}
const tabChange = (index: TabPaneName) => {
if (index == 1) {
loadPic(true)
}
}
defineExpose({
open
})
</script>
<style lang="less" scoped>