Commit 605f9060 by 芋道源码 Committed by Gitee

!344 商城装修

Merge pull request !344 from 疯狂的世界/dev
parents ce79dd68 59ff6b13
# 本地开发环境
NODE_ENV=development
VITE_DEV=true
# 请求路径
VITE_BASE_URL='http://127.0.0.1:48080'
# 上传路径
VITE_UPLOAD_URL='http://127.0.0.1:48080/admin-api/infra/file/upload'
# 接口前缀
VITE_API_BASEPATH=/dev-api
# 接口地址
VITE_API_URL=/admin-api
# 打包路径
VITE_BASE_PATH=/
# 开发环境 # 开发环境:本地只启动前端项目,依赖开发环境(后端、APP)
NODE_ENV=development NODE_ENV=development
VITE_DEV=false VITE_DEV=true
# 请求路径 # 请求路径
VITE_BASE_URL='http://localhost:48080' VITE_BASE_URL='http://api-dashboard.yudao.iocoder.cn'
# 上传路径 # 上传路径
VITE_UPLOAD_URL='http://localhost:48080/admin-api/infra/file/upload' VITE_UPLOAD_URL='http://api-dashboard.yudao.iocoder.cn/admin-api/infra/file/upload'
# 接口前缀 # 接口前缀
VITE_API_BASEPATH=/dev-api VITE_API_BASEPATH=/dev-api
...@@ -15,17 +15,23 @@ VITE_API_BASEPATH=/dev-api ...@@ -15,17 +15,23 @@ VITE_API_BASEPATH=/dev-api
# 接口地址 # 接口地址
VITE_API_URL=/admin-api VITE_API_URL=/admin-api
# 打包路径
VITE_BASE_PATH=/
# 是否删除debugger # 是否删除debugger
VITE_DROP_DEBUGGER=true VITE_DROP_DEBUGGER=false
# 是否删除console.log # 是否删除console.log
VITE_DROP_CONSOLE=false VITE_DROP_CONSOLE=false
# 是否sourcemap # 是否sourcemap
VITE_SOURCEMAP=false VITE_SOURCEMAP=true
# 打包路径
VITE_BASE_PATH=/
# 输出路径 # 输出路径
VITE_OUT_DIR=dist VITE_OUT_DIR=dist
# 商城H5会员端域名
VITE_MALL_H5_DOMAIN='http://mall.yudao.iocoder.cn'
# 验证码的开关
VITE_APP_CAPTCHA_ENABLE=false
# 本地开发环境 # 本地开发环境:本地启动所有项目(前端、后端、APP)时使用,不依赖外部环境
NODE_ENV=development NODE_ENV=development
VITE_DEV=true VITE_DEV=true
# 请求路径 # 请求路径
VITE_BASE_URL='http://api-dashboard.yudao.iocoder.cn' VITE_BASE_URL='http://localhost:48080'
# 上传路径 # 上传路径
VITE_UPLOAD_URL='http://api-dashboard.yudao.iocoder.cn/admin-api/infra/file/upload' VITE_UPLOAD_URL='http://localhost:48080/admin-api/infra/file/upload'
# 接口前缀 # 接口前缀
VITE_API_BASEPATH=/dev-api VITE_API_BASEPATH=/dev-api
...@@ -15,12 +15,6 @@ VITE_API_BASEPATH=/dev-api ...@@ -15,12 +15,6 @@ VITE_API_BASEPATH=/dev-api
# 接口地址 # 接口地址
VITE_API_URL=/admin-api VITE_API_URL=/admin-api
# 打包路径
VITE_BASE_PATH=/
# 项目本地运行端口号, 与.vscode/launch.json配合
VITE_PORT=80
# 是否删除debugger # 是否删除debugger
VITE_DROP_DEBUGGER=false VITE_DROP_DEBUGGER=false
...@@ -28,7 +22,13 @@ VITE_DROP_DEBUGGER=false ...@@ -28,7 +22,13 @@ VITE_DROP_DEBUGGER=false
VITE_DROP_CONSOLE=false VITE_DROP_CONSOLE=false
# 是否sourcemap # 是否sourcemap
VITE_SOURCEMAP=true VITE_SOURCEMAP=false
# 打包路径
VITE_BASE_PATH=/
# 商城H5会员端域名
VITE_MALL_H5_DOMAIN='http://localhost:3000'
# 验证码的开关 # 验证码的开关
VITE_APP_CAPTCHA_ENABLE=false VITE_APP_CAPTCHA_ENABLE=false
# 生产环境 # 生产环境:只在打包时使用
NODE_ENV=production NODE_ENV=production
VITE_DEV=false VITE_DEV=false
...@@ -28,4 +28,7 @@ VITE_SOURCEMAP=false ...@@ -28,4 +28,7 @@ VITE_SOURCEMAP=false
VITE_BASE_PATH=/ VITE_BASE_PATH=/
# 输出路径 # 输出路径
VITE_OUT_DIR=dist-pro VITE_OUT_DIR=dist-prod
# 商城H5会员端域名
VITE_MALL_H5_DOMAIN='http://mall.yudao.iocoder.cn'
# 生产环境 # 预发布环境:只在打包时使用
NODE_ENV=production NODE_ENV=production
VITE_DEV=false VITE_DEV=false
...@@ -29,3 +29,6 @@ VITE_BASE_PATH='http://static-vue3.yudao.iocoder.cn/' ...@@ -29,3 +29,6 @@ VITE_BASE_PATH='http://static-vue3.yudao.iocoder.cn/'
# 输出路径 # 输出路径
VITE_OUT_DIR=dist-stage VITE_OUT_DIR=dist-stage
# 商城H5会员端域名
VITE_MALL_H5_DOMAIN='http://mall.yudao.iocoder.cn'
# 开发环境 # 测试环境:只在打包时使用
NODE_ENV=production NODE_ENV=production
VITE_DEV=false VITE_DEV=false
...@@ -28,4 +28,7 @@ VITE_SOURCEMAP=false ...@@ -28,4 +28,7 @@ VITE_SOURCEMAP=false
VITE_BASE_PATH=/admin-ui-vue3/ VITE_BASE_PATH=/admin-ui-vue3/
# 输出路径 # 输出路径
VITE_OUT_DIR=dist-dev VITE_OUT_DIR=dist-test
# 商城H5会员端域名
VITE_MALL_H5_DOMAIN='http://mall.yudao.iocoder.cn'
...@@ -6,18 +6,17 @@ ...@@ -6,18 +6,17 @@
"private": false, "private": false,
"scripts": { "scripts": {
"i": "pnpm install", "i": "pnpm install",
"dev": "vite --mode base", "local-dev": "vite --mode local-dev",
"front": "vite --mode front", "dev": "vite --mode dev",
"ts:check": "vue-tsc --noEmit", "ts:check": "vue-tsc --noEmit",
"build:pro": "node --max_old_space_size=8192 ./node_modules/vite/bin/vite.js build --mode pro", "build:local-dev": "node --max_old_space_size=8192 ./node_modules/vite/bin/vite.js build --mode local-dev",
"build:dev": "node --max_old_space_size=8192 ./node_modules/vite/bin/vite.js build --mode dev", "build:dev": "node --max_old_space_size=8192 ./node_modules/vite/bin/vite.js build --mode dev",
"build:base": "node --max_old_space_size=8192 ./node_modules/vite/bin/vite.js build --mode base", "build:test": "node --max_old_space_size=8192 ./node_modules/vite/bin/vite.js build --mode test",
"build:stage": "node --max_old_space_size=8192 ./node_modules/vite/bin/vite.js build --mode stage", "build:stage": "node --max_old_space_size=8192 ./node_modules/vite/bin/vite.js build --mode stage",
"build:static": "node --max_old_space_size=8192 ./node_modules/vite/bin/vite.js build --mode static", "build:prod": "node --max_old_space_size=8192 ./node_modules/vite/bin/vite.js build --mode prod",
"build:front": "node --max_old_space_size=8192 ./node_modules/vite/bin/vite.js build --mode front",
"serve:pro": "vite preview --mode pro",
"serve:dev": "vite preview --mode dev", "serve:dev": "vite preview --mode dev",
"preview": "pnpm build:base && vite preview", "serve:prod": "vite preview --mode prod",
"preview": "pnpm build:local-dev && vite preview",
"clean": "npx rimraf node_modules", "clean": "npx rimraf node_modules",
"clean:cache": "npx rimraf node_modules/.cache", "clean:cache": "npx rimraf node_modules/.cache",
"lint:eslint": "eslint --fix --ext .js,.ts,.vue ./src", "lint:eslint": "eslint --fix --ext .js,.ts,.vue ./src",
......
...@@ -82,7 +82,9 @@ watch( ...@@ -82,7 +82,9 @@ watch(
// 克隆组件 // 克隆组件
const handleCloneComponent = (component: DiyComponent<any>) => { const handleCloneComponent = (component: DiyComponent<any>) => {
return cloneDeep(component) const instance = cloneDeep(component)
instance.uid = new Date().getTime()
return instance
} }
</script> </script>
......
...@@ -39,30 +39,9 @@ ...@@ -39,30 +39,9 @@
</el-form-item> </el-form-item>
</el-card> </el-card>
<el-card header="内容设置" class="property-group" shadow="never"> <el-card header="内容设置" class="property-group" shadow="never">
<el-text type="info" size="small"> 拖动左上角的小圆点可对其排序 </el-text> <Draggable v-model="formData.items" :empty-item="{ type: 'img' }">
<template v-if="formData.items[0]"> <template #default="{ element }">
<draggable <el-form-item label="类型" prop="type" class="m-b-8px!" label-width="40px">
:list="formData.items"
:force-fallback="true"
:animation="200"
handle=".drag-icon"
class="m-t-8px"
item-key="index"
>
<template #item="{ element, index }">
<div class="content mb-4px flex flex-col gap-4px rounded bg-gray-50 p-8px">
<div
class="m--8px m-b-8px flex flex-row items-center justify-between bg-gray-100 p-8px"
>
<Icon icon="ic:round-drag-indicator" class="drag-icon cursor-move" />
<Icon
icon="ep:delete"
class="cursor-pointer text-red-5"
@click="handleDeleteImage(index)"
v-if="formData.items.length > 1"
/>
</div>
<el-form-item label="类型" prop="type" class="m-b-8px!" label-width="50px">
<el-radio-group v-model="element.type"> <el-radio-group v-model="element.type">
<el-radio label="img">图片</el-radio> <el-radio label="img">图片</el-radio>
<el-radio label="video">视频</el-radio> <el-radio label="video">视频</el-radio>
...@@ -71,7 +50,7 @@ ...@@ -71,7 +50,7 @@
<el-form-item <el-form-item
label="图片" label="图片"
class="m-b-8px!" class="m-b-8px!"
label-width="50px" label-width="40px"
v-if="element.type === 'img'" v-if="element.type === 'img'"
> >
<UploadImg <UploadImg
...@@ -83,7 +62,7 @@ ...@@ -83,7 +62,7 @@
/> />
</el-form-item> </el-form-item>
<template v-else> <template v-else>
<el-form-item label="封面" class="m-b-8px!" label-width="50px"> <el-form-item label="封面" class="m-b-8px!" label-width="40px">
<UploadImg <UploadImg
v-model="element.imgUrl" v-model="element.imgUrl"
draggable="false" draggable="false"
...@@ -92,7 +71,7 @@ ...@@ -92,7 +71,7 @@
class="min-w-80px" class="min-w-80px"
/> />
</el-form-item> </el-form-item>
<el-form-item label="视频" class="m-b-8px!" label-width="50px"> <el-form-item label="视频" class="m-b-8px!" label-width="40px">
<UploadFile <UploadFile
v-model="element.videoUrl" v-model="element.videoUrl"
:file-type="['mp4']" :file-type="['mp4']"
...@@ -102,24 +81,18 @@ ...@@ -102,24 +81,18 @@
/> />
</el-form-item> </el-form-item>
</template> </template>
<el-form-item label="链接" class="m-b-8px!" label-width="50px"> <el-form-item label="链接" class="m-b-8px!" label-width="40px">
<AppLinkInput v-model="element.url" /> <AppLinkInput v-model="element.url" />
</el-form-item> </el-form-item>
</div>
</template> </template>
</draggable> </Draggable>
</template>
<el-button @click="handleAddImage" type="primary" plain class="w-full">
添加图片
</el-button>
</el-card> </el-card>
</el-form> </el-form>
</ComponentContainerProperty> </ComponentContainerProperty>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import draggable from 'vuedraggable' //拖拽组件 import { CarouselProperty } from './config'
import { CarouselItemProperty, CarouselProperty } from './config'
import { usePropertyForm } from '@/components/DiyEditor/util' import { usePropertyForm } from '@/components/DiyEditor/util'
// 轮播图属性面板 // 轮播图属性面板
...@@ -128,15 +101,6 @@ defineOptions({ name: 'CarouselProperty' }) ...@@ -128,15 +101,6 @@ defineOptions({ name: 'CarouselProperty' })
const props = defineProps<{ modelValue: CarouselProperty }>() const props = defineProps<{ modelValue: CarouselProperty }>()
const emit = defineEmits(['update:modelValue']) const emit = defineEmits(['update:modelValue'])
const { formData } = usePropertyForm(props.modelValue, emit) const { formData } = usePropertyForm(props.modelValue, emit)
// 添加图片
const handleAddImage = () => {
formData.value.items.push({} as CarouselItemProperty)
}
// 删除图片
const handleDeleteImage = (index: number) => {
formData.value.items.splice(index, 1)
}
</script> </script>
<style scoped lang="scss"></style> <style scoped lang="scss"></style>
...@@ -9,23 +9,9 @@ ...@@ -9,23 +9,9 @@
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<el-text tag="p"> 菜单设置 </el-text> <el-card header="菜单设置" class="property-group" shadow="never">
<el-text type="info" size="small"> 拖动左侧的小圆点可以调整顺序 </el-text> <Draggable v-model="formData.list" :empty-item="EMPTY_MENU_GRID_ITEM_PROPERTY">
<template v-if="formData.list.length"> <template #default="{ element }">
<VueDraggable
class="m-t-8px"
:list="formData.list"
item-key="index"
handle=".drag-icon"
:forceFallback="true"
:animation="200"
>
<template #item="{ element, index }">
<div class="mb-4px flex flex-col gap-4px rounded bg-gray-100 p-8px">
<div class="flex flex-row justify-between">
<Icon icon="ic:round-drag-indicator" class="drag-icon cursor-move" />
<Icon icon="ep:delete" class="text-red-500" @click="handleDeleteMenu(index)" />
</div>
<el-form-item label="图标" prop="iconUrl"> <el-form-item label="图标" prop="iconUrl">
<UploadImg v-model="element.iconUrl" height="80px" width="80px"> <UploadImg v-model="element.iconUrl" height="80px" width="80px">
<template #tip> 建议尺寸:44 * 44 </template> <template #tip> 建议尺寸:44 * 44 </template>
...@@ -54,27 +40,19 @@ ...@@ -54,27 +40,19 @@
<ColorInput v-model="element.badge.bgColor" /> <ColorInput v-model="element.badge.bgColor" />
</el-form-item> </el-form-item>
</template> </template>
</div>
</template> </template>
</VueDraggable> </Draggable>
</template> </el-card>
<el-form-item label-width="0">
<el-button @click="handleAddMenu" type="primary" plain class="m-t-8px w-full">
<Icon icon="ep:plus" class="mr-5px" /> 添加菜单
</el-button>
</el-form-item>
</el-form> </el-form>
</ComponentContainerProperty> </ComponentContainerProperty>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import VueDraggable from 'vuedraggable'
import { usePropertyForm } from '@/components/DiyEditor/util' import { usePropertyForm } from '@/components/DiyEditor/util'
import { import {
EMPTY_MENU_GRID_ITEM_PROPERTY, EMPTY_MENU_GRID_ITEM_PROPERTY,
MenuGridProperty MenuGridProperty
} from '@/components/DiyEditor/components/mobile/MenuGrid/config' } from '@/components/DiyEditor/components/mobile/MenuGrid/config'
import { cloneDeep } from 'lodash-es'
/** 宫格导航属性面板 */ /** 宫格导航属性面板 */
defineOptions({ name: 'MenuGridProperty' }) defineOptions({ name: 'MenuGridProperty' })
...@@ -82,15 +60,6 @@ defineOptions({ name: 'MenuGridProperty' }) ...@@ -82,15 +60,6 @@ defineOptions({ name: 'MenuGridProperty' })
const props = defineProps<{ modelValue: MenuGridProperty }>() const props = defineProps<{ modelValue: MenuGridProperty }>()
const emit = defineEmits(['update:modelValue']) const emit = defineEmits(['update:modelValue'])
const { formData } = usePropertyForm(props.modelValue, emit) const { formData } = usePropertyForm(props.modelValue, emit)
/* 添加菜单 */
const handleAddMenu = () => {
formData.value.list.push(cloneDeep(EMPTY_MENU_GRID_ITEM_PROPERTY))
}
/* 删除菜单 */
const handleDeleteMenu = (index: number) => {
formData.value.list.splice(index, 1)
}
</script> </script>
<style scoped lang="scss"></style> <style scoped lang="scss"></style>
...@@ -5,20 +5,8 @@ ...@@ -5,20 +5,8 @@
<!-- 表单 --> <!-- 表单 -->
<el-form label-width="60px" :model="formData" class="m-t-8px"> <el-form label-width="60px" :model="formData" class="m-t-8px">
<div v-if="formData.list.length"> <Draggable v-model="formData.list" :empty-item="EMPTY_MENU_LIST_ITEM_PROPERTY">
<VueDraggable <template #default="{ element }">
:list="formData.list"
item-key="index"
handle=".drag-icon"
:forceFallback="true"
:animation="200"
>
<template #item="{ element, index }">
<div class="mb-4px flex flex-col gap-4px rounded bg-gray-100 p-8px">
<div class="flex flex-row justify-between">
<Icon icon="ic:round-drag-indicator" class="drag-icon cursor-move" />
<Icon icon="ep:delete" class="text-red-500" @click="handleDeleteMenu(index)" />
</div>
<el-form-item label="图标" prop="iconUrl"> <el-form-item label="图标" prop="iconUrl">
<UploadImg v-model="element.iconUrl" height="80px" width="80px"> <UploadImg v-model="element.iconUrl" height="80px" width="80px">
<template #tip> 建议尺寸:44 * 44 </template> <template #tip> 建议尺寸:44 * 44 </template>
...@@ -33,27 +21,18 @@ ...@@ -33,27 +21,18 @@
<el-form-item label="链接" prop="url"> <el-form-item label="链接" prop="url">
<AppLinkInput v-model="element.url" /> <AppLinkInput v-model="element.url" />
</el-form-item> </el-form-item>
</div>
</template> </template>
</VueDraggable> </Draggable>
</div>
<el-form-item label-width="0">
<el-button @click="handleAddMenu" type="primary" plain class="m-t-8px w-full">
<Icon icon="ep:plus" class="mr-5px" /> 添加菜单
</el-button>
</el-form-item>
</el-form> </el-form>
</ComponentContainerProperty> </ComponentContainerProperty>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import VueDraggable from 'vuedraggable'
import { usePropertyForm } from '@/components/DiyEditor/util' import { usePropertyForm } from '@/components/DiyEditor/util'
import { import {
EMPTY_MENU_LIST_ITEM_PROPERTY, EMPTY_MENU_LIST_ITEM_PROPERTY,
MenuListProperty MenuListProperty
} from '@/components/DiyEditor/components/mobile/MenuList/config' } from '@/components/DiyEditor/components/mobile/MenuList/config'
import { cloneDeep } from 'lodash-es'
/** 列表导航属性面板 */ /** 列表导航属性面板 */
defineOptions({ name: 'MenuListProperty' }) defineOptions({ name: 'MenuListProperty' })
...@@ -61,15 +40,6 @@ defineOptions({ name: 'MenuListProperty' }) ...@@ -61,15 +40,6 @@ defineOptions({ name: 'MenuListProperty' })
const props = defineProps<{ modelValue: MenuListProperty }>() const props = defineProps<{ modelValue: MenuListProperty }>()
const emit = defineEmits(['update:modelValue']) const emit = defineEmits(['update:modelValue'])
const { formData } = usePropertyForm(props.modelValue, emit) const { formData } = usePropertyForm(props.modelValue, emit)
/* 添加菜单 */
const handleAddMenu = () => {
formData.value.list.push(cloneDeep(EMPTY_MENU_LIST_ITEM_PROPERTY))
}
/* 删除菜单 */
const handleDeleteMenu = (index: number) => {
formData.value.list.splice(index, 1)
}
</script> </script>
<style scoped lang="scss"></style> <style scoped lang="scss"></style>
...@@ -22,23 +22,9 @@ ...@@ -22,23 +22,9 @@
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<el-text tag="p"> 菜单设置 </el-text> <el-card header="菜单设置" class="property-group" shadow="never">
<el-text type="info" size="small"> 拖动左侧的小圆点可以调整顺序 </el-text> <Draggable v-model="formData.list" :empty-item="cloneDeep(EMPTY_MENU_SWIPER_ITEM_PROPERTY">
<template v-if="formData.list.length"> <template #default="{ element }">
<VueDraggable
class="m-t-8px"
:list="formData.list"
item-key="index"
handle=".drag-icon"
:forceFallback="true"
:animation="200"
>
<template #item="{ element, index }">
<div class="mb-4px flex flex-col gap-4px rounded bg-gray-100 p-8px">
<div class="flex flex-row justify-between">
<Icon icon="ic:round-drag-indicator" class="drag-icon cursor-move" />
<Icon icon="ep:delete" class="text-red-500" @click="handleDeleteMenu(index)" />
</div>
<el-form-item label="图标" prop="iconUrl"> <el-form-item label="图标" prop="iconUrl">
<UploadImg v-model="element.iconUrl" height="80px" width="80px"> <UploadImg v-model="element.iconUrl" height="80px" width="80px">
<template #tip> 建议尺寸:98 * 98 </template> <template #tip> 建议尺寸:98 * 98 </template>
...@@ -64,21 +50,14 @@ ...@@ -64,21 +50,14 @@
<ColorInput v-model="element.badge.bgColor" /> <ColorInput v-model="element.badge.bgColor" />
</el-form-item> </el-form-item>
</template> </template>
</div>
</template> </template>
</VueDraggable> </Draggable>
</template> </el-card>
<el-form-item label-width="0">
<el-button @click="handleAddMenu" type="primary" plain class="m-t-8px w-full">
<Icon icon="ep:plus" class="mr-5px" /> 添加菜单
</el-button>
</el-form-item>
</el-form> </el-form>
</ComponentContainerProperty> </ComponentContainerProperty>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import VueDraggable from 'vuedraggable'
import { usePropertyForm } from '@/components/DiyEditor/util' import { usePropertyForm } from '@/components/DiyEditor/util'
import { import {
EMPTY_MENU_SWIPER_ITEM_PROPERTY, EMPTY_MENU_SWIPER_ITEM_PROPERTY,
...@@ -92,15 +71,6 @@ defineOptions({ name: 'MenuSwiperProperty' }) ...@@ -92,15 +71,6 @@ defineOptions({ name: 'MenuSwiperProperty' })
const props = defineProps<{ modelValue: MenuSwiperProperty }>() const props = defineProps<{ modelValue: MenuSwiperProperty }>()
const emit = defineEmits(['update:modelValue']) const emit = defineEmits(['update:modelValue'])
const { formData } = usePropertyForm(props.modelValue, emit) const { formData } = usePropertyForm(props.modelValue, emit)
/* 添加菜单 */
const handleAddMenu = () => {
formData.value.list.push(cloneDeep(EMPTY_MENU_SWIPER_ITEM_PROPERTY))
}
/* 删除菜单 */
const handleDeleteMenu = (index: number) => {
formData.value.list.splice(index, 1)
}
</script> </script>
<style scoped lang="scss"></style> <style scoped lang="scss"></style>
import { DiyComponent } from '@/components/DiyEditor/util' import { ComponentStyle, DiyComponent } from '@/components/DiyEditor/util'
/** 公告栏属性 */ /** 公告栏属性 */
export interface NoticeBarProperty { export interface NoticeBarProperty {
...@@ -10,6 +10,8 @@ export interface NoticeBarProperty { ...@@ -10,6 +10,8 @@ export interface NoticeBarProperty {
backgroundColor: string backgroundColor: string
// 文字颜色 // 文字颜色
textColor: string textColor: string
// 组件样式
style: ComponentStyle
} }
/** 内容属性 */ /** 内容属性 */
...@@ -34,6 +36,11 @@ export const component = { ...@@ -34,6 +36,11 @@ export const component = {
} }
], ],
backgroundColor: '#fff', backgroundColor: '#fff',
textColor: '#333' textColor: '#333',
style: {
bgType: 'color',
bgColor: '#fff',
marginBottom: 8
} as ComponentStyle
} }
} as DiyComponent<NoticeBarProperty> } as DiyComponent<NoticeBarProperty>
<template> <template>
<ComponentContainerProperty v-model="formData.style">
<el-form label-width="80px" :model="formData" :rules="rules"> <el-form label-width="80px" :model="formData" :rules="rules">
<el-form-item label="公告图标" prop="iconUrl"> <el-form-item label="公告图标" prop="iconUrl">
<UploadImg v-model="formData.iconUrl" height="48px"> <UploadImg v-model="formData.iconUrl" height="48px">
...@@ -11,48 +12,26 @@ ...@@ -11,48 +12,26 @@
<el-form-item label="文字颜色" prop="文字颜色"> <el-form-item label="文字颜色" prop="文字颜色">
<ColorInput v-model="formData.textColor" /> <ColorInput v-model="formData.textColor" />
</el-form-item> </el-form-item>
<el-text tag="p"> 公告内容 </el-text>
<el-text type="info" size="small"> 拖动左上角的小圆点可以调整热词顺序 </el-text> <el-card header="公告内容" class="property-group" shadow="never">
<template v-if="formData.contents.length"> <Draggable v-model="formData.contents">
<VueDraggable <template #default="{ element }">
:list="formData.contents" <el-form-item label="公告" prop="text" label-width="40px">
item-key="index"
handle=".drag-icon"
:forceFallback="true"
:animation="200"
class="m-t-8px"
>
<template #item="{ element, index }">
<div class="mb-4px flex flex-row gap-4px rounded bg-gray-100 p-8px">
<div class="flex flex-col items-start justify-between">
<Icon icon="ic:round-drag-indicator" class="drag-icon cursor-move" />
<Icon
icon="ep:delete"
class="cursor-pointer text-red-5"
@click="handleDeleteContent(index)"
v-if="formData.contents.length > 1"
/>
</div>
<div class="w-full flex flex-col gap-8px">
<el-input v-model="element.text" placeholder="请输入公告" /> <el-input v-model="element.text" placeholder="请输入公告" />
</el-form-item>
<el-form-item label="链接" prop="url" label-width="40px">
<AppLinkInput v-model="element.url" /> <AppLinkInput v-model="element.url" />
</div>
</div>
</template>
</VueDraggable>
</template>
<el-form-item label-width="0">
<el-button @click="handleAddContent" type="primary" plain class="m-t-8px w-full">
添加内容
</el-button>
</el-form-item> </el-form-item>
</template>
</Draggable>
</el-card>
</el-form> </el-form>
</ComponentContainerProperty>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { NoticeBarProperty, NoticeContentProperty } from './config' import { NoticeBarProperty } from './config'
import { usePropertyForm } from '@/components/DiyEditor/util' import { usePropertyForm } from '@/components/DiyEditor/util'
import VueDraggable from 'vuedraggable'
// 通知栏属性面板 // 通知栏属性面板
defineOptions({ name: 'NoticeBarProperty' }) defineOptions({ name: 'NoticeBarProperty' })
// 表单校验 // 表单校验
...@@ -63,15 +42,6 @@ const rules = { ...@@ -63,15 +42,6 @@ const rules = {
const props = defineProps<{ modelValue: NoticeBarProperty }>() const props = defineProps<{ modelValue: NoticeBarProperty }>()
const emit = defineEmits(['update:modelValue']) const emit = defineEmits(['update:modelValue'])
const { formData } = usePropertyForm(props.modelValue, emit) const { formData } = usePropertyForm(props.modelValue, emit)
/* 添加公告 */
const handleAddContent = () => {
formData.value.contents.push({} as NoticeContentProperty)
}
/* 删除公告 */
const handleDeleteContent = (index: number) => {
formData.value.contents.splice(index, 1)
}
</script> </script>
<style scoped lang="scss"></style> <style scoped lang="scss"></style>
<template> <template>
<ComponentContainerProperty v-model="formData.style"> <ComponentContainerProperty v-model="formData.style">
<el-text tag="p"> 搜索热词 </el-text>
<el-text type="info" size="small"> 拖动左侧的小圆点可以调整热词顺序 </el-text>
<!-- 表单 --> <!-- 表单 -->
<el-form label-width="80px" :model="formData" class="m-t-8px"> <el-form label-width="80px" :model="formData" class="m-t-8px">
<div v-if="formData.hotKeywords.length"> <el-card header="搜索热词" class="property-group" shadow="never">
<VueDraggable <Draggable v-model="formData.hotKeywords" :empty-item="''">
:list="formData.hotKeywords" <template #default="{ index }">
item-key="index"
handle=".drag-icon"
:forceFallback="true"
:animation="200"
>
<template #item="{ index }">
<div class="mb-4px flex flex-row items-center gap-4px rounded bg-gray-100 p-8px">
<Icon icon="ic:round-drag-indicator" class="drag-icon cursor-move" />
<el-input v-model="formData.hotKeywords[index]" placeholder="请输入热词" /> <el-input v-model="formData.hotKeywords[index]" placeholder="请输入热词" />
<Icon icon="ep:delete" class="text-red-500" @click="deleteHotWord(index)" />
</div>
</template> </template>
</VueDraggable> </Draggable>
</div> </el-card>
<el-form-item label-width="0"> <el-card header="搜索样式" class="property-group" shadow="never">
<el-button @click="handleAddHotWord" type="primary" plain class="m-t-8px w-full">
添加热词
</el-button>
</el-form-item>
<el-form-item label="框体样式"> <el-form-item label="框体样式">
<el-radio-group v-model="formData!.borderRadius"> <el-radio-group v-model="formData!.borderRadius">
<el-tooltip content="方形" placement="top"> <el-tooltip content="方形" placement="top">
...@@ -70,12 +53,12 @@ ...@@ -70,12 +53,12 @@
<el-form-item class="lef" label="文本颜色" prop="textColor"> <el-form-item class="lef" label="文本颜色" prop="textColor">
<ColorInput v-model="formData.textColor" /> <ColorInput v-model="formData.textColor" />
</el-form-item> </el-form-item>
</el-card>
</el-form> </el-form>
</ComponentContainerProperty> </ComponentContainerProperty>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import VueDraggable from 'vuedraggable'
import { usePropertyForm } from '@/components/DiyEditor/util' import { usePropertyForm } from '@/components/DiyEditor/util'
import { SearchProperty } from '@/components/DiyEditor/components/mobile/SearchBar/config' import { SearchProperty } from '@/components/DiyEditor/components/mobile/SearchBar/config'
...@@ -85,15 +68,6 @@ defineOptions({ name: 'SearchProperty' }) ...@@ -85,15 +68,6 @@ defineOptions({ name: 'SearchProperty' })
const props = defineProps<{ modelValue: SearchProperty }>() const props = defineProps<{ modelValue: SearchProperty }>()
const emit = defineEmits(['update:modelValue']) const emit = defineEmits(['update:modelValue'])
const { formData } = usePropertyForm(props.modelValue, emit) const { formData } = usePropertyForm(props.modelValue, emit)
/* 添加热词 */
const handleAddHotWord = () => {
formData.value.hotKeywords.push('')
}
/* 删除热词 */
const deleteHotWord = (index: number) => {
formData.value.hotKeywords.splice(index, 1)
}
</script> </script>
<style scoped lang="scss"></style> <style scoped lang="scss"></style>
...@@ -12,7 +12,13 @@ ...@@ -12,7 +12,13 @@
}" }"
> >
<div v-for="(item, index) in property.items" :key="index" class="tab-bar-item"> <div v-for="(item, index) in property.items" :key="index" class="tab-bar-item">
<img :src="index === 0 ? item.activeIconUrl : item.iconUrl" alt="" /> <el-image :src="index === 0 ? item.activeIconUrl : item.iconUrl">
<template #error>
<div class="h-full w-full flex items-center justify-center">
<Icon icon="ep:picture" />
</div>
</template>
</el-image>
<span :style="{ color: index === 0 ? property.style.activeColor : property.style.color }"> <span :style="{ color: index === 0 ? property.style.activeColor : property.style.color }">
{{ item.text }} {{ item.text }}
</span> </span>
...@@ -48,7 +54,8 @@ defineProps<{ property: TabBarProperty }>() ...@@ -48,7 +54,8 @@ defineProps<{ property: TabBarProperty }>()
align-items: center; align-items: center;
justify-content: center; justify-content: center;
img { :deep(img),
.el-icon {
width: 26px; width: 26px;
height: 26px; height: 26px;
border-radius: 4px; border-radius: 4px;
......
...@@ -42,26 +42,8 @@ ...@@ -42,26 +42,8 @@
<el-text tag="p">图标设置</el-text> <el-text tag="p">图标设置</el-text>
<el-text type="info" size="small"> 拖动左上角的小圆点可对其排序, 图标建议尺寸 44*44 </el-text> <el-text type="info" size="small"> 拖动左上角的小圆点可对其排序, 图标建议尺寸 44*44 </el-text>
<draggable <Draggable v-model="formData.items" :limit="5">
:list="formData!.items" <template #default="{ element }">
item-key="index"
:forceFallback="true"
:animation="200"
handle=".drag-icon"
class="m-t-8px"
>
<template #item="{ element, index }">
<div class="mb-4px flex flex-row gap-4px rounded bg-gray-100 p-8px">
<div class="flex flex-col items-start justify-between">
<Icon icon="ic:round-drag-indicator" class="drag-icon cursor-move" />
<Icon
icon="ep:delete"
class="cursor-pointer text-red-5"
@click="handleDeleteItem(index)"
v-if="formData.items.length > 1"
/>
</div>
<div class="w-full flex flex-col">
<div class="m-b-8px flex items-center justify-around"> <div class="m-b-8px flex items-center justify-around">
<div class="flex flex-col items-center justify-between"> <div class="flex flex-col items-center justify-between">
<UploadImg <UploadImg
...@@ -71,7 +53,7 @@ ...@@ -71,7 +53,7 @@
:show-delete="false" :show-delete="false"
:show-btn-text="false" :show-btn-text="false"
/> />
<el-text size="small">默认图片</el-text> <el-text size="small">未选中</el-text>
</div> </div>
<div> <div>
<UploadImg <UploadImg
...@@ -81,41 +63,23 @@ ...@@ -81,41 +63,23 @@
:show-delete="false" :show-delete="false"
:show-btn-text="false" :show-btn-text="false"
/> />
<el-text>选中图片</el-text> <el-text>已选中</el-text>
</div> </div>
</div> </div>
<el-form-item prop="text" label-width="0" class="m-b-8px!"> <el-form-item prop="text" label="文字" label-width="48px" class="m-b-8px!">
<el-input v-model="element.text" placeholder="请输入文字" /> <el-input v-model="element.text" placeholder="请输入文字" />
</el-form-item> </el-form-item>
<el-form-item prop="url" label-width="0" class="m-b-0!"> <el-form-item prop="url" label="链接" label-width="48px" class="m-b-0!">
<AppLinkInput v-model="element.url" /> <AppLinkInput v-model="element.url" />
</el-form-item> </el-form-item>
</div>
</div>
</template> </template>
</draggable> </Draggable>
<el-form-item label-width="0">
<!-- 添加导航按钮 -->
<el-tooltip content="最多添加5个">
<el-button
@click="handleAddItem"
class="m-b-16px w-full"
type="primary"
plain
:disabled="formData!.items.length >= 5"
>
添加导航
</el-button>
</el-tooltip>
</el-form-item>
</el-form> </el-form>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import draggable from 'vuedraggable' //拖拽组件 import { TabBarProperty, THEME_LIST } from './config'
import { TabBarItemProperty, TabBarProperty, THEME_LIST } from './config'
import { usePropertyForm } from '@/components/DiyEditor/util' import { usePropertyForm } from '@/components/DiyEditor/util'
// 底部导航栏 // 底部导航栏
defineOptions({ name: 'TabBarProperty' }) defineOptions({ name: 'TabBarProperty' })
...@@ -124,15 +88,6 @@ const props = defineProps<{ modelValue: TabBarProperty }>() ...@@ -124,15 +88,6 @@ const props = defineProps<{ modelValue: TabBarProperty }>()
const emit = defineEmits(['update:modelValue']) const emit = defineEmits(['update:modelValue'])
const { formData } = usePropertyForm(props.modelValue, emit) const { formData } = usePropertyForm(props.modelValue, emit)
/** 添加导航项 */
const handleAddItem = () => {
formData?.value?.items?.push({} as TabBarItemProperty)
}
/** 删除导航项 */
const handleDeleteItem = (index: number) => {
formData?.value?.items?.splice(index, 1)
}
// 要的主题 // 要的主题
const handleThemeChange = () => { const handleThemeChange = () => {
const theme = THEME_LIST.find((theme) => theme.id === formData.value.theme) const theme = THEME_LIST.find((theme) => theme.id === formData.value.theme)
......
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
<Icon icon="system-uicons:reset-alt" :size="24" /> <Icon icon="system-uicons:reset-alt" :size="24" />
</el-button> </el-button>
</el-tooltip> </el-tooltip>
<el-tooltip content="预览"> <el-tooltip content="预览" v-if="previewUrl">
<el-button @click="handlePreview"> <el-button @click="handlePreview">
<Icon icon="ep:view" :size="24" /> <Icon icon="ep:view" :size="24" />
</el-button> </el-button>
...@@ -102,8 +102,8 @@ ...@@ -102,8 +102,8 @@
<!-- 组件名称 --> <!-- 组件名称 -->
<template #header> <template #header>
<div class="flex items-center gap-8px"> <div class="flex items-center gap-8px">
<Icon :icon="selectedComponent.icon" color="gray" /> <Icon :icon="selectedComponent?.icon" color="gray" />
<span>{{ selectedComponent.name }}</span> <span>{{ selectedComponent?.name }}</span>
</div> </div>
</template> </template>
<el-scrollbar <el-scrollbar
...@@ -111,7 +111,8 @@ ...@@ -111,7 +111,8 @@
view-class="p-[var(--el-card-padding)] p-b-[calc(var(--el-card-padding)+var(--el-card-padding))] property" view-class="p-[var(--el-card-padding)] p-b-[calc(var(--el-card-padding)+var(--el-card-padding))] property"
> >
<component <component
:is="selectedComponent.id + 'Property'" :key="selectedComponent?.uid || selectedComponent?.id"
:is="selectedComponent?.id + 'Property'"
v-model="selectedComponent.property" v-model="selectedComponent.property"
/> />
</el-scrollbar> </el-scrollbar>
...@@ -119,6 +120,19 @@ ...@@ -119,6 +120,19 @@
</el-aside> </el-aside>
</el-container> </el-container>
</el-container> </el-container>
<!-- 预览弹框 -->
<Dialog v-model="previewDialogVisible" title="预览" width="700">
<div class="flex justify-around">
<IFrame
class="w-375px border-4px border-rounded-8px border-solid p-2px h-667px!"
:src="previewUrl"
/>
<div class="flex flex-col">
<el-text>手机扫码预览</el-text>
<Qrcode :text="previewUrl" logo="/logo.gif" />
</div>
</div>
</Dialog>
</template> </template>
<script lang="ts"> <script lang="ts">
// 注册所有的组件 // 注册所有的组件
...@@ -137,12 +151,12 @@ import { component as TAB_BAR_COMPONENT } from './components/mobile/TabBar/confi ...@@ -137,12 +151,12 @@ import { component as TAB_BAR_COMPONENT } from './components/mobile/TabBar/confi
import { isString } from '@/utils/is' import { isString } from '@/utils/is'
import { DiyComponent, DiyComponentLibrary, PageConfig } from '@/components/DiyEditor/util' import { DiyComponent, DiyComponentLibrary, PageConfig } from '@/components/DiyEditor/util'
import { componentConfigs } from '@/components/DiyEditor/components/mobile' import { componentConfigs } from '@/components/DiyEditor/components/mobile'
import { array, oneOfType } from 'vue-types'
import { propTypes } from '@/utils/propTypes'
/** 页面装修详情页 */ /** 页面装修详情页 */
defineOptions({ name: 'DiyPageDetail' }) defineOptions({ name: 'DiyPageDetail' })
// 消息弹窗
const message = useMessage()
// 左侧组件库 // 左侧组件库
const componentLibrary = ref() const componentLibrary = ref()
// 页面设置组件 // 页面设置组件
...@@ -159,20 +173,22 @@ const selectedComponentIndex = ref<number>(-1) ...@@ -159,20 +173,22 @@ const selectedComponentIndex = ref<number>(-1)
// 组件列表 // 组件列表
const pageComponents = ref<DiyComponent<any>[]>([]) const pageComponents = ref<DiyComponent<any>[]>([])
// 定义属性 // 定义属性
const props = defineProps<{ const props = defineProps({
// 页面配置,支持Json字符串 // 页面配置,支持Json字符串
modelValue: string | PageConfig modelValue: oneOfType<string | PageConfig>([String, Object]).isRequired,
// 标题 // 标题
title: string title: propTypes.string.def(''),
// 组件库 // 组件库
libs: DiyComponentLibrary[] libs: array<DiyComponentLibrary>(),
// 是否显示顶部导航栏 // 是否显示顶部导航栏
showNavigationBar: boolean showNavigationBar: propTypes.bool.def(true),
// 是否显示底部导航菜单 // 是否显示底部导航菜单
showTabBar: boolean showTabBar: propTypes.bool.def(false),
// 是否显示页面配置 // 是否显示页面配置
showPageConfig: boolean showPageConfig: propTypes.bool.def(true),
}>() // 预览地址:提供了预览地址,才会显示预览按钮
previewUrl: propTypes.string.def('')
})
// 监听传入的页面配置 // 监听传入的页面配置
watch( watch(
...@@ -281,6 +297,7 @@ const handleMoveComponent = (index: number, direction: number) => { ...@@ -281,6 +297,7 @@ const handleMoveComponent = (index: number, direction: number) => {
/** 复制组件 */ /** 复制组件 */
const handleCopyComponent = (index: number) => { const handleCopyComponent = (index: number) => {
const component = cloneDeep(pageComponents.value[index]) const component = cloneDeep(pageComponents.value[index])
component.uid = new Date().getTime()
pageComponents.value.splice(index + 1, 0, component) pageComponents.value.splice(index + 1, 0, component)
} }
/** /**
...@@ -306,14 +323,18 @@ const handleDeleteComponent = (index: number) => { ...@@ -306,14 +323,18 @@ const handleDeleteComponent = (index: number) => {
// 工具栏操作 // 工具栏操作
const emits = defineEmits(['reset', 'preview', 'save', 'update:modelValue']) const emits = defineEmits(['reset', 'preview', 'save', 'update:modelValue'])
// 注入无感刷新页面函数
const reload = inject<() => void>('reload')
// 重置 // 重置
const handleReset = () => { const handleReset = () => {
message.warning('开发中~') if (reload) reload()
emits('reset') emits('reset')
} }
// 预览 // 预览
const previewDialogVisible = ref(false)
const handlePreview = () => { const handlePreview = () => {
message.warning('开发中~') previewDialogVisible.value = true
emits('preview') emits('preview')
} }
......
...@@ -5,6 +5,8 @@ import { TabBarProperty } from '@/components/DiyEditor/components/mobile/TabBar/ ...@@ -5,6 +5,8 @@ import { TabBarProperty } from '@/components/DiyEditor/components/mobile/TabBar/
// 页面装修组件 // 页面装修组件
export interface DiyComponent<T> { export interface DiyComponent<T> {
// 用于区分同一种组件的不同实例
uid: number
// 组件唯一标识 // 组件唯一标识
id: string id: string
// 组件名称 // 组件名称
......
<template>
<el-text type="info" size="small"> 拖动左上角的小圆点可对其排序 </el-text>
<VueDraggable
:list="formData"
:force-fallback="true"
:animation="200"
handle=".drag-icon"
class="m-t-8px"
item-key="index"
>
<template #item="{ element, index }">
<div
class="mb-4px flex flex-col gap-4px border border-gray-2 border-rounded rounded border-solid p-8px"
>
<!-- 操作按钮区 -->
<div class="m--8px m-b-4px flex flex-row items-center justify-between bg-gray-1 p-8px">
<el-tooltip content="拖动排序">
<Icon icon="ic:round-drag-indicator" class="drag-icon cursor-move" />
</el-tooltip>
<el-tooltip content="删除">
<Icon
icon="ep:delete"
class="cursor-pointer text-red-5"
v-if="formData.length > 1"
@click="handleDelete(index)"
/>
</el-tooltip>
</div>
<!-- 内容区 -->
<slot :element="element" :index="index"></slot>
</div>
</template>
</VueDraggable>
<el-tooltip :disabled="limit < 1" :content="`最多添加${limit}个`">
<el-button
type="primary"
plain
class="m-t-4px w-full"
:disabled="limit > 0 && formData.length >= limit"
@click="handleAdd"
>
<Icon icon="ep:plus" /><span>添加</span>
</el-button>
</el-tooltip>
</template>
<script setup lang="ts">
// 拖拽组件
import VueDraggable from 'vuedraggable'
import { usePropertyForm } from '@/components/DiyEditor/util'
import { any, array } from 'vue-types'
import { propTypes } from '@/utils/propTypes'
import { cloneDeep } from 'lodash-es'
// 拖拽组件封装
defineOptions({ name: 'Draggable' })
// 定义属性
const props = defineProps({
// 绑定值
modelValue: array<any>().isRequired,
// 空的元素:点击添加按钮时,创建元素并添加到列表;默认为空对象
emptyItem: any<unknown>().def({}),
// 数量限制:默认为0,表示不限制
limit: propTypes.number.def(0)
})
// 定义事件
const emit = defineEmits(['update:modelValue'])
const { formData } = usePropertyForm(props.modelValue, emit)
// 处理添加
const handleAdd = () => formData.value.push(cloneDeep(props.emptyItem || {}))
// 处理删除
const handleDelete = (index: number) => formData.value.splice(index, 1)
</script>
<style scoped lang="scss"></style>
...@@ -20,6 +20,17 @@ const getCaches = computed((): string[] => { ...@@ -20,6 +20,17 @@ const getCaches = computed((): string[] => {
}) })
const tagsView = computed(() => appStore.getTagsView) const tagsView = computed(() => appStore.getTagsView)
//region 无感刷新
const routerAlive = ref(true)
// 无感刷新,防止出现页面闪烁白屏
const reload = () => {
routerAlive.value = false
nextTick(() => (routerAlive.value = true))
}
// 为组件后代提供刷新方法
provide('reload', reload)
//endregion
</script> </script>
<template> <template>
...@@ -49,7 +60,7 @@ const tagsView = computed(() => appStore.getTagsView) ...@@ -49,7 +60,7 @@ const tagsView = computed(() => appStore.getTagsView)
} }
]" ]"
> >
<router-view> <router-view v-if="routerAlive">
<template #default="{ Component, route }"> <template #default="{ Component, route }">
<keep-alive :include="getCaches"> <keep-alive :include="getCaches">
<component :is="Component" :key="route.fullPath" /> <component :is="Component" :key="route.fullPath" />
......
...@@ -2,10 +2,12 @@ ...@@ -2,10 +2,12 @@
<div> <div>
<el-card shadow="never"> <el-card shadow="never">
<el-skeleton :loading="loading" animated> <el-skeleton :loading="loading" animated>
<el-row :gutter="20" justify="space-between"> <el-row :gutter="16" justify="space-between">
<el-col :xl="12" :lg="12" :md="12" :sm="24" :xs="24"> <el-col :xl="12" :lg="12" :md="12" :sm="24" :xs="24">
<div class="flex items-center"> <div class="flex items-center">
<img :src="avatar" alt="" class="mr-20px h-70px w-70px rounded-[50%]" /> <el-avatar :src="avatar" :size="70" class="mr-16px">
<img src="@/assets/imgs/avatar.gif" alt="" />
</el-avatar>
<div> <div>
<div class="text-20px"> <div class="text-20px">
{{ t('workplace.welcome') }} {{ username }} {{ t('workplace.happyDay') }} {{ t('workplace.welcome') }} {{ username }} {{ t('workplace.happyDay') }}
...@@ -19,7 +21,7 @@ ...@@ -19,7 +21,7 @@
<el-col :xl="12" :lg="12" :md="12" :sm="24" :xs="24"> <el-col :xl="12" :lg="12" :md="12" :sm="24" :xs="24">
<div class="h-70px flex items-center justify-end lt-sm:mt-10px"> <div class="h-70px flex items-center justify-end lt-sm:mt-10px">
<div class="px-8px text-right"> <div class="px-8px text-right">
<div class="mb-20px text-14px text-gray-400">{{ t('workplace.project') }}</div> <div class="mb-16px text-14px text-gray-400">{{ t('workplace.project') }}</div>
<CountTo <CountTo
class="text-20px" class="text-20px"
:start-val="0" :start-val="0"
...@@ -29,7 +31,7 @@ ...@@ -29,7 +31,7 @@
</div> </div>
<el-divider direction="vertical" /> <el-divider direction="vertical" />
<div class="px-8px text-right"> <div class="px-8px text-right">
<div class="mb-20px text-14px text-gray-400">{{ t('workplace.toDo') }}</div> <div class="mb-16px text-14px text-gray-400">{{ t('workplace.toDo') }}</div>
<CountTo <CountTo
class="text-20px" class="text-20px"
:start-val="0" :start-val="0"
...@@ -39,7 +41,7 @@ ...@@ -39,7 +41,7 @@
</div> </div>
<el-divider direction="vertical" border-style="dashed" /> <el-divider direction="vertical" border-style="dashed" />
<div class="px-8px text-right"> <div class="px-8px text-right">
<div class="mb-20px text-14px text-gray-400">{{ t('workplace.access') }}</div> <div class="mb-16px text-14px text-gray-400">{{ t('workplace.access') }}</div>
<CountTo <CountTo
class="text-20px" class="text-20px"
:start-val="0" :start-val="0"
...@@ -54,8 +56,8 @@ ...@@ -54,8 +56,8 @@
</el-card> </el-card>
</div> </div>
<el-row class="mt-5px" :gutter="20" justify="space-between"> <el-row class="mt-8px" :gutter="8" justify="space-between">
<el-col :xl="16" :lg="16" :md="24" :sm="24" :xs="24" class="mb-10px"> <el-col :xl="16" :lg="16" :md="24" :sm="24" :xs="24" class="mb-8px">
<el-card shadow="never"> <el-card shadow="never">
<template #header> <template #header>
<div class="h-3 flex justify-between"> <div class="h-3 flex justify-between">
...@@ -76,11 +78,11 @@ ...@@ -76,11 +78,11 @@
> >
<el-card shadow="hover"> <el-card shadow="hover">
<div class="flex items-center"> <div class="flex items-center">
<Icon :icon="item.icon" :size="25" class="mr-10px" /> <Icon :icon="item.icon" :size="25" class="mr-8px" />
<span class="text-16px">{{ item.name }}</span> <span class="text-16px">{{ item.name }}</span>
</div> </div>
<div class="mt-15px text-14px text-gray-400">{{ t(item.message) }}</div> <div class="mt-16px text-14px text-gray-400">{{ t(item.message) }}</div>
<div class="mt-20px flex justify-between text-12px text-gray-400"> <div class="mt-16px flex justify-between text-12px text-gray-400">
<span>{{ item.personal }}</span> <span>{{ item.personal }}</span>
<span>{{ formatTime(item.time, 'yyyy-MM-dd') }}</span> <span>{{ formatTime(item.time, 'yyyy-MM-dd') }}</span>
</div> </div>
...@@ -90,18 +92,18 @@ ...@@ -90,18 +92,18 @@
</el-skeleton> </el-skeleton>
</el-card> </el-card>
<el-card shadow="never" class="mt-5px"> <el-card shadow="never" class="mt-8px">
<el-skeleton :loading="loading" animated> <el-skeleton :loading="loading" animated>
<el-row :gutter="20" justify="space-between"> <el-row :gutter="20" justify="space-between">
<el-col :xl="10" :lg="10" :md="24" :sm="24" :xs="24"> <el-col :xl="10" :lg="10" :md="24" :sm="24" :xs="24">
<el-card shadow="hover" class="mb-10px"> <el-card shadow="hover" class="mb-8px">
<el-skeleton :loading="loading" animated> <el-skeleton :loading="loading" animated>
<Echart :options="pieOptionsData" :height="280" /> <Echart :options="pieOptionsData" :height="280" />
</el-skeleton> </el-skeleton>
</el-card> </el-card>
</el-col> </el-col>
<el-col :xl="14" :lg="14" :md="24" :sm="24" :xs="24"> <el-col :xl="14" :lg="14" :md="24" :sm="24" :xs="24">
<el-card shadow="hover" class="mb-10px"> <el-card shadow="hover" class="mb-8px">
<el-skeleton :loading="loading" animated> <el-skeleton :loading="loading" animated>
<Echart :options="barOptionsData" :height="280" /> <Echart :options="barOptionsData" :height="280" />
</el-skeleton> </el-skeleton>
...@@ -111,7 +113,7 @@ ...@@ -111,7 +113,7 @@
</el-skeleton> </el-skeleton>
</el-card> </el-card>
</el-col> </el-col>
<el-col :xl="8" :lg="8" :md="24" :sm="24" :xs="24" class="mb-10px"> <el-col :xl="8" :lg="8" :md="24" :sm="24" :xs="24" class="mb-8px">
<el-card shadow="never"> <el-card shadow="never">
<template #header> <template #header>
<div class="h-3 flex justify-between"> <div class="h-3 flex justify-between">
...@@ -120,9 +122,9 @@ ...@@ -120,9 +122,9 @@
</template> </template>
<el-skeleton :loading="loading" animated> <el-skeleton :loading="loading" animated>
<el-row> <el-row>
<el-col v-for="item in shortcut" :key="`team-${item.name}`" :span="8" class="mb-10px"> <el-col v-for="item in shortcut" :key="`team-${item.name}`" :span="8" class="mb-8px">
<div class="flex items-center"> <div class="flex items-center">
<Icon :icon="item.icon" class="mr-10px" /> <Icon :icon="item.icon" class="mr-8px" />
<el-link type="default" :underline="false" @click="setWatermark(item.name)"> <el-link type="default" :underline="false" @click="setWatermark(item.name)">
{{ item.name }} {{ item.name }}
</el-link> </el-link>
...@@ -131,7 +133,7 @@ ...@@ -131,7 +133,7 @@
</el-row> </el-row>
</el-skeleton> </el-skeleton>
</el-card> </el-card>
<el-card shadow="never" class="mt-10px"> <el-card shadow="never" class="mt-8px">
<template #header> <template #header>
<div class="h-3 flex justify-between"> <div class="h-3 flex justify-between">
<span>{{ t('workplace.notice') }}</span> <span>{{ t('workplace.notice') }}</span>
...@@ -141,14 +143,16 @@ ...@@ -141,14 +143,16 @@
<el-skeleton :loading="loading" animated> <el-skeleton :loading="loading" animated>
<div v-for="(item, index) in notice" :key="`dynamics-${index}`"> <div v-for="(item, index) in notice" :key="`dynamics-${index}`">
<div class="flex items-center"> <div class="flex items-center">
<img :src="avatar" alt="" class="mr-20px h-35px w-35px rounded-[50%]" /> <el-avatar :src="avatar" :size="35" class="mr-16px">
<img src="@/assets/imgs/avatar.gif" alt="" />
</el-avatar>
<div> <div>
<div class="text-14px"> <div class="text-14px">
<Highlight :keys="item.keys.map((v) => t(v))"> <Highlight :keys="item.keys.map((v) => t(v))">
{{ item.type }} : {{ item.title }} {{ item.type }} : {{ item.title }}
</Highlight> </Highlight>
</div> </div>
<div class="mt-15px text-12px text-gray-400"> <div class="mt-16px text-12px text-gray-400">
{{ formatTime(item.date, 'yyyy-MM-dd') }} {{ formatTime(item.date, 'yyyy-MM-dd') }}
</div> </div>
</div> </div>
...@@ -167,7 +171,6 @@ import { formatTime } from '@/utils' ...@@ -167,7 +171,6 @@ import { formatTime } from '@/utils'
import { useUserStore } from '@/store/modules/user' import { useUserStore } from '@/store/modules/user'
import { useWatermark } from '@/hooks/web/useWatermark' import { useWatermark } from '@/hooks/web/useWatermark'
import avatarImg from '@/assets/imgs/avatar.gif'
import type { WorkplaceTotal, Project, Notice, Shortcut } from './types' import type { WorkplaceTotal, Project, Notice, Shortcut } from './types'
import { pieOptions, barOptions } from './echarts-data' import { pieOptions, barOptions } from './echarts-data'
...@@ -177,7 +180,7 @@ const { t } = useI18n() ...@@ -177,7 +180,7 @@ const { t } = useI18n()
const userStore = useUserStore() const userStore = useUserStore()
const { setWatermark } = useWatermark() const { setWatermark } = useWatermark()
const loading = ref(true) const loading = ref(true)
const avatar = userStore.getUser.avatar ? userStore.getUser.avatar : avatarImg const avatar = userStore.getUser.avatar
const username = userStore.getUser.nickname const username = userStore.getUser.nickname
const pieOptionsData = reactive<EChartsOption>(pieOptions) as EChartsOption const pieOptionsData = reactive<EChartsOption>(pieOptions) as EChartsOption
// 获取统计数 // 获取统计数
......
...@@ -10,7 +10,10 @@ ...@@ -10,7 +10,10 @@
class="h-20 w-20% flex flex-col cursor-pointer items-center justify-center gap-2" class="h-20 w-20% flex flex-col cursor-pointer items-center justify-center gap-2"
@click="handleMenuClick(menu.routerName)" @click="handleMenuClick(menu.routerName)"
> >
<div :class="menu.bgColor" class="rounded p-3 text-white"> <div
:class="menu.bgColor"
class="h-48px w-48px flex items-center justify-center rounded text-white"
>
<Icon :icon="menu.icon" class="text-7.5!" /> <Icon :icon="menu.icon" class="text-7.5!" />
</div> </div>
<span>{{ menu.name }}</span> <span>{{ menu.name }}</span>
......
...@@ -4,9 +4,6 @@ ...@@ -4,9 +4,6 @@
v-model="formData.property" v-model="formData.property"
:title="formData.name" :title="formData.name"
:libs="PAGE_LIBS" :libs="PAGE_LIBS"
:show-page-config="true"
:show-navigation-bar="true"
:show-tab-bar="false"
@save="submitForm" @save="submitForm"
/> />
</template> </template>
......
...@@ -7,7 +7,9 @@ ...@@ -7,7 +7,9 @@
:show-page-config="selectedTemplateItem !== 0" :show-page-config="selectedTemplateItem !== 0"
:show-tab-bar="selectedTemplateItem === 0" :show-tab-bar="selectedTemplateItem === 0"
:show-navigation-bar="selectedTemplateItem !== 0" :show-navigation-bar="selectedTemplateItem !== 0"
:preview-url="previewUrl"
@save="submitForm" @save="submitForm"
@reset="handleEditorReset"
> >
<template #toolBarLeft> <template #toolBarLeft>
<el-radio-group <el-radio-group
...@@ -29,6 +31,7 @@ import * as DiyTemplateApi from '@/api/mall/promotion/diy/template' ...@@ -29,6 +31,7 @@ import * as DiyTemplateApi from '@/api/mall/promotion/diy/template'
import * as DiyPageApi from '@/api/mall/promotion/diy/page' import * as DiyPageApi from '@/api/mall/promotion/diy/page'
import { useTagsViewStore } from '@/store/modules/tagsView' import { useTagsViewStore } from '@/store/modules/tagsView'
import { DiyComponentLibrary, PAGE_LIBS } from '@/components/DiyEditor/util' import { DiyComponentLibrary, PAGE_LIBS } from '@/components/DiyEditor/util'
import { toNumber } from 'lodash-es'
/** 装修模板表单 */ /** 装修模板表单 */
defineOptions({ name: 'DiyTemplateDecorate' }) defineOptions({ name: 'DiyTemplateDecorate' })
...@@ -48,6 +51,8 @@ const formData = ref<DiyTemplateApi.DiyTemplatePropertyVO>() ...@@ -48,6 +51,8 @@ const formData = ref<DiyTemplateApi.DiyTemplatePropertyVO>()
const formRef = ref() // 表单 Ref const formRef = ref() // 表单 Ref
// 当前编辑的属性 // 当前编辑的属性
const currentFormData = ref<DiyTemplateApi.DiyTemplatePropertyVO | DiyPageApi.DiyPageVO>() const currentFormData = ref<DiyTemplateApi.DiyTemplatePropertyVO | DiyPageApi.DiyPageVO>()
// 商城H5预览地址
const previewUrl = ref('')
// 获取详情 // 获取详情
const getPageDetail = async (id: any) => { const getPageDetail = async (id: any) => {
...@@ -55,6 +60,10 @@ const getPageDetail = async (id: any) => { ...@@ -55,6 +60,10 @@ const getPageDetail = async (id: any) => {
try { try {
formData.value = await DiyTemplateApi.getDiyTemplateProperty(id) formData.value = await DiyTemplateApi.getDiyTemplateProperty(id)
currentFormData.value = formData.value currentFormData.value = formData.value
// 拼接手机预览链接
const domain = import.meta.env.VITE_MALL_H5_DOMAIN
previewUrl.value = `${domain}/#/pages/index/index?templateId=${formData.value.id}`
} finally { } finally {
formLoading.value = false formLoading.value = false
} }
...@@ -115,17 +124,43 @@ const resetForm = () => { ...@@ -115,17 +124,43 @@ const resetForm = () => {
formRef.value?.resetFields() formRef.value?.resetFields()
} }
// 重置时记录当前编辑的页面
const handleEditorReset = () => storePageIndex()
//#region 无感刷新
// 记录标识
const DIY_PAGE_INDEX_KEY = 'diy_page_index'
// 1. 记录
const storePageIndex = () =>
sessionStorage.setItem(DIY_PAGE_INDEX_KEY, `${selectedTemplateItem.value}`)
// 2. 恢复
const recoverPageIndex = () => {
// 恢复重置前的页面,默认是第一个页面
const pageIndex = toNumber(sessionStorage.getItem(DIY_PAGE_INDEX_KEY)) || 0
// 移除标记
sessionStorage.removeItem(DIY_PAGE_INDEX_KEY)
// 切换页面
if (pageIndex !== selectedTemplateItem.value) {
selectedTemplateItem.value = pageIndex
handleTemplateItemChange()
}
}
//#endregion
/** 初始化 **/ /** 初始化 **/
const { currentRoute } = useRouter() // 路由 const { currentRoute } = useRouter() // 路由
const { delView } = useTagsViewStore() // 视图操作 const { delView } = useTagsViewStore() // 视图操作
const route = useRoute() onMounted(async () => {
onMounted(() => {
resetForm() resetForm()
if (!route.params.id) { if (!currentRoute.value.params.id) {
message.warning('参数错误,页面编号不能为空!') message.warning('参数错误,页面编号不能为空!')
delView(unref(currentRoute)) delView(unref(currentRoute))
return return
} }
getPageDetail(route.params.id)
// 查询详情
await getPageDetail(currentRoute.value.params.id)
// 恢复重置前的页面
recoverPageIndex()
}) })
</script> </script>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment