diff --git a/RuoYi-Vue/easycode-web/src/components/FrontendPageDesigner.vue b/RuoYi-Vue/easycode-web/src/components/FrontendPageDesigner.vue new file mode 100644 index 0000000..b340d85 --- /dev/null +++ b/RuoYi-Vue/easycode-web/src/components/FrontendPageDesigner.vue @@ -0,0 +1,2589 @@ + + + + + diff --git a/RuoYi-Vue/easycode-web/src/components/frontendPageDesigner.test.mjs b/RuoYi-Vue/easycode-web/src/components/frontendPageDesigner.test.mjs new file mode 100644 index 0000000..d42e3fa --- /dev/null +++ b/RuoYi-Vue/easycode-web/src/components/frontendPageDesigner.test.mjs @@ -0,0 +1,267 @@ +import test from 'node:test' +import assert from 'node:assert/strict' +import { existsSync, readFileSync } from 'node:fs' +import { fileURLToPath } from 'node:url' +import { dirname, resolve } from 'node:path' + +const currentDir = dirname(fileURLToPath(import.meta.url)) +const srcDir = resolve(currentDir, '..') + +function readSource(relativePath) { + return readFileSync(resolve(srcDir, relativePath), 'utf8') +} + +test('project api exposes frontend page design endpoints', () => { + const source = readSource('api/project.js') + + assert.equal(source.includes('export function listPageDesigns'), true) + assert.equal(source.includes('export function initPageDesigns'), true) + assert.equal(source.includes('export function createPageDesign'), true) + assert.equal(source.includes('export function getPageDesign'), true) + assert.equal(source.includes('export function savePageDesign'), true) + assert.equal(source.includes('export function deletePageDesign'), true) + assert.equal(source.includes('export function listBusinessBlocks'), true) + assert.equal(source.includes('/page-designs/init'), true) + assert.equal(source.includes('url: `/front/project/${projectId}/page-designs`'), true) + assert.equal(source.includes('/business-blocks'), true) + assert.equal(source.includes('scope = \'frontend\''), true) + assert.equal(source.includes('params: { scope }'), true) +}) + +test('frontend page designer edits layouts and emits saves', () => { + assert.equal(existsSync(resolve(srcDir, 'components/FrontendPageDesigner.vue')), true) + const source = readSource('components/FrontendPageDesigner.vue') + + assert.equal(source.includes('defineEmits'), true) + assert.equal(source.includes('update:designs'), true) + assert.equal(source.includes('dirty-change'), true) + assert.equal(source.includes('save-design'), true) + assert.equal(source.includes('create-design'), true) + assert.equal(source.includes('delete-design'), true) + assert.equal(source.includes('init-designs'), true) + assert.equal(source.includes('layoutJson'), true) + assert.equal(source.includes('actionJson'), true) + assert.equal(source.includes('draggable="true"'), true) + assert.equal(source.includes('function commitCurrentDraft'), true) + assert.equal(source.includes('defineExpose'), true) +}) + +test('frontend page designer reports local draft edits as dirty before commit', () => { + const source = readSource('components/FrontendPageDesigner.vue') + + assert.equal(source.includes('function markDirty'), true) + assert.equal(source.includes("emit('dirty-change', true)"), true) + assert.equal(source.includes('const suppressDirty = ref(false)'), true) + assert.equal(source.includes('syncJson({ silent: true })'), true) + assert.equal(source.includes('v-model="draft.pageName" @input="markDirty"'), true) + assert.equal(source.includes('v-model="draft.routePath" @input="markDirty"'), true) +}) + +test('frontend page designer renders page navigation as a normal menu inside the canvas', () => { + const source = readSource('components/FrontendPageDesigner.vue') + + assert.equal(source.includes('