Browse Source

创建应用界面

yjf_v3
DESKTOP-CUI7KST\HXGK 2 years ago
parent
commit
02529b62d4
  1. 5
      node_modules/.package-lock.json
  2. 5
      package-lock.json
  3. 8
      src/router/index.ts
  4. 11
      src/types/components.d.ts
  5. 594
      src/views/sysworkflow/codepage/createpage.vue
  6. 106
      src/views/sysworkflow/codepage/editpage.vue
  7. 400
      src/views/sysworkflow/codepage/page.vue
  8. 213
      src/views/sysworkflow/codepage/page0318.vue

5
node_modules/.package-lock.json

@ -9747,7 +9747,7 @@
},
"node_modules/unplugin-vue-components": {
"version": "0.24.1",
"resolved": "https://registry.npmmirror.com/unplugin-vue-components/-/unplugin-vue-components-0.24.1.tgz",
"resolved": "https://registry.npmjs.org/unplugin-vue-components/-/unplugin-vue-components-0.24.1.tgz",
"integrity": "sha512-T3A8HkZoIE1Cja95xNqolwza0yD5IVlgZZ1PVAGvVCx8xthmjsv38xWRCtHtwl+rvZyL9uif42SRkDGw9aCfMA==",
"dev": true,
"dependencies": {
@ -9765,6 +9765,9 @@
"engines": {
"node": ">=14"
},
"funding": {
"url": "https://github.com/sponsors/antfu"
},
"peerDependencies": {
"@babel/parser": "^7.15.8",
"@nuxt/kit": "^3.2.2",

5
package-lock.json

@ -10156,7 +10156,7 @@
},
"node_modules/unplugin-vue-components": {
"version": "0.24.1",
"resolved": "https://registry.npmmirror.com/unplugin-vue-components/-/unplugin-vue-components-0.24.1.tgz",
"resolved": "https://registry.npmjs.org/unplugin-vue-components/-/unplugin-vue-components-0.24.1.tgz",
"integrity": "sha512-T3A8HkZoIE1Cja95xNqolwza0yD5IVlgZZ1PVAGvVCx8xthmjsv38xWRCtHtwl+rvZyL9uif42SRkDGw9aCfMA==",
"dev": true,
"dependencies": {
@ -10174,6 +10174,9 @@
"engines": {
"node": ">=14"
},
"funding": {
"url": "https://github.com/sponsors/antfu"
},
"peerDependencies": {
"@babel/parser": "^7.15.8",
"@nuxt/kit": "^3.2.2",

8
src/router/index.ts

@ -1,5 +1,5 @@
import { createRouter, createWebHashHistory, RouteRecordRaw } from "vue-router";
import CreatePage from "@/views/sysworkflow/codepage/createpage.vue";
export const Layout = () => import("@/layout/index.vue");
// 静态路由
@ -13,6 +13,11 @@ export const constantRoutes: RouteRecordRaw[] = [
path: "/redirect/:path(.*)",
component: () => import("@/views/redirect/index.vue"),
},
{
path:"/create",
name: "create",
component: () => import('@/views/sysworkflow/codepage/createpage.vue'),
}
],
},
@ -51,6 +56,7 @@ export const constantRoutes: RouteRecordRaw[] = [
},
],
},
// 外部链接
/*{

11
src/types/components.d.ts

@ -26,6 +26,7 @@ declare module '@vue/runtime-core' {
ElBreadcrumb: typeof import('element-plus/es')['ElBreadcrumb']
ElBreadcrumbItem: typeof import('element-plus/es')['ElBreadcrumbItem']
ElButton: typeof import('element-plus/es')['ElButton']
ElButtonGroup: typeof import('element-plus/es')['ElButtonGroup']
ElCard: typeof import('element-plus/es')['ElCard']
ElCarousel: typeof import('element-plus/es')['ElCarousel']
ElCarouselItem: typeof import('element-plus/es')['ElCarouselItem']
@ -48,7 +49,10 @@ declare module '@vue/runtime-core' {
ElDropdownMenu: typeof import('element-plus/es')['ElDropdownMenu']
ElForm: typeof import('element-plus/es')['ElForm']
ElFormItem: typeof import('element-plus/es')['ElFormItem']
ElHeader: typeof import('element-plus/es')['ElHeader']
ElIcon: typeof import('element-plus/es')['ElIcon']
ElIconExpand: typeof import('@element-plus/icons-vue')['Expand']
ElIconFold: typeof import('@element-plus/icons-vue')['Fold']
ElImage: typeof import('element-plus/es')['ElImage']
ElImageViewer: typeof import('element-plus/es')['ElImageViewer']
ElInput: typeof import('element-plus/es')['ElInput']
@ -57,9 +61,11 @@ declare module '@vue/runtime-core' {
ElMain: typeof import('element-plus/es')['ElMain']
ElMenu: typeof import('element-plus/es')['ElMenu']
ElMenuItem: typeof import('element-plus/es')['ElMenuItem']
ElMenuItemGroup: typeof import('element-plus/es')['ElMenuItemGroup']
ElOption: typeof import('element-plus/es')['ElOption']
ElPagination: typeof import('element-plus/es')['ElPagination']
ElPopover: typeof import('element-plus/es')['ElPopover']
ElProgress: typeof import('element-plus/es')['ElProgress']
ElRadio: typeof import('element-plus/es')['ElRadio']
ElRadioButton: typeof import('element-plus/es')['ElRadioButton']
ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup']
@ -103,15 +109,20 @@ declare module '@vue/runtime-core' {
IEpCaretBottom: typeof import('~icons/ep/caret-bottom')['default']
IEpCaretTop: typeof import('~icons/ep/caret-top')['default']
IEpClose: typeof import('~icons/ep/close')['default']
IEpCollection: typeof import('~icons/ep/collection')['default']
IEpDelete: typeof import('~icons/ep/delete')['default']
IEpEdit: typeof import('~icons/ep/edit')['default']
IEpMessageBox: typeof import('~icons/ep/message-box')['default']
IEpMoreFilled: typeof import('~icons/ep/more-filled')['default']
IEpOperation: typeof import('~icons/ep/operation')['default']
IEpPicture: typeof import('~icons/ep/picture')['default']
IEpPlus: typeof import('~icons/ep/plus')['default']
IEpRefresh: typeof import('~icons/ep/refresh')['default']
IEpSearch: typeof import('~icons/ep/search')['default']
IEpSetting: typeof import('~icons/ep/setting')['default']
IEpStar: typeof import('~icons/ep/star')['default']
IEpUser: typeof import('~icons/ep/user')['default']
IEpView: typeof import('~icons/ep/view')['default']
LangSelect: typeof import('./../components/LangSelect/index.vue')['default']
LayoutPage: typeof import('./../components/DesignForm/layoutPage/index.vue')['default']
List: typeof import('./../components/DesignForm/public/form/components/list.vue')['default']

594
src/views/sysworkflow/codepage/createpage.vue

@ -0,0 +1,594 @@
<!--
@ 作者: 袁纪菲
@ 时间: 2024.4.16
@ 备注: 新建应用
-->
<script lang="ts" setup>
import { ref , onMounted } from 'vue';
import { ElMessage, ElMessageBox } from 'element-plus';
interface Menu {
id: number;
name: string;
type: string; //
parentId?: number | undefined; //ID
children?: Menu[]; //
expanded?: boolean;// true
order?: number; // order
}
// menu
function transformMenu(menuItems: Menu[], parentId?: number): Menu[] {
const transformedItems: Menu[] = [];
let order = 0; //
for (const item of menuItems) {
const newItem: Menu = { ...item, parentId, order: order++ }; //
if (item.type === 'group') {
const childItems = menuItems.filter((m) => m.parentId === item.id);
newItem.children = transformMenu(childItems, item.id);
}
transformedItems.push(newItem);
}
return transformedItems;
}
//
const initialMenu = [
{ id: 1, name: '分组A', type: 'group' , expanded: true },
{ id: 2, name: '表单1', type: 'form', parentId: 1 },
{ id: 3, name: '分组B', type: 'group', parentId: 1 , expanded: true },
{ id: 4, name: '表单2', type: 'form', parentId: 3 },
];
const menu = ref<Menu[]>(transformMenu(initialMenu)); // transformMenu menu
const appName = ref('');
const createFormVisible = ref(false);
const createGroupVisible = ref(false);
const moveToVisible = ref(false);
const renameVisible = ref(false);
const newFormName = ref('');
const newGroupName = ref('');
const renameValue = ref('');
//
let renameItem: Menu | null = null;
let originalName: string = '';
const selectedParentId = ref<number | undefined>(undefined);
// ID
const selectedGroupId = ref<number | null>(null);
//ID
const currentParentForNewForm = ref<number | undefined>(undefined);
const currentParentForNewGroup = ref<number | undefined>(undefined);
//
onMounted(() => {
appName.value = localStorage.getItem('appName') || '未命名应用';
});
//
const saveAppName = () => {
localStorage.setItem('appName', appName.value);
}
//
const openCreateFormDialog = () =>{
createFormVisible.value = true;
}
//
const saveNewForm = () =>{
if (!newFormName.value.trim()) {
ElMessage.error('表单名称不能为空');
return;
}
const newForm: Menu = {
id: menu.value.length + 1, // 使ID
name: newFormName.value,
type: 'form',
parentId: currentParentForNewForm.value, // ID
};
// children
if (currentParentForNewForm.value !== undefined) {
const parent = menu.value.find((item) => item.id === currentParentForNewForm.value);
if (parent && parent.children) {
parent.children.push(newForm);
}
} else {
// menu
menu.value.push(newForm);
}
ElMessage.success('新建成功');
createFormVisible.value = false;
newFormName.value = '';
}
//
const openCreateGroupDialog = () =>{
createGroupVisible.value = true;
}
//
const saveNewGroup = () =>{
if (!newGroupName.value.trim()) {
ElMessage.error('分组名称不能为空');
return;
}
const newGroup: Menu = {
id: menu.value.length + 1, // 使ID
name: newGroupName.value,
type: 'group',
parentId: currentParentForNewGroup.value, // ID
};
// children
if (currentParentForNewGroup.value !== undefined) {
const parent = menu.value.find((item) => item.id === currentParentForNewGroup.value);
if (parent && parent.children) {
parent.children.push(newGroup);
}
} else {
// menu
menu.value.push(newGroup);
}
createGroupVisible.value = false;
newGroupName.value = '';
}
//
const openCreateChildFormDialog = (parent: Menu) => {
// 便使
createFormVisible.value = true;
// ID
currentParentForNewForm.value = parent.id;
};
//
const openCreateChildGroupDialog = (parent: Menu) => {
// 便使
createGroupVisible.value = true;
// ID
currentParentForNewGroup.value = parent.id;
};
//
const confirmDelete = (item: Menu) => {
const isParentGroup = item.children && item.children.length > 0 && item.type === 'group' && item.parentId === selectedParentId.value; //
if (isParentGroup) {
//
ElMessageBox.confirm(
`确定要删除分组 "${item.name}" 及其所有子分组和表单吗?`,
'警告',
{
confirmButtonText: '确定删除',
cancelButtonText: '取消',
type: 'warning'
}
).then(() => {
deleteMenuRecursively(item); //
}).catch(() => {}); //
} else {
//
ElMessageBox.confirm(
`确定要删除 ${item.type === 'group' ? '分组' : '表单'} "${item.name}" 吗?`,
'警告',
{
confirmButtonText: '确定删除',
cancelButtonText: '取消',
type: 'warning'
}
).then(() => {
removeSingleItem(item); //
}).catch(() => {}); //
}
};
//
const deleteMenuRecursively = (parent: Menu) => {
//
for (const child of parent.children ?? []) {
if (child.children) {
deleteMenuRecursively(child); //
}
const childIndex = menu.value.findIndex((i) => i.id === child.id);
if (childIndex !== -1) {
menu.value.splice(childIndex, 1); //
}
}
const parentIndex = menu.value.findIndex((i) => i.id === parent.id);
if (parentIndex !== -1) {
menu.value.splice(parentIndex, 1); //
}
};
//
const removeSingleItem = (item: Menu) => {
const itemIndex = menu.value.findIndex((i) => i.id === item.id);
if (itemIndex !== -1) {
menu.value.splice(itemIndex, 1); //
}
// children
if (item.parentId !== undefined) {
const parent = menu.value.find((i) => i.id === item.parentId);
if (parent && parent.children) {
const childIndex = parent.children.findIndex((c) => c.id === item.id);
if (childIndex !== -1) {
parent.children.splice(childIndex, 1);
}
}
}
};
//
const showRenameDialog = (item: Menu) => {
renameItem = item;
originalName = item.name;
renameVisible.value = true;
// itemname
};
//
const handleRenameClose = () => {
renameItem = null;
originalName = '';
renameVisible.value = false;
};
//
const handleRenameCancel = () => {
handleRenameClose();
renameValue.value = originalName;
};
//
const handleRenameSave = () => {
if (!renameValue.value.trim()) {
ElMessage.error('名称不能为空');
return;
}
if (renameItem) {
renameItem.name = renameValue.value;
ElMessage.success('重命名成功');
}
handleRenameClose();
};
//
const showMoveToDialog = (item: Menu) => {
selectedGroupId.value = item.id;
moveToVisible.value = true;
};
//
const handleMoveToClose = () => {
selectedGroupId.value = null;
moveToVisible.value = false;
};
//
const handleMoveToCancel = () => {
handleMoveToClose();
};
//
const handleMoveToSave = () => {
if (!selectedParentId.value) {
ElMessage.error('请选择目标分组');
return;
}
const movingItemIndex = menu.value.findIndex((item) => item.id === selectedGroupId.value);
if (movingItemIndex === -1) {
ElMessage.error('移动失败,找不到待移动的项');
return;
}
const movingItem = menu.value[movingItemIndex];
const targetGroup = menu.value.find((item) => item.id === selectedParentId.value);
if (!targetGroup || targetGroup.type !== 'group') {
ElMessage.error('移动失败,找不到目标分组');
return;
}
// parentId
const movedItem = menu.value.splice(movingItemIndex, 1)[0];
movedItem.parentId = targetGroup.id;
// children
if (!targetGroup.children) {
targetGroup.children = [];
}
const insertIndex = targetGroup.children.findIndex((child) => child.id > movedItem.id);
targetGroup.children.splice(insertIndex >= 0 ? insertIndex : targetGroup.children.length, 0, movedItem);
ElMessage.success('移动成功');
handleMoveToClose();
};
// toggleExpand expanded
const toggleExpand = (group: Menu) => {
group.expanded = !group.expanded;
};
</script>
<template>
<div class="common-layout">
<!-- 顶部导航栏 -->
<el-header>
<div>
<el-input
v-model="appName"
class="header-input"
clearable
placeholder="未命名应用"
@clear="saveAppName"
@blur="saveAppName"
></el-input>
</div>
<div class="header-btn">
<el-button type="primary" >编辑表单</el-button>
</div>
</el-header>
<!-- 主体布局容器 -->
<el-container>
<!-- 左侧侧边栏 -->
<el-aside>
<div class="sidebar-upper">
<!-- 搜索框 -->
<el-input placeholder="搜索" class="search-input" />
<!-- 新增按钮 -->
<el-dropdown trigger="click">
<el-button type="primary" plain class="add-btn">
<el-icon><Plus /></el-icon>
</el-button>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item @click="() => openCreateFormDialog()"><el-icon><DocumentAdd /></el-icon></el-dropdown-item>
<el-dropdown-item @click="() => openCreateGroupDialog()"><el-icon><FolderAdd /></el-icon></el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
<!-- 侧边栏下半部区域 -->
<div class="sidebar-lower">
<ul class="sidebar-menu">
<template v-for="(item) in menu">
<template v-if="item.type === 'group'">
<!-- 分组 -->
<li :key="item.id">
<div class="menu-item" :class="{ 'is-collapsed': !item.expanded }" @click="toggleExpand(item)">
<span><el-icon><Folder /></el-icon>{{ item.name }}</span>
<el-dropdown trigger="click" placement="bottom-end">
<el-icon><MoreFilled /></el-icon>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item @click="openCreateChildFormDialog(item)"><el-icon><DocumentAdd /></el-icon></el-dropdown-item>
<el-dropdown-item @click="openCreateChildGroupDialog(item)"><el-icon><FolderAdd /></el-icon></el-dropdown-item>
<el-dropdown-item @click="confirmDelete(item)"><el-icon><Delete /></el-icon></el-dropdown-item>
<el-dropdown-item @click="showRenameDialog(item)"><el-icon><EditPen /></el-icon></el-dropdown-item>
<el-dropdown-item @click="showMoveToDialog(item)"><el-icon><FolderRemove /></el-icon></el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
<!-- 分组展开后显示子表单/子分组 -->
<transition name="expand-collapse">
<ul v-if="item.children && item.expanded">
<li v-for="child in item.children" :key="child.id">
<!-- 对子表单和子分组再次进行类型判断递归渲染 -->
<template v-if="child.type === 'group'">
<!-- 子分组模板 -->
<div class="menu-item-child">
<span @click="toggleExpand(child)"><el-icon><Folder /></el-icon>{{ child.name }}</span>
<el-dropdown trigger="click" placement="bottom-end">
<el-icon><MoreFilled /></el-icon>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item @click="openCreateChildFormDialog(item)"><el-icon><DocumentAdd /></el-icon></el-dropdown-item>
<el-dropdown-item @click="openCreateChildGroupDialog(item)"><el-icon><FolderAdd /></el-icon></el-dropdown-item>
<el-dropdown-item @click="confirmDelete(item)"><el-icon><Delete /></el-icon></el-dropdown-item>
<el-dropdown-item @click="showRenameDialog(item)"><el-icon><EditPen /></el-icon></el-dropdown-item>
<el-dropdown-item @click="showMoveToDialog(item)"><el-icon><FolderRemove /></el-icon></el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
</template>
<template v-else-if="child.type === 'form'">
<!-- 子表单模板 -->
<div class="menu-item-child">
<span><el-icon><Document /></el-icon>{{ child.name }}</span>
<el-dropdown trigger="click" placement="bottom-end">
<el-icon><MoreFilled /></el-icon>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item @click="confirmDelete(item)"><el-icon><Delete /></el-icon></el-dropdown-item>
<el-dropdown-item @click="showRenameDialog(item)"><el-icon><EditPen /></el-icon></el-dropdown-item>
<el-dropdown-item @click="showMoveToDialog(item)"><el-icon><FolderRemove /></el-icon></el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
</template>
</li>
</ul>
</transition>
</li>
</template>
<!-- 表单 -->
<template v-else-if="item.type === 'form'">
<!-- 表单模板 -->
<li :key="item.id" class="menu-item">
<span><el-icon><Document /></el-icon>{{ item.name }}</span>
<el-dropdown trigger="click" placement="bottom-end">
<el-icon><MoreFilled /></el-icon>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item @click="confirmDelete(item)"><el-icon><Delete /></el-icon></el-dropdown-item>
<el-dropdown-item @click="showRenameDialog(item)"><el-icon><EditPen /></el-icon></el-dropdown-item>
<el-dropdown-item @click="showMoveToDialog(item)"><el-icon><FolderRemove /></el-icon></el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</li>
</template>
</template>
</ul>
</div>
</el-aside>
<!-- 主要内容区域 -->
<el-main>
表单预览
</el-main>
</el-container>
</div>
<!-- 新建表单对话框 -->
<el-dialog v-model="createFormVisible" title="新建表单" >
<el-form label-width="80px">
<el-form-item label="表单名称">
<el-input v-model="newFormName" />
</el-form-item>
</el-form>
<div class="dialog-footer">
<el-button @click="createFormVisible = false">取消</el-button>
<el-button type="primary" @click="saveNewForm">保存</el-button>
</div>
</el-dialog>
<!-- 新建分组对话框 -->
<el-dialog v-model="createGroupVisible" title="新建分组">
<el-form label-width="80px">
<el-form-item label="分组名称">
<el-input v-model="newGroupName" />
</el-form-item>
</el-form>
<div class="dialog-footer">
<el-button @click="createGroupVisible = false">取消</el-button>
<el-button type="primary" @click="saveNewGroup">保存</el-button>
</div>
</el-dialog>
<!-- 修改名称对话框 -->
<el-dialog v-model="renameVisible" title="修改名称" @close="handleRenameClose">
<el-form label-width="80px">
<el-form-item label="名称">
<el-input v-model="renameValue" />
</el-form-item>
</el-form>
<div class="dialog-footer">
<el-button @click="handleRenameCancel">取消</el-button>
<el-button type="primary" @click="handleRenameSave">保存</el-button>
</div>
</el-dialog>
<!-- 移动分组对话框 -->
<el-dialog
v-model="moveToVisible"
title="移动分组"
@close="handleMoveToClose">
<el-select
v-model="selectedParentId"
placeholder="选择目标分组"
>
<el-option
v-for="(group, index) in menu.filter((m) => m.type === 'group' && m.id !== selectedGroupId)"
:key="index"
:label="group.name"
:value="group.id"
/>
</el-select>
<div class="dialog-footer">
<el-button @click="handleMoveToCancel">取消</el-button>
<el-button type="primary" @click="handleMoveToSave">保存</el-button>
</div>
</el-dialog>
</template>
<style scoped>
.common-layout {
height: 100vh;
overflow: hidden;
}
.el-header {
height: 50px;
display: flex;
justify-content: space-between;
}
/* 应用名称 */
.header-input {
width: 100px;
margin-top: 10px;
}
/* 编辑表单按钮 */
.header-btn{
margin-top: 10px;
}
/* 侧边栏整体 */
.el-aside {
background-color: #f5f7fa;
width: 250px;
min-height: 100vh;
display: flex;
flex-direction: column;
position: relative;
}
/* 侧边栏上部区域 */
.sidebar-upper {
display: flex;
padding: 20px;
border-bottom: 1px solid #ebeef5;
}
/* 搜索框 */
.search-input{
width: 200px;
margin-right: 10px;
}
/* 新建按钮 */
.add-btn{
cursor: pointer;
height: 30px;
width: 30px;
margin-top: 2px;
}
/* 侧边栏下半部分菜单样式 */
.sidebar-lower {
overflow-y: auto;
padding: 5px 0;
}
.sidebar-menu {
list-style: none;
margin: 0;
padding: 0;
}
/* 父分组/表单 */
.menu-item {
display: flex;
justify-content: space-between;
padding: 8px 16px;
font-size: 14px;
line-height: 1.5;
color: #222222;
cursor: pointer;
transition: background-color 0.2s ease;
}
.menu-item:hover,
.menu-item:focus {
background-color: #cacbcc;
}
/* 子分组/表单 */
.menu-item-child{
display: flex;
justify-content: space-between;
padding: 8px 16px 8px 35px;
font-size: 14px;
line-height: 1.5;
color: #222222;
cursor: pointer;
transition: background-color 0.2s ease;
}
.menu-item-child:hover,
.menu-item-child:focus {
background-color: #cacbcc;
}
/* 定义展开/折叠动画的通用样式 */
.expand-collapse-enter-active,
.expand-collapse-leave-active {
transition: max-height 0.3s ease, opacity 0.3s ease;
overflow: hidden;
}
/* 定义展开动画 */
.expand-collapse-enter-from {
max-height: 0;
opacity: 0;
}
/* 定义折叠动画 */
.expand-collapse-leave-to {
max-height: 0;
opacity: 0;
}
/* 下拉菜单样式 */
.el-dropdown-menu {
padding: 0;
min-width: 100px;
}
/* 表单预览 */
.el-main {
flex: 1;
padding: 20px;
background-color: #ffffff;
}
/* 对话框样式 */
.dialog-footer {
text-align:right;
margin-top: 20px;
}
</style>

106
src/views/sysworkflow/codepage/editpage.vue

@ -0,0 +1,106 @@
<!--
@ 作者: 袁纪菲
@ 时间: 2024.4.16
@ 备注: 编辑应用
-->
<script lang="ts" setup>
import { reactive, ref , computed } from 'vue'
import { ElMessage} from 'element-plus';
const props = defineProps({
// eslint-disable-next-line vue/prop-name-casing
Visible:Boolean
});
const emits = defineEmits(["update:Visible", "data"]);
//
const formLabelWidth = ref('140px')
const form = reactive({
data: {
id: '',
name: '',
icon: '',
},
visible: computed({
get(){
return props.Visible
},
set(val) {
emits('update:Visible', val)
}
})
})
//
const previewImageUrl = ref<string>(''); //
//
const beforeUpload = (file: any) => {
const isJPG = file.type === 'image/jpeg' || file.type === 'image/png';
if (!isJPG) {
ElMessage.error('只能上传 JPG 或 PNG 格式的图片');
return false;
}
const isLt2M = file.size / 1024 / 1024 < 2;
if (!isLt2M) {
ElMessage.error('图片大小不能超过 2MB');
return false;
}
// URL
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = (e: ProgressEvent<FileReader>) => {
if (e.target?.result) {
previewImageUrl.value = e.target.result as string;
form.data.icon = e.target.result as string;// null
} else {
//
previewImageUrl.value = '';
ElMessage.error('无法预览图片,请重新上传');
}
};
return isJPG && isLt2M;
};
//
const submitForm = () => {
emits('data', form.data); //
emits('update:Visible', false); //
};
</script>
<template>
<div class="edit-container">
<el-dialog
:model-value="props.Visible"
title="编辑应用"
width="500">
<el-form :model="form">
<el-form-item label="应用名称" :label-width="formLabelWidth">
<el-input v-model="form.data.name" placeholder="请输入应用名称" autocomplete="off" />
</el-form-item>
<el-form-item label="应用图标" :label-width=formLabelWidth>
<el-upload
class="picture-uploader"
action=""
:auto-upload="false"
:before-upload="beforeUpload"
list-type="picture-card">
<i class="el-icon-plus"></i>
</el-upload>
<!-- 预览区域 -->
<div v-if="form.data.icon" class="preview-image">
<img :src="form.data.icon" alt="预览图片" />
</div>
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button @click="emits('update:Visible', false)">取消</el-button>
<el-button type="primary" @click="submitForm">
保存
</el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<style scoped>
</style>

400
src/views/sysworkflow/codepage/page.vue

@ -1,213 +1,251 @@
<!--
@ 作者: 袁纪菲
@ 时间: 2024.3.18
@ 备注: 应用管理父组件
@ 时间: 2024.4.16
@ 备注: 应用管理
-->
<script lang = "ts" setup>
import cardedit from './cardedit.vue';
import cardadd from './cardadd.vue';
import {ref,onMounted,onUnmounted,watch} from 'vue';
import {Delete,Edit,View,MoreFilled} from '@element-plus/icons-vue'
<script lang="ts" setup>
import editpage from './editpage.vue';
import { ref , onMounted } from 'vue';
import { useRouter } from 'vue-router';
//
const props = defineProps({
visible:Boolean
});
const drawerRefadd = ref(false);
const drawerRefedit = ref(false)
//
interface cardDatass {
id: number;
name: string;
imageUrl: '',
}
const cardData = ref<cardDatass[]>([]);
const emits = defineEmits(["update:visible", "data"]);
//
const opencardadd = () => {
drawerRefadd.value = true;
cardadd.value.onSubmit = handleAddCard;
};
//
const handleAddCard = (newCard: cardDatass) => {
cardData.value.push(newCard);
//
const router = useRouter();
const goToCreatePage = () => {
try {
router.push({ name: 'create' });
} catch (error) {
console.error("导航到创建页面时发生错误:", error);
}
};
//
const opencardedit = (index: number) => {
drawerRefedit.value = true;
// cardedit
cardedit.value.cardData = cardData.value[index];
cardedit.value.onSubmit = handleEditCard;
const dialogRefedit = ref(false);
interface cardDatas {
id: number;
name: string;
icon: string;
}
//
const cardData = ref<cardDatas[]>([]);
const emits = defineEmits(["update:Visible", "data"]);
onMounted(() => {
if (!cardData.value.length) {
for (let i = 0; i < 9; i++) {
cardData.value.push({
id: i,
name: '标题' + i,
icon: 'https://img.zcool.cn/community/01f3fb5a66a75ea80120a123a9f582.jpg@2o.jpg'
});
}
}
});
//
const showEditPage = (index: number) => {
dialogRefedit.value = true;
editpage.value.cardData = cardData.value[index];
editpage.value.onSubmit = handleEdit;
};
//
const handleEditCard = (newCard: cardDatass) => {
cardData.value.splice(0,1,newCard);
//
const handleEdit = (newCard: cardDatas) => {
cardData.value.splice(0, 1, newCard);
};
//
//
const deleteCard = (index: number) => {
cardData.value.splice(index, 1);
emits('data', cardData.value);
cardData.value.splice(index, 1);
};
// 'data'
watch(() => props.visible, () => {
},
{ immediate: true }
);
//
const handleNewCard = (newCard: cardDatass) => {
cardData.value.push(newCard);
};
onMounted(() => {
for(let i = 0;i<9;i++){
cardData.value.push({
id: i,
name: '卡片' + i,
imageUrl: ''
});
}
});
</script>
<template>
<cardadd v-model:visible="drawerRefadd" :keyval="props.visible" @data="handleNewCard"/>
<cardedit v-model:visible="drawerRefedit" :keyval="props.visible" @data="handleNewCard"/>
<el-row :gutter="10">
<el-col :xs="24" :sm="12" :md="12" :lg="6" :xl="6">
<div class="grid-content ep-bg-purple" >
<el-card class="cardlarge">
<template #header>
<div class="cardhead-large">
<el-row class="block-col-2">
<el-col style="text-align: right;">
<el-dropdown>
<span class="el-dropdown-link">
<el-icon><MoreFilled /></el-icon>
</span>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item @click="opencardadd" >添加</el-dropdown-item>
<el-dropdown-item >编辑</el-dropdown-item>
<el-dropdown-item >删除</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</el-col>
<el-col style="text-align: left;">
<span>标题一</span>
</el-col>
</el-row>
</div>
</template>
<div class="grid-content ep-bg-purple" >
<el-row :gutter="10" >
<el-col v-for="(card, index) in cardData" :key="card.id" :xs="8" :sm="12" :md="8" :lg="8" :xl="8">
<el-card class="cardpattern">
<img
v-if="card.imageUrl"
src="card.imageUrl"
title="示例图片"
class="picture"
/>
<div class="cardhead">
<span>{{ card.name }}</span>
</div>
<div class="bottom">
<el-button size="small" circle class="button" :icon="View"></el-button>
<el-button size="small" circle class="button" :icon="Edit" @click="() => opencardedit(index)"></el-button>
<el-button size="small" circle class="button" :icon="Delete" @click="() => deleteCard(index)"></el-button>
</div>
</el-card>
</el-col>
</el-row>
</div>
</el-card>
</div>
</el-col>
</el-row>
<!-- 分页 -->
<div class="example-pagination-block">
<div class="example-demonstration"></div>
<el-pagination layout="prev, pager, next" :total="50" />
<editpage v-model:Visible="dialogRefedit" @data="handleEdit" />
<div class="common-layout">
<el-container>
<el-container>
<el-main class="layout-content">
<el-header class="content-header">
<el-button class="create-btn" @click="goToCreatePage">
<el-icon><Plus /></el-icon>
新建应用
</el-button>
</el-header>
<el-main>
<el-scrollbar class="scrollbar">
<div class="xiangying">
<el-col v-for="(card) in cardData" :key="card.id">
<el-card class="cardpattern" shadow="hover" >
<div class="button">
<img :src="card.icon" class="picture" />
<span>自定义卡片</span>
<el-icon class="star">
<Star />
</el-icon>
<el-dropdown class="xiala" trigger="click">
<el-icon>
<MoreFilled />
</el-icon>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item @click = "() => showEditPage">编辑应用</el-dropdown-item>
<el-dropdown-item @click = "() => deleteCard">删除</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
<div class="cardhead">
<span>{{ card.name }}</span>
</div>
</el-card>
</el-col>
</div>
</el-scrollbar>
</el-main>
</el-main>
</el-container>
</el-container>
</div>
</template>
<style scoped>
/* 小卡片 */
.cardpattern{
padding-bottom: 0px;
margin-bottom: 10px;
min-width: 100px;
.common-layout {
width: 100%;
height: 100%;
.el-container {
height: 100%;
/* 不折叠的时候宽度是200px,折叠的时候宽度自适应 */
}
}
svg {
width: 1.5em;
margin-right: 5px;
}
.el-menu-vertical-demo:not(.el-menu--collapse) {
width: 200px;
min-height: 400px;
}
/* 小卡片标题 */
.cardhead{
padding: 10px;
font-size: 15px;
.el-aside {
width: auto;
}
/* 大卡片 */
.cardlarge{
max-width: 480px;
.el-menu-vertical-demo {
border-right: none;
margin-top: 0;
height: 100%;
background-color: #586177;
color: #fff;
}
/* 大卡片标题 */
.cardhead-large{
font-size: 20px;
.el-menu-item {
color: #ffffff;
}
/* 按钮整体 */
.bottom {
margin-top: 20px;
line-height: 10px;
/* 侧边栏标题 */
.side-header {
height: 64px;
padding: 0;
display: flex;
justify-content: space-evenly;
align-items: center;
justify-content: center;
}
/* 单个按钮 */
.button {
padding: 0px;
min-height: auto;
margin-bottom: 1px;
/* 右侧上方标题 */
.up-header {
display: flex;
align-items : center;
justify-content:left;
background-color: #fff;
padding: 0;
}
/* 图片 */
.picture {
height: 100%;
min-height: 50px;
max-height: 100px;
display: block;
width: 100%;
min-width: 50px;
/* 缩放侧边栏按钮 */
.trigger {
font-size: 18px;
line-height: 64px;
padding: 0 24px;
cursor: pointer;
}
.el-col {
border-radius: 4px;
.el-main {
overflow-y: auto;
padding-right: 0; /* 取消内外边距以免影响滚动区域 */
}
.grid-content {
border-radius: 4px;
min-height: 36px;
/* 内容框 */
.layout-content{
padding: 15px;
background: #f5f5f5;
min-height: 280px;
}
.app_box{
margin: 15px 0 0 0px;
/* 新建按钮框 */
.content-header {
display: flex;
justify-content: space-between;
text-align: right;
font-size: 12px;
height: 40px;
}
.block-col-2 .demonstration {
display: block;
color: var(--el-text-color-secondary);
font-size: 14px;
margin-bottom: 20px;
/* 新建按钮 */
.create-btn {
margin-right: 10px;
background-color: #1090ff;
color: #ffffff;
}
/* 卡片整体所在区域 */
.scrollbar{
height: 700px;
overflow-y: auto; /* 自动显示垂直滚动条 */
margin-right: 10px;
box-sizing: border-box; /* 保证高度计算包含内边距和边框 */
/* 当内容不够一屏时,禁用滚动条 */
&::-webkit-scrollbar {
width: 0;
height: 0;
}
}
/* 内容部分卡片响应式 */
.xiangying {
display: grid;
grid-auto-rows: minmax(160px, auto); /* 或者指定最大高度 */
grid-gap: 1px;
grid-template-columns: repeat(auto-fit, 230px);
overflow-y: auto; /* 添加此行以允许卡片区域内部内容滚动 */
}
/* 卡片 */
.cardpattern {
padding: 0px;
margin-bottom: 10px;
width: 220px;
height: 150px;
transition: all 0.3s ease;
}
.block-col-2 .el-dropdown-link {
display: flex;
align-items: center;
/* 卡片标题 */
.cardhead {
margin-top: 50px;
font-size: 18px;
color: #000000;
font-weight: 500;
text-align: left;
font-family: PingFangSC-Regular;
line-height: 20px;
}
/* 收藏按钮与下拉框区域 */
.button {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 3px;
}
/* 分页 */
.example-pagination-block + .example-pagination-block {
margin-top: 10px;
/* 收藏按钮 */
.star {
font-size: 20px;
margin-right: -5px;
}
.example-pagination-block .example-demonstration {
margin-bottom: 16px;
/* 占位元素 */
.placeholder {
height:14px;
/* 设置为图标按钮组的高度 */
width:100%;
/* 设置为图标按钮组的宽度 */
visibility: hidden;
/* 或者 opacity: 0; 保证不会影响布局但不可见 */
pointer-events: none;
/* 防止此元素捕获鼠标事件 */
}
/* 图片 */
.picture {
width: 3cap;
margin: 1px;
}
</style>

213
src/views/sysworkflow/codepage/page0318.vue

@ -0,0 +1,213 @@
<!--
@ 作者: 袁纪菲
@ 时间: 2024.3.18
@ 备注: 应用管理父组件
-->
<script lang = "ts" setup>
import cardedit from './cardedit.vue';
import cardadd from './cardadd.vue';
import {ref,onMounted,onUnmounted,watch} from 'vue';
import {Delete,Edit,View,MoreFilled} from '@element-plus/icons-vue'
//
const props = defineProps({
visible:Boolean
});
const drawerRefadd = ref(false);
const drawerRefedit = ref(false)
//
interface cardDatass {
id: number;
name: string;
imageUrl: '',
}
const cardData = ref<cardDatass[]>([]);
const emits = defineEmits(["update:visible", "data"]);
//
const opencardadd = () => {
drawerRefadd.value = true;
cardadd.value.onSubmit = handleAddCard;
};
//
const handleAddCard = (newCard: cardDatass) => {
cardData.value.push(newCard);
};
//
const opencardedit = (index: number) => {
drawerRefedit.value = true;
// cardedit
cardedit.value.cardData = cardData.value[index];
cardedit.value.onSubmit = handleEditCard;
};
//
const handleEditCard = (newCard: cardDatass) => {
cardData.value.splice(0,1,newCard);
};
//
const deleteCard = (index: number) => {
cardData.value.splice(index, 1);
emits('data', cardData.value);
};
// 'data'
watch(() => props.visible, () => {
},
{ immediate: true }
);
//
const handleNewCard = (newCard: cardDatass) => {
cardData.value.push(newCard);
};
onMounted(() => {
for(let i = 0;i<9;i++){
cardData.value.push({
id: i,
name: '卡片' + i,
imageUrl: ''
});
}
});
</script>
<template>
<cardadd v-model:visible="drawerRefadd" :keyval="props.visible" @data="handleNewCard"/>
<cardedit v-model:visible="drawerRefedit" :keyval="props.visible" @data="handleNewCard"/>
<el-row :gutter="10">
<el-col :xs="24" :sm="12" :md="12" :lg="6" :xl="6">
<div class="grid-content ep-bg-purple" >
<el-card class="cardlarge">
<template #header>
<div class="cardhead-large">
<el-row class="block-col-2">
<el-col style="text-align: right;">
<el-dropdown>
<span class="el-dropdown-link">
<el-icon><MoreFilled /></el-icon>
</span>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item @click="opencardadd" >添加</el-dropdown-item>
<el-dropdown-item >编辑</el-dropdown-item>
<el-dropdown-item >删除</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</el-col>
<el-col style="text-align: left;">
<span>标题一</span>
</el-col>
</el-row>
</div>
</template>
<div class="grid-content ep-bg-purple" >
<el-row :gutter="10" >
<el-col v-for="(card, index) in cardData" :key="card.id" :xs="8" :sm="12" :md="8" :lg="8" :xl="8">
<el-card class="cardpattern">
<img
v-if="card.imageUrl"
src="card.imageUrl"
title="示例图片"
class="picture"
/>
<div class="cardhead">
<span>{{ card.name }}</span>
</div>
<div class="bottom">
<el-button size="small" circle class="button" :icon="View"></el-button>
<el-button size="small" circle class="button" :icon="Edit" @click="() => opencardedit(index)"></el-button>
<el-button size="small" circle class="button" :icon="Delete" @click="() => deleteCard(index)"></el-button>
</div>
</el-card>
</el-col>
</el-row>
</div>
</el-card>
</div>
</el-col>
</el-row>
<!-- 分页 -->
<div class="example-pagination-block">
<div class="example-demonstration"></div>
<el-pagination layout="prev, pager, next" :total="50" />
</div>
</template>
<style scoped>
/* 小卡片 */
.cardpattern{
padding-bottom: 0px;
margin-bottom: 10px;
min-width: 100px;
}
/* 小卡片标题 */
.cardhead{
padding: 10px;
font-size: 15px;
}
/* 大卡片 */
.cardlarge{
max-width: 480px;
}
/* 大卡片标题 */
.cardhead-large{
font-size: 20px;
}
/* 按钮整体 */
.bottom {
margin-top: 20px;
line-height: 10px;
display: flex;
justify-content: space-evenly;
align-items: center;
}
/* 单个按钮 */
.button {
padding: 0px;
min-height: auto;
margin-bottom: 1px;
}
/* 图片 */
.picture {
height: 100%;
min-height: 50px;
max-height: 100px;
display: block;
width: 100%;
min-width: 50px;
}
.el-col {
border-radius: 4px;
}
.grid-content {
border-radius: 4px;
min-height: 36px;
}
.app_box{
margin: 15px 0 0 0px;
}
.block-col-2 .demonstration {
display: block;
color: var(--el-text-color-secondary);
font-size: 14px;
margin-bottom: 20px;
}
.block-col-2 .el-dropdown-link {
display: flex;
align-items: center;
}
/* 分页 */
.example-pagination-block + .example-pagination-block {
margin-top: 10px;
}
.example-pagination-block .example-demonstration {
margin-bottom: 16px;
}
</style>
Loading…
Cancel
Save