优化backend产品和解决方案

This commit is contained in:
2026-08-20 17:47:47 +08:00
parent 735534c6e6
commit a4f7a97763
14 changed files with 201 additions and 67 deletions
+8
View File
@@ -296,6 +296,14 @@ const fixedCmsMenu = {
icon: "Document", icon: "Document",
order: -200, order: -200,
children: [ children: [
{
id: -2001,
path: "/apps/cms/analytics/content",
title: "内容统计",
icon: "DataAnalysis",
order: 0,
children: []
},
{ {
id: -201, id: -201,
path: "/apps/cms/article", path: "/apps/cms/article",
+7 -5
View File
@@ -160,11 +160,13 @@ export function convertMenusToRoutes(menus, parentPath = '') {
} else if (hasChildren) { } else if (hasChildren) {
route.component = () => import('@/views/layouts/EmptyLayout.vue'); route.component = () => import('@/views/layouts/EmptyLayout.vue');
route.children = convertMenusToRoutes(menu.children, fullPath); route.children = convertMenusToRoutes(menu.children, fullPath);
const firstChild = menu.children[0]; const defaultChild = menu.children.find((child) =>
if (firstChild && firstChild.path) { child.path === '/apps/cms/analytics/content' || child.path === 'analytics/content'
const childFullPath = firstChild.path.startsWith('/') ) || menu.children[0];
? firstChild.path if (defaultChild && defaultChild.path) {
: `${fullPath}/${firstChild.path}`; const childFullPath = defaultChild.path.startsWith('/')
? defaultChild.path
: `${fullPath}/${defaultChild.path}`;
route.redirect = childFullPath; route.redirect = childFullPath;
} }
} else { } else {
+6
View File
@@ -12,6 +12,12 @@ const staticMainChildren = [
}, },
// CMS 文章/产品/解决方案是系统内置功能,不依赖数据库菜单配置。 // CMS 文章/产品/解决方案是系统内置功能,不依赖数据库菜单配置。
// 统一结构:index 为列表页,type 为分类页。 // 统一结构:index 为列表页,type 为分类页。
{
path: "/apps/cms/analytics/content",
name: "CmsContentAnalytics",
component: () => import("@/views/apps/cms/analytics/content/index.vue"),
meta: { requiresAuth: true, title: "内容统计", modulePath: "/apps/cms" }
},
{ {
path: "/apps/cms/article", path: "/apps/cms/article",
name: "CmsArticles", name: "CmsArticles",
@@ -98,6 +98,20 @@ const getCategoryName = (cate) => {
return category?.name || "无分类"; return category?.name || "无分类";
}; };
// 格式化日期
const formatDate = (date: string | number | Date) => {
if (!date) return "-";
const d = new Date(date);
if (isNaN(d.getTime())) return String(date);
const year = d.getFullYear();
const month = String(d.getMonth() + 1).padStart(2, "0");
const day = String(d.getDate()).padStart(2, "0");
const hours = String(d.getHours()).padStart(2, "0");
const minutes = String(d.getMinutes()).padStart(2, "0");
const seconds = String(d.getSeconds()).padStart(2, "0");
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
};
const emit = defineEmits(["update:modelValue"]); const emit = defineEmits(["update:modelValue"]);
const visible = ref(false); const visible = ref(false);
@@ -213,8 +213,8 @@
import { ref, onMounted } from "vue"; import { ref, onMounted } from "vue";
import { ElMessage, ElMessageBox } from "element-plus"; import { ElMessage, ElMessageBox } from "element-plus";
import { Search } from "@element-plus/icons-vue"; import { Search } from "@element-plus/icons-vue";
import Edit from "./index/components/edit.vue"; import Edit from "./components/edit.vue";
import Preview from "./index/components/preview.vue"; import Preview from "./components/preview.vue";
import { useAuthStore } from "@/stores/auth"; import { useAuthStore } from "@/stores/auth";
import { import {
listArticles, listArticles,
@@ -1,8 +1,9 @@
<template> <template>
<el-dialog <el-drawer
v-model="visible" v-model="visible"
:title="title" :title="title"
width="600px" size="65%"
class="cms-product-drawer"
destroy-on-close destroy-on-close
@close="handleClose" @close="handleClose"
> >
@@ -11,6 +12,7 @@
:model="formData" :model="formData"
:rules="formRules" :rules="formRules"
label-width="100px" label-width="100px"
class="product-form"
> >
<el-form-item label="产品名称" prop="title"> <el-form-item label="产品名称" prop="title">
<el-input v-model="formData.title" placeholder="请输入产品名称" /> <el-input v-model="formData.title" placeholder="请输入产品名称" />
@@ -19,11 +21,11 @@
<el-input <el-input
v-model="formData.desc" v-model="formData.desc"
type="textarea" type="textarea"
:rows="5" :rows="4"
placeholder="请输入产品描述" placeholder="请输入产品描述"
/> />
</el-form-item> </el-form-item>
<el-form-item label="产品内容" prop="content"> <el-form-item label="产品内容" prop="content" class="content-form-item">
<div class="editor-container"> <div class="editor-container">
<UmoEditor v-model="formData.content" style="height: 450px;" /> <UmoEditor v-model="formData.content" style="height: 450px;" />
</div> </div>
@@ -87,7 +89,7 @@
</el-button> </el-button>
</span> </span>
</template> </template>
</el-dialog> </el-drawer>
</template> </template>
<script setup> <script setup>
@@ -265,6 +267,30 @@ const handleSubmit = async () => {
</script> </script>
<style scoped> <style scoped>
:deep(.el-drawer__body) {
overflow-x: hidden;
padding: 20px;
}
.product-form {
width: 100%;
}
:deep(.el-form-item__content) {
min-width: 0;
max-width: 100%;
}
.editor-container {
width: 100%;
min-width: 0;
max-width: 100%;
border: 1px solid var(--el-border-color, #dcdfe6);
border-radius: 4px;
overflow: hidden;
box-sizing: border-box;
}
.dialog-footer { .dialog-footer {
display: flex; display: flex;
justify-content: flex-end; justify-content: flex-end;
@@ -1,8 +1,9 @@
<template> <template>
<el-dialog <el-drawer
v-model="visible" v-model="visible"
:title="title" :title="title"
width="600px" size="65%"
class="cms-solution-drawer"
destroy-on-close destroy-on-close
@close="handleClose" @close="handleClose"
> >
@@ -11,19 +12,25 @@
:model="formData" :model="formData"
:rules="formRules" :rules="formRules"
label-width="100px" label-width="100px"
class="solution-form"
> >
<el-form-item label="服务名称" prop="title"> <el-form-item label="方案名称" prop="title">
<el-input v-model="formData.title" placeholder="请输入服务名称" /> <el-input v-model="formData.title" placeholder="请输入解决方案名称" />
</el-form-item> </el-form-item>
<el-form-item label="服务描述" prop="desc"> <el-form-item label="方案描述" prop="desc">
<el-input <el-input
v-model="formData.desc" v-model="formData.desc"
type="textarea" type="textarea"
:rows="5" :rows="4"
placeholder="请输入服务描述" placeholder="请输入解决方案描述"
/> />
</el-form-item> </el-form-item>
<el-form-item label="Logo" prop="thumb"> <el-form-item label="方案内容" prop="content" class="content-form-item">
<div class="editor-container">
<UmoEditor v-model="formData.content" style="height: 450px;" />
</div>
</el-form-item>
<el-form-item label="方案图片" prop="thumb">
<div class="flex-direction"> <div class="flex-direction">
<el-upload <el-upload
class="image-uploader" class="image-uploader"
@@ -42,7 +49,7 @@
/> />
<div v-else class="upload-placeholder"> <div v-else class="upload-placeholder">
<el-icon class="image-uploader-icon"><Plus /></el-icon> <el-icon class="image-uploader-icon"><Plus /></el-icon>
<div class="el-upload__text">点击上传服务图标</div> <div class="el-upload__text">点击上传方案图片</div>
</div> </div>
</el-upload> </el-upload>
<div <div
@@ -63,7 +70,7 @@
@click="handleRemoveImage" @click="handleRemoveImage"
style="margin-top: 8px" style="margin-top: 8px"
> >
删除图标 删除图片
</el-button> </el-button>
</el-form-item> </el-form-item>
<el-form-item label="排序" prop="sort"> <el-form-item label="排序" prop="sort">
@@ -82,7 +89,7 @@
</el-button> </el-button>
</span> </span>
</template> </template>
</el-dialog> </el-drawer>
</template> </template>
<script setup> <script setup>
@@ -101,7 +108,7 @@ const props = defineProps({
}, },
title: { title: {
type: String, type: String,
default: "添加服务", default: "添加解决方案",
}, },
isEdit: { isEdit: {
type: Boolean, type: Boolean,
@@ -144,7 +151,7 @@ const beforeImageUpload = (file) => {
// 图片上传成功 // 图片上传成功
const handleImageSuccess = (response) => { const handleImageSuccess = (response) => {
if (response.code === 200 || response.code === 201) { if (response.code === 200 || response.code === 201) {
formData.link_logo = response.data.url || response.data.path; formData.thumb = response.data.url || response.data.path;
ElMessage.success( ElMessage.success(
response.code === 201 ? "文件已存在,直接使用" : "图片上传成功", response.code === 201 ? "文件已存在,直接使用" : "图片上传成功",
); );
@@ -160,7 +167,7 @@ const handleImageError = () => {
// 删除图片 // 删除图片
const handleRemoveImage = () => { const handleRemoveImage = () => {
formData.link_logo = ""; formData.thumb = "";
ElMessage.success("图片已删除"); ElMessage.success("图片已删除");
}; };
@@ -174,6 +181,7 @@ const getImageUrl = (imagePath) => {
const formData = reactive({ const formData = reactive({
title: "", title: "",
desc: "", desc: "",
content: "",
thumb: "", thumb: "",
url: "", url: "",
sort: 0, sort: 0,
@@ -182,11 +190,11 @@ const formData = reactive({
const formRules = { const formRules = {
title: [ title: [
{ required: true, message: "请输入服务名称", trigger: "blur" }, { required: true, message: "请输入解决方案名称", trigger: "blur" },
{ max: 100, message: "长度不超过100个字符", trigger: "blur" }, { max: 100, message: "长度不超过100个字符", trigger: "blur" },
], ],
desc: [ desc: [
{ required: true, message: "请输入服务描述", trigger: "blur" }, { required: true, message: "请输入解决方案描述", trigger: "blur" },
{ max: 200, message: "描述长度不超过200个字符", trigger: "blur" }, { max: 200, message: "描述长度不超过200个字符", trigger: "blur" },
], ],
}; };
@@ -214,6 +222,7 @@ watch(visible, (val) => {
const resetForm = () => { const resetForm = () => {
formData.title = ""; formData.title = "";
formData.desc = ""; formData.desc = "";
formData.content = "";
formData.thumb = ""; formData.thumb = "";
formData.url = ""; formData.url = "";
formData.sort = 0; formData.sort = 0;
@@ -259,6 +268,30 @@ const handleSubmit = async () => {
</script> </script>
<style scoped> <style scoped>
:deep(.el-drawer__body) {
overflow-x: hidden;
padding: 20px;
}
.solution-form {
width: 100%;
}
:deep(.el-form-item__content) {
min-width: 0;
max-width: 100%;
}
.editor-container {
width: 100%;
min-width: 0;
max-width: 100%;
border: 1px solid var(--el-border-color, #dcdfe6);
border-radius: 4px;
overflow: hidden;
box-sizing: border-box;
}
.dialog-footer { .dialog-footer {
display: flex; display: flex;
justify-content: flex-end; justify-content: flex-end;
@@ -1,7 +1,7 @@
<template> <template>
<div class="container-box"> <div class="container-box">
<div class="header-bar"> <div class="header-bar">
<h2>特色服务管理</h2> <h2>解决方案管理</h2>
<div class="header-actions"> <div class="header-actions">
<el-button type="primary" @click="handleTypes"> <el-button type="primary" @click="handleTypes">
<el-icon> <el-icon>
@@ -13,7 +13,7 @@
<el-icon> <el-icon>
<Plus /> <Plus />
</el-icon> </el-icon>
添加内容 添加解决方案
</el-button> </el-button>
<el-button @click="refresh" :loading="loading"> <el-button @click="refresh" :loading="loading">
<el-icon> <el-icon>
@@ -28,7 +28,7 @@
<!-- 搜索栏 --> <!-- 搜索栏 -->
<div class="search-bar"> <div class="search-bar">
<el-input v-model="searchForm.keyword" placeholder="请输入内容搜索" clearable style="width: 200px; margin-right: 10px" <el-input v-model="searchForm.keyword" placeholder="请输入解决方案名称搜索" clearable style="width: 200px; margin-right: 10px"
@keyup.enter="handleSearch" /> @keyup.enter="handleSearch" />
<el-select v-model="searchForm.status" placeholder="状态筛选" clearable style="width: 120px; margin-right: 10px"> <el-select v-model="searchForm.status" placeholder="状态筛选" clearable style="width: 120px; margin-right: 10px">
<el-option label="启用" :value="1" /> <el-option label="启用" :value="1" />
@@ -46,9 +46,23 @@
<!-- 数据表格 --> <!-- 数据表格 -->
<el-table :data="servicesList" style="width: 100%" v-loading="loading" @selection-change="handleSelectionChange"> <el-table :data="servicesList" style="width: 100%" v-loading="loading" @selection-change="handleSelectionChange">
<el-table-column prop="id" label="ID" width="80" align="center" /> <el-table-column prop="id" label="ID" width="80" align="center" />
<el-table-column prop="thumb" label="服务图片" min-width="150" align="center" /> <el-table-column prop="thumb" label="方案图片" min-width="150" align="center">
<el-table-column prop="title" label="服务名称" min-width="150" align="center" /> <template #default="{ row }">
<el-table-column prop="desc" label="服务描述" min-width="150" align="center" /> <el-image v-if="row.thumb" :src="getImageUrl(row.thumb)" :preview-src-list="[getImageUrl(row.thumb)]"
fit="cover" class="solution-thumb" :preview-teleported="true" lazy>
<template #error>
<div class="image-slot">
<el-icon>
<Picture />
</el-icon>
</div>
</template>
</el-image>
<span v-else class="text-gray">暂无图片</span>
</template>
</el-table-column>
<el-table-column prop="title" label="方案名称" min-width="150" align="center" />
<el-table-column prop="desc" label="方案描述" min-width="150" align="center" />
<el-table-column prop="url" label="跳转地址" min-width="150" align="center" /> <el-table-column prop="url" label="跳转地址" min-width="150" align="center" />
<el-table-column prop="sort" label="排序" width="80" align="center" /> <el-table-column prop="sort" label="排序" width="80" align="center" />
<el-table-column prop="create_time" label="创建时间" width="180" align="center" /> <el-table-column prop="create_time" label="创建时间" width="180" align="center" />
@@ -148,8 +162,8 @@ const fetchList = async () => {
pagination.total = res.data.total pagination.total = res.data.total
} }
} catch (error) { } catch (error) {
console.error('获取特色服务列表失败:', error) console.error('获取解决方案列表失败:', error)
ElMessage.error('获取特色服务列表失败') ElMessage.error('获取解决方案列表失败')
} finally { } finally {
loading.value = false loading.value = false
} }
@@ -193,7 +207,7 @@ const handleSelectionChange = (selection) => {
// 添加 // 添加
const handleAdd = () => { const handleAdd = () => {
isEdit.value = false isEdit.value = false
dialogTitle.value = '添加特色服务' dialogTitle.value = '添加解决方案'
currentRow.value = {} currentRow.value = {}
dialogVisible.value = true dialogVisible.value = true
} }
@@ -201,7 +215,7 @@ const handleAdd = () => {
// 编辑 // 编辑
const handleEdit = (row) => { const handleEdit = (row) => {
isEdit.value = true isEdit.value = true
dialogTitle.value = '编辑特色服务' dialogTitle.value = '编辑解决方案'
currentRow.value = row currentRow.value = row
dialogVisible.value = true dialogVisible.value = true
} }
@@ -209,7 +223,7 @@ const handleEdit = (row) => {
// 删除 // 删除
const handleDelete = async (row) => { const handleDelete = async (row) => {
try { try {
await ElMessageBox.confirm('确定要删除该特色服务吗?', '提示', { await ElMessageBox.confirm('确定要删除该解决方案吗?', '提示', {
confirmButtonText: '确定', confirmButtonText: '确定',
cancelButtonText: '取消', cancelButtonText: '取消',
type: 'warning' type: 'warning'
@@ -224,8 +238,8 @@ const handleDelete = async (row) => {
} }
} catch (error) { } catch (error) {
if (error !== 'cancel') { if (error !== 'cancel') {
console.error('删除特色服务失败:', error) console.error('删除解决方案失败:', error)
ElMessage.error('删除特色服务失败') ElMessage.error('删除解决方案失败')
} }
} }
} }
@@ -241,8 +255,8 @@ const handleStatusChange = async (row, val) => {
row.status = val === 1 ? 0 : 1 row.status = val === 1 ? 0 : 1
} }
} catch (error) { } catch (error) {
console.error('更新特色服务状态失败:', error) console.error('更新解决方案状态失败:', error)
ElMessage.error('更新特色服务状态失败') ElMessage.error('更新解决方案状态失败')
row.status = val === 1 ? 0 : 1 row.status = val === 1 ? 0 : 1
} }
} }
@@ -1,4 +1,5 @@
<template> <template>
<div class="umo-editor-wrapper">
<umo-editor <umo-editor
ref="editorRef" ref="editorRef"
v-bind="options" v-bind="options"
@@ -6,6 +7,7 @@
@created="onCreated" @created="onCreated"
@file-upload="onFileUpload" @file-upload="onFileUpload"
/> />
</div>
</template> </template>
<script setup> <script setup>
@@ -64,3 +66,20 @@ watch(
} }
) )
</script> </script>
<style scoped>
.umo-editor-wrapper {
width: 100%;
max-width: 100%;
min-width: 0;
height: 100%;
box-sizing: border-box;
overflow: hidden;
}
:deep(.umo-editor),
:deep(umo-editor) {
width: 100% !important;
max-width: 100% !important;
box-sizing: border-box;
}
</style>
+10 -5
View File
@@ -63,7 +63,7 @@
v-for="module in basicModules" v-for="module in basicModules"
:key="module.path" :key="module.path"
class="module-card" class="module-card"
@click="handleNavigate(module.path)" @click="handleNavigate(module)"
> >
<div class="card-header"> <div class="card-header">
<span class="card-title">{{ module.name }}</span> <span class="card-title">{{ module.name }}</span>
@@ -85,7 +85,7 @@
v-for="module in systemModules" v-for="module in systemModules"
:key="module.path" :key="module.path"
class="module-card" class="module-card"
@click="handleNavigate(module.path)" @click="handleNavigate(module)"
> >
<div class="card-header"> <div class="card-header">
<span class="card-title">{{ module.name }}</span> <span class="card-title">{{ module.name }}</span>
@@ -107,7 +107,7 @@
v-for="module in uncategorizedModules" v-for="module in uncategorizedModules"
:key="module.path" :key="module.path"
class="module-card" class="module-card"
@click="handleNavigate(module.path)" @click="handleNavigate(module)"
> >
<div class="card-header"> <div class="card-header">
<span class="card-title">{{ module.name }}</span> <span class="card-title">{{ module.name }}</span>
@@ -222,8 +222,13 @@ const userName = computed(
const userAvatar = computed(() => authStore.user?.avatar || ""); const userAvatar = computed(() => authStore.user?.avatar || "");
// 处理导航跳转 // 处理导航跳转
function handleNavigate(path: string) { function handleNavigate(module: ModuleItem) {
if (path) router.push(path); const isWebsiteManagement = module.name === "网站管理" || module.path.startsWith("/apps/cms");
const targetPath = isWebsiteManagement
? "/apps/cms/analytics/content"
: module.path;
if (targetPath) router.push(targetPath);
} }
// 刷新菜单 // 刷新菜单
+4
View File
@@ -60,6 +60,7 @@ func cmsSolutionToMap(row models.CmsSolution) map[string]interface{} {
"title": row.Title, "title": row.Title,
"thumb": row.Thumb, "thumb": row.Thumb,
"desc": row.Desc, "desc": row.Desc,
"content": row.Content,
"url": row.URL, "url": row.URL,
"sort": row.Sort, "sort": row.Sort,
"status": row.Status, "status": row.Status,
@@ -144,6 +145,7 @@ type cmsSolutionPayload struct {
Title string `json:"title"` Title string `json:"title"`
Thumb string `json:"thumb"` Thumb string `json:"thumb"`
Desc string `json:"desc"` Desc string `json:"desc"`
Content string `json:"content"`
URL string `json:"url"` URL string `json:"url"`
Sort int `json:"sort"` Sort int `json:"sort"`
Status int8 `json:"status"` Status int8 `json:"status"`
@@ -187,6 +189,7 @@ func (c *BackendSolutionController) Create() {
Title: title, Title: title,
Thumb: strings.TrimSpace(p.Thumb), Thumb: strings.TrimSpace(p.Thumb),
Desc: strings.TrimSpace(p.Desc), Desc: strings.TrimSpace(p.Desc),
Content: p.Content,
URL: strings.TrimSpace(p.URL), URL: strings.TrimSpace(p.URL),
Sort: p.Sort, Sort: p.Sort,
Status: p.Status, Status: p.Status,
@@ -245,6 +248,7 @@ func (c *BackendSolutionController) Update() {
"title": title, "title": title,
"thumb": strings.TrimSpace(p.Thumb), "thumb": strings.TrimSpace(p.Thumb),
"desc": strings.TrimSpace(p.Desc), "desc": strings.TrimSpace(p.Desc),
"content": p.Content,
"url": strings.TrimSpace(p.URL), "url": strings.TrimSpace(p.URL),
"sort": p.Sort, "sort": p.Sort,
"status": p.Status, "status": p.Status,
-6
View File
@@ -2,8 +2,6 @@ package models
import ( import (
"time" "time"
"github.com/beego/beego/v2/client/orm"
) )
type BackendMenuFront struct { type BackendMenuFront struct {
@@ -27,7 +25,3 @@ type BackendMenuFront struct {
func (m *BackendMenuFront) TableName() string { func (m *BackendMenuFront) TableName() string {
return "yz_backend_menu_front" return "yz_backend_menu_front"
} }
func init() {
orm.RegisterModel(new(BackendMenuFront))
}
+4
View File
@@ -12,6 +12,7 @@ type CmsSolution struct {
Title string `orm:"column(title);size(100)" json:"title"` Title string `orm:"column(title);size(100)" json:"title"`
Thumb string `orm:"column(thumb);size(500);default()" json:"thumb"` Thumb string `orm:"column(thumb);size(500);default()" json:"thumb"`
Desc string `orm:"column(desc);size(500);default()" json:"desc"` Desc string `orm:"column(desc);size(500);default()" json:"desc"`
Content string `orm:"column(content);type(mediumtext);null" json:"content"`
URL string `orm:"column(url);size(500);default()" json:"url"` URL string `orm:"column(url);size(500);default()" json:"url"`
Sort int `orm:"column(sort);default(0)" json:"sort"` Sort int `orm:"column(sort);default(0)" json:"sort"`
Status int8 `orm:"column(status);default(1)" json:"status"` Status int8 `orm:"column(status);default(1)" json:"status"`
@@ -55,6 +56,7 @@ CREATE TABLE IF NOT EXISTS yz_cms_solution (
title varchar(100) NOT NULL DEFAULT '', title varchar(100) NOT NULL DEFAULT '',
thumb varchar(500) NOT NULL DEFAULT '', thumb varchar(500) NOT NULL DEFAULT '',
` + "`desc`" + ` varchar(500) NOT NULL DEFAULT '', ` + "`desc`" + ` varchar(500) NOT NULL DEFAULT '',
content mediumtext,
url varchar(500) NOT NULL DEFAULT '', url varchar(500) NOT NULL DEFAULT '',
sort int NOT NULL DEFAULT 0, sort int NOT NULL DEFAULT 0,
status tinyint NOT NULL DEFAULT 1, status tinyint NOT NULL DEFAULT 1,
@@ -67,6 +69,8 @@ CREATE TABLE IF NOT EXISTS yz_cms_solution (
if err != nil { if err != nil {
return return
} }
// 确保已有表补齐 content 字段
_, _ = Orm.Raw("ALTER TABLE yz_cms_solution ADD COLUMN content mediumtext").Exec()
_, err = Orm.Raw(` _, err = Orm.Raw(`
CREATE TABLE IF NOT EXISTS yz_cms_solution_category ( CREATE TABLE IF NOT EXISTS yz_cms_solution_category (
id bigint unsigned NOT NULL AUTO_INCREMENT, id bigint unsigned NOT NULL AUTO_INCREMENT,
+5
View File
@@ -69,11 +69,16 @@ func Init(_ string) {
new(CmsArticleCategory), new(CmsArticleCategory),
new(CmsArticle), new(CmsArticle),
new(CmsProductCategory),
new(CmsProduct),
new(CmsSolutionCategory),
new(CmsSolution),
new(CmsFrontendTemplate), new(CmsFrontendTemplate),
new(CmsFrontendBanner), new(CmsFrontendBanner),
new(CmsFrontendFriendlink), new(CmsFrontendFriendlink),
new(CmsFrontendOnepage), new(CmsFrontendOnepage),
new(BackendMenuFront),
new(SystemReminderList), new(SystemReminderList),