1
0
mirror of synced 2025-12-08 06:26:46 +08:00

大屏设计器

This commit is contained in:
qianlishi
2025-01-20 15:53:04 +08:00
parent a62cebac04
commit 4b3e88a65f
13 changed files with 496 additions and 7 deletions

View File

@@ -54,7 +54,8 @@
"vue-draggable-next": "^2.2.1",
"vue-i18n": "9.2.2",
"vue-router": "^4.4.0",
"vue-types": "^4.2.1"
"vue-types": "^4.2.1",
"vue3-sketch-ruler": "^2.2.7"
},
"devDependencies": {
"@types/element-resize-detector": "^1.1.6",

View File

@@ -88,6 +88,9 @@ dependencies:
vue-types:
specifier: ^4.2.1
version: 4.2.1(vue@3.5.12)
vue3-sketch-ruler:
specifier: ^2.2.7
version: 2.2.7
devDependencies:
'@types/element-resize-detector':
@@ -3398,6 +3401,7 @@ packages:
/fast-uri@3.0.3:
resolution: {integrity: sha512-aLrHthzCjH5He4Z2H9YZ+v6Ujb9ocRuW6ZzkJQOrTxleEijANq4v1TsaPaVG1PZcuurEzrLcWRyYBYXD5cEiaw==}
requiresBuild: true
dev: true
/fastest-levenshtein@1.0.16:
@@ -4146,6 +4150,7 @@ packages:
/json-schema-traverse@1.0.0:
resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==}
requiresBuild: true
dev: true
/json-stable-stringify-without-jsonify@1.0.1:
@@ -5292,6 +5297,7 @@ packages:
/require-from-string@2.0.2:
resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==}
engines: {node: '>=0.10.0'}
requiresBuild: true
dev: true
/require-main-filename@2.0.0:
@@ -6434,6 +6440,10 @@ packages:
resolution: {integrity: sha512-YFK6u5yltqtAOfTBcij/KGAS2SoZvzbNIAf9qTULauPObEp53xj22tDuohrrM2vNkgoD5kejXICIUBt2Q4ZDqQ==}
dev: true
/vue3-sketch-ruler@2.2.7:
resolution: {integrity: sha512-vzw/CsMjBgi0rqDz2GpPoTirUXUES+okgJDQGXlUC/mPFHQDvQBa2iEqw+af2L9E8cKjtWBd5IA9oHE7VFTSCg==}
dev: false
/vue@3.5.12(typescript@4.9.5):
resolution: {integrity: sha512-CLVZtXtn2ItBIi/zHZ0Sg1Xkb7+PU32bJJ8Bmy7ts3jxXTcbfsEfBivFYYWz1Hur+lalqGAh65Coin0r+HRUfg==}
peerDependencies:

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

View File

