增加流程配置

pull/676/head
yuyuer13 2025-01-22 19:22:08 +08:00
parent 2659165220
commit fbec62e3c1
35 changed files with 9943 additions and 5903 deletions

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,5 @@
<svg width="128" height="128" viewBox="0 0 128 128" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="64" cy="64" r="64" fill="#E5E9F2"/>
<circle cx="64" cy="48" r="24" fill="#C0CCDA"/>
<path d="M64 80C42.464 80 24.572 92.436 16 110.444C24.572 121.564 43.36 128 64 128C84.64 128 103.428 121.564 112 110.444C103.428 92.436 85.536 80 64 80Z" fill="#C0CCDA"/>
</svg>

After

Width:  |  Height:  |  Size: 380 B

View File

@ -0,0 +1,51 @@
interface AccordionItem {
title: string;
type: 'form' | 'text';
content: FormItem[] | string;
}
interface FormItem {
label: string;
type: 'text' | 'select';
value: string;
options?: { label: string; value: string; }[];
}
export const accordionData: AccordionItem[] = [
{
title: '基本设置',
type: 'form',
content: [
{
label: '名称',
type: 'text',
value: ''
},
{
label: '类型',
type: 'select',
value: '',
options: [
{ label: '类型1', value: 'type1' },
{ label: '类型2', value: 'type2' }
]
}
]
},
{
title: '高级设置',
type: 'form',
content: [
{
label: '配置项',
type: 'text',
value: ''
}
]
},
{
title: '其他信息',
type: 'text',
content: '这是一些其他信息的描述文本'
}
]

View File

@ -0,0 +1,72 @@
{
"categories": [
{
"name": "UML类图",
"nodes": [
{
"type": "uml-class",
"name": "类",
"icon": "📦",
"template": {
"title": "类名",
"attributes": ["+ 属性1: string", "- 属性2: number"],
"methods": ["+ 方法1()", "# 方法2(param: string)"]
}
},
{
"type": "interface",
"name": "接口",
"icon": "🔌",
"template": {
"title": "接口名",
"methods": ["+ 方法1()", "+ 方法2()"]
}
}
]
},
{
"name": "思维导图",
"nodes": [
{
"type": "topic",
"name": "主题",
"icon": "💡",
"template": {
"title": "主题"
}
},
{
"type": "subtopic",
"name": "子主题",
"icon": "📝",
"template": {
"title": "子主题"
}
}
]
},
{
"name": "网络拓扑",
"nodes": [
{
"type": "server",
"name": "服务器",
"icon": "🖥️",
"template": {
"title": "Server",
"ip": "192.168.1.1"
}
},
{
"type": "router",
"name": "路由器",
"icon": "📡",
"template": {
"title": "Router",
"ip": "192.168.1.254"
}
}
]
}
]
}

View File

@ -0,0 +1,55 @@
{
"files": [
{
"id": 1,
"name": "业务流程图",
"preview": "business-process",
"modifiedDate": "2024-01-20"
},
{
"id": 2,
"name": "思维导图",
"preview": "mind-map",
"modifiedDate": "2024-01-18"
},
{
"id": 3,
"name": "UML类图",
"preview": "uml",
"modifiedDate": "2024-01-15"
}
],
"templates": [
{
"id": 1,
"name": "项目管理流程",
"description": "适用于项目规划和任务管理",
"preview": "business-process"
},
{
"id": 2,
"name": "组织架构图",
"description": "快速创建公司组织结构图",
"preview": "org-chart"
}
],
"communityItems": [
{
"id": 1,
"title": "电商系统架构图",
"preview": "business-process",
"author": "张三",
"authorAvatar": "default-avatar",
"likes": 128,
"comments": 32
}
],
"deletedFiles": [
{
"id": 1,
"name": "旧版流程图",
"preview": "business-process",
"deleteTime": "2024-03-10"
}
]
}

View File

@ -0,0 +1,15 @@
<svg width="400" height="225" viewBox="0 0 400 225" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="400" height="225" fill="#F5F7FA"/>
<rect x="50" y="92.5" width="100" height="40" rx="4" fill="#409EFF"/>
<text x="100" y="117.5" text-anchor="middle" fill="white" font-size="14">开始</text>
<path d="M150 112.5H200" stroke="#409EFF" stroke-width="2" marker-end="url(#arrow)"/>
<rect x="200" y="92.5" width="100" height="40" rx="4" fill="#409EFF"/>
<text x="250" y="117.5" text-anchor="middle" fill="white" font-size="14">处理</text>
<path d="M300 112.5H350" stroke="#409EFF" stroke-width="2" marker-end="url(#arrow)"/>
<defs>
<marker id="arrow" viewBox="0 0 10 10" refX="9" refY="5"
markerWidth="6" markerHeight="6" orient="auto">
<path d="M 0 0 L 10 5 L 0 10 z" fill="#409EFF"/>
</marker>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 859 B

View File

@ -0,0 +1,81 @@
<svg viewBox="0 0 1000 600" preserveAspectRatio="xMidYMid slice" xmlns="http://www.w3.org/2000/svg">
<!-- 渐变背景 -->
<defs>
<linearGradient id="skyGradient" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" style="stop-color:#2c5edf;stop-opacity:1" />
<stop offset="100%" style="stop-color:#5b9fff;stop-opacity:1" />
</linearGradient>
<!-- 流程图连线动画 -->
<path id="flowPath1" d="M100,200 C200,200 300,300 400,300" />
<path id="flowPath2" d="M400,300 C500,300 600,200 700,200" />
</defs>
<!-- 背景 -->
<rect width="100%" height="100%" fill="url(#skyGradient)" />
<!-- 装饰性圆点 -->
<g class="dots" fill="#ffffff" fill-opacity="0.1">
<circle cx="100" cy="100" r="2">
<animate attributeName="r" values="2;4;2" dur="3s" repeatCount="indefinite" />
</circle>
<circle cx="300" cy="200" r="3">
<animate attributeName="r" values="3;5;3" dur="4s" repeatCount="indefinite" />
</circle>
<circle cx="500" cy="150" r="2">
<animate attributeName="r" values="2;4;2" dur="3.5s" repeatCount="indefinite" />
</circle>
<circle cx="700" cy="300" r="3">
<animate attributeName="r" values="3;5;3" dur="4.5s" repeatCount="indefinite" />
</circle>
</g>
<!-- 流程图节点和连线 -->
<g class="flow-elements">
<!-- 节点 -->
<rect x="80" y="180" width="40" height="40" rx="5" fill="#ffffff" opacity="0.2">
<animate attributeName="opacity" values="0.2;0.4;0.2" dur="3s" repeatCount="indefinite" />
</rect>
<rect x="380" y="280" width="40" height="40" rx="5" fill="#ffffff" opacity="0.2">
<animate attributeName="opacity" values="0.2;0.4;0.2" dur="3s" repeatCount="indefinite" />
</rect>
<rect x="680" y="180" width="40" height="40" rx="5" fill="#ffffff" opacity="0.2">
<animate attributeName="opacity" values="0.2;0.4;0.2" dur="3s" repeatCount="indefinite" />
</rect>
<!-- 动态连线 -->
<g stroke="#ffffff" stroke-width="2" stroke-opacity="0.3" fill="none">
<path d="">
<animate
attributeName="d"
values="M120,200 C200,200 300,300 380,300;M120,200 C200,200 300,290 380,300;M120,200 C200,200 300,300 380,300"
dur="4s"
repeatCount="indefinite"
/>
</path>
<path d="">
<animate
attributeName="d"
values="M420,300 C500,300 600,200 680,200;M420,300 C500,310 600,200 680,200;M420,300 C500,300 600,200 680,200"
dur="4s"
repeatCount="indefinite"
/>
</path>
</g>
<!-- 流动的点 -->
<circle r="3" fill="#ffffff">
<animateMotion
dur="3s"
repeatCount="indefinite"
path="M120,200 C200,200 300,300 380,300"
/>
</circle>
<circle r="3" fill="#ffffff">
<animateMotion
dur="3s"
repeatCount="indefinite"
path="M420,300 C500,300 600,200 680,200"
/>
</circle>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

