Merge pull request #89 from JeremyYu-cn/feat-pinia

Rebuilt: base store to pinia
This commit is contained in:
Jeremy Yu 2024-03-17 17:00:14 +00:00 committed by GitHub
commit 8372b3d893
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
21 changed files with 739 additions and 731 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)

51
package-lock.json generated
View File

@ -25,6 +25,7 @@
"moveable-helper": "^0.4.0", "moveable-helper": "^0.4.0",
"nanoid": "^3.1.23", "nanoid": "^3.1.23",
"normalize.css": "^8.0.1", "normalize.css": "^8.0.1",
"pinia": "^2.1.7",
"qr-code-styling": "^1.6.0-rc.1", "qr-code-styling": "^1.6.0-rc.1",
"selecto": "^1.13.0", "selecto": "^1.13.0",
"throttle-debounce": "^3.0.1", "throttle-debounce": "^3.0.1",
@ -3276,6 +3277,56 @@
"node": ">=6" "node": ">=6"
} }
}, },
"node_modules/pinia": {
"version": "2.1.7",
"resolved": "https://registry.npmjs.org/pinia/-/pinia-2.1.7.tgz",
"integrity": "sha512-+C2AHFtcFqjPih0zpYuvof37SFxMQ7OEG2zV9jRI12i9BOy3YQVAHwdKtyyc8pDcDyIc33WCIsZaCFWU7WWxGQ==",
"dependencies": {
"@vue/devtools-api": "^6.5.0",
"vue-demi": ">=0.14.5"
},
"funding": {
"url": "https://github.com/sponsors/posva"
},
"peerDependencies": {
"@vue/composition-api": "^1.4.0",
"typescript": ">=4.4.4",
"vue": "^2.6.14 || ^3.3.0"
},
"peerDependenciesMeta": {
"@vue/composition-api": {
"optional": true
},
"typescript": {
"optional": true
}
}
},
"node_modules/pinia/node_modules/vue-demi": {
"version": "0.14.7",
"resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.7.tgz",
"integrity": "sha512-EOG8KXDQNwkJILkx/gPcoL/7vH+hORoBaKgGe+6W7VFMvCYJfmF2dGbvgDroVnI8LU7/kTu8mbjRZGBU1z9NTA==",
"hasInstallScript": true,
"bin": {
"vue-demi-fix": "bin/vue-demi-fix.js",
"vue-demi-switch": "bin/vue-demi-switch.js"
},
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/antfu"
},
"peerDependencies": {
"@vue/composition-api": "^1.0.0-rc.1",
"vue": "^3.0.0-0 || ^2.6.0"
},
"peerDependenciesMeta": {
"@vue/composition-api": {
"optional": true
}
}
},
"node_modules/postcss": { "node_modules/postcss": {
"version": "8.4.35", "version": "8.4.35",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.35.tgz", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.35.tgz",

View File

@ -28,6 +28,7 @@
"moveable-helper": "^0.4.0", "moveable-helper": "^0.4.0",
"nanoid": "^3.1.23", "nanoid": "^3.1.23",
"normalize.css": "^8.0.1", "normalize.css": "^8.0.1",
"pinia": "^2.1.7",
"qr-code-styling": "^1.6.0-rc.1", "qr-code-styling": "^1.6.0-rc.1",
"selecto": "^1.13.0", "selecto": "^1.13.0",
"throttle-debounce": "^3.0.1", "throttle-debounce": "^3.0.1",

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

@ -32,6 +32,7 @@ import useConfirm from '@/common/methods/confirm'
import { useSetupMapGetters } from '@/common/hooks/mapGetters' import { useSetupMapGetters } from '@/common/hooks/mapGetters'
import imgWaterFall from './components/imgWaterFall.vue' import imgWaterFall from './components/imgWaterFall.vue'
import { IGetTempListData } from '@/api/home' import { IGetTempListData } from '@/api/home'
import useUserStore from '@/store/modules/base/user'
type TState = { type TState = {
loading: boolean loading: boolean
@ -52,6 +53,7 @@ const listRef = ref<HTMLElement | null>(null)
const route = useRoute() const route = useRoute()
const router = useRouter() const router = useRouter()
const store = useStore() const store = useStore()
const userStore = useUserStore()
const state = reactive<TState>({ const state = reactive<TState>({
loading: false, loading: false,
loadDone: false, loadDone: false,
@ -64,8 +66,9 @@ const { tempEditing, dHistoryParams } = useSetupMapGetters(['tempEditing', 'dHis
const pageOptions: TPageOptions = { page: 0, pageSize: 20, cate: 1 } const pageOptions: TPageOptions = { page: 0, pageSize: 20, cate: 1 }
const { cate, edit } = route.query const { cate, edit } = route.query
cate && (pageOptions.cate = (cate as LocationQueryValue) || 1) cate && (pageOptions.cate = (cate as LocationQueryValue) ?? 1)
edit && store.commit('managerEdit', true) // edit && store.commit('managerEdit', true)
edit && userStore.managerEdit(true)
// onMounted(async () => {}) // onMounted(async () => {})
@ -116,7 +119,8 @@ async function selectItem(item: IGetTempListData) {
return false return false
} }
} }
store.commit('managerEdit', false) // store.commit('managerEdit', false)
userStore.managerEdit(false)
store.commit('setDWidgets', []) store.commit('setDWidgets', [])
setTempId(item.id) setTempId(item.id)

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

@ -13,7 +13,9 @@ import utils from './utils'
import 'normalize.css/normalize.css' import 'normalize.css/normalize.css'
import '@/assets/styles/index.less' import '@/assets/styles/index.less'
import elementConfig from './utils/widgets/elementConfig' import elementConfig from './utils/widgets/elementConfig'
import { createPinia } from 'pinia'
const pinia = createPinia()
const app = createApp(App) const app = createApp(App)
elementConfig.components.forEach((component) => { elementConfig.components.forEach((component) => {
@ -26,6 +28,7 @@ elementConfig.plugins.forEach((plugin) => {
app app
.use(store) .use(store)
.use(pinia)
.use(router) .use(router)
.use(utils) .use(utils)
.mount('#app') .mount('#app')

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

@ -1,10 +1,10 @@
import { createStore } from 'vuex' import { createStore } from 'vuex'
import base from './modules/base' // import base from './modules/base'
import design from './modules/design' import design from './modules/design'
export default createStore({ export default createStore({
...base, // ...base,
modules: { modules: {
design, design,
}, },

View File

@ -2,18 +2,19 @@
* store.dispatch() * store.dispatch()
*/ */
interface Iprops { // interface Iprops {
commit: (a: any, b: any) => void // commit: (a: any, b: any) => void
state: any // state: any
} // }
export default { // export default {
hideLoading(props: Iprops, data: Type.Object) { // hideLoading(props: Iprops, data: Type.Object) {
setTimeout(() => { // setTimeout(() => {
props.commit('loading', false) // props.commit('loading', false)
}, 600) // }, 600)
}, // },
setFonts(store: Iprops, list: Type.Object) { // setFonts(store: Iprops, list: Type.Object) {
store.state.fonts = list // store.state.fonts = list
}, // },
}
// }

View File

@ -2,48 +2,86 @@
* @Author: ShawnPhang * @Author: ShawnPhang
* @Date: 2021-12-16 16:20:16 * @Date: 2021-12-16 16:20:16
* @Description: * @Description:
* @LastEditors: ShawnPhang <https://m.palxp.cn> * @LastEditors: ShawnPhang <https://m.palxp.cn>, Jeremy Yu <https://github.com/JeremyYu-cn>
* @LastEditTime: 2023-09-28 17:42:25 * @LastEditTime: 2024-03-17 15:00:00
*/ */
import mutations from './mutations'
import actions from './actions'
import _config from '@/config'
const all = { import { defineStore } from 'pinia'
state: {
// import actions from './actions'
// import _config from '@/config'
type TStoreBaseState = {
loading: boolean | null
scroll: boolean
/** fonts */
fonts: string[]
/** 抠图服务 */
app: string | null
}
type TUserAction = {
hideLoading: () => void
setFonts: (list: string[]) => void
}
const useBaseStore = defineStore<'base', TStoreBaseState, {}, TUserAction>('base', {
state: () => ({
loading: null, loading: null,
online: true, // 登录状态,
user: {
name: localStorage.getItem('username'),
}, // 储存用户信息
scroll: true, scroll: true,
manager: '', // 是否为管理员模式
tempEditing: false, // 管理员是否正在编辑模板
fonts: [], // 缓存字体列表 fonts: [], // 缓存字体列表
app: null, // 抠图服务 app: null, // 抠图服务
}, }),
getters: {
online: (state: Type.Object) => {
return state.online
},
user: (state: Type.Object) => {
return state.user
},
manager: (state: Type.Object) => {
return state.manager
},
tempEditing: (state: Type.Object) => {
return state.tempEditing
},
fonts: (state: Type.Object) => {
return state.fonts
},
},
mutations: {
...mutations,
},
actions: { actions: {
...actions, /** 隐藏loading */
}, hideLoading() {
} setTimeout(() => {
export default all this.loading = false
}, 600)
},
setFonts(list: string[]) {
this.fonts = list
},
}
})
export default useBaseStore
// const all = {
// state: {
// loading: null,
// online: true, // 登录状态,
// user: {
// name: localStorage.getItem('username'),
// }, // 储存用户信息
// scroll: true,
// manager: '', // 是否为管理员模式
// tempEditing: false, // 管理员是否正在编辑模板
// fonts: [], // 缓存字体列表
// app: null, // 抠图服务
// },
// getters: {
// online: (state: Type.Object) => {
// return state.online
// },
// user: (state: Type.Object) => {
// return state.user
// },
// manager: (state: Type.Object) => {
// return state.manager
// },
// tempEditing: (state: Type.Object) => {
// return state.tempEditing
// },
// fonts: (state: Type.Object) => {
// return state.fonts
// },
// },
// mutations: {
// ...mutations,
// },
// actions: {
// ...actions,
// },
// }

View File

@ -8,35 +8,35 @@
// import { Toast } from 'vant' // import { Toast } from 'vant'
export default { // export default {
loading(state: Type.Object, data: any) { // loading(state: Type.Object, data: any) {
// Toast.clear(); // // Toast.clear();
// let msg = '' // // let msg = ''
// if (typeof data === 'string') { // // if (typeof data === 'string') {
// msg = data // // msg = data
// } else { // // } else {
// Toast.clear(); // // Toast.clear();
// return false // // return false
// } // // }
// Toast.loading({ // // Toast.loading({
// duration: 0, // 持续展示 toast // // duration: 0, // 持续展示 toast
// loadingType: 'spinner', // // loadingType: 'spinner',
// message: msg // // message: msg
// }); // // });
}, // },
changeRoute(state: Type.Object, from: string) { // changeRoute(state: Type.Object, from: string) {
state.routeFrom = from // state.routeFrom = from
}, // },
changeOnline(state: Type.Object, status: string) { // changeOnline(state: Type.Object, status: string) {
state.online = status // state.online = status
}, // },
changeUser(state: Type.Object, name: string) { // changeUser(state: Type.Object, name: string) {
state.user.name = name // state.user.name = name
// state.user = Object.assign({}, state.user) // // state.user = Object.assign({}, state.user)
state.user = { ...state.user } // state.user = { ...state.user }
localStorage.setItem('username', name) // localStorage.setItem('username', name)
}, // },
managerEdit(state: Type.Object, status: string) { // managerEdit(state: Type.Object, status: string) {
state.tempEditing = status // state.tempEditing = status
}, // },
} // }

View File

@ -0,0 +1,58 @@
/*
* @Author: Jeremy Yu
* @Date: 2024-03-17 15:00:00
* @Description: User全局状态管理
* @LastEditors: Jeremy Yu <https://github.com/JeremyYu-cn>
* @LastEditTime: 2024-03-17 15:00:00
*/
import { defineStore } from "pinia"
type TUserStoreState = {
/** 登录状态 */
online: boolean
/** 储存用户信息 */
user: {
name: string | null
}
/**是否为管理员模式 */
manager: string
/** 管理员是否正在编辑模板 */
tempEditing: boolean
}
type TUserAction = {
/** 修改登录状态 */
changeOnline: (state: boolean) => void
/** 修改登录用户 */
changeUser: (userName: string) => void
managerEdit: (status: boolean) => void
}
const useUserStore = defineStore<'userStore', TUserStoreState, {}, TUserAction>('userStore', {
state: () => ({
online: true, // 登录状态,
user: {
name: localStorage.getItem('username'),
}, // 储存用户信息
manager: '', // 是否为管理员模式
tempEditing: false, // 管理员是否正在编辑模板
}),
actions: {
changeOnline(status: boolean) {
this.online = status
},
changeUser(name: string) {
this.user.name = name
// state.user = Object.assign({}, state.user)
// state.user = { ...state.user }
localStorage.setItem('username', name)
},
managerEdit(status: boolean) {
this.tempEditing = status
},
}
})
export default useUserStore

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,

0
src/store/pinia.ts Normal file
View File

View File

@ -6,8 +6,10 @@
* @LastEditTime: 2024-02-26 17:54:00 * @LastEditTime: 2024-02-26 17:54:00
*/ */
import axios, { AxiosRequestConfig, AxiosResponse, AxiosStatic } from 'axios' import axios, { AxiosRequestConfig, AxiosResponse, AxiosStatic } from 'axios'
import store from '@/store' // import store from '@/store'
import app_config, { LocalStorageKey } from '@/config' import app_config, { LocalStorageKey } from '@/config'
import useUserStore from '@/store/modules/base/user';
import useBaseStore from '@/store/modules/base';
axios.defaults.timeout = 30000 axios.defaults.timeout = 30000
axios.defaults.headers.authorization = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6MTAwMDEsImV4cCI6MTc4ODU3NDc1MDU4NX0.L_t6DFD48Dm6rUPfgIgOWJkz18En1m_-hhMHcpbxliY'; axios.defaults.headers.authorization = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6MTAwMDEsImV4cCI6MTc4ODU3NDc1MDU4NX0.L_t6DFD48Dm6rUPfgIgOWJkz18En1m_-hhMHcpbxliY';
@ -53,7 +55,8 @@ axios.interceptors.response.use((res: AxiosResponse<any>) => {
} }
if (res.data.code === 401) { if (res.data.code === 401) {
console.log('登录失效') console.log('登录失效')
store.commit('changeOnline', false) useUserStore().changeOnline(false)
// store.commit('changeOnline', false)
} }
if (res.data.result && res.data.code === 200) { if (res.data.result && res.data.code === 200) {
@ -67,7 +70,8 @@ axios.interceptors.response.use((res: AxiosResponse<any>) => {
(error) => { (error) => {
// if (error.response.status === 401) { // if (error.response.status === 401) {
// } // }
store.dispatch('hideLoading') useBaseStore().hideLoading()
// store.dispatch('hideLoading')
return Promise.reject(error) return Promise.reject(error)
}, },
) )

View File

@ -12,7 +12,8 @@
<span style="color: #999; font-size: 14px; margin-right: 0.5rem">{{ state.stateBollean ? '启用' : '停用' }}</span> <el-switch v-model="state.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> <div class="divide__line">|</div>
<el-button plain type="primary" @click="saveTemp">保存模板</el-button> <el-button plain type="primary" @click="saveTemp">保存模板</el-button>
<el-button @click="$store.commit('managerEdit', false)">取消</el-button> <el-button @click="userStore.managerEdit(false)">取消</el-button>
<!-- <el-button @click="$store.commit('managerEdit', false)">取消</el-button> -->
<div class="divide__line">|</div> <div class="divide__line">|</div>
</template> </template>
<!-- <el-button @click="draw">绘制(测试)</el-button> --> <!-- <el-button @click="draw">绘制(测试)</el-button> -->
@ -39,6 +40,7 @@ import _config from '@/config'
import useConfirm from '@/common/methods/confirm' import useConfirm from '@/common/methods/confirm'
import wGroup from '@/components/modules/widgets/wGroup/wGroup.vue' import wGroup from '@/components/modules/widgets/wGroup/wGroup.vue'
import { useSetupMapGetters } from '@/common/hooks/mapGetters' import { useSetupMapGetters } from '@/common/hooks/mapGetters'
import useUserStore from '@/store/modules/base/user'
type TProps = { type TProps = {
modelValue?: boolean modelValue?: boolean
@ -60,6 +62,7 @@ const emit = defineEmits<TEmits>()
const route = useRoute() const route = useRoute()
const router = useRouter() const router = useRouter()
const store = useStore() const store = useStore()
const userStore = useUserStore()
const canvasImage = ref<typeof SaveImage | null>(null) const canvasImage = ref<typeof SaveImage | null>(null)
const { const {
dPage, dWidgets, tempEditing, dHistory, dPageHistory dPage, dWidgets, tempEditing, dHistory, dPageHistory

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: '',

956
yarn.lock

File diff suppressed because it is too large Load Diff