@@ -0,0 +1,62 @@
@font-face {
font-family: "iconfont"; /* Project id 4809046 */
src: url('//at.alicdn.com/t/c/font_4809046_35va4vzemas.woff2?t=1737061376697') format('woff2'),
url('//at.alicdn.com/t/c/font_4809046_35va4vzemas.woff?t=1737061376697') format('woff'),
url('//at.alicdn.com/t/c/font_4809046_35va4vzemas.ttf?t=1737061376697') format('truetype');
}
.iconfont {
font-family: "iconfont" !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.icon-guanbi:before {
content: "\e66d";
}
.icon-shouye:before {
content: "\e639";
}
.icon-tubiao-qiapian:before {
content: "\eb95";
}
.icon-peizhi-fenqupeizhi:before {
content: "\e617";
}
.icon-tuceng:before {
content: "\e636";
}
.icon-lishijilu:before {
content: "\e85f";
}
.icon-zujianku:before {
content: "\e6e4";
}
.icon-chakan:before {
content: "\e600";
}
.icon-fenxiang1:before {
content: "\e608";
}
.icon-bianji:before {
content: "\e616";
}
.icon-icon_function_shouqi:before {
content: "\e891";
}
.icon-zhankai:before {
content: "\e607";
}

View File

@@ -23,7 +23,6 @@
type: Number,
},
});
const computedStyle = computed(() =>
props.size
? { width: props.size + "px", height: props.size + "px", color: props.color }

File diff suppressed because one or more lines are too long

View File

@@ -2,11 +2,12 @@
* @Description:
* @Author: qianlishi
* @Date: 2024-12-30 18:16:00
* @LastEditors: qianlishi
* @LastEditTime: 2025-01-11 22:10:56
* @LastEditors: Do not edit
* @LastEditTime: 2025-01-17 02:05:48
*/
import './styles/tailwind.css';
import './styles/index.less';
import '@/assets/stlyes/font.css'
import '@/font/iconfont.js';
import { createApp } from 'vue';
import { setupNaiveDiscreteApi, setupNaive, setupDirectives } from '@/plugins';

View File

@@ -6,12 +6,14 @@ import { isObject } from './is/index';
import { cloneDeep } from 'lodash-es';
import { storage } from './Storage';
import { GLOBAL_DICT_CODE_NAME } from '@/enums/common';
/**
* render 图标
* */
export function renderIcon(icon) {
return () => h(NIcon, null, { default: () => h(icon) });
}
/**
* font 图标(Font class)
* */

View File

@@ -1,6 +1,41 @@
<!--
* @Author: qianlishi 1432731663@qq.com
* @Date: 2025-01-16 15:34:57
* @LastEditors: Do not edit
* @LastEditTime: 2025-01-17 08:13:07
-->
<template>
<div>大屏设计器</div>
<div class="designContainer">
<n-layout>
<n-layout-header>
<!-- 顶部 -->
<LayoutHeader />
</n-layout-header>
<n-layout has-sider sider-placement="right">
<!-- 左侧 -->
<LayoutLeft />
<n-layout-content>
<!-- 中间画布 -->
<LayOutContent />
</n-layout-content>
<!-- 右侧配置 -->
<LayOutRight />
</n-layout>
</n-layout>
</div>
</template>
<script lang='ts' setup>
import LayoutHeader from './layout/LayoutHeader/index.vue'
import LayoutLeft from './layout/LayoutLeft/index.vue'
import LayOutContent from './layout/LayoutContent/index.vue'
import LayOutRight from './layout/LayoutRight/index.vue'
</script>
<style lang="less" scoped>
.designContainer {
width: 100%;
height: 100vh;
overflow: hidden;
background: red;
}
</style>

View File

@@ -0,0 +1,67 @@
<template>
<div class="layContent wrapper whitewrapper" id="layContent">
<sketch-rule
:scale="scale"
:rate="rate"
:thick="thick"
:canvasWidth="canvasWidth"
:canvasHeight="canvasHeight"
:width="width"
:height="height"
:palette="paletteStyle"
:gridRatio="gridRatio"
:autoCenter="false"
>
<div>图表</div>
</sketch-rule>
</div>
</template>
<script lang='ts' setup>
import { ref, reactive } from 'vue'
import 'vue3-sketch-ruler/lib/style.css'
import SketchRule from 'vue3-sketch-ruler'
const scale = ref(1) // 初始化标尺的缩放及画布
const rate = ref(1) // 初始化标尺的缩放
const thick = ref(20) // 标尺的厚度
const width = ref(1920) // 放置标尺窗口的宽度
const height = ref(1080) // 放置标尺窗口的高度
const canvasWidth = ref(1920) // 画布宽
const canvasHeight = ref(1080) // 画布高
const gridRatio = ref(0.5) // 刻度分散比例(颗粒度)
const paletteStyle = ref({
bgColor: "transparent",
lineType: "dashed",
longfgColor: '#4d4d4d',
shortfgColor: '#1d2129',
fontColor: '#1d2129',
shadowColor: '#18181c',
borderColor: '#18181c',
cornerActiveColor: '#18181c'
})
</script>
<style lang='less' scoped>
.layContent {
width: 100%;
height: 100%;
position: absolute;
z-index: 0;
}
.wrapper {
background-size: 15px 15px, 15px 15px;
border: 1px solid #dadadc;
}
.whitewrapper {
background-color: #fafafc;
background-image: linear-gradient(#fafafc 14px, transparent 0),
linear-gradient(90deg, transparent 14px, #373739 0);
}
</style>

View File

@@ -0,0 +1,58 @@
<!--
* @Description: 设计器头部组件
* @Author: qianlishi
* @Date: 2025-01-16 16:30:35
* @LastEditors: Do not edit
* @LastEditTime: 2025-01-17 05:00:53
-->
<template>
<div class="header">
<div class="nav-left">
<div class="home mr-20">
<n-popover trigger="hover">
<template #trigger>
<span class="iconfont icon-shouye" />
</template>
<span>首页</span>
</n-popover>
</div>
<div class="echarts mr-10">
<n-popover trigger="hover">
<template #trigger>
<span class="iconfont icon-tubiao-qiapian" />
</template>
<span>图标收缩</span>
</n-popover>
</div>
<div class="config mr-10">
<n-popover trigger="hover">
<template #trigger>
<span class="iconfont icon-peizhi-fenqupeizhi" />
</template>
<span>配置项收缩</span>
</n-popover>
</div>
</div>
<div class="nav-content"></div>
<div class="nav-right"></div>
</div>
</template>
<script lang='ts' setup>
</script>
<style lang='less' scoped>
.header {
width: 100%;
height: 60px;
border-bottom: 2px solid #fafafa;
box-sizing: border-box;
display: flex;
padding: 0 20px;
.nav-left {
display: flex;
line-height: 60px;
.iconfont {
font-size: 20px;
}
}
}
</style>

View File

@@ -0,0 +1,220 @@
<template>
<div class="menu-left">
<div class="menu-nav">
<n-menu v-model:value="selectValue" class="menu" :options="menuOptions" />
</div>
<div class="menu-sub" :class="[ isActive ? '' : 'active']">
<div class="title">
<span>组件列表</span>
<span class="iconfont icon-guanbi" @click="close"></span>
</div>
<div class="menu-sub-box">
<div class="menu-sub-box-one">
<n-menu v-model:value="selectValue1" class="menu" :options="menuOptions1" />
</div>
<div class="menu-sub-box-two">
<n-menu v-model:value="selectValue2" class="menu" :options="menuOptions2" />
</div>
<div class="menu-sub-box-imgs">
<div class="input">
<n-input type="text" size="small" placeholder="搜索组件" />
</div>
<n-scrollbar>
<div class="item" v-for="item in 10">
<div class="item-title">柱状图</div>
<div class="item-box">
<img src="@/assets/echImgs/bar_line.png" class="item-box-img" alt="">
</div>
</div>
</n-scrollbar>
</div>
</div>
</div>
</div>
</template>
<script lang='ts' setup>
import { reactive, ref } from 'vue'
import { renderFontClassIcon } from '@/utils';
const menuOptions = reactive([
{
label: '组件库',
key: 'pinball-1973',
icon: renderFontClassIcon('icon-zujianku')
},
{
label: '图层',
key: 'pinball-1974',
icon: renderFontClassIcon('icon-tuceng')
},
{
label: '历史记录',
key: 'pinball-1975',
icon: renderFontClassIcon('icon-lishijilu')
}
])
const menuOptions1 = [
{
label: '所有',
icon: renderFontClassIcon('icon-zujianku'),
key: '1'
},
{
label: '图表',
icon: renderFontClassIcon('icon-zujianku'),
key: '2'
}
]
const menuOptions2 = [
{
label: '全部',
key: '1'
},
{
label: '柱状图',
key: '2'
},
]
const selectValue = ref<string>(menuOptions[0].key)
const selectValue1 = ref('1')
const selectValue2 = ref('1')
const isActive = ref(true)
const close = () => {
isActive.value = false
}
</script>
<style lang='less' scoped>
.menu-left {
height: calc(100vh - 60px);
display: flex;
.menu-nav {
width: 70px;
height: 100%;
background: #fafafa;
// background: red;
::v-deep .menu {
.n-menu-item {
height: auto !important;
&.n-menu-item--selected {
&::after {
content: '';
position: absolute;
left: 2px;
top: 0;
height: 100%;
width: 3px;
border-top-right-radius: 3px;
border-bottom-right-radius: 3px;
}
}
.n-menu-item-content {
// &.n-menu-item-content:not(.n-menu-item-content--disabled):hover {
// background: none;
// }
display: flex;
flex-direction: column;
padding: 6px 12px !important;
font-size: 14px !important;
}
.n-menu-item-content__icon {
font-size: 18px !important;
margin-right: 0 !important;
}
}
}
}
.menu-sub {
overflow: hidden;
&.active {
width: 0;
}
.title {
width: 100%;
line-height: 50px;
border-bottom: 2px solid #fafafa;
padding:0 20px;
display: flex;
justify-content: space-between;
}
::v-deep .menu-sub-box {
display: flex;
height: 100%;
transition: all 1s;
&-one {
width: 60px;
height: 30px;
.n-menu-item-content {
display: flex;
flex-direction: column;
padding: 0 !important;
font-size: 12px !important;
box-sizing: border-box;
&.n-menu-item-content__icon{
margin-right: 0!important;
}
&.n-menu-item-content:not(.n-menu-item-content--disabled):hover::before {
background: none;
}
&.n-menu-item-content.n-menu-item-content--selected::before {
background: none;
}
}
.n-menu-item-content__icon {
margin-right: 0px !important;
}
}
&-two {
width: 70px;
background: #fafafa;
.menu {
.n-menu-item {
height: 30px;
.n-menu-item-content {
display: grid;
text-align: center;
padding: 6px 12px !important;
font-size: 12px !important;
}
}
}
}
&-imgs {
width: 160px;
.input {
margin: 4px;
}
.item {
margin-top: 6px;
&-title {
font-size: 12px;
color: #999;
padding: 4px;
background: #e5e6eb;
border-radius: 5px 5px 0 0;
}
&-box {
background: #f3f3f3;
display: flex;
justify-content: center;
align-items: center;
text-align: center;
padding: 6px 0;
overflow: hidden;
&-img {
max-width: 140px;
height: 100px;
border-radius: 5px;
object-fit: contain;
&:hover {
transform: scale(1.1);
transition: all 0.5s;
}
}
}
}
}
}
}
}
</style>

View File

@@ -0,0 +1,34 @@
<template>
<n-layout-sider
collapse-mode="transform"
:collapsed-width="0"
:width="350"
:native-scrollbar="false"
show-trigger="bar"
>
<div class="config">
<n-scrollbar trigger="none">
<n-tabs size="small" type="segment">
<n-tab-pane name="chap1" tab="配置">
配置
</n-tab-pane>
<n-tab-pane name="chap2" tab="动画">动画</n-tab-pane>
<n-tab-pane name="chap3" tab="数据">数据</n-tab-pane>
<n-tab-pane name="chap4" tab="事件">事件</n-tab-pane>
</n-tabs>
</n-scrollbar>
</div>
</n-layout-sider>
</template>
<script lang='ts' setup>
</script>
<style lang='less' scoped>
.config {
height: calc(100vh - 60px);
background: #f7f7fa;
::v-deep .n-tab-pane {
background:#f7f7fa;
}
}
</style>