@ -0,0 +1,17 @@
<svg width="400" height="225" viewBox="0 0 400 225" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="400" height="225" fill="#F5F7FA"/>
<circle cx="200" cy="112.5" r="40" fill="#409EFF"/>
<text x="200" y="117.5" text-anchor="middle" fill="white" font-size="14">核心主题</text>
<circle cx="100" cy="72.5" r="30" fill="#67C23A"/>
<text x="100" y="77.5" text-anchor="middle" fill="white" font-size="12">分支1</text>
<circle cx="100" cy="152.5" r="30" fill="#67C23A"/>
<text x="100" y="157.5" text-anchor="middle" fill="white" font-size="12">分支2</text>
<circle cx="300" cy="72.5" r="30" fill="#67C23A"/>
<text x="300" y="77.5" text-anchor="middle" fill="white" font-size="12">分支3</text>
<circle cx="300" cy="152.5" r="30" fill="#67C23A"/>
<text x="300" y="157.5" text-anchor="middle" fill="white" font-size="12">分支4</text>
<path d="M160 112.5L130 72.5" stroke="#67C23A" stroke-width="2"/>
<path d="M160 112.5L130 152.5" stroke="#67C23A" stroke-width="2"/>
<path d="M240 112.5L270 72.5" stroke="#67C23A" stroke-width="2"/>
<path d="M240 112.5L270 152.5" stroke="#67C23A" stroke-width="2"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1,13 @@
<svg width="400" height="225" viewBox="0 0 400 225" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="400" height="225" fill="#F5F7FA"/>
<rect x="150" y="22.5" width="100" height="40" rx="4" fill="#409EFF"/>
<text x="200" y="47.5" text-anchor="middle" fill="white" font-size="14">CEO</text>
<rect x="50" y="92.5" width="100" height="40" rx="4" fill="#67C23A"/>
<text x="100" y="117.5" text-anchor="middle" fill="white" font-size="14">技术部</text>
<rect x="250" y="92.5" width="100" height="40" rx="4" fill="#67C23A"/>
<text x="300" y="117.5" text-anchor="middle" fill="white" font-size="14">市场部</text>
<path d="M200 62.5V82.5" stroke="#409EFF" stroke-width="2"/>
<path d="M100 82.5V92.5" stroke="#409EFF" stroke-width="2"/>
<path d="M300 82.5V92.5" stroke="#409EFF" stroke-width="2"/>
<path d="M100 82.5H300" stroke="#409EFF" stroke-width="2"/>
</svg>

After

Width:  |  Height:  |  Size: 895 B

View File

@ -0,0 +1,10 @@
<svg width="400" height="225" viewBox="0 0 400 225" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="400" height="225" fill="#F5F7FA"/>
<rect x="100" y="42.5" width="200" height="140" rx="4" fill="white" stroke="#409EFF" stroke-width="2"/>
<line x1="100" y1="82.5" x2="300" y2="82.5" stroke="#409EFF" stroke-width="2"/>
<line x1="100" y1="142.5" x2="300" y2="142.5" stroke="#409EFF" stroke-width="2"/>
<text x="200" y="67.5" text-anchor="middle" fill="#409EFF" font-size="14" font-weight="bold">Class</text>
<text x="110" y="107.5" fill="#666666" font-size="12">- attribute1: Type</text>
<text x="110" y="127.5" fill="#666666" font-size="12">- attribute2: Type</text>
<text x="110" y="167.5" fill="#666666" font-size="12">+ method(): ReturnType</text>
</svg>

After

Width:  |  Height:  |  Size: 787 B

14
src/assets/hero-bg.svg Normal file
View File

