|
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> |
|||
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 { |
|||
//页面跳转 |
|||
const router = useRouter(); |
|||
const goToCreatePage = () => { |
|||
try { |
|||
router.push({ name: 'create' }); |
|||
} catch (error) { |
|||
console.error("导航到创建页面时发生错误:", error); |
|||
} |
|||
}; |
|||
const dialogRefedit = ref(false); |
|||
interface cardDatas { |
|||
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); |
|||
}; |
|||
icon: string; |
|||
} |
|||
//初始化应用卡片数据 |
|||
const cardData = ref<cardDatas[]>([]); |
|||
const emits = defineEmits(["update:Visible", "data"]); |
|||
|
|||
onMounted(() => { |
|||
for(let i = 0;i<9;i++){ |
|||
if (!cardData.value.length) { |
|||
for (let i = 0; i < 9; i++) { |
|||
cardData.value.push({ |
|||
id: i, |
|||
name: '卡片' + i, |
|||
imageUrl: '' |
|||
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 handleEdit = (newCard: cardDatas) => { |
|||
cardData.value.splice(0, 1, newCard); |
|||
}; |
|||
//删除卡片 |
|||
const deleteCard = (index: number) => { |
|||
cardData.value.splice(index, 1); |
|||
}; |
|||
</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> |
|||
|
|||
<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="opencardadd" >添加</el-dropdown-item> |
|||
<el-dropdown-item >编辑</el-dropdown-item> |
|||
<el-dropdown-item >删除</el-dropdown-item> |
|||
<el-dropdown-item @click = "() => showEditPage">编辑应用</el-dropdown-item> |
|||
<el-dropdown-item @click = "() => deleteCard">删除</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> |
|||
|
|||
<!-- 分页 --> |
|||
<div class="example-pagination-block"> |
|||
<div class="example-demonstration"></div> |
|||
<el-pagination layout="prev, pager, next" :total="50" /> |
|||
</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; |
|||
} |
|||
/* 新建按钮 */ |
|||
.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; |
|||
color: var(--el-text-color-secondary); |
|||
font-size: 14px; |
|||
margin-bottom: 20px; |
|||
/* 当内容不够一屏时,禁用滚动条 */ |
|||
&::-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 { |
|||
/* 卡片标题 */ |
|||
.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; |
|||
} |
|||
/* 收藏按钮 */ |
|||
.star { |
|||
font-size: 20px; |
|||
margin-right: -5px; |
|||
} |
|||
/* 分页 */ |
|||
.example-pagination-block + .example-pagination-block { |
|||
margin-top: 10px; |
|||
|
|||
/* 占位元素 */ |
|||
.placeholder { |
|||
height:14px; |
|||
/* 设置为图标按钮组的高度 */ |
|||
width:100%; |
|||
/* 设置为图标按钮组的宽度 */ |
|||
visibility: hidden; |
|||
/* 或者 opacity: 0; 保证不会影响布局但不可见 */ |
|||
pointer-events: none; |
|||
/* 防止此元素捕获鼠标事件 */ |
|||
} |
|||
.example-pagination-block .example-demonstration { |
|||
margin-bottom: 16px; |
|||
/* 图片 */ |
|||
.picture { |
|||
width: 3cap; |
|||
margin: 1px; |
|||
} |
|||
|
|||
</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> |
|||