1
0
mirror of https://github.com/sahadev/vue-component-creater-ui.git synced 2025-09-28 07:13:21 +08:00

Merge branch 'vue3' into antd-vue3

This commit is contained in:
shangbin
2021-12-13 18:35:27 +08:00
51 changed files with 4966 additions and 1867 deletions

View File

@@ -1,11 +1,11 @@
<template>
<el-card class="attribute-container">
<center>
<div style="text-align: center;">
<el-switch v-model="editMode" active-text="自由编辑" inactive-text="约束编辑" active-color="#13ce66"
inactive-color="#13ce66">
</el-switch>
</center>
</div>
<div style="margin-top: 20px;">
<div name="1" v-show="!editMode">
@@ -13,8 +13,8 @@
<div class="item" v-for="(item, index) in localAttributes" :key="index">
<el-input v-model="item.key" :placeholder="'key' + index" class="half-width"></el-input>
<div class="split">:</div>
<el-input v-model="item.value" :placeholder="'value' + index" class="half-width"></el-input>
<i class="el-icon-minus" @click="deleteItem(index)"></i>
<el-input v-model="item.value" :placeholder="'value' + index" class="half-width" style="flex-grow: 3;"></el-input>
<el-icon @click="deleteItem(index)" style="margin-left: 5px;"><minus /></el-icon>
</div>
<div class="quick-add-root">
@@ -44,28 +44,31 @@
</div>
</div>
<center style="margin-top: 10px;">
<div style="margin-top: 10px;text-align:center;">
<el-tooltip class="item" effect="dark" content="新增属性 ctrl+a" placement="bottom">
<el-button type="primary" class="center" @click="createNew" icon="el-icon-circle-plus-outline" circle>
<el-button type="primary" class="center" @click="createNew" circle>
<el-icon><circle-plus /></el-icon>
</el-button>
</el-tooltip>
<el-tooltip class="item" effect="dark" content="保存属性 ctrl+s" placement="bottom">
<el-button type="success" class="center" @click="save" icon="el-icon-refresh" circle></el-button>
</el-tooltip>
<el-tooltip class="item" effect="dark" content="移除该组件 ctrl+d" placement="bottom">
<el-button v-if="enableRemoveButton" type="danger" class="center" icon="el-icon-delete" @click="remove" circle>
<el-button type="success" class="center" @click="save" circle>
<el-icon><refresh /></el-icon>
</el-button>
</el-tooltip>
<el-tooltip class="item" effect="dark" content="复制一个兄弟组件 ctrl+c" placement="bottom">
<el-button v-if="enableBroButton" type="primary" class="center" icon="el-icon-copy-document" @click="copyBro"
circle>
<el-tooltip v-if="enableRemoveButton" class="item" effect="dark" content="移除该组件 ctrl+d" placement="bottom">
<el-button type="danger" class="center" @click="remove" circle>
<el-icon><delete /></el-icon>
</el-button>
</el-tooltip>
<center>
<el-tooltip v-if="enableBroButton" class="item" effect="dark" content="复制一个兄弟组件 ctrl+c" placement="bottom">
<el-button type="primary" class="center" @click="copyBro" circle>
<el-icon><document-copy /></el-icon>
</el-button>
</el-tooltip>
<div style="text-algin: center;">
<span class="shortcut-tip">支持快捷键操作</span>
</center>
</center>
</div>
</div>
</el-card>
</template>
@@ -73,7 +76,7 @@
<script>
import { getRawComponentKey, getRawComponentContent } from "@/utils/common";
import { brotherEleEnum, copyBroCode } from "@/libs/bro-ele-config";
const keymaster = require('keymaster');
import keymaster from "keymaster"
export default {
props: ['__rawVueInfo__', 'enableRemoveButton', 'shortcutInitMode'],// __rawVueInfo__为当前编辑的原始代码对象, shortcutInitMode快捷键的初始化方式
@@ -117,8 +120,6 @@ export default {
initShortcut() {
console.log(`init by mode: ${this.shortcutInitMode}`)
keymaster('⌘+a, ctrl+a', () => {
if (this.enable) {
this.createNew();
@@ -159,9 +160,7 @@ export default {
this.localAttributes.push({ key: "", value: "" });
},
save() {
try {
let resultList = [];
if (!this.editMode) {
resultList = this.localAttributes.filter((item) => {
@@ -198,7 +197,7 @@ export default {
},
copyBro() {
copyBroCode(this.__rawVueInfo__);
this.$emit('codeRefresh');
this.$store.commit('onDragEnd');
},
onShow() {
// 这种方式适用于常规模式下的初始化,因为这个实例初始化后不会被销毁,一直常驻内存。但又不能受到其它实例销毁时的影响,所以需要明确的再次初始化。
@@ -271,7 +270,7 @@ export default {
.half-width {
width: 0%;
flex-grow: 1;
flex-grow: 2;
}
.center {

View File

@@ -1,36 +1,36 @@
<template>
<el-dialog title="代码预览" :visible.sync="codeDialogVisible" width="70%" top="10vh" :before-close="handleClose"
:center=true>
<pre style="max-height: 60vh;">
<code v-html="formatCode"></code>
</pre>
<div>
<center style="color: #666; font-size: 12px;">使用代码前请确认相应的组件库已集成至项目</center>
</div>
<span slot="footer">
<el-tooltip effect="dark" content="拷贝" placement="left">
<img class="round-icon" :src="iconCopy" alt="" @click="copyCheck">
</el-tooltip>
<el-tooltip effect="dark" content="下载" placement="right">
<img class="round-icon" :src="iconDownload" alt="" @click="download">
</el-tooltip>
</span>
<el-dialog title="代码预览" v-model="codeDialogVisible" width="70%" top="10vh" :before-close="handleClose" :center=true>
<!-- 这里加v-if是因为CodeEditor内部不支持watch数据监测 -->
<CodeEditor v-if="codeDialogVisible" style="max-height: 65vh;" ref="codeEditor" :initCode="prettyCode" mode="text/html"></CodeEditor>
<div style="color: #666; font-size: 12px; text-align:center; margin: 5px;">使用代码前请确认相应的组件库已集成至项目</div>
<template v-slot:footer>
<span>
<el-tooltip effect="dark" content="拷贝" placement="left">
<img class="round-icon" :src="iconCopy" alt="" @click="copyCheck">
</el-tooltip>
<el-tooltip effect="dark" content="下载" placement="right">
<img class="round-icon" :src="iconDownload" alt="" @click="download">
</el-tooltip>
</span>
</template>
</el-dialog>
</template>
<script>
import './prism.css'
import Prism from "prismjs";
import prettier from "prettier/standalone";
import parserHtml from "prettier/parser-html";
import copy from 'copy-to-clipboard';
import { saveAs } from "file-saver";
import CodeEditor from './CodeEditor.vue'
export default {
props: ['rawCode', 'codeDialogVisible'],
components: {
CodeEditor
},
data() {
return {
@@ -95,10 +95,6 @@ export default {
}
},
formatCode() {
return Prism.highlight(this.prettyCode, Prism.languages.markup, "html");
}
},
fillter: {},
};
@@ -107,7 +103,7 @@ export default {
<style scoped>
/* 在此自动生成 */
::v-deep .el-dialog__body {
:v-deep(.el-dialog__body) {
padding: 0 30px !important;
}

View File

@@ -1,12 +1,12 @@
<template>
<div class="codemirror">
<codemirror v-model="code" :options="cmOption" @cursorActivity="onCmCursorActivity" @ready="onCmReady"
<codemirror v-model:value="code" :options="cmOption" @cursorActivity="onCmCursorActivity" @ready="onCmReady"
@focus="onCmFocus" @blur="onCmBlur" />
</div>
</template>
<script>
import { codemirror } from 'vue-codemirror'
import Codemirror from "codemirror-editor-vue3";
// base style
import 'codemirror/lib/codemirror.css'
@@ -50,20 +50,23 @@ import 'codemirror/addon/fold/indent-fold.js'
import 'codemirror/addon/fold/markdown-fold.js'
import 'codemirror/addon/fold/xml-fold.js'
require(['axios'], axios => {
self.axios = axios.create({
baseURL: '',
timeout: 1000,
});
});
import axios from 'axios';
export default {
props: ['initCode', 'mode'],
name: 'code-editor',
title: 'Mode: text/x-vue & Theme: monokai',
components: {
codemirror
Codemirror
},
created() {
self.axios = axios.create({
baseURL: '',
timeout: 1000,
});
},
computed: {
code: {
get() {
@@ -74,6 +77,12 @@ export default {
}
}
},
watch: {
initCode() {
this.codeStore = this.initCode;
}
},
data() {
return {
codeStore: this.initCode,

View File

@@ -1,125 +0,0 @@
<template>
<preview :value="preview" class="panel"></preview>
</template>
<script>
import Preview from '@/components/Preview';
import { parseComponent } from 'vue-template-compiler/browser';
import getImports from '@/utils/get-imports';
import getPkgs from '@/utils/get-pkgs';
import isAbsouteUrl from 'is-absolute-url';
import * as params from '@/utils/params';
export default {
props: ['code'],
components: {
Preview
},
data: () => ({
preview: '',
}),
watch: {
code: {
handler: function (newValue) {
this.compile(newValue);
},
immediate: true
}
},
methods: {
async compile(code) {
if (!code) {
return;
}
const imports = [];
const { template, script, styles, customBlocks } = parseComponent(code);
let config;
if ((config = customBlocks.find(n => n.type === 'config'))) {
params.clear();
params.parse(config.content);
}
let compiled;
const pkgs = [];
let scriptContent = 'exports = { default: {} }';
if (script) {
try {
compiled = window.Babel.transform(script.content, {
presets: ['es2015', 'es2016', 'es2017', 'stage-0'],
plugins: [[getImports, { imports }]]
}).code;
} catch (e) {
this.preview = `<pre style="color: red">${e.message}</pre>`;
return;
}
scriptContent = await getPkgs(compiled, imports, pkgs);
}
const heads = this.genHeads();
const scripts = [];
pkgs.forEach(pkg => {
scripts.push(
`<script src=//packd.now.sh/${pkg.module}${pkg.path}?name=${pkg.name
}><\/script>`
);
});
styles.forEach(style => {
heads.push(`<style>${style.content}</style>`);
});
scripts.push(`
<script>
var exports = {};
${scriptContent}
var component = exports.default;
component.template = component.template || ${JSON.stringify(
template.content
)}
new Vue(component).$mount('#app')
<\/script>`);
this.preview = {
head: heads.join('\n'),
body: '<div lc_id="app"></div>' + scripts.join('\n')
};
},
genHeads() {
let heads = [];
const { pkgs, css } = params.get();
return [].concat(
[]
.concat(pkgs)
.map(
pkg =>
`<script src="${isAbsouteUrl(pkg) ? '' : prefix}${pkg}"><\/script>`
),
css.map(
item =>
`<link rel=stylesheet href="${isAbsouteUrl(item) ? '' : prefix
}${item}">`
)
);
},
}
};
</script>
<style src="modern-normalize"></style>
<style scoped>
.panel {
background-color: white;
}
</style>

View File

@@ -1,37 +1,24 @@
<template>
<el-drawer :visible.sync="drawer" :with-header="false" size="70%" direction="btt">
<el-drawer v-model="drawer" :with-header="false" size="70%" direction="btt">
<div class="container">
<center>组件结构检视图
<div style="text-align: center;">组件结构检视图
<br>
<span style="font-size:12px;">Components
Structure</span>
</center>
</div>
<el-row :gutter="20" style="height:0px;flex-grow:1;">
<el-col :span="16" style="height: 100%;">
<div style="overflow: scroll;height:100%; margin: 0 20px;padding: 10px;">
<vue-nestable v-model="treeData" @change="onLevelChange">
<template slot-scope="{ item }">
<vue-nestable-handle :item="item">
<i class="el-icon-rank icon-s"></i>
</vue-nestable-handle>
<span @click="onNodeClick(item)">{{ item.text }}</span>
</template>
<div slot="placeholder">
<b>The editor is empty.</b>
</div>
</vue-nestable>
<nested-draggable :data="treeData" />
</div>
</el-col>
<el-col :span="8">
<attribute-input ref="attributeInput" :enableRemoveButton="true" v-if="currentEditRawInfo && drawer"
@save="onSaveAttr" shortcutInitMode="auto" @remove="onRemove" @codeRefresh="codeRefresh"
:__rawVueInfo__="currentEditRawInfo">
<attribute-input ref="attributeInput" :enableRemoveButton="true" v-if="currentEditRawInfo" @save="onSaveAttr"
shortcutInitMode="auto" @remove="onRemove" :__rawVueInfo__="currentEditRawInfo">
</attribute-input>
</el-col>
</el-row>
@@ -41,80 +28,33 @@
</template>
<script>
import "./halower-tree.min.css";
import "@/assets/nestable.css"
import { isObject, getRawComponentKey, getRawComponentContent } from "@/utils/common";
import { VueNestable, VueNestableHandle } from 'vue-nestable';
import nestedDraggable from './nested.vue'
import { defineAsyncComponent } from 'vue'
export default {
props: ['visible'],
emits: ['onLevelChange', 'remove', 'save', 'update:visible', 'reRender'],
components: {
AttributeInput: resolve => { require(["./AttributeInput"], resolve) },
VueNestable,
VueNestableHandle
AttributeInput: defineAsyncComponent(() => import("@/components/AttributeInput.vue")),
nestedDraggable
},
data() {
return {
// 在此自动生成
treeData: [],
currentEditRawInfo: null
};
},
beforeCreate() { },
created() { },
beforeMount() { },
mounted() { },
mounted() {
},
beforeUpdate() { },
updated() { },
destoryed() { },
methods: {
// 在此自动生成
request() {
// 网络请求,可选
},
codeRefresh() {
this.$emit('codeRefresh');
},
onLevelChange(value, options) {
this.$emit('onLevelChange', value.id, options.pathTo);
},
convertStructure(rawInfo) {
const title = getRawComponentKey(rawInfo);
const object = rawInfo[title];
const children = [];
if (isObject(object)) {
for (const key in object) {
if (object.hasOwnProperty(key)) {
if (key === '__children') {
const element = object[key];
element.forEach(item => {
const temp = this.convertStructure(item);
temp && children.push(temp);
})
} else if (isObject(object[key])) {
// 组成一个新的结构,适配只有一个子节点的数据结构
const __obj = {};
__obj[key] = object[key];
const child = this.convertStructure(__obj);
child && children.push(child);
}
}
}
return {
text: title,
expanded: true,
children: children,
rawInfo: rawInfo,
id: getRawComponentContent(rawInfo).lc_id
}
} else {
return null;
}
},
onNodeClick(nodeInfo) {
this.currentEditRawInfo = nodeInfo.rawInfo;
@@ -130,12 +70,17 @@ export default {
},
updateCode(codeRawInfo) {
this.treeData = [this.convertStructure(codeRawInfo)];
this._codeRawInfo = codeRawInfo;
const content = getRawComponentContent(codeRawInfo);
const children = content.__children;
this.treeData = children;
},
},
watch: {
canInitShortcut(newValue) {
renderCount(){
// 这里利用了vuedraggable v-model的特性它会更改对象本身的引用
this.$emit('reRender', this._codeRawInfo);
}
},
computed: {
@@ -147,8 +92,21 @@ export default {
this.$emit('update:visible', false);
}
},
renderCount(){
return this.$store.state.renderCount;
},
canInitShortcut() {
return this.currentEditRawInfo !== null && this.drawer;
},
currentEditRawInfo() {
if (this.$store.state.currentEditComp) {
const vccData = this.$store.state.currentEditComp.vccData;
return window.tree[vccData.lc_id];
} else {
return null;
}
}
},
fillter: {},
@@ -162,7 +120,7 @@ center {
padding: 20px;
}
::v-deep .el-drawer__body {
:v-deep(.el-drawer__body) {
height: 100%;
}

View File

@@ -1,5 +1,5 @@
<template>
<el-dialog title="JS逻辑编辑" :visible.sync="codeDialogVisible" width="70%" top="10vh" :before-close="handleClose"
<el-dialog title="JS逻辑编辑" v-model="codeDialogVisible" width="70%" top="10vh" :before-close="handleClose"
:center=true>
<CodeEditor style="max-height: 65vh;" ref="codeEditor" :initCode="code" mode="text/javascript"></CodeEditor>
@@ -18,6 +18,7 @@ import CodeEditor from './CodeEditor.vue'
export default {
props: ['codeDialogVisible'],
emits: ['saveJSCode', 'update:codeDialogVisible'],
components: {
CodeEditor
},
@@ -117,7 +118,7 @@ export default {
<style scoped>
/* 在此自动生成 */
::v-deep .el-dialog__body {
:v-deep(.el-dialog__body) {
padding: 0 30px !important;
}
</style>

View File

@@ -19,26 +19,28 @@
<div>
<el-dropdown @command="handleCommand">
<span class="el-dropdown-link">
<i ref="help" class="el-icon-question" style="font-size:22px;color:#4dba87;"></i>
<el-icon style="font-size:22px;color:#4dba87;"><question-filled /></el-icon>
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item icon="el-icon-circle-check">基础组件数: {{ componentUnitNum }}
</el-dropdown-item>
<el-dropdown-item icon="el-icon-document" command="lcg">LCG平台</el-dropdown-item>
<el-dropdown-item icon="el-icon-document" command="help">说明文档</el-dropdown-item>
<el-dropdown-item icon="el-icon-chat-line-round" command="chat">在线沟通</el-dropdown-item>
</el-dropdown-menu>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item icon="el-icon-circle-check">基础组件数: {{ componentUnitNum }}
</el-dropdown-item>
<el-dropdown-item icon="el-icon-document" command="lcg">LCG平台</el-dropdown-item>
<el-dropdown-item icon="el-icon-document" command="help">说明文档</el-dropdown-item>
<el-dropdown-item icon="el-icon-chat-line-round" command="chat">在线沟通</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
</div>
</nav>
<nav v-if="currentSelectBrand.titleArray && currentSelectBrand.titleArray.length > 0">
<center style="margin-bottom:10px;">
<div style="margin-bottom:10px;text-align:center;">
<div style="padding:5px;font-size:12px;color:grey;">快速查找需要的</div>
<el-autocomplete class="inline-input" v-model="componentSearch" :fetch-suggestions="querySearch" size="mini"
placeholder="请输入..." @select="handleSelect"></el-autocomplete>
</center>
</div>
<div class="dismiss-scroll">
<div v-for="(item, index) in currentSelectBrand.titleArray" :key="item.title" class="second-nav"
:class="{'active':currentSelectBrand.selectIndex === index}" @click="selectSubnav(currentSelectBrand, index)">
@@ -103,7 +105,7 @@ export default {
titleArray: [], // 快速索引标题默认为空VCC会自动按照className搜集快速索引标题。
},],
currentIndex: 1
currentIndex: 0
};
},
methods: {
@@ -272,7 +274,7 @@ nav {
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.12), 0 0 6px rgba(0, 0, 0, 0.04);
}
::v-deep .el-submenu__title {
:v-deep(.el-submenu__title) {
padding: 0 15px !important;
}

View File

@@ -1,5 +1,5 @@
<template>
<el-dialog title="Vue二次编辑" :visible.sync="vueDialogVisible" width="70%" top="10vh" :before-close="handleClose"
<el-dialog title="Vue二次编辑" v-model="vueDialogVisible" width="70%" top="10vh" :before-close="handleClose"
:center=true>
<CodeEditor style="max-height: 65vh;" ref="codeEditor" :initCode="code" mode="text/html"></CodeEditor>
@@ -20,6 +20,7 @@ import { ergodic, findAObject } from '../utils/common';
export default {
props: ['vueDialogVisible'],
emits: ['codeParseSucess', 'update:vueDialogVisible'],
components: {
CodeEditor
},
@@ -88,7 +89,7 @@ export default {
<style scoped>
/* 在此自动生成 */
::v-deep .el-dialog__body {
:v-deep(.el-dialog__body) {
padding: 0 30px !important;
}
</style>

File diff suppressed because one or more lines are too long

89
src/components/nested.vue Normal file
View File

@@ -0,0 +1,89 @@
<template>
<draggable class="dragArea" tag="ul" :list="data" @start="onStartDrag" @choose="onClick"
@end="onEndDrag">
<template #item="{ element }">
<li class="itemArea">
<p>{{ getRawComponentKey(element) }}</p>
<nested-draggable :data="getRawComponentContent(element).__children" />
</li>
</template>
</draggable>
</template>
<script>
import draggable from "vuedraggable";
import { getRawComponentKey, getRawComponentContent } from "@/utils/common";
export default {
props: {
data: {
required: true,
type: Array
}
},
data() {
return {
}
},
computed: {
},
methods: {
getRawComponentKey,
getRawComponentContent,
onStartDrag(event) {
event.item.classList.add("is-dragging");
},
onClick(event) {
if(this.$store.state.currentEditComp){
this.$store.state.currentEditComp.item.classList.remove("is-dragging");
}
event.item.classList.add("is-dragging");
event.vccData = getRawComponentContent(this.data[event.oldIndex]);
this.$store.commit('storeCurrentEditComp', event);
},
onEndDrag(event) {
event.item.classList.remove("is-dragging");
this.$store.commit('onDragEnd');
}
},
components: {
draggable
},
name: "nested-draggable"
};
</script>
<style scoped lang="scss">
.dragArea {
min-height: 20px;
border: 1px dashed rgb(126, 126, 128);
border-radius: 5px;
padding-inline-start: 30px;
padding-right: 2px;
padding-bottom: 2px;
}
p {
margin: 10px 0;
}
.itemArea {
min-height: 20px;
border: 1px dashed rgb(126, 126, 128);
padding-right: 2px;
padding-bottom: 2px;
border-radius: 5px;
padding-inline-start: 10px;
margin: 10px 0 0;
}
.is-dragging {
background-color: rgba(106, 127, 233, 0.274);
border: 1px dashed rgb(73, 100, 241);
-webkit-border-radius: 5px;
border-radius: 5px;
}
</style>