merge: merge main branch

This commit is contained in:
IchliebedichZhu 2024-03-16 11:30:41 +00:00
commit dc37a3b9c1
8 changed files with 92 additions and 73 deletions

View File

@ -1,19 +1,21 @@
**[在线体验网址](https://design.palxp.cn/)** | **[中文文档](https://xp.palxp.cn/)** | [常见问题](https://xp.palxp.cn/#/articles/1689323321667) | [架构说明](https://xp.palxp.cn/#/articles/1689321259854) **[在线体验网站](https://design.palxp.cn/)** | **[在线文档](https://xp.palxp.cn/)** | [常见问题](https://xp.palxp.cn/#/articles/1689323321667)
--- ---
## Poster Design ## 迅排设计
迅排设计是一款漂亮易用且功能强大的开源创意图片编辑器是对标稿定设计、创客贴、Canva 等商业产品的免费在线设计工具。 一款漂亮易用且功能强大的创意图片编辑器对标稿定设计、创客贴、Canva 等商业产品。
适用于多种场景:海报图片生成、电商分享图、文章长图、视频/公众号封面等,无需下载软件即可轻松实现云端编辑、迅速完成图文排版。
[![](https://xp.palxp.cn/images/2023-7-16-1689500112694.gif)](https://design.palxp.cn/) [![](https://xp.palxp.cn/images/2023-7-16-1689500112694.gif)](https://design.palxp.cn/)
适用于多种场景:海报图片生成、电商分享图、文章长图、视频/公众号封面等,无需下载软件即可轻松实现云端编辑、迅速完成图文排版。 ### 特点
- 丝滑的页面操作体验,丰富的交互细节,基础功能完善 - 丝滑的页面操作体验,丰富的交互细节,基础功能完善
- 采用服务端生成图片,能确保多端出图统一性,支持各种 CSS 特性 - 采用服务端生成图片,能确保多端出图统一性,支持各种 CSS 特性
- 简易 AI 抠图工具,上传图片一键去除背景 - 简易 AI 抠图工具,上传图片一键去除背景
- 技术栈Vue3 、Vite2 、Vuex 、ElementPlus开发体验畅快 - 技术栈Vue3 、Vite5 、Vuex 、ElementPlus开发体验畅快
- 图片生成Puppeteer、Express - 图片生成Puppeteer、Express
### 支持功能 ### 支持功能
@ -35,25 +37,35 @@
git clone https://github.com/palxiao/poster-design.git git clone https://github.com/palxiao/poster-design.git
cd poster-design cd poster-design
npm run prepared npm run prepared
npm run serve npm run dev
cd screreenshot
npm run dev
``` ```
![](https://xp.palxp.cn/images/2023-7-16-1689498291322.png) ![](https://xp.palxp.cn/images/2023-7-16-1689498291322.png)
访问 http://127.0.0.1:5173/ 查看网页。点此查看[完整说明文档](https://xp.palxp.cn/#/articles/1689319644311)。 访问 http://127.0.0.1:5173/ 查看网页。点此查看[更多说明文档](https://xp.palxp.cn/#/articles/1689319644311)。
### 图片生成服务 ### 图片生成服务
代码位于根目录 [/screenshot](https://github.com/palxiao/poster-design/tree/main/screenshot)接口API文档点此查看[接口 API 文档](https://xp.palxp.cn/apidoc/screenshot.html)。 代码位于根目录 [/screenshot](https://github.com/palxiao/poster-design/tree/main/screenshot)接口API文档点此查看[接口 API 文档](https://xp.palxp.cn/apidoc/screenshot.html)。
> 更多相关事项请进入该目录下查看 [README.md](https://github.com/palxiao/poster-design/blob/main/screenshot/README.md) 文件。 Docker 部署:[参考说明](https://xp.palxp.cn/#/articles/1689319644311?id=docker%e5%ae%b9%e5%99%a8)。
### 服务端 ### 服务端
目前本项目演示 Demo 中的后端接口参考:[接口 API 文档](https://xp.palxp.cn/apidoc/index.html)。 后端需要自己开发,目前本项目演示 Demo 中的后端接口参考:[接口 API 文档](https://xp.palxp.cn/apidoc/index.html)。
## 其它 ## 其它
我们尝试沉淀一个高质量内容社区,形成可持续学习的平台,同时解决开发者在项目中遇到的疑难和困惑,帮大家少走一些弯路。
<img style="width: 380px;" src="https://github.com/palxiao/poster-design/assets/21021314/643dcc8b-ef73-4c76-a78c-a7c377b5f268" />
也欢迎关注公众号:品味前端,回复“加群”进行交流。
<img style="width: 380px;" src="https://xp.palxp.cn/images/2024-3-1-1709306365949.png" />
-----
本项目最早使用 Vue2 开发,现改用 Vue3 重构中。[一些迭代计划记录](https://xp.palxp.cn/#/articles/1689319986889?id=%e8%bf%ad%e4%bb%a3%e8%ae%a1%e5%88%92). 本项目最早使用 Vue2 开发,现改用 Vue3 重构中。[一些迭代计划记录](https://xp.palxp.cn/#/articles/1689319986889?id=%e8%bf%ad%e4%bb%a3%e8%ae%a1%e5%88%92).
目前开源版仍在持续迭代中,还有很多的不足,可以将你遇到的问题在 Issues 中提出,或者提交 Pull Request 帮助完善。 目前开源版仍在持续迭代中,还有很多的不足,可以将你遇到的问题在 Issues 中提出,或者提交 Pull Request 帮助完善。
@ -67,13 +79,7 @@ npm run serve
- [qr-code-styling](https://qr-code-styling.com/): 风格化二维码 - [qr-code-styling](https://qr-code-styling.com/): 风格化二维码
- [rembg](https://github.com/danielgatis/rembg): 图片抠图,使用 u2net 预训练模型 - [rembg](https://github.com/danielgatis/rembg): 图片抠图,使用 u2net 预训练模型
### 交流群 开源不易,最后别忘了给本项目点个 **Star** ~
| 作者微信:备注加群 | 关注公众号 |
| --- | --- |
| <img style="width: 240px;" src="https://xp.palxp.cn/images/2024-3-1-1709306328344.png" /> | <img style="width: 320px;" src="https://xp.palxp.cn/images/2024-3-1-1709306365949.png" /> |
开源不易,别忘了给本项目点个 **Star** ~
[![Star History Chart](https://api.star-history.com/svg?repos=palxiao/poster-design&type=Date)](https://star-history.com/#palxiao/poster-design&Date) [![Star History Chart](https://api.star-history.com/svg?repos=palxiao/poster-design&type=Date)](https://star-history.com/#palxiao/poster-design&Date)

View File

@ -1,5 +1,6 @@
<template> <template>
<div id="page-design" ref="page_design" :style="{ paddingTop: dPaddingTop + 'px' }"> <div id="page-design" ref="page_design" :style="{ paddingTop: dPaddingTop + 'px' }">
<!-- <el-scrollbar> -->
<div <div
id="out-page" id="out-page"
class="out-page" class="out-page"
@ -54,6 +55,7 @@
<!-- <size-control v-if="dSelectWidgets.length === 0" /> --> <!-- <size-control v-if="dSelectWidgets.length === 0" /> -->
</div> </div>
</div> </div>
<!-- </el-scrollbar> -->
</div> </div>
</template> </template>
@ -61,7 +63,7 @@
import { nextTick, onMounted, ref } from 'vue' import { nextTick, onMounted, ref } from 'vue'
import { mapGetters, mapActions, useStore } from 'vuex' import { mapGetters, mapActions, useStore } from 'vuex'
import { getTarget } from '@/common/methods/target' import { getTarget } from '@/common/methods/target'
// import { ElScrollbar } from 'element-plus'
import setWidgetData from '@/common/methods/DesignFeatures/setWidgetData' import setWidgetData from '@/common/methods/DesignFeatures/setWidgetData'
import PointImg from '@/utils/plugins/pointImg' import PointImg from '@/utils/plugins/pointImg'
import getComponentsData from '@/common/methods/DesignFeatures/setComponents' import getComponentsData from '@/common/methods/DesignFeatures/setComponents'

View File

@ -3,7 +3,7 @@
* @Date: 2021-08-02 19:10:06 * @Date: 2021-08-02 19:10:06
* @Description: 选项选择未拆分字体选择器 * @Description: 选项选择未拆分字体选择器
* @LastEditors: ShawnPhang <https://m.palxp.cn> * @LastEditors: ShawnPhang <https://m.palxp.cn>
* @LastEditTime: 2023-11-20 18:21:55 * @LastEditTime: 2024-03-15 17:34:00
--> -->
<template> <template>
<div ref="select" class="value-select" :style="{ width: inputWidth }"> <div ref="select" class="value-select" :style="{ width: inputWidth }">
@ -65,7 +65,7 @@ import { computed, onMounted, reactive, ref, watch } from 'vue';
type TProps = { type TProps = {
label?: string label?: string
modelValue?: Record<string, any> modelValue?: Record<string, any> | string | number
suffix?: string suffix?: string
data: Record<string, any> data: Record<string, any>
disable?: boolean disable?: boolean
@ -189,9 +189,8 @@ function down() {
</style> </style>
<style lang="less" scoped> <style lang="less" scoped>
// Color variables (appears count calculates by raw css) @color0: #e1e1e1;
@color0: #e1e1e1; // Appears 2 times @color1: #d1d1d1;
@color1: #d1d1d1; // Appears 2 times
.value-select { .value-select {
// height: 60px; // height: 60px;

View File

@ -3,7 +3,7 @@
* @Date: 2022-03-09 16:29:54 * @Date: 2022-03-09 16:29:54
* @Description: ctrl建相关的操作 * @Description: ctrl建相关的操作
* @LastEditors: ShawnPhang <https://m.palxp.cn> * @LastEditors: ShawnPhang <https://m.palxp.cn>
* @LastEditTime: 2023-10-09 09:49:54 * @LastEditTime: 2024-03-15 17:34:51
*/ */
import store from '@/store' import store from '@/store'
import handlePaste from './handlePaste' import handlePaste from './handlePaste'
@ -50,7 +50,7 @@ function checkGroupChild(pid: number | string, key: any) {
return itHas return itHas
} }
/** /**
* *
*/ */
function copy() { function copy() {
if (store.getters.dActiveElement.uuid === '-1') { if (store.getters.dActiveElement.uuid === '-1') {
@ -64,13 +64,14 @@ function copy() {
* *
*/ */
function paste() { function paste() {
handlePaste() handlePaste().then(() => {
if (store.getters.dCopyElement.length === 0) { if (store.getters.dCopyElement.length === 0) {
return return
} else if (store.getters.dActiveElement.isContainer && checkGroupChild(store.getters.dActiveElement.uuid, 'editable')) { } else if (store.getters.dActiveElement.isContainer && checkGroupChild(store.getters.dActiveElement.uuid, 'editable')) {
return return
} }
!store.getters.dActiveElement.editable && store.dispatch('pasteWidget') !store.getters.dActiveElement.editable && store.dispatch('pasteWidget')
})
} }
/** /**
* *

View File

@ -3,7 +3,7 @@
* @Date: 2023-10-09 09:47:40 * @Date: 2023-10-09 09:47:40
* @Description: * @Description:
* @LastEditors: ShawnPhang <https://m.palxp.cn> * @LastEditors: ShawnPhang <https://m.palxp.cn>
* @LastEditTime: 2023-10-09 10:35:21 * @LastEditTime: 2024-03-15 17:35:18
*/ */
// window.addEventListener('paste', (e: any) => { // window.addEventListener('paste', (e: any) => {
@ -17,45 +17,56 @@ import Qiniu from '@/common/methods/QiNiu'
import _config from '@/config' import _config from '@/config'
import { getImage } from '@/common/methods/getImgDetail' import { getImage } from '@/common/methods/getImgDetail'
import wImage from '@/components/modules/widgets/wImage/wImage.vue' import wImage from '@/components/modules/widgets/wImage/wImage.vue'
// import wText from '@/components/modules/widgets/wText/wText.vue' import wText from '@/components/modules/widgets/wText/wText.vue'
import { wTextSetting } from '@/components/modules/widgets/wText/wTextSetting'
export default () => { export default () => {
navigator.clipboard return new Promise<void>((resolve) => {
.read() navigator.clipboard
.then(async (dataTransfer: any) => { .read()
for (let i = 0; i < dataTransfer.length; i++) { .then(async (dataTransfer: any) => {
const item = dataTransfer[i] if (store.getters.dActiveElement.editable) {
if (item.types.toString().indexOf('image') !== -1) { return
const imageBlob = await item.getType(item.types[0])
const file = new File([imageBlob], 'screenshot.png', { type: 'image/png' })
// 上传图片
const qnOptions = { bucket: 'xp-design', prePath: 'user' }
const result: any = await Qiniu.upload(file, qnOptions)
const { width, height }: any = await getImage(file)
const url = _config.IMG_URL + result.key
await api.material.addMyPhoto({ width, height, url })
// 添加图片到画布中
store.commit('setShowMoveable', false) // 清理掉上一次的选择
const setting = JSON.parse(JSON.stringify(wImage.setting))
setting.width = width
setting.height = height
setting.imgUrl = url
const { width: pW, height: pH } = store.getters.dPage
setting.left = pW / 2 - width / 2
setting.top = pH / 2 - height / 2
store.dispatch('addWidget', setting)
break
} else if (item.types.toString().indexOf('text') !== -1) {
store.commit('setShowMoveable', false) // 清理掉上一次的选择
const setting = JSON.parse(JSON.stringify(wTextSetting))
setting.text = await navigator.clipboard.readText()
store.dispatch('addWidget', setting)
break
} }
} for (let i = 0; i < dataTransfer.length; i++) {
}) const item = dataTransfer[i]
.catch((error) => { if (item.types.toString().indexOf('image') !== -1) {
console.error('无法读取剪贴板内容:', error) const imageBlob = await item.getType(item.types[0])
}) const file = new File([imageBlob], 'screenshot.png', { type: 'image/png' })
// 上传图片
const qnOptions = { bucket: 'xp-design', prePath: 'user' }
const result: any = await Qiniu.upload(file, qnOptions)
const { width, height }: any = await getImage(file)
const url = _config.IMG_URL + result.key
await api.material.addMyPhoto({ width, height, url })
// 添加图片到画布中
store.commit('setShowMoveable', false) // 清理掉上一次的选择
const setting = JSON.parse(JSON.stringify(wImage.setting))
setting.width = width
setting.height = height
setting.imgUrl = url
const { width: pW, height: pH } = store.getters.dPage
setting.left = pW / 2 - width / 2
setting.top = pH / 2 - height / 2
store.dispatch('addWidget', setting)
// 清空剪贴板,防止多次上传图片
navigator.clipboard.write([
new ClipboardItem({
'text/plain': new Blob([''], {type: 'text/plain'})
})
])
break
} else if (item.types.toString().indexOf('text') !== -1) {
store.commit('setShowMoveable', false) // 清理掉上一次的选择
const setting = JSON.parse(JSON.stringify(wText.setting))
setting.text = await navigator.clipboard.readText()
store.dispatch('addWidget', setting)
break
}
}
})
.catch((error) => {
// 剪贴板内容为空
resolve()
})
})
} }

View File

@ -237,7 +237,7 @@ export default {
if (activeElement.type === 'page') { if (activeElement.type === 'page') {
return return
} }
navigator.clipboard.writeText('') // 清空系统剪贴板内容
const container = [] const container = []
const selectWidgets = store.state.dSelectWidgets const selectWidgets = store.state.dSelectWidgets
if (selectWidgets.length === 0) { if (selectWidgets.length === 0) {

View File

@ -29,7 +29,6 @@ const all = {
dDraging: false, // 是否正在抓取组件 dDraging: false, // 是否正在抓取组件
dResizeing: false, // 是否正在调整组件宽高 dResizeing: false, // 是否正在调整组件宽高
dShowRefLine: true, // 是否显示参考线 dShowRefLine: true, // 是否显示参考线
dResizeWH: { dResizeWH: {
// 初始化调整大小时组件的宽高 // 初始化调整大小时组件的宽高
width: 0, width: 0,

View File

@ -58,6 +58,7 @@ export default defineConfig({
}, },
server: { server: {
hmr: { overlay: false }, hmr: { overlay: false },
host: '127.0.0.1'
// proxy: { // proxy: {
// '/api': { // '/api': {
// target: '', // target: '',