@ -0,0 +1,14 @@
<svg width="1440" height="800" viewBox="0 0 1440 800" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M0 0H1440V800H0V0Z" fill="url(#paint0_linear)"/>
<g opacity="0.1">
<circle cx="720" cy="400" r="300" stroke="#409EFF" stroke-width="2"/>
<circle cx="720" cy="400" r="200" stroke="#409EFF" stroke-width="2"/>
<circle cx="720" cy="400" r="100" stroke="#409EFF" stroke-width="2"/>
</g>
<defs>
<linearGradient id="paint0_linear" x1="0" y1="0" x2="1440" y2="800" gradientUnits="userSpaceOnUse">
<stop stop-color="#ECF5FF"/>
<stop offset="1" stop-color="#E4E7ED"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 644 B

View File

@ -0,0 +1,4 @@
<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M15 2L28 15L15 28L2 15L15 2Z" stroke="#409EFF" stroke-width="0.5"/>
<circle cx="15" cy="15" r="2" fill="#409EFF"/>
</svg>

After

Width:  |  Height:  |  Size: 231 B

View File

@ -0,0 +1,43 @@
<svg width="800" height="600" viewBox="0 0 800 600" fill="none" xmlns="http://www.w3.org/2000/svg">
<!-- 背景装饰圆圈 -->
<circle cx="400" cy="300" r="250" stroke="white" stroke-width="2" stroke-opacity="0.1"/>
<circle cx="400" cy="300" r="200" stroke="white" stroke-width="2" stroke-opacity="0.1"/>
<circle cx="400" cy="300" r="150" stroke="white" stroke-width="2" stroke-opacity="0.1"/>
<!-- 流程图示意 -->
<g opacity="0.2">
<!-- 开始节点 -->
<circle cx="250" cy="200" r="30" fill="white"/>
<!-- 处理节点1 -->
<rect x="350" y="170" width="100" height="60" rx="4" fill="white"/>
<!-- 判断节点 -->
<path d="M500 200 L550 230 L500 260 L450 230 Z" fill="white"/>
<!-- 处理节点2 -->
<rect x="350" y="300" width="100" height="60" rx="4" fill="white"/>
<!-- 结束节点 -->
<circle cx="550" cy="330" r="30" fill="white"/>
<!-- 连接线 -->
<path d="M280 200 H350" stroke="white" stroke-width="2"/>
<path d="M450 200 H450" stroke="white" stroke-width="2"/>
<path d="M550 230 V330" stroke="white" stroke-width="2"/>
<path d="M500 230 H400 V300" stroke="white" stroke-width="2"/>
<path d="M450 330 H520" stroke="white" stroke-width="2"/>
</g>
<!-- 装饰点 -->
<circle cx="200" cy="150" r="4" fill="white" fill-opacity="0.3"/>
<circle cx="600" cy="450" r="4" fill="white" fill-opacity="0.3"/>
<circle cx="150" cy="400" r="4" fill="white" fill-opacity="0.3"/>
<circle cx="650" cy="200" r="4" fill="white" fill-opacity="0.3"/>
<!-- 装饰线 -->
<line x1="100" y1="100" x2="150" y2="150" stroke="white" stroke-opacity="0.1"/>
<line x1="700" y1="500" x2="650" y2="450" stroke="white" stroke-opacity="0.1"/>
<line x1="100" y1="500" x2="150" y2="450" stroke="white" stroke-opacity="0.1"/>
<line x1="700" y1="100" x2="650" y2="150" stroke="white" stroke-opacity="0.1"/>
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

4
src/assets/logo.svg Normal file
View File

@ -0,0 +1,4 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="4" y="4" width="24" height="24" rx="4" fill="#409EFF"/>
<path d="M10 16H22M16 10V22" stroke="white" stroke-width="2" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 258 B

View File

@ -0,0 +1,48 @@
/* 通用的文件列表横向布局样式 */
.file-list {
display: flex;
flex-wrap: wrap;
gap: 20px;
padding: 20px 0;
}
.file-card {
width: 280px;
background: #fff;
border-radius: 8px;
overflow: hidden;
transition: transform 0.2s;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
}
.file-card:hover {
transform: translateY(-2px);
}
.file-preview {
width: 100%;
height: 160px;
background: #f5f7fa;
}
.file-preview img {
width: 100%;
height: 100%;
object-fit: cover;
}
.file-info {
padding: 12px;
}
.file-info h3 {
font-size: 14px;
font-weight: 500;
color: #1a1a1a;
margin-bottom: 4px;
}
.file-info p {
font-size: 12px;
color: #909399;
}

1
src/assets/vue.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="37.07" height="36" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 198"><path fill="#41B883" d="M204.8 0H256L128 220.8L0 0h97.92L128 51.2L157.44 0h47.36Z"></path><path fill="#41B883" d="m0 0l128 220.8L256 0h-51.2L128 132.48L50.56 0H0Z"></path><path fill="#35495E" d="M50.56 0L128 133.12L204.8 0h-47.36L128 51.2L97.92 0H50.56Z"></path></svg>

After

Width:  |  Height:  |  Size: 496 B

View File

@ -200,103 +200,107 @@ const clear = () => {
</script>
<template>
<div
:class="prefixCls"
class="fixed right-0 top-[45%] h-40px w-40px cursor-pointer bg-[var(--el-color-primary)] text-center leading-40px"
@click="drawer = true"
>
<Icon color="#fff" icon="ep:setting" />
</div>
<div>
<div
:class="prefixCls"
class="fixed right-0 top-[45%] h-40px w-40px cursor-pointer bg-[var(--el-color-primary)] text-center leading-40px"
@click="drawer = true"
>
<Icon color="#fff" icon="ep:setting" />
</div>
<ElDrawer v-model="drawer" :z-index="4000" direction="rtl" size="350px">
<template #header>
<span class="text-16px font-700">{{ t('setting.projectSetting') }}</span>
</template>
<ElDrawer v-model="drawer" :z-index="4000" direction="rtl" size="350px">
<template #header>
<span class="text-16px font-700">{{ t('setting.projectSetting') }}</span>
</template>
<div class="text-center">
<!-- 主题 -->
<ElDivider>{{ t('setting.theme') }}</ElDivider>
<ThemeSwitch />
<div class="text-center">
<!-- 主题 -->
<ElDivider>{{ t('setting.theme') }}</ElDivider>
<ThemeSwitch />
<!-- 布局 -->
<ElDivider>{{ t('setting.layout') }}</ElDivider>
<LayoutRadioPicker />
<!-- 布局 -->
<ElDivider>{{ t('setting.layout') }}</ElDivider>
<LayoutRadioPicker />
<!-- 系统主题 -->
<ElDivider>{{ t('setting.systemTheme') }}</ElDivider>
<ColorRadioPicker
v-model="systemTheme"
:schema="[
'#409eff',
'#009688',
'#536dfe',
'#ff5c93',
'#ee4f12',
'#0096c7',
'#9c27b0',
'#ff9800'
]"
@change="setSystemTheme"
/>
<!-- 头部主题 -->
<ElDivider>{{ t('setting.headerTheme') }}</ElDivider>
<ColorRadioPicker
v-model="headerTheme"
:schema="[
'#fff',
'#151515',
'#5172dc',
'#e74c3c',
'#24292e',
'#394664',
'#009688',
'#383f45'
]"
@change="setHeaderTheme"
/>
<!-- 菜单主题 -->
<template v-if="layout !== 'top'">
<ElDivider>{{ t('setting.menuTheme') }}</ElDivider>
<!-- 系统主题 -->
<ElDivider>{{ t('setting.systemTheme') }}</ElDivider>
<ColorRadioPicker
v-model="menuTheme"
v-model="systemTheme"
:schema="[
'#409eff',
'#009688',
'#536dfe',
'#ff5c93',
'#ee4f12',
'#0096c7',
'#9c27b0',
'#ff9800'
]"
@change="setSystemTheme"
/>
<!-- 头部主题 -->
<ElDivider>{{ t('setting.headerTheme') }}</ElDivider>
<ColorRadioPicker
v-model="headerTheme"
:schema="[
'#fff',
'#001529',
'#212121',
'#273352',
'#191b24',
'#383f45',
'#001628',
'#344058'
'#151515',
'#5172dc',
'#e74c3c',
'#24292e',
'#394664',
'#009688',
'#383f45'
]"
@change="setMenuTheme"
@change="setHeaderTheme"
/>
</template>
</div>
<!-- 界面显示 -->
<ElDivider>{{ t('setting.interfaceDisplay') }}</ElDivider>
<InterfaceDisplay />
<!-- 菜单主题 -->
<template v-if="layout !== 'top'">
<ElDivider>{{ t('setting.menuTheme') }}</ElDivider>
<ColorRadioPicker
v-model="menuTheme"
:schema="[
'#fff',
'#1e293b', //
'#2c3e50', //
'#252b3a', //
'#2b2d42', //
'#1a237e', //
'#263238', //
'#1f2937' //
]"
@change="setMenuTheme"
/>
</template>
</div>
<ElDivider />
<div>
<ElButton class="w-full" type="primary" @click="copyConfig">{{ t('setting.copy') }}</ElButton>
</div>
<div class="mt-5px">
<ElButton class="w-full" type="danger" @click="clear">
{{ t('setting.clearAndReset') }}
</ElButton>
</div>
</ElDrawer>
<!-- 界面显示 -->
<ElDivider>{{ t('setting.interfaceDisplay') }}</ElDivider>
<InterfaceDisplay />
<ElDivider />
<div>
<ElButton class="w-full" type="primary" @click="copyConfig">{{ t('setting.copy') }}</ElButton>
</div>
<div class="mt-5px">
<ElButton class="w-full" type="danger" @click="clear">
{{ t('setting.clearAndReset') }}
</ElButton>
</div>
</ElDrawer>
</div>
</template>
<style lang="scss" scoped>
$prefix-cls: #{$namespace}-setting;
.#{$prefix-cls} {
z-index: 1200;
border-radius: 6px 0 0 6px;
z-index: 1200;/*修正没有z-index会被表格层覆盖,值不要超过4000*/
/* 修正没有z-index会被表格层覆盖,值不要超过4000 */
}
</style>

View File

@ -5,13 +5,13 @@
--left-menu-min-width: 64px;
--left-menu-bg-color: #001529;
--left-menu-bg-color: #1e293b;
--left-menu-bg-light-color: #0f2438;
--left-menu-bg-light-color: #2c3e50;
--left-menu-bg-active-color: var(--el-color-primary);
--left-menu-text-color: #bfcbd9;
--left-menu-text-color: #e2e8f0;
--left-menu-text-active-color: #fff;

View File

@ -0,0 +1,16 @@
<template>
<div>
<h2>联系我们</h2>
<p>这里是联系我们页面的内容</p>
</div>
</template>
<script>
export default {
name: 'Contact'
}
</script>
<style scoped>
/* 添加页面特定的样式 */
</style>

View File

@ -0,0 +1,75 @@
<template>
<div class="features">
<div class="features-content">
<h2>功能特点</h2>
<div class="feature-grid">
<div class="feature-card">
<div class="feature-icon">📊</div>
<h3>丰富的图形库</h3>
<p>提供大量预设图形和模板满足各类图表绘制需求</p>
</div>
<div class="feature-card">
<div class="feature-icon">🔄</div>
<h3>实时协作</h3>
<p>支持多人同时编辑实时同步变更</p>
</div>
<div class="feature-card">
<div class="feature-icon">💾</div>
<h3>自动保存</h3>
<p>自动保存所有更改永不丢失工作进度</p>
</div>
<div class="feature-card">
<div class="feature-icon">📱</div>
<h3>跨平台支持</h3>
<p>支持桌面和移动设备随时随地访问</p>
</div>
</div>
</div>
</div>
</template>
<style scoped>
.features-content {
max-width: 1200px;
margin: 0 auto;
}
h2 {
font-size: 36px;
font-weight: 600;
color: #1a1a1a;
text-align: center;
margin-bottom: 48px;
}
.feature-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 32px;
}
.feature-card {
background: #fff;
padding: 32px;
border-radius: 8px;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
text-align: center;
}
.feature-icon {
font-size: 40px;
margin-bottom: 16px;
}
h3 {
font-size: 20px;
font-weight: 600;
color: #1a1a1a;
margin-bottom: 16px;
}
p {
color: #666;
line-height: 1.6;
}
</style>

View File

