|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 897 B |
|
After Width: | Height: | Size: 2.6 KiB |
|
After Width: | Height: | Size: 867 B |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 2.4 KiB |
|
After Width: | Height: | Size: 4.0 KiB |
|
After Width: | Height: | Size: 7.8 KiB |
|
Before Width: | Height: | Size: 7.8 KiB After Width: | Height: | Size: 55 KiB |
@ -0,0 +1,125 @@ |
|||||
|
<!-- |
||||
|
@ 作者: 秦东 |
||||
|
@ 时间: 2024-04-22 14:39:07 |
||||
|
@ 备注: 选择图标弹窗 |
||||
|
--> |
||||
|
<script lang='ts' setup> |
||||
|
import SvgIcon from "@/components/SvgIcon/index.vue"; |
||||
|
import SvgPage from "@/components/IconSelect/svgPage.vue"; |
||||
|
|
||||
|
const props = defineProps({ |
||||
|
isShow:{ |
||||
|
type: Boolean, |
||||
|
default: false, |
||||
|
}, |
||||
|
svgName:{ |
||||
|
type:String, |
||||
|
require: false, |
||||
|
default:"" |
||||
|
}, |
||||
|
iconList:{ |
||||
|
type:Object, |
||||
|
default(){ |
||||
|
return {} |
||||
|
} |
||||
|
} |
||||
|
}); |
||||
|
const visible = ref(false); // 弹窗显示状态 |
||||
|
const iconSelectorRef = ref(); |
||||
|
const iconSelectorDialogRef = ref(); |
||||
|
const emits = defineEmits(["update:isShow","update:svgName"]); |
||||
|
const filterIconNames = ref<string[]>([]); |
||||
|
//点击窗外。取消显示 |
||||
|
onClickOutside(iconSelectorRef, () => (visible.value = false), { |
||||
|
ignore: [iconSelectorDialogRef], |
||||
|
}); |
||||
|
|
||||
|
|
||||
|
watch(() => props.isShow, (val:boolean) =>{ |
||||
|
if(val){ |
||||
|
filterIconNames.value = props.iconList |
||||
|
} |
||||
|
}) |
||||
|
/** |
||||
|
@ 作者: 秦东 |
||||
|
@ 时间: 2024-04-22 16:43:49 |
||||
|
@ 功能: 关闭 |
||||
|
*/ |
||||
|
const handleClose = () => { |
||||
|
emits("update:isShow",false) |
||||
|
} |
||||
|
/** |
||||
|
@ 作者: 秦东 |
||||
|
@ 时间: 2024-04-22 16:54:03 |
||||
|
@ 功能: 确定 |
||||
|
*/ |
||||
|
const handleSelect = (svgName:string) => { |
||||
|
emits("update:svgName",svgName) |
||||
|
handleClose() |
||||
|
} |
||||
|
</script> |
||||
|
<template> |
||||
|
<el-dialog |
||||
|
v-model="props.isShow" |
||||
|
title="选择图标" |
||||
|
width="500" |
||||
|
:before-close="handleClose" |
||||
|
> |
||||
|
<el-input |
||||
|
v-model="filterValue" |
||||
|
class="p-2" |
||||
|
placeholder="搜索图标" |
||||
|
clearable |
||||
|
@input="handleFilter" |
||||
|
/> |
||||
|
<el-divider border-style="dashed" style="margin:0px;" /> |
||||
|
|
||||
|
<el-scrollbar height="250px"> |
||||
|
<ul class="icon_list"> |
||||
|
<li |
||||
|
v-for="(iconName, index) in filterIconNames" |
||||
|
:key="index" |
||||
|
class="icon-item" |
||||
|
@click="handleSelect(iconName)" |
||||
|
> |
||||
|
<el-tooltip :content="iconName" placement="bottom" effect="light"> |
||||
|
<svg-icon color="var(--el-text-color-regular)" :icon-class="iconName" /> |
||||
|
</el-tooltip> |
||||
|
</li> |
||||
|
</ul> |
||||
|
</el-scrollbar> |
||||
|
|
||||
|
</el-dialog> |
||||
|
</template> |
||||
|
<style lang='scss' scoped> |
||||
|
.icon_list { |
||||
|
display: flex; |
||||
|
flex-wrap: wrap; |
||||
|
padding-left: 0px; |
||||
|
margin-top: 10px; |
||||
|
|
||||
|
.icon-item { |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
align-items: center; |
||||
|
justify-items: center; |
||||
|
width: 10%; |
||||
|
padding: 5px; |
||||
|
margin: 0 5px 5px 0; |
||||
|
cursor: pointer; |
||||
|
border: 1px solid #ccc; |
||||
|
padding: 5px 10px; |
||||
|
&:hover { |
||||
|
color: var(--el-color-primary); |
||||
|
border-color: var(--el-color-primary); |
||||
|
transition: all 0.2s; |
||||
|
transform: scaleX(1.1); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
.svgBox{ |
||||
|
:deep(.el-popper){ |
||||
|
padding:0px; |
||||
|
} |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,87 @@ |
|||||
|
<!-- |
||||
|
@ 作者: 秦东 |
||||
|
@ 时间: 2024-04-22 16:05:27 |
||||
|
@ 备注: 图标选择列表 |
||||
|
--> |
||||
|
<script lang='ts' setup> |
||||
|
|
||||
|
const allIconNames: string[] = []; // 所有的图标名称集合 |
||||
|
|
||||
|
const filterValue = ref(""); // 筛选的值 |
||||
|
const filterIconNames = ref<string[]>([]); // 过滤后的图标名称集合 |
||||
|
/** |
||||
|
@ 作者: 秦东 |
||||
|
@ 时间: 2024-04-22 16:05:52 |
||||
|
@ 功能: 加载svg |
||||
|
*/ |
||||
|
const loadIcons =() => { |
||||
|
const icons = import.meta.glob("../../assets/icons/*.svg"); |
||||
|
for (const icon in icons) { |
||||
|
const iconName = icon.split("assets/icons/")[1].split(".svg")[0]; |
||||
|
allIconNames.push(iconName); |
||||
|
} |
||||
|
filterIconNames.value = allIconNames; |
||||
|
// console.log("icon 加载--->",allIconNames); |
||||
|
} |
||||
|
onMounted(()=>{ |
||||
|
loadIcons(); |
||||
|
}) |
||||
|
</script> |
||||
|
<template> |
||||
|
<div class="svgBox"> |
||||
|
<el-input |
||||
|
v-model="filterValue" |
||||
|
class="p-2" |
||||
|
placeholder="搜索图标" |
||||
|
clearable |
||||
|
@input="handleFilter" |
||||
|
/> |
||||
|
<el-divider border-style="dashed" style="margin:0px;" /> |
||||
|
<el-scrollbar height="150px"> |
||||
|
<ul class="icon_list"> |
||||
|
<li |
||||
|
v-for="(iconName, index) in filterIconNames" |
||||
|
:key="index" |
||||
|
class="icon-item" |
||||
|
@click="handleSelect(iconName)" |
||||
|
> |
||||
|
<el-tooltip :content="iconName" placement="bottom" effect="light"> |
||||
|
<svg-icon color="var(--el-text-color-regular)" :icon-class="iconName" /> |
||||
|
</el-tooltip> |
||||
|
</li> |
||||
|
</ul> |
||||
|
</el-scrollbar> |
||||
|
</div> |
||||
|
</template> |
||||
|
<style lang='scss' scoped> |
||||
|
.icon_list { |
||||
|
display: flex; |
||||
|
flex-wrap: wrap; |
||||
|
padding-left: 10px; |
||||
|
margin-top: 10px; |
||||
|
|
||||
|
.icon-item { |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
align-items: center; |
||||
|
justify-items: center; |
||||
|
width: 10%; |
||||
|
padding: 5px; |
||||
|
margin: 0 5px 5px 0; |
||||
|
cursor: pointer; |
||||
|
border: 1px solid #ccc; |
||||
|
padding: 5px 10px; |
||||
|
&:hover { |
||||
|
color: var(--el-color-primary); |
||||
|
border-color: var(--el-color-primary); |
||||
|
transition: all 0.2s; |
||||
|
transform: scaleX(1.1); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
.svgBox{ |
||||
|
:deep(.el-popper){ |
||||
|
padding:0px; |
||||
|
} |
||||
|
} |
||||
|
</style> |
||||
@ -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; |
||||
|
// 更新对应item的name属性,并保存到菜单数组中 |
||||
|
}; |
||||
|
// 关闭修改名称对话框 |
||||
|
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> |
||||
@ -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> |
||||
@ -1,213 +1,252 @@ |
|||||
<!-- |
<!-- |
||||
@ 作者: 袁纪菲 |
@ 作者: 袁纪菲 |
||||
@ 时间: 2024.3.18 |
@ 时间: 2024.4.16 |
||||
@ 备注: 应用管理父组件 |
@ 备注: 应用管理 |
||||
--> |
--> |
||||
<script lang = "ts" setup> |
<script lang="ts" setup> |
||||
import cardedit from './cardedit.vue'; |
import editpage from './editpage.vue'; |
||||
import cardadd from './cardadd.vue'; |
import { ref , onMounted } from 'vue'; |
||||
import {ref,onMounted,onUnmounted,watch} from 'vue'; |
import { useRouter } from 'vue-router'; |
||||
import {Delete,Edit,View,MoreFilled} from '@element-plus/icons-vue' |
|
||||
|
|
||||
// 弹窗状态 |
//页面跳转 |
||||
const props = defineProps({ |
const router = useRouter(); |
||||
visible:Boolean |
const goToCreatePage = () => { |
||||
}); |
try { |
||||
const drawerRefadd = ref(false); |
router.push({ name: 'create' }); |
||||
const drawerRefedit = ref(false) |
} catch (error) { |
||||
|
console.error("导航到创建页面时发生错误:", error); |
||||
// 卡片数据 |
} |
||||
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 dialogRefedit = ref(false); |
||||
const opencardedit = (index: number) => { |
interface cardDatas { |
||||
drawerRefedit.value = true; |
id: number; |
||||
// 将当前卡片的数据传给cardedit组件 |
name: string; |
||||
cardedit.value.cardData = cardData.value[index]; |
icon: string; |
||||
cardedit.value.onSubmit = handleEditCard; |
} |
||||
|
//初始化应用卡片数据 |
||||
|
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) => { |
const handleEdit = (newCard: cardDatas) => { |
||||
cardData.value.splice(0,1,newCard); |
cardData.value.splice(0, 1, newCard); |
||||
}; |
}; |
||||
//删除 |
//删除卡片 |
||||
const deleteCard = (index: number) => { |
const deleteCard = (index: number) => { |
||||
cardData.value.splice(index, 1); |
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> |
</script> |
||||
<template> |
<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 |
|
||||
|
|
||||
|
|
||||
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> |
|
||||
|
|
||||
<!-- 分页 --> |
<editpage v-model:Visible="dialogRefedit" @data="handleEdit" /> |
||||
<div class="example-pagination-block"> |
<div class="common-layout"> |
||||
<div class="example-demonstration"></div> |
<el-container> |
||||
<el-pagination layout="prev, pager, next" :total="50" /> |
<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> |
</div> |
||||
|
|
||||
</template> |
</template> |
||||
<style scoped> |
<style scoped> |
||||
/* 小卡片 */ |
.common-layout { |
||||
.cardpattern{ |
width: 100%; |
||||
padding-bottom: 0px; |
height: 100%; |
||||
margin-bottom: 10px; |
.el-container { |
||||
min-width: 100px; |
height: 100%; |
||||
|
/* 不折叠的时候宽度是200px,折叠的时候宽度自适应 */ |
||||
|
} |
||||
|
} |
||||
|
svg { |
||||
|
width: 1.5em; |
||||
|
margin-right: 5px; |
||||
|
} |
||||
|
.el-menu-vertical-demo:not(.el-menu--collapse) { |
||||
|
width: 200px; |
||||
|
min-height: 400px; |
||||
} |
} |
||||
/* 小卡片标题 */ |
.el-aside { |
||||
.cardhead{ |
width: auto; |
||||
padding: 10px; |
|
||||
font-size: 15px; |
|
||||
} |
} |
||||
/* 大卡片 */ |
.el-menu-vertical-demo { |
||||
.cardlarge{ |
border-right: none; |
||||
max-width: 480px; |
margin-top: 0; |
||||
|
height: 100%; |
||||
|
background-color: #586177; |
||||
|
color: #fff; |
||||
} |
} |
||||
/* 大卡片标题 */ |
.el-menu-item { |
||||
.cardhead-large{ |
color: #ffffff; |
||||
font-size: 20px; |
|
||||
} |
} |
||||
|
/* 侧边栏标题 */ |
||||
/* 按钮整体 */ |
.side-header { |
||||
.bottom { |
height: 64px; |
||||
margin-top: 20px; |
padding: 0; |
||||
line-height: 10px; |
|
||||
display: flex; |
display: flex; |
||||
justify-content: space-evenly; |
|
||||
align-items: center; |
align-items: center; |
||||
|
justify-content: center; |
||||
} |
} |
||||
/* 单个按钮 */ |
/* 右侧上方标题 */ |
||||
.button { |
.up-header { |
||||
padding: 0px; |
display: flex; |
||||
min-height: auto; |
align-items : center; |
||||
margin-bottom: 1px; |
justify-content:left; |
||||
|
background-color: #fff; |
||||
|
padding: 0; |
||||
} |
} |
||||
/* 图片 */ |
/* 缩放侧边栏按钮 */ |
||||
.picture { |
.trigger { |
||||
height: 100%; |
font-size: 18px; |
||||
min-height: 50px; |
line-height: 64px; |
||||
max-height: 100px; |
padding: 0 24px; |
||||
display: block; |
cursor: pointer; |
||||
width: 100%; |
|
||||
min-width: 50px; |
|
||||
} |
} |
||||
|
.el-main { |
||||
.el-col { |
overflow-y: auto; |
||||
border-radius: 4px; |
padding-right: 0; /* 取消内外边距以免影响滚动区域 */ |
||||
} |
} |
||||
|
/* 内容框 */ |
||||
.grid-content { |
.layout-content{ |
||||
border-radius: 4px; |
padding: 15px; |
||||
min-height: 36px; |
background: #f5f5f5; |
||||
|
min-height: 280px; |
||||
} |
} |
||||
|
/* 新建按钮框 */ |
||||
.app_box{ |
.content-header { |
||||
margin: 15px 0 0 0px; |
display: flex; |
||||
|
justify-content: space-between; |
||||
|
text-align: right; |
||||
|
font-size: 12px; |
||||
|
height: 40px; |
||||
} |
} |
||||
|
/* 新建按钮 */ |
||||
|
.create-btn { |
||||
|
margin-right: 10px; |
||||
|
background-color: #1090ff; |
||||
|
color: #ffffff; |
||||
|
} |
||||
|
/* 卡片整体所在区域 */ |
||||
|
.scrollbar{ |
||||
|
height: 700px; |
||||
|
overflow-y: auto; /* 自动显示垂直滚动条 */ |
||||
|
margin-right: 10px; |
||||
|
box-sizing: border-box; /* 保证高度计算包含内边距和边框 */ |
||||
|
|
||||
.block-col-2 .demonstration { |
/* 当内容不够一屏时,禁用滚动条 */ |
||||
display: block; |
&::-webkit-scrollbar { |
||||
color: var(--el-text-color-secondary); |
width: 0; |
||||
font-size: 14px; |
height: 0; |
||||
margin-bottom: 20px; |
} |
||||
|
} |
||||
|
/* 内容部分卡片响应式 */ |
||||
|
.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; |
.cardhead { |
||||
align-items: center; |
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 { |
.star { |
||||
margin-top: 10px; |
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> |
</style> |
||||
|
|||||
@ -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> |
||||
@ -0,0 +1,165 @@ |
|||||
|
<!-- |
||||
|
@ 作者: 秦东 |
||||
|
@ 时间: 2024-04-23 16:56:36 |
||||
|
@ 备注: 菜单分组 |
||||
|
--> |
||||
|
<script lang='ts' setup> |
||||
|
import { appMenuTreeInfo } from "@/api/date/type" |
||||
|
import { ginOthenMenuTree,saveAppMenu } from '@/api/DesignForm/requestapi' |
||||
|
const props = defineProps({ |
||||
|
appCont:{ |
||||
|
type:Object, |
||||
|
default(){ |
||||
|
return {} |
||||
|
} |
||||
|
}, |
||||
|
menuParentKey:{ |
||||
|
type:String, |
||||
|
default:"" |
||||
|
}, |
||||
|
isShow:{ |
||||
|
type: Boolean, |
||||
|
default: false, |
||||
|
}, |
||||
|
menuType:{ |
||||
|
type:Number, |
||||
|
default:1 |
||||
|
} |
||||
|
}); |
||||
|
const emits = defineEmits(["update:isShow","updateMenu"]); |
||||
|
watch(()=>props.isShow,(val:boolean)=>{ |
||||
|
if(val){ |
||||
|
console.log("props.appCont",props.appCont) |
||||
|
console.log("props.menuParentKey",props.menuParentKey) |
||||
|
getOldMenuTree() |
||||
|
} |
||||
|
}) |
||||
|
const loading = ref(false) |
||||
|
const saveMenuGroup = ref(ElForm); |
||||
|
const menuOldTree = ref<appMenuTreeInfo[]>([]) |
||||
|
const menuGroupInfo = reactive({ |
||||
|
name:"", |
||||
|
group:"" |
||||
|
}) |
||||
|
const ssd = ref() |
||||
|
/** |
||||
|
@ 作者: 秦东 |
||||
|
@ 时间: 2024-04-24 08:50:40 |
||||
|
@ 功能: 关闭 |
||||
|
*/ |
||||
|
const handleClose = () => { |
||||
|
loading.value = false; |
||||
|
emits("updateMenu") |
||||
|
emits("update:isShow",false) |
||||
|
resetForm() |
||||
|
} |
||||
|
/** |
||||
|
@ 作者: 秦东 |
||||
|
@ 时间: 2024-04-24 09:09:24 |
||||
|
@ 功能: 获取已有菜单树 |
||||
|
*/ |
||||
|
const getOldMenuTree = () => { |
||||
|
ginOthenMenuTree({id:props.appCont.uuid}) |
||||
|
.then((data) =>{ |
||||
|
console.log("获取已有菜单树",data) |
||||
|
if(data.code == 0){ |
||||
|
menuOldTree.value = data.data |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
/** |
||||
|
@ 作者: 秦东 |
||||
|
@ 时间: 2024-04-24 09:29:59 |
||||
|
@ 功能: 表单验证规则 |
||||
|
*/ |
||||
|
const rules = reactive({ |
||||
|
name: [{ required: true, message: "请输入分组名称", trigger: "blur" }], |
||||
|
}); |
||||
|
/** |
||||
|
@ 作者: 秦东 |
||||
|
@ 时间: 2024-04-24 09:33:56 |
||||
|
@ 功能: 重置表单 |
||||
|
*/ |
||||
|
const resetForm = () => { |
||||
|
saveMenuGroup.value.resetFields(); |
||||
|
saveMenuGroup.value.clearValidate(); |
||||
|
|
||||
|
menuGroupInfo.name = undefined; |
||||
|
menuGroupInfo.group = ""; |
||||
|
} |
||||
|
/** |
||||
|
@ 作者: 秦东 |
||||
|
@ 时间: 2024-04-24 09:35:12 |
||||
|
@ 功能: 写入菜单分组 |
||||
|
*/ |
||||
|
const saveMenuData = () => { |
||||
|
console.log("写入菜单分组",menuGroupInfo) |
||||
|
loading.value = true; |
||||
|
saveMenuGroup.value.validate((valid: any) => { |
||||
|
if (valid) { |
||||
|
let groupId = menuGroupInfo.group |
||||
|
if(menuGroupInfo.group == "") { |
||||
|
groupId = props.appCont.uuid |
||||
|
} |
||||
|
let sendCont = { |
||||
|
name:menuGroupInfo.name, |
||||
|
appkey:props.appCont.uuid, |
||||
|
type:props.menuType, |
||||
|
group:groupId |
||||
|
} |
||||
|
console.log("写入菜单分组",sendCont) |
||||
|
saveAppMenu(sendCont) |
||||
|
.then((data) =>{ |
||||
|
handleClose() |
||||
|
}) |
||||
|
.finally(() =>{ |
||||
|
loading.value = false; |
||||
|
}) |
||||
|
}else{ |
||||
|
loading.value = false; |
||||
|
} |
||||
|
}) |
||||
|
} |
||||
|
</script> |
||||
|
<template> |
||||
|
<el-dialog |
||||
|
v-model="props.isShow" |
||||
|
title="新建分组" |
||||
|
width="500" |
||||
|
:before-close="handleClose" |
||||
|
|
||||
|
> |
||||
|
<el-form |
||||
|
ref="saveMenuGroup" |
||||
|
:model="menuGroupInfo" |
||||
|
:rules="rules" |
||||
|
label-width="120px" |
||||
|
> |
||||
|
<el-form-item label="分组名称" prop="name"> |
||||
|
<el-input v-model="menuGroupInfo.name" clearable placeholder="请输入分组名称" /> |
||||
|
</el-form-item> |
||||
|
<el-form-item label="选择上级分组"> |
||||
|
<el-tree-select |
||||
|
v-model="menuGroupInfo.group" |
||||
|
:data="menuOldTree" |
||||
|
check-strictly |
||||
|
:render-after-expand="false" |
||||
|
show-checkbox |
||||
|
node-key="id" |
||||
|
value-key="id" |
||||
|
check-on-click-node |
||||
|
clearable |
||||
|
/> |
||||
|
</el-form-item> |
||||
|
</el-form> |
||||
|
<template #footer> |
||||
|
<div class="dialog-footer"> |
||||
|
<el-button @click="handleClose">取消</el-button> |
||||
|
<el-button type="primary" v-loading="loading" :disabled="loading" @click="saveMenuData">确定</el-button> |
||||
|
</div> |
||||
|
</template> |
||||
|
</el-dialog> |
||||
|
</template> |
||||
|
<style lang='scss' scoped> |
||||
|
|
||||
|
</style> |
||||
@ -0,0 +1,155 @@ |
|||||
|
<!-- |
||||
|
@ 作者: 秦东 |
||||
|
@ 时间: 2024-04-22 08:56:40 |
||||
|
@ 备注: |
||||
|
--> |
||||
|
<script lang='ts' setup> |
||||
|
import type { |
||||
|
AllowDropType, |
||||
|
NodeDropType, |
||||
|
} from 'element-plus/es/components/tree/src/tree.type' |
||||
|
import SvgIcon from "@/components/SvgIcon/index.vue"; |
||||
|
import { threeShiyanData } from "@/api/date/type" |
||||
|
import AppMenuSvgPage from "@/components/IconSelect/appMenuSvgPage.vue"; |
||||
|
|
||||
|
const props = defineProps({ |
||||
|
formKey:{ |
||||
|
type:String, |
||||
|
default:"" |
||||
|
}, |
||||
|
menusTree:{ |
||||
|
type:Object, |
||||
|
default(){ |
||||
|
return {} |
||||
|
} |
||||
|
} |
||||
|
}); |
||||
|
const svgIsShow = ref(false) |
||||
|
/** |
||||
|
@ 作者: 秦东 |
||||
|
@ 时间: 2024-04-22 10:06:20 |
||||
|
@ 功能: 树移动完毕后执行的操作 |
||||
|
*/ |
||||
|
const handleDrop = ( |
||||
|
draggingNode: Node, |
||||
|
dropNode: Node, |
||||
|
dropType: NodeDropType, |
||||
|
ev: DragEvents |
||||
|
) => { |
||||
|
console.log('draggingNode:', draggingNode) |
||||
|
console.log('dropNode:', dropNode) |
||||
|
console.log('dropType:', dropType) |
||||
|
console.log('ev:', ev) |
||||
|
} |
||||
|
|
||||
|
const treeList = ref<any[]>([]) |
||||
|
/** |
||||
|
@ 作者: 秦东 |
||||
|
@ 时间: 2024-04-22 15:53:35 |
||||
|
@ 功能: 加载SVG图标 |
||||
|
*/ |
||||
|
const allIconNames:string[] = []; |
||||
|
const loadIcons = () => { |
||||
|
const icons = import.meta.glob("../../../../assets/icons/*.svg"); |
||||
|
for (const icon in icons) { |
||||
|
const iconName = icon.split("assets/icons/")[1].split(".svg")[0]; |
||||
|
allIconNames.push(iconName); |
||||
|
} |
||||
|
// console.log("icons",icons) |
||||
|
// console.log("allIconNames",allIconNames) |
||||
|
} |
||||
|
onMounted(()=>{ |
||||
|
treeList.value = threeShiyanData |
||||
|
loadIcons(); |
||||
|
}) |
||||
|
/** |
||||
|
@ 作者: 秦东 |
||||
|
@ 时间: 2024-04-22 16:52:03 |
||||
|
@ 功能: 设置表单图标 |
||||
|
*/ |
||||
|
const svgName = ref("") |
||||
|
const svgId= ref("") |
||||
|
const setAppMenusSvg = (val:any) =>{ |
||||
|
svgName.value = val.svg |
||||
|
svgId.value = val.id |
||||
|
svgIsShow.value = true; |
||||
|
} |
||||
|
watch(()=>svgName.value,(val:string)=>{ |
||||
|
console.log("监听----svgName--->",val) |
||||
|
console.log("监听----threeShiyanData--->",threeShiyanData) |
||||
|
diguiData(treeList.value) |
||||
|
}) |
||||
|
|
||||
|
/** |
||||
|
@ 作者: 秦东 |
||||
|
@ 时间: 2024-04-23 09:44:14 |
||||
|
@ 功能: 递归判断是哪个节点换了图标 |
||||
|
*/ |
||||
|
const diguiData = (val:any) =>{ |
||||
|
if(Array.isArray(val)){ |
||||
|
try { |
||||
|
val.forEach((item:any) => { |
||||
|
if(item.id == svgId.value){ |
||||
|
item.svg = svgName.value |
||||
|
throw item; |
||||
|
} |
||||
|
if(item.children&&Array.isArray(item.children)){ |
||||
|
diguiData(item.children) |
||||
|
} |
||||
|
}); |
||||
|
} catch (e) { |
||||
|
console.log('跳出循环:', threeShiyanData); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
<template> |
||||
|
<el-tree |
||||
|
:data="props.menusTree" |
||||
|
draggable |
||||
|
default-expand-all |
||||
|
node-key="id" |
||||
|
:expand-on-click-node="false" |
||||
|
@node-drop="handleDrop" |
||||
|
> |
||||
|
<template #default="{ node, data }"> |
||||
|
<div class="appMenuTitle"> |
||||
|
<el-space wrap> |
||||
|
<svg-icon prefix="icon" :icon-class="data.svg" @click="setAppMenusSvg(data)" /> |
||||
|
<el-text>{{node.label}}</el-text> |
||||
|
</el-space> |
||||
|
<el-dropdown> |
||||
|
<svg-icon class="svgBox" prefix="icon" icon-class="set" /> |
||||
|
<template #dropdown> |
||||
|
<el-dropdown-item>修改名称</el-dropdown-item> |
||||
|
<el-dropdown-item>复制</el-dropdown-item> |
||||
|
<el-dropdown-item>移动到</el-dropdown-item> |
||||
|
<el-dropdown-item divided>新建子分组</el-dropdown-item> |
||||
|
<el-dropdown-item>新建普通表单</el-dropdown-item> |
||||
|
<el-dropdown-item>新建流程表单</el-dropdown-item> |
||||
|
<el-dropdown-item divided>隐藏PC端</el-dropdown-item> |
||||
|
<el-dropdown-item>隐藏移动端</el-dropdown-item> |
||||
|
<el-dropdown-item divided><el-text class="mx-1" type="danger">删除</el-text></el-dropdown-item> |
||||
|
</template> |
||||
|
</el-dropdown> |
||||
|
</div> |
||||
|
|
||||
|
</template> |
||||
|
</el-tree> |
||||
|
<AppMenuSvgPage v-model:is-show="svgIsShow" v-model:svg-name="svgName" :icon-list="allIconNames" /> |
||||
|
</template> |
||||
|
<style lang='scss' scoped> |
||||
|
.appMenuTitle{ |
||||
|
width: 100%; |
||||
|
padding-right: 5px; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: space-between; |
||||
|
.svgBox{ |
||||
|
display: none; |
||||
|
} |
||||
|
} |
||||
|
.appMenuTitle:hover .svgBox{ |
||||
|
display: block; |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,79 @@ |
|||||
|
<template> |
||||
|
|
||||
|
<div style="margin-bottom: 15px; height: 40px; line-height: 40px;"> |
||||
|
<el-tree-select |
||||
|
v-model="leftValue1" style="width: 290px;" :data="leftTreeSource" |
||||
|
check-strictly :render-after-expand="false" filterable @change="leftChanged" /> |
||||
|
<span style="margin-left: 8px;margin-right: 8px;">的值填充到</span> |
||||
|
<el-tree-select |
||||
|
v-model="rightValue1" style="width: 290px;" :data="rightTreeSource" |
||||
|
check-strictly :render-after-expand="false" filterable @change="rightChanged" /> |
||||
|
<Delete style="width: 22px; height: 22px; margin-right: 35px;cursor: pointer; color: #50A6FF;margin-top: 10px; display: block; float:right" @click="delRole" /> |
||||
|
</div> |
||||
|
|
||||
|
|
||||
|
|
||||
|
</template> |
||||
|
|
||||
|
<script setup lang="ts"> |
||||
|
|
||||
|
|
||||
|
const props = defineProps({ |
||||
|
leftTreeSource: { |
||||
|
type: Object, |
||||
|
default() { |
||||
|
return {} |
||||
|
} |
||||
|
}, |
||||
|
rightTreeSource: { |
||||
|
type: Object, |
||||
|
default() { |
||||
|
return {} |
||||
|
} |
||||
|
}, |
||||
|
leftValue: { |
||||
|
type: String, |
||||
|
default() { |
||||
|
return {} |
||||
|
} |
||||
|
}, |
||||
|
rightValue: { |
||||
|
type: String, |
||||
|
default() { |
||||
|
return {} |
||||
|
} |
||||
|
}, |
||||
|
currentKey: { |
||||
|
type: String, |
||||
|
default() { |
||||
|
return {} |
||||
|
} |
||||
|
}, |
||||
|
}) |
||||
|
const leftValue1 = ref() |
||||
|
const rightValue1 = ref() |
||||
|
|
||||
|
leftValue1.value = props.leftValue |
||||
|
rightValue1.value = props.rightValue |
||||
|
|
||||
|
|
||||
|
|
||||
|
let emit = defineEmits(["update:leftValue", "update:rightValue","delRole"]); |
||||
|
|
||||
|
function leftChanged(){ |
||||
|
emit('update:leftValue',leftValue1) |
||||
|
} |
||||
|
function rightChanged(){ |
||||
|
emit('update:rightValue',rightValue1) |
||||
|
} |
||||
|
|
||||
|
function delRole(){ |
||||
|
//alert(props.currentKey) |
||||
|
emit('delRole',props.currentKey) |
||||
|
} |
||||
|
|
||||
|
|
||||
|
|
||||
|
</script> |
||||
|
|
||||
|
<style></style> |
||||
@ -0,0 +1,107 @@ |
|||||
|
|
||||
|
<script lang='ts' setup> |
||||
|
import { ref, onMounted } from "vue"; |
||||
|
import tinymce from "tinymce/tinymce"; |
||||
|
import "tinymce/models/dom"; // 特别注意 tinymce 6.0.0 版本之后必须引入,否则不显示 |
||||
|
import "tinymce/themes/silver/theme"; |
||||
|
import Editor from "@tinymce/tinymce-vue"; // 引入组件 |
||||
|
import { v4 as uuidv4 } from "uuid"; |
||||
|
|
||||
|
let onlyNumber = uuidv4().replaceAll('-','').toString(); //获取唯一编码 |
||||
|
|
||||
|
/** |
||||
|
* 初始富文本组件 |
||||
|
*/ |
||||
|
const tinymceInit = { |
||||
|
selector: "#"+onlyNumber, |
||||
|
language_url: "/tinymce/langs/zh-Hans.js", // 引入语言包(该语言包在public下,注意文件名称) |
||||
|
language: "zh-Hans", // 这里名称根据 zh-Hans.js 里面写的名称而定 |
||||
|
skin_url: "/tinymce/skins/ui/oxide", // 这里引入的样式 |
||||
|
height: 260, // 限制高度 |
||||
|
statusbar:false, |
||||
|
toolbar:false, |
||||
|
branding: false, //是否禁用“Powered by TinyMCE” |
||||
|
menubar: false, //顶部菜单栏显示 |
||||
|
forced_root_block:'', |
||||
|
newline_behavior:"", |
||||
|
content_css: "/tinymce/skins/content/default/content.css", //以css文件方式自定义可编辑区域的css样式,css文件需自己创建并引入 |
||||
|
auto_focus : true, |
||||
|
} |
||||
|
const props = defineProps({ |
||||
|
aftText:{ |
||||
|
type: String, |
||||
|
default(){ |
||||
|
return {} |
||||
|
} |
||||
|
}, |
||||
|
aftTextCopy:{ |
||||
|
type: String, |
||||
|
default(){ |
||||
|
return {} |
||||
|
} |
||||
|
}, |
||||
|
}) |
||||
|
|
||||
|
let $emit = defineEmits(["textChange"]); |
||||
|
|
||||
|
const tinymceHtml = ref("") |
||||
|
tinymceHtml.value = props.aftText |
||||
|
|
||||
|
|
||||
|
|
||||
|
onMounted(() => { |
||||
|
tinymce.init({}); // 初始化富文本 |
||||
|
}); |
||||
|
watch(()=>tinymceHtml.value, (val:any) => { |
||||
|
$emit('textChange',val); |
||||
|
}, |
||||
|
{ deep: true } |
||||
|
) |
||||
|
|
||||
|
|
||||
|
const addIcon = (currentObject:any) =>{ |
||||
|
tinymce.activeEditor?.execCommand('mceInsertContent', false, `<span style="margin:3px;background-color: #4189EF;border-radius: 5px; padding:3px" contenteditable="false" data-keyid= "${currentObject.id}" >${currentObject.label}</span>`); |
||||
|
} |
||||
|
const addIcon_field = (currentObject:any) =>{ |
||||
|
tinymce.activeEditor?.execCommand('mceInsertContent', false, `<span style="margin:3px;background-color: #4189EF;border-radius: 5px; padding:3px" contenteditable="false" data-keyid= "${currentObject.id}" >${currentObject.treeAttrs.show}</span>`); |
||||
|
} |
||||
|
const addIcon_org = (currentObject:any) =>{ |
||||
|
let id = "orgOrPerson:"+currentObject.id |
||||
|
tinymce.activeEditor?.execCommand('mceInsertContent', false, `<span style="margin:3px;background-color: #4189EF;border-radius: 5px; padding:3px" contenteditable="false" data-keyid= "${id}" >${currentObject.label}</span>`); |
||||
|
} |
||||
|
//取消 |
||||
|
const handelCancel = (associatedFormsHideDialogTextCopy:any) =>{ |
||||
|
tinymceHtml.value = associatedFormsHideDialogTextCopy |
||||
|
} |
||||
|
const tinymceReInit = ()=>{ |
||||
|
|
||||
|
|
||||
|
let str:string = props.aftTextCopy |
||||
|
} |
||||
|
const focusEditor = ()=>{ |
||||
|
tinymce.EditorManager.get(onlyNumber)?.focus(true) |
||||
|
} |
||||
|
|
||||
|
|
||||
|
defineExpose({ |
||||
|
tinymceHtml, |
||||
|
addIcon, |
||||
|
addIcon_org, |
||||
|
addIcon_field, |
||||
|
handelCancel, |
||||
|
tinymceReInit, |
||||
|
focusEditor, |
||||
|
}) |
||||
|
|
||||
|
</script> |
||||
|
<template > |
||||
|
<div > |
||||
|
<editor :id="onlyNumber" v-model="tinymceHtml" :init="tinymceInit"></editor> |
||||
|
</div> |
||||
|
|
||||
|
|
||||
|
</template> |
||||
|
|
||||
|
<style lang='scss' scoped> |
||||
|
|
||||
|
</style> |
||||
@ -0,0 +1,108 @@ |
|||||
|
|
||||
|
<script lang='ts' setup> |
||||
|
import { ref, onMounted } from "vue"; |
||||
|
import tinymce from "tinymce/tinymce"; |
||||
|
import "tinymce/models/dom"; // 特别注意 tinymce 6.0.0 版本之后必须引入,否则不显示 |
||||
|
import "tinymce/themes/silver/theme"; |
||||
|
import Editor from "@tinymce/tinymce-vue"; // 引入组件 |
||||
|
import { v4 as uuidv4 } from "uuid"; |
||||
|
|
||||
|
let onlyNumber = uuidv4().replaceAll('-','').toString(); //获取唯一编码 |
||||
|
|
||||
|
/** |
||||
|
* 初始富文本组件 |
||||
|
*/ |
||||
|
const tinymceInit = { |
||||
|
selector: "#"+onlyNumber, |
||||
|
language_url: "/tinymce/langs/zh-Hans.js", // 引入语言包(该语言包在public下,注意文件名称) |
||||
|
language: "zh-Hans", // 这里名称根据 zh-Hans.js 里面写的名称而定 |
||||
|
skin_url: "/tinymce/skins/ui/oxide", // 这里引入的样式 |
||||
|
height: 260, // 限制高度 |
||||
|
statusbar:false, |
||||
|
toolbar:false, |
||||
|
branding: false, //是否禁用“Powered by TinyMCE” |
||||
|
menubar: false, //顶部菜单栏显示 |
||||
|
forced_root_block:'', |
||||
|
newline_behavior:"", |
||||
|
content_css: "/tinymce/skins/content/default/content.css", //以css文件方式自定义可编辑区域的css样式,css文件需自己创建并引入 |
||||
|
auto_focus : true, |
||||
|
} |
||||
|
const props = defineProps({ |
||||
|
aftText:{ |
||||
|
type: String, |
||||
|
default(){ |
||||
|
return {} |
||||
|
} |
||||
|
}, |
||||
|
aftTextCopy:{ |
||||
|
type: String, |
||||
|
default(){ |
||||
|
return {} |
||||
|
} |
||||
|
}, |
||||
|
}) |
||||
|
|
||||
|
let $emit = defineEmits(["textChange"]); |
||||
|
|
||||
|
const tinymceHtml = ref("") |
||||
|
tinymceHtml.value = props.aftText |
||||
|
|
||||
|
|
||||
|
|
||||
|
onMounted(() => { |
||||
|
tinymce.init({}); // 初始化富文本 |
||||
|
}); |
||||
|
watch(()=>tinymceHtml.value, (val:any) => { |
||||
|
$emit('textChange',val); |
||||
|
}, |
||||
|
{ deep: true } |
||||
|
) |
||||
|
|
||||
|
|
||||
|
const addIcon = (currentObject:any) =>{ |
||||
|
tinymce.activeEditor?.execCommand('mceInsertContent', false, `<span style="margin:3px;background-color: #4189EF;border-radius: 5px; padding:3px" contenteditable="false" data-keyid= "${currentObject.id}" >${currentObject.label}</span>`); |
||||
|
} |
||||
|
const addIcon_field = (currentObject:any) =>{ |
||||
|
tinymce.activeEditor?.execCommand('mceInsertContent', false, `<span style="margin:3px;background-color: #4189EF;border-radius: 5px; padding:3px" contenteditable="false" data-keyid= "${currentObject.id}" >${currentObject.treeAttrs.show}</span>`); |
||||
|
} |
||||
|
const addIcon_org = (currentObject:any) =>{ |
||||
|
let id = "orgOrPerson:"+currentObject.id |
||||
|
tinymce.activeEditor?.execCommand('mceInsertContent', false, `<span style="margin:3px;background-color: #4189EF;border-radius: 5px; padding:3px" contenteditable="false" data-keyid= "${id}" >${currentObject.label}</span>`); |
||||
|
} |
||||
|
//取消 |
||||
|
const handelCancel = (associatedFormsHideDialogTextCopy:any) =>{ |
||||
|
tinymceHtml.value = associatedFormsHideDialogTextCopy |
||||
|
} |
||||
|
const tinymceReInit = ()=>{ |
||||
|
|
||||
|
|
||||
|
let str:string = props.aftTextCopy |
||||
|
} |
||||
|
|
||||
|
const focusEditor = ()=>{ |
||||
|
tinymce.EditorManager.get(onlyNumber)?.focus(true) |
||||
|
} |
||||
|
|
||||
|
|
||||
|
defineExpose({ |
||||
|
tinymceHtml, |
||||
|
addIcon, |
||||
|
addIcon_org, |
||||
|
addIcon_field, |
||||
|
handelCancel, |
||||
|
tinymceReInit, |
||||
|
focusEditor, |
||||
|
}) |
||||
|
|
||||
|
</script> |
||||
|
<template > |
||||
|
<div > |
||||
|
<editor :id="onlyNumber" v-model="tinymceHtml" :init="tinymceInit"></editor> |
||||
|
</div> |
||||
|
|
||||
|
|
||||
|
</template> |
||||
|
|
||||
|
<style lang='scss' scoped> |
||||
|
|
||||
|
</style> |
||||