From 14585292f99c2be3d3ecc3d353e26db32f6f06a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=85=A8=E6=A0=88=E5=B0=8F=E5=AD=A6=E7=94=9F?= <1518079521@qq.com> Date: Sat, 18 May 2024 17:29:39 +0800 Subject: [PATCH] update --- admin/src/App.vue | 1 + admin/src/lang/zh-cn/common.json | 8 +- .../layout/admin/components/aside/index.vue | 28 +- .../layout/admin/components/aside/side.vue | 4 +- .../admin/components/header/user-info.vue | 2 +- .../bussiness/components/aside/index.vue | 34 + .../bussiness/components/aside/menu-item.vue | 67 ++ .../bussiness/components/aside/side.vue | 242 ++++++ .../bussiness/components/header/index.vue | 186 ++++ .../components/header/layout-setting.vue | 72 ++ .../components/header/switch-lang.vue | 32 + .../bussiness/components/header/user-info.vue | 147 ++++ .../src/layout/bussiness/components/tabs.vue | 137 +++ admin/src/layout/bussiness/index.vue | 44 + admin/src/layout/bussiness/layout.json | 4 + .../darkside/components/aside/index.vue | 71 ++ .../darkside/components/aside/menu-item.vue | 108 +++ .../layout/darkside/components/aside/side.vue | 101 +++ .../darkside/components/header/index.vue | 190 +++++ .../components/header/layout-setting.vue | 72 ++ .../components/header/switch-lang.vue | 32 + .../darkside/components/header/user-info.vue | 147 ++++ admin/src/layout/darkside/components/tabs.vue | 137 +++ admin/src/layout/darkside/index.vue | 47 + admin/src/layout/darkside/layout.json | 4 + .../layout/default/components/aside/index.vue | 29 +- .../default/components/aside/menu-item.vue | 67 +- .../layout/default/components/aside/side.vue | 215 +---- .../default/components/header/index.vue | 26 +- .../default/components/header/user-info.vue | 2 +- admin/src/layout/default/index.vue | 43 +- admin/src/layout/default/layout.json | 4 + .../home/components/aside/menu-item.vue | 6 +- .../src/layout/home/components/aside/side.vue | 4 +- admin/src/layout/index.vue | 14 +- .../profession/components/aside/index.vue | 55 ++ .../profession/components/aside/menu-item.vue | 67 ++ .../profession/components/aside/side.vue | 244 ++++++ .../profession/components/header/index.vue | 190 +++++ .../components/header/layout-setting.vue | 72 ++ .../components/header/switch-lang.vue | 32 + .../components/header/user-info.vue | 147 ++++ .../src/layout/profession/components/tabs.vue | 137 +++ admin/src/layout/profession/index.vue | 44 + admin/src/layout/profession/layout.json | 4 + admin/src/main.ts | 2 + admin/src/router/index.ts | 19 +- admin/src/router/routers.ts | 8 + admin/src/stores/modules/diy.ts | 46 +- admin/src/stores/modules/poster.ts | 803 ++++++++++++++++++ admin/src/stores/modules/system.ts | 14 +- admin/src/styles/common.scss | 57 +- admin/src/styles/element-plus.scss | 31 +- admin/src/styles/icon/addon-iconfont.css | 3 +- admin/src/styles/icon/iconfont.css | 158 +++- admin/src/styles/icon/iconfont.json | 266 ++++++ admin/src/utils/common.ts | 11 +- admin/src/utils/qqmap.ts | 4 +- admin/src/utils/request.ts | 2 +- admin/src/utils/test.ts | 12 +- 60 files changed, 4420 insertions(+), 335 deletions(-) create mode 100644 admin/src/layout/bussiness/components/aside/index.vue create mode 100644 admin/src/layout/bussiness/components/aside/menu-item.vue create mode 100644 admin/src/layout/bussiness/components/aside/side.vue create mode 100644 admin/src/layout/bussiness/components/header/index.vue create mode 100644 admin/src/layout/bussiness/components/header/layout-setting.vue create mode 100644 admin/src/layout/bussiness/components/header/switch-lang.vue create mode 100644 admin/src/layout/bussiness/components/header/user-info.vue create mode 100644 admin/src/layout/bussiness/components/tabs.vue create mode 100644 admin/src/layout/bussiness/index.vue create mode 100644 admin/src/layout/bussiness/layout.json create mode 100644 admin/src/layout/darkside/components/aside/index.vue create mode 100644 admin/src/layout/darkside/components/aside/menu-item.vue create mode 100644 admin/src/layout/darkside/components/aside/side.vue create mode 100644 admin/src/layout/darkside/components/header/index.vue create mode 100644 admin/src/layout/darkside/components/header/layout-setting.vue create mode 100644 admin/src/layout/darkside/components/header/switch-lang.vue create mode 100644 admin/src/layout/darkside/components/header/user-info.vue create mode 100644 admin/src/layout/darkside/components/tabs.vue create mode 100644 admin/src/layout/darkside/index.vue create mode 100644 admin/src/layout/darkside/layout.json create mode 100644 admin/src/layout/default/layout.json create mode 100644 admin/src/layout/profession/components/aside/index.vue create mode 100644 admin/src/layout/profession/components/aside/menu-item.vue create mode 100644 admin/src/layout/profession/components/aside/side.vue create mode 100644 admin/src/layout/profession/components/header/index.vue create mode 100644 admin/src/layout/profession/components/header/layout-setting.vue create mode 100644 admin/src/layout/profession/components/header/switch-lang.vue create mode 100644 admin/src/layout/profession/components/header/user-info.vue create mode 100644 admin/src/layout/profession/components/tabs.vue create mode 100644 admin/src/layout/profession/index.vue create mode 100644 admin/src/layout/profession/layout.json create mode 100644 admin/src/stores/modules/poster.ts diff --git a/admin/src/App.vue b/admin/src/App.vue index e14da8520..49dd8d6ce 100644 --- a/admin/src/App.vue +++ b/admin/src/App.vue @@ -21,6 +21,7 @@ const systemStore = useSystemStore() const locale = computed(() => (systemStore.lang === 'zh-cn' ? zhCn : en)) // 查询website信息 systemStore.getWebsiteInfo() +systemStore.getWebsiteLayout() const toggleDark = useToggle(useDark()) diff --git a/admin/src/lang/zh-cn/common.json b/admin/src/lang/zh-cn/common.json index 2db029cf4..00f0e6ff3 100644 --- a/admin/src/lang/zh-cn/common.json +++ b/admin/src/lang/zh-cn/common.json @@ -16,6 +16,10 @@ "cancel": "取消", "search": "搜索", "reset": "重置", + "export": "导出列表", + "exportPlaceholder": "确定要导出数据吗?", + "exportTip": "批量导出数据", + "exportConfirm": "确定并导出", "warning": "提示", "isShow": "是否显示", "show": "显示", @@ -147,7 +151,9 @@ "localBuild": "手动编译", "cloudBuild": "云编译", "showDialogCloseTips": "升级任务尚未完成,关闭将取消升级,是否要继续关闭?", - "upgradeCompleteTips": "升级完成后还需要编译admin wap web端可选择云编译或者是手动编译" + "upgradeCompleteTips": "升级完成后还需要编译admin wap web端可选择云编译或者是手动编译", + "upgradeTips": "应用和插件升级时,系统会自动备份当前程序及数据库。升级功能不会造成您当前程序的损坏或者数据的丢失,请放心使用,但是升级过程可能会因为兼容性等各种原因出现意外的升级错误,当出现错误时,请参考链接 https://www.kancloud.cn/niushop/niushop_v6/3228611 手动回退上一版本!", + "knownToKnow": "我已知晓,不需要再次提示" }, "cloudbuild": { "title": "云编译", diff --git a/admin/src/layout/admin/components/aside/index.vue b/admin/src/layout/admin/components/aside/index.vue index 441fa1605..81c87e62f 100644 --- a/admin/src/layout/admin/components/aside/index.vue +++ b/admin/src/layout/admin/components/aside/index.vue @@ -2,11 +2,13 @@
- - - +
+ + + +
@@ -180,4 +182,20 @@ watch(route, () => { font-size: var(--el-font-size-base); } } + +.layout-aside .el-scrollbar__wrap--hidden-default, .layout-aside .el-scrollbar{ + overflow: inherit !important; +} +.layout-aside .menu-item.is-active{ + position: relative; + &:after{ + content: ""; + position: absolute; + top: 0; + bottom: 0; + width: 1px; + background: var(--el-color-primary); + right: -1px; + } +} diff --git a/admin/src/layout/admin/components/aside/side.vue b/admin/src/layout/admin/components/aside/side.vue index ae91b4f18..a53b9c867 100644 --- a/admin/src/layout/admin/components/aside/side.vue +++ b/admin/src/layout/admin/components/aside/side.vue @@ -2,8 +2,8 @@ - - + +
diff --git a/admin/src/layout/admin/components/header/user-info.vue b/admin/src/layout/admin/components/header/user-info.vue index bb1b22a5e..ec8d6e0b1 100644 --- a/admin/src/layout/admin/components/header/user-info.vue +++ b/admin/src/layout/admin/components/header/user-info.vue @@ -39,7 +39,7 @@ - +
diff --git a/admin/src/layout/bussiness/components/aside/index.vue b/admin/src/layout/bussiness/components/aside/index.vue new file mode 100644 index 000000000..a5a28e804 --- /dev/null +++ b/admin/src/layout/bussiness/components/aside/index.vue @@ -0,0 +1,34 @@ + + + + + diff --git a/admin/src/layout/bussiness/components/aside/menu-item.vue b/admin/src/layout/bussiness/components/aside/menu-item.vue new file mode 100644 index 000000000..b958d0da0 --- /dev/null +++ b/admin/src/layout/bussiness/components/aside/menu-item.vue @@ -0,0 +1,67 @@ + + + + + diff --git a/admin/src/layout/bussiness/components/aside/side.vue b/admin/src/layout/bussiness/components/aside/side.vue new file mode 100644 index 000000000..e499a70b6 --- /dev/null +++ b/admin/src/layout/bussiness/components/aside/side.vue @@ -0,0 +1,242 @@ + + + + + diff --git a/admin/src/layout/bussiness/components/header/index.vue b/admin/src/layout/bussiness/components/header/index.vue new file mode 100644 index 000000000..abde2d85a --- /dev/null +++ b/admin/src/layout/bussiness/components/header/index.vue @@ -0,0 +1,186 @@ + + + + + diff --git a/admin/src/layout/bussiness/components/header/layout-setting.vue b/admin/src/layout/bussiness/components/header/layout-setting.vue new file mode 100644 index 000000000..4ca0fd2b4 --- /dev/null +++ b/admin/src/layout/bussiness/components/header/layout-setting.vue @@ -0,0 +1,72 @@ + + + + + diff --git a/admin/src/layout/bussiness/components/header/switch-lang.vue b/admin/src/layout/bussiness/components/header/switch-lang.vue new file mode 100644 index 000000000..e48de6e30 --- /dev/null +++ b/admin/src/layout/bussiness/components/header/switch-lang.vue @@ -0,0 +1,32 @@ + + + + + diff --git a/admin/src/layout/bussiness/components/header/user-info.vue b/admin/src/layout/bussiness/components/header/user-info.vue new file mode 100644 index 000000000..896c8aef8 --- /dev/null +++ b/admin/src/layout/bussiness/components/header/user-info.vue @@ -0,0 +1,147 @@ + + + + + diff --git a/admin/src/layout/bussiness/components/tabs.vue b/admin/src/layout/bussiness/components/tabs.vue new file mode 100644 index 000000000..d58f79a43 --- /dev/null +++ b/admin/src/layout/bussiness/components/tabs.vue @@ -0,0 +1,137 @@ + + + + + diff --git a/admin/src/layout/bussiness/index.vue b/admin/src/layout/bussiness/index.vue new file mode 100644 index 000000000..ee46eb0af --- /dev/null +++ b/admin/src/layout/bussiness/index.vue @@ -0,0 +1,44 @@ + + + + + diff --git a/admin/src/layout/bussiness/layout.json b/admin/src/layout/bussiness/layout.json new file mode 100644 index 000000000..6be8c0a65 --- /dev/null +++ b/admin/src/layout/bussiness/layout.json @@ -0,0 +1,4 @@ +{ + "layout": "bussiness", + "cover": "/app/assets/images/layout_bussiness.png" +} diff --git a/admin/src/layout/darkside/components/aside/index.vue b/admin/src/layout/darkside/components/aside/index.vue new file mode 100644 index 000000000..a7125f431 --- /dev/null +++ b/admin/src/layout/darkside/components/aside/index.vue @@ -0,0 +1,71 @@ + + + + + diff --git a/admin/src/layout/darkside/components/aside/menu-item.vue b/admin/src/layout/darkside/components/aside/menu-item.vue new file mode 100644 index 000000000..3c74d3abc --- /dev/null +++ b/admin/src/layout/darkside/components/aside/menu-item.vue @@ -0,0 +1,108 @@ + + + + + diff --git a/admin/src/layout/darkside/components/aside/side.vue b/admin/src/layout/darkside/components/aside/side.vue new file mode 100644 index 000000000..5fa9e23a5 --- /dev/null +++ b/admin/src/layout/darkside/components/aside/side.vue @@ -0,0 +1,101 @@ + + + + + diff --git a/admin/src/layout/darkside/components/header/index.vue b/admin/src/layout/darkside/components/header/index.vue new file mode 100644 index 000000000..5c0cd4c43 --- /dev/null +++ b/admin/src/layout/darkside/components/header/index.vue @@ -0,0 +1,190 @@ + + + + + diff --git a/admin/src/layout/darkside/components/header/layout-setting.vue b/admin/src/layout/darkside/components/header/layout-setting.vue new file mode 100644 index 000000000..4ca0fd2b4 --- /dev/null +++ b/admin/src/layout/darkside/components/header/layout-setting.vue @@ -0,0 +1,72 @@ + + + + + diff --git a/admin/src/layout/darkside/components/header/switch-lang.vue b/admin/src/layout/darkside/components/header/switch-lang.vue new file mode 100644 index 000000000..e48de6e30 --- /dev/null +++ b/admin/src/layout/darkside/components/header/switch-lang.vue @@ -0,0 +1,32 @@ + + + + + diff --git a/admin/src/layout/darkside/components/header/user-info.vue b/admin/src/layout/darkside/components/header/user-info.vue new file mode 100644 index 000000000..896c8aef8 --- /dev/null +++ b/admin/src/layout/darkside/components/header/user-info.vue @@ -0,0 +1,147 @@ + + + + + diff --git a/admin/src/layout/darkside/components/tabs.vue b/admin/src/layout/darkside/components/tabs.vue new file mode 100644 index 000000000..d58f79a43 --- /dev/null +++ b/admin/src/layout/darkside/components/tabs.vue @@ -0,0 +1,137 @@ + + + + + diff --git a/admin/src/layout/darkside/index.vue b/admin/src/layout/darkside/index.vue new file mode 100644 index 000000000..13121b350 --- /dev/null +++ b/admin/src/layout/darkside/index.vue @@ -0,0 +1,47 @@ + + + + + diff --git a/admin/src/layout/darkside/layout.json b/admin/src/layout/darkside/layout.json new file mode 100644 index 000000000..6b762332d --- /dev/null +++ b/admin/src/layout/darkside/layout.json @@ -0,0 +1,4 @@ +{ + "layout": "darkside", + "cover": "/app/assets/images/layout_darkside.png" +} diff --git a/admin/src/layout/default/components/aside/index.vue b/admin/src/layout/default/components/aside/index.vue index c2c9bfe25..c30b7137d 100644 --- a/admin/src/layout/default/components/aside/index.vue +++ b/admin/src/layout/default/components/aside/index.vue @@ -1,6 +1,6 @@ - +
@@ -68,10 +68,6 @@ const props = defineProps({ const meta = computed(() => props.routes.meta) -const resolvePath = (path: string) => { - return `${props.routePath}/${path}` -} - const indexList = ref(); const showDialog = ref(false) const checkIndexList = () => { diff --git a/admin/src/layout/home/components/aside/side.vue b/admin/src/layout/home/components/aside/side.vue index d727dbef2..cc38559b4 100644 --- a/admin/src/layout/home/components/aside/side.vue +++ b/admin/src/layout/home/components/aside/side.vue @@ -12,8 +12,8 @@ - - + +
diff --git a/admin/src/layout/index.vue b/admin/src/layout/index.vue index aa076219f..ab6af558c 100644 --- a/admin/src/layout/index.vue +++ b/admin/src/layout/index.vue @@ -6,6 +6,7 @@ import { ref, markRaw, defineAsyncComponent, provide } from 'vue' import { getAppType } from '@/utils/common' import useUserStore from '@/stores/modules/user' +import useSystemStore from '@/stores/modules/system' const sysLayout = import.meta.glob('./*/index.vue') const addonLayout = import.meta.glob('@/addon/**/layout/index.vue') @@ -21,13 +22,20 @@ switch (getAppType()) { break default: const siteInfo = useUserStore().siteInfo - if (siteInfo && siteInfo.apps && siteInfo.apps.length == 1) siteLayout = siteInfo.apps[0].key + if (siteInfo && siteInfo.apps) { + const layouts = useSystemStore().layoutConfig + if (siteInfo.apps.length == 1) { + layouts[siteInfo.apps[0].key] != undefined && (siteLayout = layouts[siteInfo.apps[0].key]) + } else { + layouts.system != undefined && (siteLayout = layouts.system) + } + } } const layout = ref(null) Object.keys(modules).forEach(key => { - key.indexOf(siteLayout) !== -1 && (layout.value = markRaw(defineAsyncComponent(modules[key]))) + key.indexOf(`/${siteLayout}/`) !== -1 && (layout.value = markRaw(defineAsyncComponent(modules[key]))) }) !layout.value && (layout.value = markRaw(defineAsyncComponent(modules['./default/index.vue']))) @@ -39,7 +47,7 @@ provide('setLayout', (name: any) => { if (siteLayout == name) return siteLayout = name Object.keys(modules).forEach(key => { - key.indexOf(name) !== -1 && (layout.value = markRaw(defineAsyncComponent(modules[key]))) + key.indexOf(`/${name}/`) !== -1 && (layout.value = markRaw(defineAsyncComponent(modules[key]))) }) }) diff --git a/admin/src/layout/profession/components/aside/index.vue b/admin/src/layout/profession/components/aside/index.vue new file mode 100644 index 000000000..c2c9bfe25 --- /dev/null +++ b/admin/src/layout/profession/components/aside/index.vue @@ -0,0 +1,55 @@ + + + + + diff --git a/admin/src/layout/profession/components/aside/menu-item.vue b/admin/src/layout/profession/components/aside/menu-item.vue new file mode 100644 index 000000000..b958d0da0 --- /dev/null +++ b/admin/src/layout/profession/components/aside/menu-item.vue @@ -0,0 +1,67 @@ + + + + + diff --git a/admin/src/layout/profession/components/aside/side.vue b/admin/src/layout/profession/components/aside/side.vue new file mode 100644 index 000000000..f19153fda --- /dev/null +++ b/admin/src/layout/profession/components/aside/side.vue @@ -0,0 +1,244 @@ + + + + + diff --git a/admin/src/layout/profession/components/header/index.vue b/admin/src/layout/profession/components/header/index.vue new file mode 100644 index 000000000..5d8346c9b --- /dev/null +++ b/admin/src/layout/profession/components/header/index.vue @@ -0,0 +1,190 @@ + + + + + diff --git a/admin/src/layout/profession/components/header/layout-setting.vue b/admin/src/layout/profession/components/header/layout-setting.vue new file mode 100644 index 000000000..4ca0fd2b4 --- /dev/null +++ b/admin/src/layout/profession/components/header/layout-setting.vue @@ -0,0 +1,72 @@ + + + + + diff --git a/admin/src/layout/profession/components/header/switch-lang.vue b/admin/src/layout/profession/components/header/switch-lang.vue new file mode 100644 index 000000000..e48de6e30 --- /dev/null +++ b/admin/src/layout/profession/components/header/switch-lang.vue @@ -0,0 +1,32 @@ + + + + + diff --git a/admin/src/layout/profession/components/header/user-info.vue b/admin/src/layout/profession/components/header/user-info.vue new file mode 100644 index 000000000..896c8aef8 --- /dev/null +++ b/admin/src/layout/profession/components/header/user-info.vue @@ -0,0 +1,147 @@ + + + + + diff --git a/admin/src/layout/profession/components/tabs.vue b/admin/src/layout/profession/components/tabs.vue new file mode 100644 index 000000000..d58f79a43 --- /dev/null +++ b/admin/src/layout/profession/components/tabs.vue @@ -0,0 +1,137 @@ + + + + + diff --git a/admin/src/layout/profession/index.vue b/admin/src/layout/profession/index.vue new file mode 100644 index 000000000..ee46eb0af --- /dev/null +++ b/admin/src/layout/profession/index.vue @@ -0,0 +1,44 @@ + + + + + diff --git a/admin/src/layout/profession/layout.json b/admin/src/layout/profession/layout.json new file mode 100644 index 000000000..924b82dcd --- /dev/null +++ b/admin/src/layout/profession/layout.json @@ -0,0 +1,4 @@ +{ + "layout": "profession", + "cover": "/app/assets/images/layout_profession.png" +} diff --git a/admin/src/main.ts b/admin/src/main.ts index 708a19ac9..01600bdab 100644 --- a/admin/src/main.ts +++ b/admin/src/main.ts @@ -9,6 +9,7 @@ import { useElementIcon } from './utils/common' import 'highlight.js/styles/stackoverflow-light.css'; import hljs from 'highlight.js/lib/common' import hljsVuePlugin from '@highlightjs/vue-plugin' +import VueUeditorWrap from 'vue-ueditor-wrap' window.hl = hljs @@ -19,6 +20,7 @@ async function run() { app.use(roter) app.use(ElementPlus) app.use(hljsVuePlugin) + app.use(VueUeditorWrap) useElementIcon(app) app.mount('#app') } diff --git a/admin/src/router/index.ts b/admin/src/router/index.ts index bd6be2ea0..bef8a5757 100644 --- a/admin/src/router/index.ts +++ b/admin/src/router/index.ts @@ -8,9 +8,18 @@ import useUserStore from '@/stores/modules/user' import { setWindowTitle, getAppType, urlToRouteRaw } from '@/utils/common' import storage from '@/utils/storage' +// 加载插件中定义的router +const ADDON_ROUTE = [] +const addonRoutes = import.meta.globEager('@/addon/**/router/index.ts') +for (const key in addonRoutes) { + const addon = addonRoutes[key] + addon.ROUTE && ADDON_ROUTE.push(...addon.ROUTE) + addon.NO_LOGIN_ROUTES && NO_LOGIN_ROUTES.push(...addon.NO_LOGIN_ROUTES) +} + const router = createRouter({ history: createWebHistory(import.meta.env.BASE_URL), - routes: [ADMIN_ROUTE, HOME_ROUTE, SITE_ROUTE, ...STATIC_ROUTES] + routes: [ADMIN_ROUTE, HOME_ROUTE, SITE_ROUTE, ...STATIC_ROUTES, ...ADDON_ROUTE] }) /** @@ -102,7 +111,13 @@ router.beforeEach(async (to, from, next) => { // 设置首页路由 let firstRoute: symbol | string | undefined = findFirstValidRoute(userStore.routers) if (getAppType() != 'admin') { - firstRoute = userStore.addonIndexRoute[ userStore.siteInfo?.apps[0].key ] + for (let i = 0; i < userStore.siteInfo?.apps.length; i++) { + const item = userStore.siteInfo?.apps[i] + if (userStore.addonIndexRoute[item.key]) { + firstRoute = userStore.addonIndexRoute[item.key] + break + } + } } ROOT_ROUTER.redirect = { name: firstRoute } diff --git a/admin/src/router/routers.ts b/admin/src/router/routers.ts index 63761f268..2b855dbbe 100644 --- a/admin/src/router/routers.ts +++ b/admin/src/router/routers.ts @@ -35,6 +35,10 @@ export const ADMIN_ROUTE: RouteRecordRaw = { }, { path: 'login', + meta: { + type: 1, + title: '用户登录' + }, component: () => import('@/app/views/login/index.vue') } ] @@ -74,6 +78,10 @@ export const SITE_ROUTE: RouteRecordRaw = { }, { path: 'login', + meta: { + type: 1, + title: '用户登录' + }, component: () => import('@/app/views/login/index.vue') } ] diff --git a/admin/src/stores/modules/diy.ts b/admin/src/stores/modules/diy.ts index 3a207e7c6..4abd9b06b 100644 --- a/admin/src/stores/modules/diy.ts +++ b/admin/src/stores/modules/diy.ts @@ -13,6 +13,7 @@ const useDiyStore = defineStore('diy', { currentComponent: 'edit-page', // 当前正在编辑的组件名称 pageMode: 'diy', editTab: 'content',// 编辑页面 + pageTitle: '', // 页面名称(用于后台展示) name: '', // 页面标识 type: '', // 页面模板 typeName: '', // 页面模板名称 @@ -41,7 +42,7 @@ const useDiyStore = defineStore('diy', { components: [], // 组件集合 position: ['top_fixed','right_fixed','bottom_fixed','left_fixed','fixed'], global: { - title: "页面", // 页面标题 + title: "页面", // 页面标题(用于前台展示) pageStartBgColor: "", // 页面背景颜色(开始) pageEndBgColor: "", // 页面背景颜色(结束) @@ -51,14 +52,20 @@ const useDiyStore = defineStore('diy', { imgWidth: '', // 页面背景图片宽度 imgHeight: '', // 页面背景图片高度 - // 顶部状态栏 + // 顶部导航栏 topStatusBar: { + isShow: true, // 是否显示 bgColor: "#ffffff", // 背景颜色 isTransparent: false, // 是否透明 - isShow: true, // 是否显示 - style: 'style-1', // 风格样式 - textColor: "#333333", // 文字颜色 + style: 'style-1', // 导航栏风格样式(style-1:文字,style-2:图片+文字,style-3:图片+搜索,style-4:定位) + styleName: '风格1', + textColor: "#333333", // 文字颜色() textAlign: 'center', // 文字对齐方式 + inputPlaceholder : '请输入搜索关键词', + imgUrl:'', // 图片 + link: { // 跳转链接 + name: "" + } }, bottomTabBarSwitch: true, // 底部导航开关 @@ -129,14 +136,20 @@ const useDiyStore = defineStore('diy', { imgWidth: '', // 页面背景图片宽度 imgHeight: '', // 页面背景图片高度 - // 顶部状态栏 + // 顶部导航栏 topStatusBar: { + isShow: true, // 是否显示 bgColor: "#ffffff", // 背景颜色 isTransparent: false, // 是否透明 - isShow: true, // 是否显示 - style: 'style-1', // 风格样式 - textColor: "#333333", // 文字颜色 + style: 'style-1', // 导航栏风格样式(style-1:文字,style-2:图片+文字,style-3:图片+搜索,style-4:定位) + styleName: '风格1', + textColor: "#333333", // 文字颜色() textAlign: 'center', // 文字对齐方式 + inputPlaceholder : '请输入搜索关键词', + imgUrl:'', // 图片 + link: { // 跳转链接 + name: "" + } }, bottomTabBarSwitch: true, // 底部导航开关 @@ -206,7 +219,7 @@ const useDiyStore = defineStore('diy', { Object.assign(component, template); if(component.template){ - // 按照组件初始的属性加载覆盖 + // 按照组件初始的属性覆盖默认值 Object.assign(component, component.template); delete component.template; } @@ -418,15 +431,24 @@ const useDiyStore = defineStore('diy', { }, // 组件验证 verify() { - if (this.global.title === "") { + if (this.pageTitle === "") { ElMessage({ - message: t('pageNamePlaceholder'), + message: t('diyPageTitlePlaceholder'), type: 'warning' }) this.changeCurrentIndex(-99); return false; } + // if (this.global.title === "") { + // ElMessage({ + // message: t('diyTitlePlaceholder'), + // type: 'warning' + // }) + // this.changeCurrentIndex(-99); + // return false; + // } + for (var i = 0; i < this.value.length; i++) { try { if (this.value[i].verify) { diff --git a/admin/src/stores/modules/poster.ts b/admin/src/stores/modules/poster.ts new file mode 100644 index 000000000..60a898d98 --- /dev/null +++ b/admin/src/stores/modules/poster.ts @@ -0,0 +1,803 @@ +import {defineStore} from 'pinia' +import {t} from '@/lang' +import {ElMessage, ElMessageBox} from 'element-plus' +import {cloneDeep} from 'lodash-es' +import {img} from '@/utils/common' + +const usePosterStore = defineStore('poster', { + state: () => { + return { + + contentBoxWidth: 360, // 360*2=720 + contentBoxHeight: 640, // 640*2=1280 + + id: 0, + name: '', // 页面名称 + type: '', // 海报类型 + typeName: '', + channel: '', // 海报支持的渠道 + status: 1, // 是否启用 + isDefault: 0, // 是否默认 + addon: '', // 海报所属插件 + + currentIndex: -99, // 当前正在编辑的组件下标 + currentComponent: 'edit-page', // 当前正在编辑的组件名称 + predefineColors: [ + '#F4391c', + '#ff4500', + '#ff8c00', + '#FFD009', + '#ffd700', + '#19C650', + '#90ee90', + '#00ced1', + '#1e90ff', + '#c71585', + '#FF407E', + '#CFAF70', + '#A253FF', + 'rgba(255, 69, 0, 0.68)', + 'rgb(255, 120, 0)', + 'hsl(181, 100%, 37%)', + 'hsla(209, 100%, 56%, 0.73)', + '#c7158577' + ], + components: [], // 组件集合 + global: { + width: 720, // 海报宽度 + height: 1280, // 海报高度 + bgType: 'url', + bgColor: "#ffffff", // 背景颜色 + bgUrl: '', // 背景图片 + }, + // 组件类型,文本:text,image:图片,qrcode:二维码 + template: { + width: 100, // 宽度 + height: 100, // 高度 + minWidth: 30, // 最小宽度 + minHeight: 30, // 最小高度 + x: 0, // 横向坐标 → + y: 0, // 纵向坐标 ↑ + angle: 0, // 旋转角度 0~360 + zIndex: 0 // 层级 + }, + // 各组件类型的默认值 + templateType: { + text: { + height: 30, + minWidth: 60, + minHeight: 22, + fontFamily: '', + fontSize: 20, + fontColor: '#303133' + }, + image: {}, + qrcode: {}, + // 绘画 + draw: { + draw_type: 'Polygon', + points: [[0, 1210], [720, 1210], [720, 1280], [0, 1280]], + bgColor: '#eeeeee' + } + }, + // 组件集合 + value: [] + } + }, + getters: { + editComponent: (state) => { + if (state.currentIndex == -99) { + return state.global; + } else { + return state.value[state.currentIndex]; + } + }, + }, + actions: { + // 初始化数据 + init() { + this.global = { + width: 720, // 海报宽度 + height: 1280, // 海报高度 + bgType: 'url', + bgColor: "#ffffff", // 页面背景颜色(开始) + bgUrl: '' // 页面背景图片 + }; + this.value = []; + }, + // 添加组件 + addComponent(key: string, data: any) { + + // 删除不用的字段 + let component = cloneDeep(data); + + component.id = this.generateRandom(); + component.componentName = key; + component.componentTitle = component.title; + + delete component.title; + delete component.icon; + + // 继承默认属性 + let template: any = cloneDeep(this.template); + Object.assign(component, template); + + let templateType: any = cloneDeep(this.templateType); + Object.assign(component, templateType[component.type]); + + if (component.template) { + // 按照组件初始的属性覆盖默认值 + Object.assign(component, component.template); + delete component.template; + } + + if (!this.checkComponentIsAdd(component)) { + // 组件最多只能添加n个 + ElMessage({ + type: 'warning', + message: `${component.componentTitle}${t('componentCanOnlyAdd')}${component.uses}${t('piece')}`, + }); + return; + } + + component.zIndex = this.value.length + 1; + this.value.push(component); + // 添加组件后(不是编辑调用的),选择最后一个 + this.currentIndex = this.value.length - 1; + + this.currentComponent = 'edit-' + component.path; + }, + // 生成随机数 + generateRandom(len: number = 5) { + return Number(Math.random().toString().substr(3, len) + Date.now()).toString(36); + }, + // 选中正在编辑的组件 + changeCurrentIndex(index: number, component: any = null) { + this.currentIndex = index; + if (this.currentIndex == -99) { + this.currentComponent = 'edit-page'; + } else if (component) { + this.currentComponent = 'edit-' + component.path; + } + }, + // 删除组件 + delComponent() { + if (this.currentIndex == -99) return; + + ElMessageBox.confirm( + t('delComponentTips'), + t('warning'), + { + confirmButtonText: t('confirm'), + cancelButtonText: t('cancel'), + type: 'warning', + autofocus: false + } + ).then(() => { + this.value.splice(this.currentIndex, 1); + + // 如果组件全部删除,则选中页面设置 + if (this.value.length === 0) { + this.currentIndex = -99; + } + + // 如果当前选中的组件不存在,则选择上一个 + if (this.currentIndex === this.value.length) { + this.currentIndex--; + } + + let component = cloneDeep(this.value[this.currentIndex]); + this.changeCurrentIndex(this.currentIndex, component) + + }).catch(() => { + }) + + }, + // 上移一层组件 + moveUpComponent() { + if (this.currentIndex < -1) return; // 从0开始 + + this.value[this.currentIndex].zIndex++; + if (this.value[this.currentIndex].zIndex >= this.value.length) { + this.value[this.currentIndex].zIndex = this.value.length; + } + + }, + // 下移一层组件 + moveDownComponent() { + if (this.currentIndex < -1) return; // 从0开始 + + this.value[this.currentIndex].zIndex--; + + if (this.value[this.currentIndex].zIndex < 0) { + this.value[this.currentIndex].zIndex = 0; + } + }, + // 复制组件 + copyComponent() { + if (this.currentIndex < 0) return; // 从0开始 + + let component = cloneDeep(this.value[this.currentIndex]); // 当前选中组件 + + component.id = this.generateRandom(); // 更新id,刷新组件数据 + component.x = 0; // 重置坐标 + component.y = 0; // 重置坐标 + + // 暂不复制宽高 + // let box: any = document.getElementById(this.value[this.currentIndex].id) + // component.width = box.offsetWidth + // component.height = box.offsetHeight + // component.auto = false; + + if (!this.checkComponentIsAdd(component)) { + ElMessage({ + type: 'warning', + message: `${t('notCopy')},${component.componentTitle}${t('componentCanOnlyAdd')}${component.uses}${t('piece')}`, + }); + return; + } + + var index = this.currentIndex + 1; + this.value.splice(index, 0, component); + + this.changeCurrentIndex(index, component); + }, + // 检测组件是否允许添加,true:允许 false:不允许 + checkComponentIsAdd(component: any) { + + //为0时不处理 + if (component.uses === 0) return true; + + var count = 0; + + //遍历已添加的自定义组件,检测是否超出数量 + for (var i in this.value) if (this.value[i].componentName === component.componentName) count++; + + if (count >= component.uses) return false; + else return true; + }, + // 重置当前组件数据 + resetComponent() { + if (this.currentIndex < 0) return; // 从0开始 + + ElMessageBox.confirm( + t('resetComponentTips'), + t('warning'), + { + confirmButtonText: t('confirm'), + cancelButtonText: t('cancel'), + type: 'warning', + autofocus: false + } + ).then(() => { + // 重置当前选中的组件数据 + for (let i = 0; i < this.components.length; i++) { + if (this.components[i].componentName == this.editComponent.componentName) { + Object.assign(this.editComponent, this.components[i]); + + let templateType: any = cloneDeep(this.templateType); + Object.assign(this.editComponent, templateType[this.editComponent.type]); + this.editComponent.angle = 0; + break; + } + } + + }).catch(() => { + }) + + }, + // 组件验证 + verify() { + if (this.name === "") { + ElMessage({ + message: t('posterNamePlaceholder'), + type: 'warning' + }); + this.changeCurrentIndex(-99); + return false; + } + + if (this.value.length == 0) { + ElMessage({ + message: t('diyPosterValueEmptyTips'), + type: 'warning' + }); + this.changeCurrentIndex(-99); + return false; + } + + for (var i = 0; i < this.value.length; i++) { + try { + if (this.value[i].verify) { + var res = this.value[i].verify(i); + if (!res.code) { + this.changeCurrentIndex(i, this.value[i]); + ElMessage({ + message: res.message, + type: 'warning' + }); + return false; + } + } + } catch (e) { + console.log("verify Error:", e, i, this.value[i]); + } + } + return true; + }, + // 移动事件 + mouseDown(e: any, id: any, index: any) { + const box: any = document.getElementById(id); + const disX = e.clientX - box.offsetLeft; + const disY = e.clientY - box.offsetTop; + + // 鼠标移动时 + document.onmousemove = (e) => { + if (this.contentBoxWidth == box.offsetWidth) { + box.style.left = 0 + } else { + box.style.left = e.clientX - disX + 'px' + } + box.style.top = e.clientY - disY + 'px'; + + // 边界判断 + if (e.clientX - disX < 0) { + box.style.left = 0 + } + + if (e.clientX - disX > this.contentBoxWidth - box.offsetWidth) { + box.style.left = this.contentBoxWidth - box.offsetWidth + 'px' + } + + + if (e.clientY - disY < 0) { + box.style.top = 0 + } + + if (e.clientY - disY > this.contentBoxHeight - box.offsetHeight) { + box.style.top = this.contentBoxHeight - box.offsetHeight + 'px' + } + + this.value[index].x = box.offsetLeft; + this.value[index].y = box.offsetTop + + }; + + // 鼠标抬起时 + document.onmouseup = (e) => { + document.onmousemove = null + } + }, + // 拖拽缩放事件 + resizeMouseDown(e: any, item: any, index: any) { + const oEv = e; + oEv.stopPropagation(); + const box: any = document.getElementById(item.id); + const className = e.target.className; + + // 获取移动前盒子的宽高, + const oldWidth = box.offsetWidth; + const oldHeight = box.offsetHeight; + + // 获取鼠标距离屏幕的left和top值 + const oldX = oEv.clientX; + const oldY = oEv.clientY; + + // 元素相对于最近的父级定位 + const oldLeft = box.offsetLeft; + const oldTop = box.offsetTop; + + // 设置最小的宽度 + let minWidth = 100; + let minHeight = 100; + + if (item.type == 'text') { + // 文本类型 + minWidth = 60; + minHeight = 22; + } else if (item.type == 'image' || item.type == 'qrcode') { + // 图片类型 + minWidth = 30; + minHeight = 30; + } else if (item.type == 'draw') { + // 绘画类型 + minWidth = 20; + minHeight = 20; + } + + document.onmousemove = (e) => { + const oEv = e; + // console.log('move', "width:" + oldWidth, + // ',oldLeft: ' + oldLeft, ',oldTop: ' + oldTop, + // ',oldX:clientX-- ' + oldX + ':' + oEv.clientX, + // ',oldY:clientY-- ' + oldY + ':' + oEv.clientY, + // ) + + // 左上角 + if (className == 'box1') { + let width = oldWidth - (oEv.clientX - oldX); + const maxWidth = this.contentBoxWidth; + + let height = oldHeight - (oEv.clientY - oldY); + const maxHeight = this.contentBoxHeight - oldTop; + + let left = oldLeft + (oEv.clientX - oldX); + let top = oldTop + (oEv.clientY - oldY); + + if (width < minWidth) { + width = minWidth + } + if (width > maxWidth) { + width = maxWidth + } + + if (height < minHeight) { + height = minHeight + } + if (height > maxHeight) { + height = maxHeight + } + + if (oldLeft == 0 && oldTop == 0) { + // 坐标:left = 0,top = 0 + + if (width == minWidth && height == minHeight) { + // 宽高 = 最小值,left = 最小宽度,top = 最小高度 + left = minWidth; + top = minHeight; + } else if (width == minWidth && height > minHeight) { + // 宽 = 最小值,高 > 最小值,left = 最小宽度,top = 不予处理 + left = minWidth; + } else if (width > minWidth && height == minHeight) { + // 宽 > 最小值,高 = 最小值,left = 不予处理,top = 最小高度 + top = minHeight; + } else if (width > minWidth && height > minHeight) { + // 宽 > 最小值,高 > 最小值,left = 不予处理,top = 不予处理 + } + } else if (oldLeft == 0 && oldTop > 0) { + // 坐标:left = 0,top > 0 + + if (width == minWidth && height == minHeight) { + // 宽高 = 最小值,left = 最小宽度,top = 元素上偏移位置 + left = minWidth; + top = box.offsetTop; + } else if (width == minWidth && height > minHeight) { + // 宽 = 最小值,高 > 最小值,left = 最小宽度,top = 元素上偏移位置 + left = minWidth; + top = box.offsetTop; + } else if (width > minWidth && height == minHeight) { + // 宽 > 最小值,高 = 最小值,left = 不予处理,top = 元素上偏移位置 + top = box.offsetTop; + } else if (width > minWidth && height > minHeight) { + // 宽 > 最小值,高 > 最小值,left = 不予处理,top = 不予处理 + } + } else if (oldLeft > 0 && oldTop == 0) { + // 坐标:left > 0,top = 0 + + if (width == minWidth && height == minHeight) { + // 宽高 = 最小值,left = 元素左偏移位置,top = 元素上偏移位置 + left = box.offsetLeft; + top = box.offsetTop; + } else if (width == minWidth && height > minHeight) { + // 宽 = 最小值,高 > 最小值,left = 元素左偏移位置,top = 0 + left = box.offsetLeft; + top = 0; + } else if (width > minWidth && height == minHeight) { + // 宽 > 最小值,高 = 最小值,left = 不予处理,top = 元素上偏移位置 + top = box.offsetTop; + } else if (width > minWidth && height > minHeight) { + // 宽 > 最小值,高 > 最小值,left = 不予处理,top = 不予处理 + } + } else if (oldLeft > 0 && oldTop > 0) { + // 坐标:left > 0,top > 0 + + if (width == minWidth && height == minHeight) { + // 宽高 = 最小值,left = 元素左偏移位置,top = 元素上偏移位置 + left = box.offsetLeft; + top = box.offsetTop; + } else if (width == minWidth && height > minHeight) { + // 宽 = 最小值,高 > 最小值,left = 元素左偏移位置,top = 元素上偏移位置 + left = box.offsetLeft; + top = box.offsetTop; + } else if (width > minWidth && height == minHeight) { + // 宽 > 最小值,高 = 最小值,left = 不予处理,top = 元素上偏移位置 + top = box.offsetTop; + } else if (width > minWidth && height > minHeight) { + // 宽 > 最小值,高 > 最小值,left = 不予处理,top = 不予处理 + } + } + + // 左上宽 + if (left < 0) { + left = 0; + width = oldWidth - (oEv.clientX - oldX) + (oldLeft + (oEv.clientX - oldX)); + } + + // 左上 高 + if (top < 0) { + top = 0; + height = oldTop + (oEv.clientY - oldY) + (oldHeight - (oEv.clientY - oldY)); + } + + box.children[0].style.width = width + 'px'; + + // 文本设置高度,图片自适应 无需设置 + if (item.type == 'text' || item.type == 'draw') { + box.children[0].style.height = height + 'px'; + } + box.style.left = left + 'px'; + box.style.top = top + 'px'; + } else if (className == 'box2') { + // 右上角 + + let width = oldWidth + (oEv.clientX - oldX); + const maxWidth = this.contentBoxWidth - oldLeft; + + let height = oldHeight - (oEv.clientY - oldY); + const maxHeight = this.contentBoxHeight - oldTop; + + let top = oldTop + (oEv.clientY - oldY); + + if (width < minWidth) { + width = minWidth + } + if (width > maxWidth) { + width = maxWidth + } + + if (height < minHeight) { + height = minHeight + } + if (height > maxHeight) { + height = maxHeight + } + + if (oldLeft == 0 && oldTop == 0) { + // 坐标:left = 0,top = 0 + + if (width == minWidth && height == minHeight) { + // 宽高 = 最小值,top = 最小高度 + top = minHeight + } else if (width == minWidth && height > minHeight) { + // 宽 = 最小值,高 > 最小值,不予处理 + } else if (width > minWidth && height == minHeight) { + // 宽 > 最小值,高 = 最小值,top = 最小高度 + top = minHeight + } else if (width > minWidth && height > minHeight) { + // 宽 > 最小值,高 > 最小值,不予处理 + } + } else if (oldLeft == 0 && oldTop > 0) { + // 坐标:left = 0,top > 0 + + if (width == minWidth && height == minHeight) { + // 宽高 = 最小值,top = 元素上偏移位置 + top = box.offsetTop + } else if (width == minWidth && height > minHeight) { + // 宽 = 最小值,高 > 最小值,top = 元素上偏移位置 + top = box.offsetTop + } else if (width > minWidth && height == minHeight) { + // 宽 > 最小值,高 = 最小值,top = 元素上偏移位置 + top = box.offsetTop + } else if (width > minWidth && height > minHeight) { + // 宽 > 最小值,高 > 最小值,不予处理 + } + } else if (oldLeft > 0 && oldTop == 0) { + // 坐标:left = 0,top = 0 + + if (width == minWidth && height == minHeight) { + // 宽高 = 最小值,top = 元素上偏移位置 + top = box.offsetTop + } else if (width == minWidth && height > minHeight) { + // 宽 = 最小值,高 > 最小值,top = 0 + top = 0 + } else if (width > minWidth && height == minHeight) { + // 宽 > 最小值,高 = 最小值,top = 元素上偏移位置 + top = box.offsetTop + } else if (width > minWidth && height > minHeight) { + // 宽 > 最小值,高 > 最小值,不予处理 + } + } else if (oldLeft > 0 && oldTop > 0) { + // 坐标:left > 0,top > 0 + + if (width == minWidth && height == minHeight) { + // 宽高 = 最小值,top = 元素上偏移位置 + top = box.offsetTop + } else if (width == minWidth && height > minHeight) { + // 宽 = 最小值,高 > 最小值,top = 元素上偏移位置 + top = box.offsetTop + } else if (width > minWidth && height == minHeight) { + // 宽 > 最小值,高 = 最小值,top = 元素上偏移位置 + top = box.offsetTop + } else if (width > minWidth && height > minHeight) { + // 宽 > 最小值,高 > 最小值,不予处理 + } + } + + // 右上高 + if (top < 0) { + top = 0; + height = oldTop + (oEv.clientY - oldY) + (oldHeight - (oEv.clientY - oldY)) + } + + box.children[0].style.width = width + 'px'; + + // 文本设置高度,图片自适应 无需设置 + if (item.type == 'text' || item.type == 'draw') { + box.children[0].style.height = height + 'px' + } + box.style.top = top + 'px' + } else if (className == 'box3') { + // 左下角 + + let width = oldWidth - (oEv.clientX - oldX); + const maxWidth = this.contentBoxWidth; + + let height = oldHeight + (oEv.clientY - oldY); + const maxHeight = this.contentBoxHeight - oldTop; + + let left = oldLeft + (oEv.clientX - oldX); + + if (width < minWidth) { + width = minWidth + } + if (width > maxWidth) { + width = maxWidth + } + + if (height < minHeight) { + height = minHeight + } + if (height > maxHeight) { + height = maxHeight + } + + if (oldLeft == 0 && oldTop == 0) { + // 坐标:left = 0,top = 0 + + if (width == minWidth && height == minHeight) { + // 宽高 = 最小值,left = 最小宽度 + left = minWidth + } else if (width == minWidth && height > minHeight) { + // 宽 = 最小值,高 > 最小值,left = 最小宽度 + left = minWidth + } else if (width > minWidth && height == minHeight) { + // 宽 > 最小值,高 = 最小值,不予处理 + } else if (width > minWidth && height > minHeight) { + // 宽 > 最小值,高 > 最小值,不予处理 + } + } else if (oldLeft == 0 && oldTop > 0) { + // 坐标:left = 0,top > 0 + + if (width == minWidth && height == minHeight) { + // 宽高 = 最小值,left = 最小宽度 + left = minWidth + } else if (width == minWidth && height > minHeight) { + // 宽 = 最小值,高 > 最小值,left = 最小宽度 + left = minWidth + } else if (width > minWidth && height == minHeight) { + // 宽 > 最小值,高 = 最小值,不予处理 + } else if (width > minWidth && height > minHeight) { + // 宽 > 最小值,高 > 最小值,不予处理 + } + } else if (oldLeft > 0 && oldTop == 0) { + // 坐标:left > 0,top = 0 + + if (width == minWidth && height == minHeight) { + // 宽高 = 最小值,left = 元素左偏移位置 + left = box.offsetLeft + } else if (width == minWidth && height > minHeight) { + // 宽 = 最小值,高 > 最小值,left = 元素左偏移位置 + left = box.offsetLeft + } else if (width > minWidth && height == minHeight) { + // 宽 > 最小值,高 = 最小值,不予处理 + } else if (width > minWidth && height > minHeight) { + // 宽 > 最小值,高 > 最小值,不予处理 + } + } else if (oldLeft > 0 && oldTop > 0) { + // 坐标:left > 0,top > 0 + + if (width == minWidth && height == minHeight) { + // 宽高 = 最小值,left = 元素左偏移位置 + left = box.offsetLeft + } else if (width == minWidth && height > minHeight) { + // 宽 = 最小值,高 > 最小值,left = 元素左偏移位置 + left = box.offsetLeft + } else if (width > minWidth && height == minHeight) { + // 宽 > 最小值,高 = 最小值,不予处理 + } else if (width > minWidth && height > minHeight) { + // 宽 > 最小值,高 > 最小值,不予处理 + } + } + + if (left < 0) { + left = 0; + width = oldWidth - (oEv.clientX - oldX) + (oldLeft + (oEv.clientX - oldX)) + } + + box.children[0].style.width = width + 'px'; + + // 文本设置高度,图片自适应 无需设置 + if (item.type == 'text' || item.type == 'draw') { + box.children[0].style.height = height + 'px' + } + box.style.left = left + 'px' + } else if (className == 'box4') { + // 右下角 + + let width = oldWidth + (oEv.clientX - oldX); + const maxWidth = this.contentBoxWidth - oldLeft; + + let height = oldHeight + (oEv.clientY - oldY); + const maxHeight = this.contentBoxHeight - oldTop; + + if (width < minWidth) { + width = minWidth + } + if (width > maxWidth) { + width = maxWidth + } + + if (height < minHeight) { + height = minHeight + } + if (height > maxHeight) { + height = maxHeight + } + + box.children[0].style.width = width + 'px'; + + // 文本设置高度,图片自适应 无需设置 + if (item.type == 'text' || item.type == 'draw') { + box.children[0].style.height = height + 'px' + } + } + + this.value[index].x = box.offsetLeft; + this.value[index].y = box.offsetTop; + + this.value[index].width = parseInt(box.children[0].style.width.replace('px', '')); + + }; + + // 鼠标抬起时 + document.onmouseup = () => { + document.onmousemove = null; + document.onmouseup = null + } + }, + getGlobalStyle() { + let style = ''; + if (this.global.bgType == 'color') { + style += `background-color:${this.global.bgColor};`; + } else if (this.global.bgType == 'url') { + if (this.global.bgUrl) { + style += `background-image:url("${img(this.global.bgUrl)}")`; + } + } + return style; + }, + getMaxX() { + const box: any = document.getElementById(this.editComponent.id); + let x = this.contentBoxWidth; + if (box) { + x -= box.offsetWidth; + } + return x; + }, + getMaxY() { + const box: any = document.getElementById(this.editComponent.id); + let y = this.contentBoxHeight; + if (box) { + y -= box.offsetHeight; + } + return y; + }, + getMaxWidth() { + let width = this.contentBoxWidth; + width -= this.editComponent.x; + return width; + }, + } +}); + +export default usePosterStore \ No newline at end of file diff --git a/admin/src/stores/modules/system.ts b/admin/src/stores/modules/system.ts index 4f44af4d9..4c676469c 100644 --- a/admin/src/stores/modules/system.ts +++ b/admin/src/stores/modules/system.ts @@ -1,7 +1,7 @@ import { defineStore } from 'pinia' import storage from '@/utils/storage' import { useCssVar } from '@vueuse/core' -import { getWebConfig } from '@/app/api/sys' +import {getWebConfig, getWebsiteLayout} from '@/app/api/sys' interface System { menuIsCollapse: boolean, @@ -12,7 +12,8 @@ interface System { sidebar: string, sidebarStyle: string, currHeadMenuName: any, - website: Object + website: Object, + layoutConfig: Object } const theme = storage.get('theme') ?? {} @@ -20,7 +21,6 @@ const theme = storage.get('theme') ?? {} const useSystemStore = defineStore('system', { state: (): System => { return { - // menuIsCollapse: storage.get('menuiscollapse') ?? false, menuIsCollapse: false, menuDrawer: false, dark: theme.dark ?? false, @@ -29,7 +29,8 @@ const useSystemStore = defineStore('system', { lang: storage.get('lang') ?? 'zh-cn', sidebarStyle: theme.sidebarStyle ?? 'threeType', currHeadMenuName: '', - website: {} + website: {}, + layoutConfig: {} } }, actions: { @@ -50,6 +51,11 @@ const useSystemStore = defineStore('system', { await getWebConfig().then(({ data }) => { this.website = data }).catch() + }, + async getWebsiteLayout() { + await getWebsiteLayout().then(({ data }) => { + this.layoutConfig = data + }).catch() } } }) diff --git a/admin/src/styles/common.scss b/admin/src/styles/common.scss index 533e0853d..8f3b12458 100644 --- a/admin/src/styles/common.scss +++ b/admin/src/styles/common.scss @@ -27,16 +27,6 @@ html { } } -.w-e-full-screen-container { - z-index: 10; -} - -.w-e-toolbar { - .w-e-bar-divider { - display: none; - } -} - .main-container{ // background-color: #fff; background-color: var(--el-bg-color-overlay); @@ -64,7 +54,7 @@ html { right: 10px; left: 10px; bottom: 0; - z-index: 4; + z-index: 1000; display: flex; align-items: center; justify-content: center; @@ -82,12 +72,6 @@ html.dark { .table-search-wrap { background-color: var(--el-bg-color)!important; } - --w-e-toolbar-bg-color: var(--el-bg-color-overlay); - --w-e-textarea-bg-color: var(--el-bg-color-overlay); - --w-e-textarea-color: var(--el-input-text-color); - --w-e-toolbar-border-color: var(--el-border-color); - --w-e-toolbar-active-bg-color:var(--el-bg-color); - --w-e-toolbar-active-color:var(--el-text-color-primary); } :root input:-webkit-autofill, @@ -124,6 +108,7 @@ select:-webkit-autofill { .region-input { --region-input-border-color: var(--el-border-color); + --el-input-border-radius: 0; display: flex; box-shadow: 0 0 0 1px var(--region-input-border-color) inset; border-radius: var(--el-input-border-radius,var(--el-border-radius-base)); @@ -201,30 +186,30 @@ html.dark { } // 详情的头部 -.detail-head{ +.detail-head { display: flex; + margin: 15px; align-items: center; - margin-left: 30px; - margin-top: 15px; - margin-bottom: 15px; - .left{ - color: #666; - margin-top: 1px; + + .left { font-size: 14px; line-height: 1; + margin-top: 1px; cursor: pointer; + color: #666; } - .adorn{ - color: #999; - margin: 0 12px; + + .adorn { font-size: 14px; + margin: 0 12px; + color: #999; } + .right{ font-size: 24px; } } - // ********************************************** 修改整体样式 ********************************************** // 修改选择框、ipnut、时间选择、按钮,input带按钮的圆角 .el-input__wrapper, .el-input-group__append, .el-textarea__inner{ @@ -248,19 +233,7 @@ html.dark { -webkit-line-clamp: 2; -webkit-box-orient: vertical; } -// 滚动条 -//.el-scrollbar__bar.is-vertical{ -// width: 10px !important; -//} -//.el-scrollbar__bar.is-vertical .el-scrollbar__thumb{ -// background-color: #8b8b8b !important; -// opacity: 1 !important; -// width: 9px !important; -//} -//.el-scrollbar__bar.is-vertical .el-scrollbar__thumb:hover{ -// background-color: #636363 !important; -// opacity: 1 !important; -//} + .el-card{ border-radius: 0 !important; } @@ -269,4 +242,4 @@ html.dark { } .text-page-title{ line-height: 32px; -} \ No newline at end of file +} diff --git a/admin/src/styles/element-plus.scss b/admin/src/styles/element-plus.scss index 2bc94ecd8..1b490acb0 100644 --- a/admin/src/styles/element-plus.scss +++ b/admin/src/styles/element-plus.scss @@ -34,19 +34,22 @@ } -.el-textarea__inner::-webkit-scrollbar { - width: 6px ; - height: 6px ; -} +.el-textarea__inner { -.el-textarea__inner::-webkit-scrollbar-thumb { - border-radius: 3px ; - -moz-border-radius: 3px ; - -webkit-border-radius: 3px ; - background-color: #909399; - opacity: .3; -} + &::-webkit-scrollbar { + width: 6px ; + height: 6px ; + } -.el-textarea__inner::-webkit-scrollbar-track { - background-color: transparent ; -} \ No newline at end of file + &::-webkit-scrollbar-thumb { + border-radius: 3px ; + -moz-border-radius: 3px ; + -webkit-border-radius: 3px ; + background-color: #909399; + opacity: .3; + } + + &::-webkit-scrollbar-track { + background-color: transparent ; + } +} diff --git a/admin/src/styles/icon/addon-iconfont.css b/admin/src/styles/icon/addon-iconfont.css index 4ad8e42ac..4401b31bd 100644 --- a/admin/src/styles/icon/addon-iconfont.css +++ b/admin/src/styles/icon/addon-iconfont.css @@ -1 +1,2 @@ -/* addon iconfont */ +@import "addon/o2o/iconfont.css"; +@import "addon/tourism/iconfont.css"; diff --git a/admin/src/styles/icon/iconfont.css b/admin/src/styles/icon/iconfont.css index 65dbd60e4..05b70656b 100644 --- a/admin/src/styles/icon/iconfont.css +++ b/admin/src/styles/icon/iconfont.css @@ -1,8 +1,8 @@ @font-face { font-family: "iconfont"; /* Project id 3883393 */ - src: url('//at.alicdn.com/t/c/font_3883393_eqk4fw84z0e.woff2?t=1712631982793') format('woff2'), - url('//at.alicdn.com/t/c/font_3883393_eqk4fw84z0e.woff?t=1712631982793') format('woff'), - url('//at.alicdn.com/t/c/font_3883393_eqk4fw84z0e.ttf?t=1712631982793') format('truetype'); + src: url('//at.alicdn.com/t/c/font_3883393_t75he8bd12.woff2?t=1715329624274') format('woff2'), + url('//at.alicdn.com/t/c/font_3883393_t75he8bd12.woff?t=1715329624274') format('woff'), + url('//at.alicdn.com/t/c/font_3883393_t75he8bd12.ttf?t=1715329624274') format('truetype'); } .iconfont { @@ -13,6 +13,158 @@ -moz-osx-font-smoothing: grayscale; } +.iconjifenshangpin:before { + content: "\e707"; +} + +.iconnicheng1:before { + content: "\e708"; +} + +.iconhuihua1:before { + content: "\e709"; +} + +.icongeren:before { + content: "\e70a"; +} + +.iconhuiyuan12:before { + content: "\e70b"; +} + +.iconerweima:before { + content: "\e70d"; +} + +.iconhuajiaqian:before { + content: "\e70e"; +} + +.iconshoujia:before { + content: "\e70f"; +} + +.iconshangpintupian:before { + content: "\e710"; +} + +.icontupian1:before { + content: "\e711"; +} + +.iconjinbi:before { + content: "\e712"; +} + +.iconhuangguan:before { + content: "\e713"; +} + +.icona-Group13:before { + content: "\e712"; +} + +.iconyingxiao:before { + content: "\e706"; +} + +.iconxiazai19:before { + content: "\e682"; +} + +.iconshangjiantou:before { + content: "\e678"; +} + +.iconxiajiantou:before { + content: "\e681"; +} + +.iconriqi:before { + content: "\e657"; +} + +.icontuikuan:before { + content: "\e75e"; +} + +.icongouwu:before { + content: "\e65a"; +} + +.icondaishouhuo:before { + content: "\e65c"; +} + +.icondaifahuo:before { + content: "\e669"; +} + +.iconguahao:before { + content: "\e66a"; +} + +.icondaifukuan:before { + content: "\e672"; +} + +.icon31yiguanzhudianpu:before { + content: "\e656"; +} + +.icon31huidaodingbu:before { + content: "\e658"; +} + +.iconbianji:before { + content: "\e659"; +} + +.icontuihuobaozhang:before { + content: "\e653"; +} + +.iconhome:before { + content: "\e633"; +} + +.icon31daifahuo:before { + content: "\e634"; +} + +.icon31daifukuan:before { + content: "\e637"; +} + +.icontuikuantuihuo:before { + content: "\e639"; +} + +.icon31dianhua:before { + content: "\e63c"; +} + +.icon31shijian:before { + content: "\e63d"; +} + +.iconshuiqijiaoliu:before { + content: "\e67d"; +} + +.iconguanwang:before { + content: "\e704"; +} + +.icontransferout:before { + content: "\e651"; +} + +.icontransferout-copy:before { + content: "\ecad"; +} + .iconxingzhuang-wenzi:before { content: "\eb99"; } diff --git a/admin/src/styles/icon/iconfont.json b/admin/src/styles/icon/iconfont.json index 2ad0acab0..f5ae15ba3 100644 --- a/admin/src/styles/icon/iconfont.json +++ b/admin/src/styles/icon/iconfont.json @@ -5,6 +5,272 @@ "css_prefix_text": "icon", "description": "系统图标", "glyphs": [ + { + "icon_id": "40268072", + "name": "积分商品", + "font_class": "jifenshangpin", + "unicode": "e707", + "unicode_decimal": 59143 + }, + { + "icon_id": "40266595", + "name": "昵称", + "font_class": "nicheng1", + "unicode": "e708", + "unicode_decimal": 59144 + }, + { + "icon_id": "8361787", + "name": "绘画", + "font_class": "huihua1", + "unicode": "e709", + "unicode_decimal": 59145 + }, + { + "icon_id": "8361772", + "name": "个人3", + "font_class": "geren", + "unicode": "e70a", + "unicode_decimal": 59146 + }, + { + "icon_id": "8361774", + "name": "会员", + "font_class": "huiyuan12", + "unicode": "e70b", + "unicode_decimal": 59147 + }, + { + "icon_id": "8361765", + "name": "二维码", + "font_class": "erweima", + "unicode": "e70d", + "unicode_decimal": 59149 + }, + { + "icon_id": "40266986", + "name": "划价签", + "font_class": "huajiaqian", + "unicode": "e70e", + "unicode_decimal": 59150 + }, + { + "icon_id": "40267219", + "name": "售价", + "font_class": "shoujia", + "unicode": "e70f", + "unicode_decimal": 59151 + }, + { + "icon_id": "40267446", + "name": "商品图片", + "font_class": "shangpintupian", + "unicode": "e710", + "unicode_decimal": 59152 + }, + { + "icon_id": "639372", + "name": "图片", + "font_class": "tupian1", + "unicode": "e711", + "unicode_decimal": 59153 + }, + { + "icon_id": "8361802", + "name": "金币", + "font_class": "jinbi", + "unicode": "e712", + "unicode_decimal": 59154 + }, + { + "icon_id": "8361785", + "name": "皇冠2", + "font_class": "huangguan", + "unicode": "e713", + "unicode_decimal": 59155 + }, + { + "icon_id": "40262026", + "name": "Group 13", + "font_class": "a-Group13", + "unicode": "e712", + "unicode_decimal": 59154 + }, + { + "icon_id": "5389828", + "name": "营销", + "font_class": "yingxiao", + "unicode": "e706", + "unicode_decimal": 59142 + }, + { + "icon_id": "720977", + "name": "坐标", + "font_class": "xiazai19", + "unicode": "e682", + "unicode_decimal": 59010 + }, + { + "icon_id": "1718351", + "name": "上箭头", + "font_class": "shangjiantou", + "unicode": "e678", + "unicode_decimal": 59000 + }, + { + "icon_id": "1718353", + "name": "下箭头", + "font_class": "xiajiantou", + "unicode": "e681", + "unicode_decimal": 59009 + }, + { + "icon_id": "5287753", + "name": "日期", + "font_class": "riqi", + "unicode": "e657", + "unicode_decimal": 58967 + }, + { + "icon_id": "26779921", + "name": "退款", + "font_class": "tuikuan", + "unicode": "e75e", + "unicode_decimal": 59230 + }, + { + "icon_id": "37128747", + "name": "购物", + "font_class": "gouwu", + "unicode": "e65a", + "unicode_decimal": 58970 + }, + { + "icon_id": "37128750", + "name": "待收货", + "font_class": "daishouhuo", + "unicode": "e65c", + "unicode_decimal": 58972 + }, + { + "icon_id": "37128751", + "name": "待发货", + "font_class": "daifahuo", + "unicode": "e669", + "unicode_decimal": 58985 + }, + { + "icon_id": "37128753", + "name": "挂号", + "font_class": "guahao", + "unicode": "e66a", + "unicode_decimal": 58986 + }, + { + "icon_id": "37128754", + "name": "待付款", + "font_class": "daifukuan", + "unicode": "e672", + "unicode_decimal": 58994 + }, + { + "icon_id": "200868", + "name": "3.1已关注店铺", + "font_class": "31yiguanzhudianpu", + "unicode": "e656", + "unicode_decimal": 58966 + }, + { + "icon_id": "201583", + "name": "3.1回到顶部", + "font_class": "31huidaodingbu", + "unicode": "e658", + "unicode_decimal": 58968 + }, + { + "icon_id": "201638", + "name": "编辑", + "font_class": "bianji", + "unicode": "e659", + "unicode_decimal": 58969 + }, + { + "icon_id": "104881", + "name": "退货保障", + "font_class": "tuihuobaozhang", + "unicode": "e653", + "unicode_decimal": 58963 + }, + { + "icon_id": "160001", + "name": "home", + "font_class": "home", + "unicode": "e633", + "unicode_decimal": 58931 + }, + { + "icon_id": "201095", + "name": "3.1待发货", + "font_class": "31daifahuo", + "unicode": "e634", + "unicode_decimal": 58932 + }, + { + "icon_id": "201096", + "name": "3.1待付款", + "font_class": "31daifukuan", + "unicode": "e637", + "unicode_decimal": 58935 + }, + { + "icon_id": "201102", + "name": "3.1退款退货", + "font_class": "tuikuantuihuo", + "unicode": "e639", + "unicode_decimal": 58937 + }, + { + "icon_id": "201577", + "name": "3.1电话", + "font_class": "31dianhua", + "unicode": "e63c", + "unicode_decimal": 58940 + }, + { + "icon_id": "201648", + "name": "3.1 时间", + "font_class": "31shijian", + "unicode": "e63d", + "unicode_decimal": 58941 + }, + { + "icon_id": "5930930", + "name": "税企交流", + "font_class": "shuiqijiaoliu", + "unicode": "e67d", + "unicode_decimal": 59005 + }, + { + "icon_id": "7551465", + "name": "官网", + "font_class": "guanwang", + "unicode": "e704", + "unicode_decimal": 59140 + }, + { + "icon_id": "8036109", + "name": "transfer-out", + "font_class": "transferout", + "unicode": "e651", + "unicode_decimal": 58961 + }, + { + "icon_id": "39893069", + "name": "transfer-out-copy", + "font_class": "transferout-copy", + "unicode": "ecad", + "unicode_decimal": 60589 + }, { "icon_id": "4354254", "name": "形状-文字", diff --git a/admin/src/utils/common.ts b/admin/src/utils/common.ts index a4202e28a..801aa163b 100644 --- a/admin/src/utils/common.ts +++ b/admin/src/utils/common.ts @@ -129,6 +129,15 @@ export function img(path: string): string { return isUrl(path) ? path : `${import.meta.env.VITE_IMG_DOMAIN || location.origin}/${path}` } +/** + * 输出asset img + * @param path + * @returns + */ +export function assetImg (path: string) { + return new URL('@/', import.meta.url) + path +} + /** * 获取字符串字节长度 * @param str @@ -291,4 +300,4 @@ export function filterNumber(event:any){ export function filterSpecial(event:any){ event.target.value = event.target.value.replace(/[^\u4e00-\u9fa5a-zA-Z0-9]/g, '') event.target.value = event.target.value.replace(/[`~!@#$%^&*()_\-+=<>?:"{}|,.\/;'\\[\]·~!@#¥%……&*()——\-+={}|《》?:“”【】、;‘’,。、]/g,'') -} \ No newline at end of file +} diff --git a/admin/src/utils/qqmap.ts b/admin/src/utils/qqmap.ts index 9daa73d86..225719e40 100644 --- a/admin/src/utils/qqmap.ts +++ b/admin/src/utils/qqmap.ts @@ -171,12 +171,12 @@ export const createMarker = (map: any) => { * @param params */ export const latLngToAddress = (params: any) => { - return jsonp(`https://apis.map.qq.com/ws/geocoder/v1/?key=${params.mapKey}&location=${params.lat},${params.lng}&output=jsonp&callback=callback`) + return jsonp(`https://apis.map.qq.com/ws/geocoder/v1/?key=${params.mapKey}&location=${params.lat},${params.lng}&output=jsonp&callback=latLngToAddress`, { callbackName: 'latLngToAddress' }) } /** * 地址解析 */ export const addressToLatLng = (params: any) => { - return jsonp(`https://apis.map.qq.com/ws/geocoder/v1/?key=${params.mapKey}&address=${params.address}&output=jsonp&callback=callback`) + return jsonp(`https://apis.map.qq.com/ws/geocoder/v1/?key=${params.mapKey}&address=${params.address}&output=jsonp&callback=addressToLatLng`, { callbackName: 'addressToLatLng' }) } diff --git a/admin/src/utils/request.ts b/admin/src/utils/request.ts index 98686a0b8..133d58b83 100644 --- a/admin/src/utils/request.ts +++ b/admin/src/utils/request.ts @@ -27,7 +27,7 @@ class Request { constructor() { this.instance = axios.create({ baseURL: import.meta.env.VITE_APP_BASE_URL.substr(-1) == '/' ? import.meta.env.VITE_APP_BASE_URL : `${import.meta.env.VITE_APP_BASE_URL}/`, - timeout: 30000, + timeout: 0, headers: { 'Content-Type': 'application/json', 'lang': storage.get('lang') ?? 'zh-cn' diff --git a/admin/src/utils/test.ts b/admin/src/utils/test.ts index d32c9b780..23e244720 100644 --- a/admin/src/utils/test.ts +++ b/admin/src/utils/test.ts @@ -81,6 +81,14 @@ const test = { // 金额,只允许保留两位小数 return /^[1-9]\d*(,\d{3})*(\.\d{1,2})?$|^0\.\d{1,2}$/.test(value) }, + /** + * 验证小数 + */ + decimal(value: string, digit: number) { + const regexPattern = `^\\d+(?:\\.\\d{1,${digit}})?$` + // 金额,只允许保留两位小数 + return new RegExp(regexPattern).test(value) + }, /** * 中文 */ @@ -209,7 +217,7 @@ const test = { */ image(value: string) { const newValue = value.split('?')[0] - const IMAGE_REGEXP = /\.(jpeg|jpg|gif|png|svg|webp|jfif|bmp|dpg)/i + const IMAGE_REGEXP = /\.(jpeg|jpg|gif|png|svg|jfif|bmp|dpg)/i // todo 暂不支持webp格式 return IMAGE_REGEXP.test(newValue) }, /** @@ -236,4 +244,4 @@ const test = { } } -export default test \ No newline at end of file +export default test