@ -0,0 +1,399 @@
<template>
<div class="file-manager">
<!-- 左侧菜单 -->
<div class="sidebar">
<!-- Logo -->
<div class="logo">
<router-link to="/" class="back-btn">
<i class="icon"></i>
</router-link>
<img src="@/assets/logo.svg" alt="Logo">
<span>Flow Design</span>
</div>
<!-- 新建按钮组 -->
<div class="action-buttons">
<button class="btn-new" @click="handleCreate">
<i class="icon">+</i>
新建
</button>
<button class="btn-template" @click="openTemplateDialog">
<i class="icon">📋</i>
从模板新建
</button>
</div>
<!-- 菜单列表 -->
<nav class="menu-list">
<router-link
to="/files/my-files"
class="menu-item"
active-class="active"
>
<i class="icon">📁</i>
我的文件
</router-link>
<router-link
to="/files/favorites"
class="menu-item"
active-class="active"
>
<i class="icon"></i>
我的收藏
</router-link>
<router-link
to="/files/recent"
class="menu-item"
active-class="active"
>
<i class="icon">🕒</i>
最近
</router-link>
<div class="menu-divider"></div>
<router-link
to="/files/recommended"
class="menu-item"
active-class="active"
>
<i class="icon">🔥</i>
推荐
</router-link>
<router-link
to="/files/community"
class="menu-item"
active-class="active"
>
<i class="icon">👥</i>
社区
</router-link>
<div class="menu-divider"></div>
<router-link
to="/files/trash"
class="menu-item"
active-class="active"
>
<i class="icon">🗑</i>
回收站
</router-link>
</nav>
</div>
<!-- 右侧内容区 -->
<div class="content">
<!-- 顶部搜索栏 -->
<div class="search-bar">
<div class="search-input">
<i class="icon">🔍</i>
<input type="text" placeholder="搜索文件">
</div>
<div class="view-options">
<button class="btn-view active">
<i class="icon">📊</i>
</button>
<button class="btn-view">
<i class="icon">📋</i>
</button>
</div>
</div>
<!-- 路由出口 -->
<router-view></router-view>
</div>
</div>
</template>
<script setup lang="ts">
import { useRouter } from 'vue-router'
import nodesData from '@/assets/data/nodes.json'
import businessProcess from '@/assets/examples/business-process.svg'
import mindMap from '@/assets/examples/mind-map.svg'
import umlDiagram from '@/assets/examples/uml.svg'
import orgChart from '@/assets/examples/org-chart.svg'
import defaultAvatar from '@/assets/avatars/default-avatar.svg'
const router = useRouter()
//
const previewMap = {
'business-process': businessProcess,
'mind-map': mindMap,
'uml': umlDiagram,
'org-chart': orgChart,
'default-avatar': defaultAvatar
}
//
const processedData = {
files: nodesData.files.map(file => ({
...file,
preview: previewMap[file.preview as keyof typeof previewMap]
})),
templates: nodesData.templates.map(template => ({
...template,
preview: previewMap[template.preview as keyof typeof previewMap]
})),
communityItems: nodesData.communityItems.map(item => ({
...item,
preview: previewMap[item.preview as keyof typeof previewMap],
authorAvatar: previewMap[item.authorAvatar as keyof typeof previewMap]
})),
deletedFiles: nodesData.deletedFiles.map(file => ({
...file,
preview: previewMap[file.preview as keyof typeof previewMap]
}))
}
// 使
defineExpose(processedData)
const handleCreate = () => {
router.push('/flow-edit')
}
const openTemplateDialog = () => {
router.push('/flow-edit')
}
</script>
<style scoped>
.file-manager {
display: flex;
min-height: 100vh;
width: 100vw;
background: #fff;
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
overflow: hidden;
}
.sidebar {
width: 240px;
min-width: 240px;
background: #fff;
border-right: 1px solid #e4e7ed;
padding: 20px;
display: flex;
flex-direction: column;
height: 100%;
overflow-y: auto;
}
.logo {
display: flex;
align-items: center;
gap: 8px;
padding: 0 12px;
margin-bottom: 24px;
}
.logo img {
height: 32px;
}
.logo span {
font-size: 18px;
font-weight: 600;
color: #1a1a1a;
}
.action-buttons {
display: flex;
flex-direction: column;
gap: 8px;
margin-bottom: 24px;
}
.btn-new, .btn-template {
display: flex;
align-items: center;
gap: 8px;
padding: 12px;
border: none;
border-radius: 6px;
font-size: 14px;
cursor: pointer;
transition: all 0.2s;
}
.btn-new {
background: #409eff;
color: #fff;
}
.btn-new:hover {
background: #66b1ff;
}
.btn-template {
background: #f5f7fa;
color: #606266;
}
.btn-template:hover {
background: #e4e7ed;
}
.menu-list {
display: flex;
flex-direction: column;
gap: 4px;
}
.menu-item {
display: flex;
align-items: center;
gap: 8px;
padding: 12px;
border-radius: 6px;
color: #606266;
text-decoration: none;
transition: all 0.2s;
}
.menu-item:hover {
background: #f5f7fa;
}
.menu-item.active {
background: #ecf5ff;
color: #409eff;
}
.menu-divider {
height: 1px;
background: #e4e7ed;
margin: 12px 0;
}
.content {
flex: 1;
padding: 20px;
background: #f5f7fa;
overflow-y: auto;
height: 100%;
width: calc(100% - 240px);
}
.search-bar {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 24px;
}
.search-input {
display: flex;
align-items: center;
gap: 8px;
background: #fff;
padding: 8px 16px;
border-radius: 6px;
width: 300px;
}
.search-input input {
border: none;
outline: none;
font-size: 14px;
width: 100%;
}
.view-options {
display: flex;
gap: 8px;
}
.btn-view {
padding: 8px;
border: none;
border-radius: 4px;
background: #fff;
cursor: pointer;
transition: all 0.2s;
}
.btn-view:hover, .btn-view.active {
background: #ecf5ff;
color: #409eff;
}
.file-list {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 20px;
}
.file-card {
background: #fff;
border-radius: 8px;
overflow: hidden;
transition: transform 0.2s;
}
.file-card:hover {
transform: translateY(-2px);
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
}
.file-preview {
aspect-ratio: 16/9;
background: #f5f7fa;
}
.file-preview img {
width: 100%;
height: 100%;
object-fit: cover;
}
.file-info {
padding: 12px;
}
.file-info h3 {
font-size: 14px;
font-weight: 500;
color: #1a1a1a;
margin-bottom: 4px;
}
.file-info p {
font-size: 12px;
color: #909399;
}
.back-btn {
display: flex;
align-items: center;
justify-content: center;
width: 24px;
height: 24px;
border-radius: 4px;
color: #606266;
text-decoration: none;
transition: all 0.2s;
}
.back-btn:hover {
background: #f5f7fa;
color: #409eff;
}
@media (max-width: 768px) {
.sidebar {
width: 200px;
min-width: 200px;
}
.content {
width: calc(100% - 200px);
}
.search-input {
width: 200px;
}
}
</style>

View File

@ -0,0 +1,27 @@
<template>
<SplitPane>
<template #toolbar>
<FlowToolbar></FlowToolbar>
</template>
<template #left>
<FlowAccordion />
</template>
<template #center>
<FlowCanvas />
</template>
<template #right>
<Accordion :items="accordionData" />
</template>
</SplitPane>
</template>
<script setup lang="ts">
import { ref, provide } from 'vue'
import { Graph } from '@maxgraph/core'
import { SplitPane, Accordion, FlowAccordion, FlowCanvas, FlowToolbar } from '../components/wu-flow-lib'
import { accordionData } from '@/assets/data/accordion-data'
const graph = ref<Graph>()
provide('graph', graph)
</script>

View File

@ -0,0 +1,67 @@
<template>
<div class="flow-editor">
<!-- 顶部区域菜单和工具栏 -->
<FlowHeader />
<!-- 主内容区域左中右布局 -->
<div class="flow-content">
<!-- 左侧节点库和风格库 -->
<div class="left-panel">
<ShapeLibrary />
<StyleLibrary />
</div>
<!-- 中间画布 -->
<div class="center-panel">
<FlowCanvas />
</div>
<!-- 右侧属性面板 -->
<div class="right-panel">
<PropertyPanel />
</div>
</div>
</div>
</template>
<script setup lang="ts">
import FlowHeader from '../components/wu-flow-lib/components/FlowHeader.vue'
import FlowCanvas from '../components/wu-flow-lib/components/FlowCanvas.vue'
import ShapeLibrary from '../components/wu-flow-lib/components/ShapeLibrary.vue'
import StyleLibrary from '../components/wu-flow-lib/components/StyleLibrary.vue'
import PropertyPanel from '../components/wu-flow-lib/components/PropertyPanel.vue'
</script>
<style scoped>
.flow-editor {
height: 100vh;
display: flex;
flex-direction: column;
}
.flow-content {
flex: 1;
display: flex;
overflow: hidden;
}
.left-panel {
width: 280px;
display: flex;
flex-direction: column;
border-right: 1px solid #dcdfe6;
background: #fff;
}
.center-panel {
flex: 1;
position: relative;
background: #f5f7fa;
}
.right-panel {
width: 300px;
border-left: 1px solid #dcdfe6;
background: #fff;
}
</style>

