550 lines
13 KiB
Plaintext
550 lines
13 KiB
Plaintext
/**
|
||
* 页面装修业务逻辑模块
|
||
* 参考CRMEB项目,提供完整的装修管理功能
|
||
*/
|
||
|
||
/**
|
||
* 装修页面数据接口
|
||
*/
|
||
export interface DesignItem {
|
||
id: string | number
|
||
name: string
|
||
type: 'homepage' | 'category' | 'product' | 'custom'
|
||
status: 0 | 1 // 0: 草稿, 1: 已发布
|
||
categoryId?: string | number
|
||
categoryName?: string
|
||
path?: string
|
||
preview_url?: string
|
||
content: DesignComponent[]
|
||
version?: string
|
||
created_at?: string
|
||
updated_at?: string
|
||
}
|
||
|
||
/**
|
||
* 装修组件接口
|
||
*/
|
||
export interface DesignComponent {
|
||
id: string
|
||
type: 'image' | 'text' | 'product' | 'carousel' | 'divider' | 'spacer' | 'button' | 'form'
|
||
name: string
|
||
icon: string
|
||
description?: string
|
||
componentName?: string
|
||
config?: Record<string, any>
|
||
children?: DesignComponent[]
|
||
}
|
||
|
||
/**
|
||
* 装修模板接口
|
||
*/
|
||
export interface DesignTemplate {
|
||
id: string | number
|
||
name: string
|
||
description: string
|
||
type: string
|
||
preview: string
|
||
content: DesignComponent[]
|
||
created_at?: string
|
||
}
|
||
|
||
/**
|
||
* 获取装修页面列表
|
||
* @param params 查询参数
|
||
* @returns 装修页面列表
|
||
*/
|
||
export function getDesignList(params?: Record<string, any>): Promise<DesignItem[]> {
|
||
return new Promise((resolve, reject) => {
|
||
// TODO: 实际应调用后端API
|
||
const designList: DesignItem[] = [
|
||
{
|
||
id: 1,
|
||
name: '首页装修',
|
||
type: 'homepage',
|
||
status: 1,
|
||
content: [],
|
||
updated_at: '2026-01-30 14:30:00'
|
||
},
|
||
{
|
||
id: 2,
|
||
name: '年货节活动页',
|
||
type: 'custom',
|
||
status: 1,
|
||
content: [],
|
||
updated_at: '2026-01-28 10:15:00'
|
||
}
|
||
]
|
||
setTimeout(() => resolve(designList), 300)
|
||
})
|
||
}
|
||
|
||
/**
|
||
* 获取首页装修详情
|
||
* @returns 首页装修数据
|
||
*/
|
||
export function getHomePageDesign(): Promise<DesignItem> {
|
||
return new Promise((resolve, reject) => {
|
||
const homepage: DesignItem = {
|
||
id: 'homepage',
|
||
name: '首页装修',
|
||
type: 'homepage',
|
||
status: 1,
|
||
content: [
|
||
{
|
||
id: 'carousel-1',
|
||
type: 'carousel',
|
||
name: '轮播图',
|
||
icon: 'C',
|
||
description: '首页顶部轮播图展示',
|
||
config: {
|
||
autoplay: true,
|
||
duration: 5000,
|
||
height: 300
|
||
}
|
||
},
|
||
{
|
||
id: 'product-1',
|
||
type: 'product',
|
||
name: '商品展示',
|
||
icon: 'P',
|
||
description: '热销商品列表',
|
||
config: {
|
||
count: 8,
|
||
columns: 2,
|
||
layout: 'grid'
|
||
}
|
||
}
|
||
],
|
||
version: '1.0.0',
|
||
updated_at: '2026-01-30 14:30:00'
|
||
}
|
||
setTimeout(() => resolve(homepage), 300)
|
||
})
|
||
}
|
||
|
||
/**
|
||
* 获取商品页装修详情
|
||
* @returns 商品页装修数据
|
||
*/
|
||
export function getProductPageDesign(): Promise<DesignItem> {
|
||
return new Promise((resolve, reject) => {
|
||
const productPage: DesignItem = {
|
||
id: 'product',
|
||
name: '商品页装修',
|
||
type: 'product',
|
||
status: 1,
|
||
content: [
|
||
{
|
||
id: 'image-1',
|
||
type: 'image',
|
||
name: '商品图',
|
||
icon: 'I',
|
||
description: '商品主图展示'
|
||
},
|
||
{
|
||
id: 'product-info',
|
||
type: 'text',
|
||
name: '商品信息',
|
||
icon: 'T',
|
||
description: '商品名称和价格'
|
||
}
|
||
],
|
||
version: '1.0.0',
|
||
updated_at: '2026-01-30 14:30:00'
|
||
}
|
||
setTimeout(() => resolve(productPage), 300)
|
||
})
|
||
}
|
||
|
||
/**
|
||
* 获取分类装修列表
|
||
* @returns 分类装修列表
|
||
*/
|
||
export function getCategoryDesigns(): Promise<DesignItem[]> {
|
||
return new Promise((resolve, reject) => {
|
||
const categories: DesignItem[] = [
|
||
{
|
||
id: 1,
|
||
name: '默认分类装修',
|
||
type: 'category',
|
||
status: 1,
|
||
categoryId: 0,
|
||
categoryName: '全部分类',
|
||
content: [],
|
||
updated_at: '2026-01-30 14:30:00'
|
||
},
|
||
{
|
||
id: 2,
|
||
name: '热销商品分类',
|
||
type: 'category',
|
||
status: 0,
|
||
categoryId: 1,
|
||
categoryName: '推荐分类',
|
||
content: [],
|
||
updated_at: '2026-01-29 10:15:00'
|
||
}
|
||
]
|
||
setTimeout(() => resolve(categories), 300)
|
||
})
|
||
}
|
||
|
||
/**
|
||
* 获取自定义页面列表
|
||
* @returns 自定义页面列表
|
||
*/
|
||
export function getCustomPages(): Promise<DesignItem[]> {
|
||
return new Promise((resolve, reject) => {
|
||
const customPages: DesignItem[] = [
|
||
{
|
||
id: 1,
|
||
name: '新年促销页',
|
||
type: 'custom',
|
||
status: 1,
|
||
path: '/pages/promotion/newyear',
|
||
content: [],
|
||
updated_at: '2026-01-28 09:00:00'
|
||
}
|
||
]
|
||
setTimeout(() => resolve(customPages), 300)
|
||
})
|
||
}
|
||
|
||
/**
|
||
* 获取页面模板库
|
||
* @returns 模板列表
|
||
*/
|
||
export function getTemplateLibrary(): Promise<DesignTemplate[]> {
|
||
return new Promise((resolve, reject) => {
|
||
const templates: DesignTemplate[] = [
|
||
{
|
||
id: 1,
|
||
name: '电商风格A',
|
||
description: '简洁现代的电商布局',
|
||
type: 'homepage',
|
||
preview: '@/static/images/template-a.png',
|
||
content: []
|
||
},
|
||
{
|
||
id: 2,
|
||
name: '电商风格B',
|
||
description: '豪华展示的电商布局',
|
||
type: 'homepage',
|
||
preview: '@/static/images/template-b.png',
|
||
content: []
|
||
},
|
||
{
|
||
id: 3,
|
||
name: '精品风格',
|
||
description: '精品商品展示布局',
|
||
type: 'homepage',
|
||
preview: '@/static/images/template-c.png',
|
||
content: []
|
||
},
|
||
{
|
||
id: 4,
|
||
name: '商城风格',
|
||
description: '完整商城功能布局',
|
||
type: 'homepage',
|
||
preview: '@/static/images/template-d.png',
|
||
content: []
|
||
}
|
||
]
|
||
setTimeout(() => resolve(templates), 300)
|
||
})
|
||
}
|
||
|
||
/**
|
||
* 获取可用组件库
|
||
* @returns 组件列表
|
||
*/
|
||
export function getAvailableComponents(): Promise<DesignComponent[]> {
|
||
return new Promise((resolve, reject) => {
|
||
const components: DesignComponent[] = [
|
||
{
|
||
id: 'image',
|
||
type: 'image',
|
||
name: '图片组件',
|
||
icon: 'I',
|
||
description: '展示图片和图片轮播',
|
||
componentName: 'ImageComponent',
|
||
config: {
|
||
defaultWidth: '100%',
|
||
defaultHeight: 'auto'
|
||
}
|
||
},
|
||
{
|
||
id: 'text',
|
||
type: 'text',
|
||
name: '文本组件',
|
||
icon: 'T',
|
||
description: '展示文本内容和段落',
|
||
componentName: 'TextComponent'
|
||
},
|
||
{
|
||
id: 'product',
|
||
type: 'product',
|
||
name: '商品组件',
|
||
icon: 'P',
|
||
description: '展示商品列表和推荐',
|
||
componentName: 'ProductComponent',
|
||
config: {
|
||
defaultCount: 6,
|
||
defaultColumns: 2
|
||
}
|
||
},
|
||
{
|
||
id: 'carousel',
|
||
type: 'carousel',
|
||
name: '轮播组件',
|
||
icon: 'C',
|
||
description: '图片和内容轮播',
|
||
componentName: 'CarouselComponent',
|
||
config: {
|
||
autoplay: true,
|
||
duration: 5000
|
||
}
|
||
},
|
||
{
|
||
id: 'divider',
|
||
type: 'divider',
|
||
name: '分割线',
|
||
icon: 'D',
|
||
description: '分割不同内容区域',
|
||
componentName: 'DividerComponent'
|
||
},
|
||
{
|
||
id: 'spacer',
|
||
type: 'spacer',
|
||
name: '间距组件',
|
||
icon: 'S',
|
||
description: '调整元素间距',
|
||
componentName: 'SpacerComponent',
|
||
config: {
|
||
defaultHeight: 16
|
||
}
|
||
},
|
||
{
|
||
id: 'button',
|
||
type: 'button',
|
||
name: '按钮组件',
|
||
icon: 'B',
|
||
description: '创建点击按钮',
|
||
componentName: 'ButtonComponent'
|
||
},
|
||
{
|
||
id: 'form',
|
||
type: 'form',
|
||
name: '表单组件',
|
||
icon: 'F',
|
||
description: '收集用户输入数据',
|
||
componentName: 'FormComponent'
|
||
}
|
||
]
|
||
setTimeout(() => resolve(components), 300)
|
||
})
|
||
}
|
||
|
||
/**
|
||
* 保存装修页面
|
||
* @param design 装修数据
|
||
* @returns 保存结果
|
||
*/
|
||
export function saveDesign(design: DesignItem): Promise<{ id: string | number; message: string }> {
|
||
return new Promise((resolve, reject) => {
|
||
if (!design.name || design.name.trim() === '') {
|
||
reject(new Error('装修名称不能为空'))
|
||
return
|
||
}
|
||
if (!design.type) {
|
||
reject(new Error('装修类型不能为空'))
|
||
return
|
||
}
|
||
// TODO: 实际应调用后端API保存
|
||
const result = {
|
||
id: design.id || Math.random().toString(36).substr(2, 9),
|
||
message: '保存成功'
|
||
}
|
||
setTimeout(() => resolve(result), 500)
|
||
})
|
||
}
|
||
|
||
/**
|
||
* 发布装修页面
|
||
* @param designId 装修页面ID
|
||
* @returns 发布结果
|
||
*/
|
||
export function publishDesign(designId: string | number): Promise<{ message: string }> {
|
||
return new Promise((resolve, reject) => {
|
||
if (!designId) {
|
||
reject(new Error('装修ID不能为空'))
|
||
return
|
||
}
|
||
// TODO: 实际应调用后端API发布
|
||
setTimeout(() => {
|
||
resolve({ message: '发布成功' })
|
||
}, 500)
|
||
})
|
||
}
|
||
|
||
/**
|
||
* 删除装修页面
|
||
* @param designId 装修页面ID
|
||
* @returns 删除结果
|
||
*/
|
||
export function deleteDesign(designId: string | number): Promise<{ message: string }> {
|
||
return new Promise((resolve, reject) => {
|
||
if (!designId) {
|
||
reject(new Error('装修ID不能为空'))
|
||
return
|
||
}
|
||
// TODO: 实际应调用后端API删除
|
||
setTimeout(() => {
|
||
resolve({ message: '删除成功' })
|
||
}, 500)
|
||
})
|
||
}
|
||
|
||
/**
|
||
* 获取装修预览URL
|
||
* @param designId 装修ID
|
||
* @returns 预览URL
|
||
*/
|
||
export function getDesignPreviewUrl(designId: string | number): string {
|
||
return `/pages/mall/design/preview/${designId}`
|
||
}
|
||
|
||
/**
|
||
* 获取装修编辑URL
|
||
* @param designId 装修ID
|
||
* @returns 编辑URL
|
||
*/
|
||
export function getDesignEditorUrl(designId: string | number): string {
|
||
return `/pages/mall/admin/design/editor?id=${designId}`
|
||
}
|
||
|
||
/**
|
||
* 格式化日期时间
|
||
* @param dateStr 日期字符串
|
||
* @returns 格式化后的日期
|
||
*/
|
||
export function formatDateTime(dateStr?: string): string {
|
||
if (!dateStr) return '--'
|
||
try {
|
||
const date = new Date(dateStr)
|
||
const year = date.getFullYear()
|
||
const month = String(date.getMonth() + 1).padStart(2, '0')
|
||
const day = String(date.getDate()).padStart(2, '0')
|
||
const hours = String(date.getHours()).padStart(2, '0')
|
||
const minutes = String(date.getMinutes()).padStart(2, '0')
|
||
return `${year}-${month}-${day} ${hours}:${minutes}`
|
||
} catch {
|
||
return dateStr
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 验证组件配置
|
||
* @param component 组件配置
|
||
* @returns 验证结果
|
||
*/
|
||
export function validateComponent(component: DesignComponent): { valid: boolean; errors: string[] } {
|
||
const errors: string[] = []
|
||
|
||
if (!component.id) {
|
||
errors.push('组件ID不能为空')
|
||
}
|
||
|
||
if (!component.type) {
|
||
errors.push('组件类型不能为空')
|
||
}
|
||
|
||
if (!component.name) {
|
||
errors.push('组件名称不能为空')
|
||
}
|
||
|
||
return {
|
||
valid: errors.length === 0,
|
||
errors
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 生成组件ID
|
||
* @param type 组件类型
|
||
* @returns 生成的组件ID
|
||
*/
|
||
export function generateComponentId(type: string): string {
|
||
const timestamp = Date.now().toString(36)
|
||
const random = Math.random().toString(36).substr(2, 5)
|
||
return `${type}-${timestamp}-${random}`
|
||
}
|
||
|
||
/**
|
||
* 获取装修约束条件
|
||
* @returns 约束条件对象
|
||
*/
|
||
export function getDesignConstraints(): DesignConstraints {
|
||
return {
|
||
maxComponents: 50,
|
||
allowedComponentTypes: ['image', 'text', 'product', 'carousel', 'divider', 'spacer', 'button', 'form'],
|
||
maxImageSize: 5242880, // 5MB
|
||
supportedImageFormats: ['jpg', 'jpeg', 'png', 'gif', 'webp']
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 深度克隆装修数据
|
||
* @param design 装修数据
|
||
* @returns 克隆后的数据
|
||
*/
|
||
export function cloneDesign(design: DesignItem): DesignItem {
|
||
return JSON.parse(JSON.stringify(design))
|
||
}
|
||
|
||
/**
|
||
* 验证装修数据完整性
|
||
* @param design 装修数据
|
||
* @returns 验证结果
|
||
*/
|
||
export function validateDesign(design: DesignItem): { valid: boolean; message: string } {
|
||
if (!design.name || design.name.trim() === '') {
|
||
return { valid: false, message: '装修名称不能为空' }
|
||
}
|
||
|
||
if (!design.type) {
|
||
return { valid: false, message: '装修类型不能为空' }
|
||
}
|
||
|
||
if (!Array.isArray(design.content)) {
|
||
return { valid: false, message: '装修内容格式错误' }
|
||
}
|
||
|
||
if (design.content.length > getDesignConstraints().maxComponents) {
|
||
return { valid: false, message: `组件数量超过限制(最多${getDesignConstraints().maxComponents}个)` }
|
||
}
|
||
|
||
return { valid: true, message: '验证通过' }
|
||
}
|
||
|
||
/**
|
||
* 导出装修为JSON
|
||
* @param design 装修数据
|
||
* @returns JSON字符串
|
||
*/
|
||
export function exportDesignJSON(design: DesignItem): string {
|
||
return JSON.stringify(design, null, 2)
|
||
}
|
||
|
||
/**
|
||
* 从JSON导入装修
|
||
* @param jsonStr JSON字符串
|
||
* @returns 装修数据
|
||
*/
|
||
export function importDesignJSON(jsonStr: string): DesignItem {
|
||
try {
|
||
return JSON.parse(jsonStr) as DesignItem
|
||
} catch (error) {
|
||
throw new Error('JSON格式错误,无法导入')
|
||
}
|
||
}
|