129
src/views/flow/Home.vue Normal file
View File

@ -0,0 +1,129 @@
<template>
<div class="home">
<header class="header">
<nav class="nav">
<div class="logo">
<img src="@/assets/logo.svg" alt="Flow Design">
</div>
<div class="nav-links">
<router-link to="/home">首页</router-link>
<router-link to="/home/features">功能</router-link>
<router-link to="/home/pricing">价格</router-link>
<router-link to="/home/contact">联系我们</router-link>
</div>
<div class="auth-buttons">
<router-link to="/login" class="login-btn">登录</router-link>
<router-link to="/register" class="register-btn">注册</router-link>
<router-link to="/files" class="register-btn">设计</router-link>
</div>
</nav>
</header>
<router-view></router-view>
</div>
</template>
<script setup lang="ts">
import { onMounted } from 'vue'
import { useRouter } from 'vue-router'
const router = useRouter()
onMounted(() => {
localStorage.setItem('token', '123456')
const token = localStorage.getItem('token')
if (token) {
router.push('/files')
}
console.log('onMounted')
})
</script>
<style scoped>
.header {
position: fixed;
top: 0;
left: 0;
right: 0;
background: white;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
z-index: 100;
}
.nav {
max-width: 1200px;
margin: 0 auto;
padding: 1rem;
display: flex;
align-items: center;
justify-content: space-between;
}
.logo {
display: flex;
align-items: center;
gap: 8px;
}
.logo img {
height: 32px;
}
.logo span {
font-size: 20px;
font-weight: 600;
color: #1a1a1a;
}
.nav-links {
display: flex;
gap: 2rem;
}
.nav-links a {
color: #606266;
text-decoration: none;
font-size: 1rem;
transition: color 0.3s;
}
.nav-links a:hover {
color: #409eff;
}
.auth-buttons {
display: flex;
gap: 1rem;
}
.login-btn, .register-btn {
padding: 0.5rem 1.5rem;
border-radius: 4px;
text-decoration: none;
font-size: 0.9rem;
transition: all 0.3s;
}
.login-btn {
color: #409eff;
border: 1px solid #409eff;
}
.login-btn:hover {
background: #ecf5ff;
}
.register-btn {
background: #409eff;
color: white;
border: 1px solid #409eff;
}
.register-btn:hover {
background: #66b1ff;
border-color: #66b1ff;
}
/* 为了防止内容被固定头部遮挡 */
.home {
padding-top: 72px;
}
</style>

View File

@ -0,0 +1,276 @@
<template>
<div class="home-default">
<!-- Hero Section -->
<main class="hero">
<div class="hero-overlay">
<img src="@/assets/examples/hero-bg.svg" class="hero-bg" alt="background" />
</div>
<div class="hero-content">
<h1>专业的流程图设计工具</h1>
<p>简单易用的在线流程图绘制工具帮助您快速创建流程图思维导图UML图等</p>
<router-link to="/files" class="btn btn-large">立即开始</router-link>
</div>
</main>
<!-- Examples Section -->
<section class="examples">
<div class="examples-content">
<h2>流程图案例展示</h2>
<p class="section-desc">适用于各种场景的流程图模板帮助您快速开始</p>
<div class="example-grid">
<div class="example-card">
<div class="example-image">
<img src="@/assets/examples/business-process.svg" alt="业务流程图">
<div class="example-overlay">
<router-link to="/flow-edit" class="btn-try">立即尝试</router-link>
</div>
</div>
<h3>业务流程图</h3>
<p>清晰展示业务流程优化工作效率</p>
</div>
<div class="example-card">
<div class="example-image">
<img src="@/assets/examples/mind-map.svg" alt="思维导图">
<div class="example-overlay">
<router-link to="/flow-edit" class="btn-try">立即尝试</router-link>
</div>
</div>
<h3>思维导图</h3>
<p>梳理思路结构激发创意灵感</p>
</div>
<div class="example-card">
<div class="example-image">
<img src="@/assets/examples/uml.svg" alt="UML图">
<div class="example-overlay">
<router-link to="/flow-edit" class="btn-try">立即尝试</router-link>
</div>
</div>
<h3>UML类图</h3>
<p>规范软件设计提升开发效率</p>
</div>
<div class="example-card">
<div class="example-image">
<img src="@/assets/examples/org-chart.svg" alt="组织架构图">
<div class="example-overlay">
<router-link to="/flow-edit" class="btn-try">立即尝试</router-link>
</div>
</div>
<h3>组织架构图</h3>
<p>直观展示组织结构明确职责分工</p>
</div>
</div>
</div>
</section>
</div>
</template>
<style scoped>
.home-default {
display: flex;
flex-direction: column;
background: #fff;
width: 100%;
}
.hero {
margin-top: 64px;
min-height: 600px;
width: 100%;
position: relative;
display: flex;
align-items: center;
justify-content: center;
/* 移除背景图片相关样式 */
}
.hero-overlay {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
width: 100%;
}
.hero-content {
max-width: 800px;
margin: 0 auto;
text-align: center;
padding: 0 24px;
position: relative;
z-index: 1;
}
.hero h1 {
font-size: 56px;
font-weight: 700;
color: #ffffff;
margin-bottom: 24px;
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.hero p {
font-size: 22px;
color: rgba(255, 255, 255, 0.9);
margin-bottom: 40px;
line-height: 1.6;
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
}
.btn-large {
padding: 16px 48px;
font-size: 18px;
background: #409eff;
color: #fff;
border: 2px solid transparent;
}
.btn-large:hover {
background: transparent;
border-color: #fff;
transform: translateY(-2px);
}
.examples {
padding: 80px 24px;
background: #fff;
width: 100%;
}
.examples-content {
max-width: 1200px;
margin: 0 auto;
width: 100%;
}
.examples h2 {
font-size: 36px;
font-weight: 600;
color: #1a1a1a;
text-align: center;
margin-bottom: 16px;
}
.section-desc {
text-align: center;
color: #666;
font-size: 18px;
margin-bottom: 48px;
}
.example-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: 32px;
}
.example-card {
background: #fff;
border-radius: 8px;
overflow: hidden;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
transition: transform 0.3s;
}
.example-card:hover {
transform: translateY(-4px);
}
.example-image {
position: relative;
aspect-ratio: 16/9;
overflow: hidden;
}
.example-image img {
width: 100%;
height: 100%;
object-fit: cover;
transition: transform 0.3s;
}
.example-overlay {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
opacity: 0;
transition: opacity 0.3s;
}
.example-card:hover .example-overlay {
opacity: 1;
}
.example-card:hover .example-image img {
transform: scale(1.05);
}
.btn-try {
padding: 8px 24px;
background: #409eff;
color: #fff;
border-radius: 4px;
text-decoration: none;
font-weight: 500;
transition: background 0.3s;
}
.btn-try:hover {
background: #66b1ff;
}
.example-card h3 {
font-size: 20px;
font-weight: 600;
color: #1a1a1a;
margin: 16px;
}
.example-card p {
font-size: 14px;
color: #666;
margin: 0 16px 16px;
line-height: 1.6;
}
@media (max-width: 768px) {
.example-grid {
grid-template-columns: 1fr;
}
.examples {
padding: 40px 16px;
}
.hero {
min-height: 500px;
}
.hero h1 {
font-size: 40px;
}
.hero p {
font-size: 18px;
}
}
.hero-bg {
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
object-fit: cover;
}
</style>

429
src/views/flow/Login.vue Normal file
View File

@ -0,0 +1,429 @@
<template>
<div class="login-container">
<!-- 左侧广告区域 -->
<div class="login-banner">
<div class="banner-content">
<h1>Flow Design</h1>
<p class="slogan">可视化流程设计工具</p>
<div class="features">
<div class="feature-item">
<i class="feature-icon">🎯</i>
<div class="feature-text">
<h3>简单易用</h3>
<p>拖拽式设计快速上手</p>
</div>
</div>
<div class="feature-item">
<i class="feature-icon">🔄</i>
<div class="feature-text">
<h3>实时协作</h3>
<p>多人在线即时同步</p>
</div>
</div>
<div class="feature-item">
<i class="feature-icon">📊</i>
<div class="feature-text">
<h3>专业可靠</h3>
<p>企业级流程解决方案</p>
</div>
</div>
</div>
</div>
<img src="@/assets/images/flow-banner.svg" alt="Flow Design Banner" class="banner-image">
</div>
<!-- 右侧登录区域 -->
<div class="login-content">
<div class="login-box">
<div class="login-header">
<img src="@/assets/logo.svg" alt="Flow Design" class="logo">
<h2>欢迎使用 Flow Design</h2>
<p class="subtitle">打造专业的流程设计工具</p>
</div>
<el-form
ref="formRef"
:model="form"
:rules="rules"
class="login-form"
>
<el-form-item prop="username">
<el-input
v-model="form.username"
placeholder="用户名/邮箱"
:prefix-icon="User"
/>
</el-form-item>
<el-form-item prop="password">
<el-input
v-model="form.password"
type="password"
placeholder="密码"
:prefix-icon="Lock"
show-password
/>
</el-form-item>
<el-form-item>
<div class="form-options">
<el-checkbox v-model="form.rememberMe"></el-checkbox>
<el-link type="primary" href="#" class="forgot-password">忘记密码</el-link>
</div>
</el-form-item>
<el-form-item>
<el-button
type="primary"
class="login-button"
:loading="loading"
@click="submitForm(formRef)"
>
</el-button>
</el-form-item>
<div class="form-footer">
<span>还没有账号</span>
<router-link to="/register" class="register-link">立即注册</router-link>
</div>
</el-form>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, reactive } from 'vue'
import { useRouter } from 'vue-router'
import { ElMessage } from 'element-plus'
import type { FormInstance, FormRules } from 'element-plus'
import { User, Lock } from '@element-plus/icons-vue'
import { login } from '@/api/auth' // login
const router = useRouter()
const formRef = ref<FormInstance>()
const loading = ref(false)
const form = reactive({
username: '',
password: '',
rememberMe: false
})
const rules = reactive<FormRules>({
username: [
{ required: true, message: '请输入用户名', trigger: 'blur' },
{ min: 3, max: 20, message: '长度在 3 到 20 个字符', trigger: 'blur' }
],
password: [
{ required: true, message: '请输入密码', trigger: 'blur' },
{ min: 6, message: '密码长度不能小于6位', trigger: 'blur' }
]
})
const submitForm = (formEl: FormInstance | undefined): Promise<void> => {
if (!formEl) return Promise.reject('表单不存在')
return new Promise((resolve, reject) => {
formEl.validate((valid) => {
if (!valid) {
reject('表单验证失败')
return
}
loading.value = true
login({
username: form.username,
password: form.password,
rememberMe: form.rememberMe
})
.then(res => {
localStorage.setItem('token', res.data.token)
ElMessage({
message: '登录成功!',
type: 'success',
duration: 1500
})
router.push('/files')
resolve()
})
.catch(error => {
console.error('登录失败:', error)
reject(error)
})
.finally(() => {
loading.value = false
})
})
})
}
</script>
<style scoped>
.login-container {
min-height: 100vh;
min-width: 100vw;
display: flex;
background: #fff;
overflow: hidden;
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
.login-banner {
width: 30%;
background: linear-gradient(135deg, #409eff 0%, #66b1ff 100%);
padding: 40px;
position: relative;
color: white;
display: flex;
flex-direction: column;
justify-content: center;
overflow: hidden;
min-height: 100vh;
box-sizing: border-box;
}
.banner-content {
position: relative;
z-index: 1;
max-width: 460px;
margin: auto 0;
display: flex;
flex-direction: column;
gap: 20px;
}
.banner-content h1 {
font-size: 32px;
margin: 0;
}
.slogan {
font-size: 18px;
margin: 0 0 20px 0;
opacity: 0.9;
}
.features {
display: flex;
flex-direction: column;
gap: 24px;
margin-top: 20px;
}
.feature-item {
display: flex;
align-items: center;
gap: 12px;
}
.feature-icon {
font-size: 20px;
background: rgba(255, 255, 255, 0.2);
padding: 10px;
border-radius: 10px;
}
.feature-text h3 {
font-size: 16px;
margin-bottom: 2px;
}
.feature-text p {
opacity: 0.8;
font-size: 13px;
}
.banner-image {
position: absolute;
bottom: -10%;
right: -10%;
width: 120%;
opacity: 0.1;
pointer-events: none;
}
.login-content {
flex: 1;
display: flex;
justify-content: center;
align-items: center;
background: linear-gradient(135deg, #f5f7fa 0%, #e4e7ed 100%);
padding: 20px;
}
.login-box {
width: 400px;
max-width: 90%;
background: white;
border-radius: 8px;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
padding: 40px;
}
.login-header {
text-align: center;
margin-bottom: 40px;
}
.login-header .logo {
height: 48px;
margin-bottom: 16px;
}
.login-header h2 {
font-size: 24px;
color: #1a1a1a;
margin-bottom: 8px;
}
.login-header .subtitle {
font-size: 14px;
color: #909399;
}
.form-item {
position: relative;
margin-bottom: 24px;
}
.input-prefix {
position: absolute;
left: 12px;
top: 50%;
transform: translateY(-50%);
color: #909399;
}
.icon-user::before {
content: "👤";
}
.icon-lock::before {
content: "🔒";
}
input {
width: 100%;
padding: 12px 12px 12px 36px;
border: 1px solid #dcdfe6;
border-radius: 4px;
font-size: 14px;
transition: all 0.3s;
}
input:focus {
border-color: #409eff;
outline: none;
box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.2);
}
.form-options {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 24px;
}
.remember-me {
display: flex;
align-items: center;
gap: 8px;
color: #606266;
font-size: 14px;
cursor: pointer;
}
.forgot-password {
font-size: 14px;
color: #409eff;
text-decoration: none;
}
.login-button {
width: 100%;
padding: 12px;
background: #409eff;
color: white;
border: none;
border-radius: 4px;
font-size: 16px;
cursor: pointer;
transition: background-color 0.3s;
}
.login-button:hover {
background: #66b1ff;
}
.form-footer {
margin-top: 24px;
text-align: center;
font-size: 14px;
color: #606266;
}
.register-link {
color: #409eff;
text-decoration: none;
margin-left: 4px;
}
.register-link:hover {
color: #66b1ff;
}
@media screen and (max-width: 768px) {
.login-container {
flex-direction: column;
}
.login-banner {
width: 100%;
height: 30vh;
padding: 20px;
}
.banner-content {
text-align: center;
}
.features {
display: none;
}
.login-content {
height: 70vh;
}
.login-box {
padding: 20px;
}
}
.login-form {
width: 100%;
}
.form-options {
display: flex;
justify-content: space-between;
align-items: center;
}
.login-button {
width: 100%;
}
:deep(.el-input__wrapper) {
padding-left: 11px;
}
:deep(.el-input__prefix) {
font-size: 16px;
}
</style>

View File

@ -0,0 +1,16 @@
<template>
<div>
<h2>价格</h2>
<p>这里是价格页面的内容</p>
</div>
</template>
<script>
export default {
name: 'Pricing'
}
</script>
<style scoped>
/* 添加页面特定的样式 */
</style>

492
src/views/flow/Register.vue Normal file
View File

@ -0,0 +1,492 @@
<template>
<div class="register-container">
<!-- 左侧广告区域 -->
<div class="register-banner">
<div class="banner-content">
<h1>Flow Design</h1>
<p class="slogan">可视化流程设计工具</p>
<div class="features">
<div class="feature-item">
<i class="feature-icon">🎯</i>
<div class="feature-text">
<h3>简单易用</h3>
<p>拖拽式设计快速上手</p>
</div>
</div>
<div class="feature-item">
<i class="feature-icon">🔄</i>
<div class="feature-text">
<h3>实时协作</h3>
<p>多人在线即时同步</p>
</div>
</div>
<div class="feature-item">
<i class="feature-icon">📊</i>
<div class="feature-text">
<h3>专业可靠</h3>
<p>企业级流程解决方案</p>
</div>
</div>
</div>
</div>
<img src="@/assets/images/flow-banner.svg" alt="Flow Design Banner" class="banner-image">
</div>
<!-- 右侧注册区域 -->
<div class="register-content">
<div class="register-box">
<div class="register-header">
<img src="@/assets/logo.svg" alt="Flow Design" class="logo">
<h2>欢迎加入 Flow Design</h2>
<p class="subtitle">开启您的流程设计之旅</p>
</div>
<el-form
ref="formRef"
:model="form"
:rules="rules"
class="register-form"
>
<el-form-item prop="username">
<el-input
v-model="form.username"
placeholder="用户名"
:prefix-icon="User"
/>
</el-form-item>
<el-form-item prop="email">
<el-input
v-model="form.email"
placeholder="邮箱"
:prefix-icon="Message"
/>
</el-form-item>
<el-form-item prop="password">
<el-input
v-model="form.password"
type="password"
placeholder="密码"
:prefix-icon="Lock"
show-password
/>
</el-form-item>
<el-form-item prop="confirmPassword">
<el-input
v-model="form.confirmPassword"
type="password"
placeholder="确认密码"
:prefix-icon="Lock"
show-password
/>
</el-form-item>
<el-form-item prop="agreeTerms">
<el-checkbox v-model="form.agreeTerms">
我已阅读并同意<el-link type="primary" href="#" class="terms-link">服务条款</el-link>
</el-checkbox>
</el-form-item>
<el-form-item>
<el-button
type="primary"
class="register-button"
:loading="loading"
:disabled="!form.agreeTerms"
@click="submitForm(formRef)"
>
</el-button>
</el-form-item>
<div class="form-footer">
<span>已有账号</span>
<router-link to="/login" class="login-link">立即登录</router-link>
</div>
</el-form>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, reactive } from 'vue'
import { useRouter } from 'vue-router'
import { ElMessage } from 'element-plus'
import 'element-plus/es/components/message/style/css'
import type { FormInstance, FormRules } from 'element-plus'
import { User, Message, Lock } from '@element-plus/icons-vue'
import { register } from '@/api/auth'
const router = useRouter()
const formRef = ref<FormInstance>()
const loading = ref(false)
const form = reactive({
username: '',
email: '',
password: '',
confirmPassword: '',
agreeTerms: false
})
//
const validatePass2 = (rule: any, value: any, callback: any) => {
if (value === '') {
callback(new Error('请再次输入密码'))
} else if (value !== form.password) {
callback(new Error('两次输入密码不一致!'))
} else {
callback()
}
}
const rules = reactive<FormRules>({
username: [
{ required: true, message: '请输入用户名', trigger: 'blur' },
{ min: 3, max: 20, message: '长度在 3 到 20 个字符', trigger: 'blur' }
],
email: [
{ required: true, message: '请输入邮箱地址', trigger: 'blur' },
{ type: 'email', message: '请输入正确的邮箱地址', trigger: 'blur' }
],
password: [
{ required: true, message: '请输入密码', trigger: 'blur' },
{ min: 6, message: '密码长度不能小于6位', trigger: 'blur' }
],
confirmPassword: [
{ required: true, message: '请再次输入密码', trigger: 'blur' },
{ validator: validatePass2, trigger: 'blur' }
],
agreeTerms: [
{
validator: (rule: any, value: any, callback: any) => {
if (!value) {
callback(new Error('请阅读并同意服务条款'))
} else {
callback()
}
},
trigger: 'change'
}
]
})
const submitForm = (formEl: FormInstance | undefined): Promise<void> => {
if (!formEl) return Promise.reject('表单不存在')
return new Promise((resolve, reject) => {
formEl.validate((valid) => {
if (!valid) {
reject('表单验证失败')
return
}
loading.value = true
register({
username: form.username,
email: form.email,
password: form.password
})
.then(() => {
ElMessage({
message: '注册成功!正在跳转到登录页面...',
type: 'success',
duration: 1500
})
setTimeout(() => {
router.push('/login')
}, 1500)
resolve()
})
.catch(error => {
console.error('注册失败:', error)
reject(error)
})
.finally(() => {
loading.value = false
})
})
})
}
</script>
<style scoped>
/* 复用登录页面的基础样式,只修改类名 */
.register-container {
min-height: 100vh;
min-width: 100vw;
display: flex;
background: #fff;
overflow: hidden;
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
.register-banner {
width: 30%;
background: linear-gradient(135deg, #409eff 0%, #66b1ff 100%);
padding: 40px;
position: relative;
color: white;
display: flex;
flex-direction: column;
justify-content: center;
overflow: hidden;
min-height: 100vh;
box-sizing: border-box;
}
.banner-content {
position: relative;
z-index: 1;
max-width: 460px;
margin: auto 0;
display: flex;
flex-direction: column;
gap: 20px;
}
.banner-content h1 {
font-size: 32px;
margin: 0;
}
.slogan {
font-size: 18px;
margin: 0 0 20px 0;
opacity: 0.9;
}
.features {
display: flex;
flex-direction: column;
gap: 24px;
margin-top: 20px;
}
.feature-item {
display: flex;
align-items: center;
gap: 12px;
}
.feature-icon {
font-size: 20px;
background: rgba(255, 255, 255, 0.2);
padding: 10px;
border-radius: 10px;
}
.feature-text h3 {
font-size: 16px;
margin-bottom: 2px;
}
.feature-text p {
opacity: 0.8;
font-size: 13px;
}
.banner-image {
position: absolute;
bottom: -10%;
right: -10%;
width: 120%;
opacity: 0.1;
pointer-events: none;
}
/* 其他样式保持与登录页一致,只需将 login 替换为 register */
.register-content {
flex: 1;
display: flex;
justify-content: center;
align-items: center;
background: linear-gradient(135deg, #f5f7fa 0%, #e4e7ed 100%);
padding: 20px;
}
.register-box {
width: 400px;
max-width: 90%;
background: white;
border-radius: 8px;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
padding: 40px;
}
/* 复用其他样式并添加邮箱图标 */
.icon-email::before {
content: "📧";
}
/* 服务条款样式 */
.agree-terms {
display: flex;
align-items: center;
gap: 8px;
color: #606266;
font-size: 14px;
}
.terms-link {
color: #409eff;
text-decoration: none;
}
.terms-link:hover {
color: #66b1ff;
}
/* 禁用状态的注册按钮 */
.register-button:disabled {
background: #a0cfff;
cursor: not-allowed;
}
/* 响应式设计 */
@media screen and (max-width: 768px) {
.register-container {
flex-direction: column;
}
.register-banner {
width: 100%;
height: 30vh;
padding: 20px;
}
.banner-content {
text-align: center;
}
.features {
display: none;
}
.register-content {
height: 70vh;
}
.register-box {
padding: 20px;
}
}
/* 添加缺失的输入框样式 */
.form-item {
position: relative;
margin-bottom: 24px;
}
.input-prefix {
position: absolute;
left: 12px;
top: 50%;
transform: translateY(-50%);
color: #909399;
}
.icon-user::before {
content: "👤";
}
.icon-lock::before {
content: "🔒";
}
input {
width: 100%;
padding: 12px 12px 12px 36px;
border: 1px solid #dcdfe6;
border-radius: 4px;
font-size: 14px;
transition: all 0.3s;
}
input:focus {
border-color: #409eff;
outline: none;
box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.2);
}
/* 注册按钮样式 */
.register-button {
width: 100%;
padding: 12px;
background: #409eff;
color: white;
border: none;
border-radius: 4px;
font-size: 16px;
cursor: pointer;
transition: background-color 0.3s;
}
.register-button:hover:not(:disabled) {
background: #66b1ff;
}
/* 注册框头部样式 */
.register-header {
text-align: center;
margin-bottom: 40px;
}
.register-header .logo {
height: 48px;
margin-bottom: 16px;
}
.register-header h2 {
font-size: 24px;
color: #1a1a1a;
margin-bottom: 8px;
}
.register-header .subtitle {
font-size: 14px;
color: #909399;
}
.form-footer {
margin-top: 24px;
text-align: center;
font-size: 14px;
color: #606266;
}
.login-link {
color: #409eff;
text-decoration: none;
margin-left: 4px;
}
.login-link:hover {
color: #66b1ff;
}
/* 修改表单相关样式 */
.register-form {
width: 100%;
}
.register-button {
width: 100%;
}
:deep(.el-input__wrapper) {
padding-left: 11px;
}
:deep(.el-input__prefix) {
font-size: 16px;
}
/* 其他样式保持不变... */
</style>

View File

@ -0,0 +1,99 @@
<template>
<div class="community">
<div class="community-header">
<h2>社区作品</h2>
<div class="filter-options">
<select v-model="currentCategory">
<option value="all">全部分类</option>
<option value="business">商业图表</option>
<option value="tech">技术架构</option>
<option value="edu">教育培训</option>
</select>
</div>
</div>
<div class="community-grid">
<div class="community-card" v-for="item in communityItems" :key="item.id">
<div class="work-preview">
<img :src="item.preview" :alt="item.title">
</div>
<div class="work-info">
<h3>{{ item.title }}</h3>
<div class="author-info">
<img :src="item.authorAvatar" :alt="item.author" class="avatar">
<span>{{ item.author }}</span>
</div>
<div class="work-stats">
<span>👍 {{ item.likes }}</span>
<span>💬 {{ item.comments }}</span>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import businessProcess from '@/assets/examples/business-process.svg'
import defaultAvatar from '@/assets/avatars/default-avatar.svg'
const currentCategory = ref('all')
const communityItems = [
{
id: 1,
title: '电商系统架构图',
preview: businessProcess,
author: '张三',
authorAvatar: defaultAvatar,
likes: 128,
comments: 32
}
]
</script>
<style scoped>
@import '@/assets/styles/file-list.css';
.community-header {
padding: 0 20px;
margin-bottom: 24px;
}
.community-grid {
display: flex;
flex-wrap: wrap;
gap: 24px;
padding: 0 20px;
}
.community-card {
width: 280px;
background: #fff;
border-radius: 8px;
overflow: hidden;
transition: transform 0.2s;
}
.work-preview {
width: 100%;
height: 160px;
}
.work-preview img {
width: 100%;
height: 100%;
object-fit: cover;
}
.work-info {
padding: 12px;
}
.avatar {
width: 24px;
height: 24px;
border-radius: 50%;
}
</style>

View File

@ -0,0 +1,6 @@
<template>
<div class="favorites">
<h2>我的收藏</h2>
<!-- 内容 -->
</div>
</template>

View File

@ -0,0 +1,50 @@
<template>
<div class="my-files">
<div class="file-list">
<div class="file-card" v-for="file in files" :key="file.id">
<div class="file-preview">
<img :src="file.preview" :alt="file.name">
</div>
<div class="file-info">
<h3>{{ file.name }}</h3>
<p>最后修改{{ file.modifiedDate }}</p>
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import businessProcess from '@/assets/examples/business-process.svg'
import mindMap from '@/assets/examples/mind-map.svg'
import umlDiagram from '@/assets/examples/uml.svg'
const files = [
{
id: 1,
name: '业务流程图',
preview: businessProcess,
modifiedDate: '2024-01-20'
},
{
id: 2,
name: '思维导图',
preview: mindMap,
modifiedDate: '2024-01-18'
},
{
id: 3,
name: 'UML类图',
preview: umlDiagram,
modifiedDate: '2024-01-15'
}
]
</script>
<style scoped>
@import '@/assets/styles/file-list.css';
.my-files {
padding: 20px;
}
</style>

View File

@ -0,0 +1,45 @@
<template>
<div class="recent">
<div class="file-list">
<div class="file-card" v-for="file in files" :key="file.id">
<div class="file-preview">
<img :src="file.preview" :alt="file.name">
</div>
<div class="file-info">
<h3>{{ file.name }}</h3>
<p>访问时间{{ file.accessTime }}</p>
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import businessProcess from '@/assets/examples/business-process.svg'
import mindMap from '@/assets/examples/mind-map.svg'
const files = [
{
id: 1,
name: '项目流程图',
preview: businessProcess,
accessTime: '2024-03-15 14:30'
},
{
id: 2,
name: '系统架构图',
preview: mindMap,
accessTime: '2024-03-14 16:20'
}
]
</script>
<style scoped>
.file-list {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 20px;
}
/* 复用 FileManager.vue 中的卡片样式 */
</style>

View File

@ -0,0 +1,75 @@
<template>
<div class="recommended">
<div class="template-categories">
<h3>热门模板</h3>
<div class="template-grid">
<div class="template-card" v-for="template in templates" :key="template.id">
<div class="template-preview">
<img :src="template.preview" :alt="template.name">
</div>
<div class="template-info">
<h4>{{ template.name }}</h4>
<p>{{ template.description }}</p>
<button class="btn-use">使用模板</button>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import businessProcess from '@/assets/examples/business-process.svg'
import orgChart from '@/assets/examples/org-chart.svg'
const templates = [
{
id: 1,
name: '项目管理流程',
description: '适用于项目规划和任务管理',
preview: businessProcess
},
{
id: 2,
name: '组织架构图',
description: '快速创建公司组织结构图',
preview: orgChart
}
]
</script>
<style scoped>
.template-grid {
display: flex;
flex-wrap: wrap;
gap: 24px;
padding: 20px;
}
.template-card {
width: 280px;
background: #fff;
border-radius: 8px;
overflow: hidden;
transition: transform 0.2s;
}
.template-preview {
width: 100%;
height: 160px;
}
.template-preview img {
width: 100%;
height: 100%;
object-fit: cover;
}
.template-info {
padding: 12px;
}
.btn-use {
margin-top: 12px;
}
</style>

View File

@ -0,0 +1,64 @@
<template>
<div class="trash">
<div class="trash-header">
<h2>回收站</h2>
<button class="btn-clear" @click="clearTrash"></button>
</div>
<div class="file-list">
<div class="file-card" v-for="file in deletedFiles" :key="file.id">
<div class="file-preview">
<img :src="file.preview" :alt="file.name">
</div>
<div class="file-info">
<h3>{{ file.name }}</h3>
<p>删除时间{{ file.deleteTime }}</p>
<div class="file-actions">
<button class="btn-restore" @click="restoreFile(file.id)"></button>
<button class="btn-delete" @click="deleteFile(file.id)"></button>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import businessProcess from '@/assets/examples/business-process.svg'
const deletedFiles = [
{
id: 1,
name: '旧版流程图',
preview: businessProcess,
deleteTime: '2024-03-10'
}
]
const restoreFile = (id: number) => {
//
}
const deleteFile = (id: number) => {
//
}
const clearTrash = () => {
//
}
</script>
<style scoped>
@import '@/assets/styles/file-list.css';
.trash-header {
padding: 0 20px;
margin-bottom: 24px;
}
.file-actions {
margin-top: 12px;
display: flex;
gap: 8px;
}
</style>

View File

@ -0,0 +1,48 @@
/* 通用的文件列表横向布局样式 */
.file-list {
display: flex;
flex-wrap: wrap;
gap: 20px;
padding: 20px 0;
}
.file-card {
width: 280px;
background: #fff;
border-radius: 8px;
overflow: hidden;
transition: transform 0.2s;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
}
.file-card:hover {
transform: translateY(-2px);
}
.file-preview {
width: 100%;
height: 160px;
background: #f5f7fa;
}
.file-preview img {
width: 100%;
height: 100%;
object-fit: cover;
}
.file-info {
padding: 12px;
}
.file-info h3 {
font-size: 14px;
font-weight: 500;
color: #1a1a1a;
margin-bottom: 4px;
}
.file-info p {
font-size: 12px;
color: #909399;
}