commit
bb4a06b526
61 changed files with 38938 additions and 0 deletions
@ -0,0 +1,2 @@ |
|||
> 1% |
|||
last 2 versions |
@ -0,0 +1,18 @@ |
|||
module.exports = { |
|||
root: true, |
|||
env: { |
|||
node: true |
|||
}, |
|||
'extends': [ |
|||
'plugin:vue/essential', |
|||
'eslint:recommended' |
|||
], |
|||
rules: { |
|||
'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off', |
|||
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off', |
|||
'no-unused-vars': process.env.NODE_ENV === 'production' ? 'error' : 'off' |
|||
}, |
|||
parserOptions: { |
|||
parser: 'babel-eslint' |
|||
} |
|||
} |
@ -0,0 +1,21 @@ |
|||
.DS_Store |
|||
node_modules |
|||
/dist |
|||
|
|||
# local env files |
|||
.env.local |
|||
.env.*.local |
|||
|
|||
# Log files |
|||
npm-debug.log* |
|||
yarn-debug.log* |
|||
yarn-error.log* |
|||
|
|||
# Editor directories and files |
|||
.idea |
|||
.vscode |
|||
*.suo |
|||
*.ntvs* |
|||
*.njsproj |
|||
*.sln |
|||
*.sw? |
@ -0,0 +1,12 @@ |
|||
module.exports = { |
|||
presets: [ |
|||
'@vue/cli-plugin-babel/preset' |
|||
], |
|||
plugins: [ |
|||
// ['import', {
|
|||
// libraryName: 'ant-design-vue',
|
|||
// libraryDirectory: 'es',
|
|||
// style: true
|
|||
// }, 'vant'],
|
|||
] |
|||
} |
@ -0,0 +1,16 @@ |
|||
const path = require("path"); |
|||
|
|||
let config = { |
|||
modules_root: path.join(__dirname, '../public'), |
|||
modules_dir: './vendor', |
|||
modules: { |
|||
vue: ['vue'], |
|||
ant: ['ant-design-vue'], |
|||
css: ['ant-design-vue/dist/antd.css'], |
|||
vuex: ['vuex'], |
|||
vuerouter: ['vue-router'], |
|||
plugins: ['lodash', 'axios', 'vuex-persistedstate'], |
|||
} |
|||
}; |
|||
|
|||
module.exports = config; |
@ -0,0 +1,45 @@ |
|||
var path = require("path"); |
|||
var webpack = require("webpack"); |
|||
const $config = require("./config.js") |
|||
const MiniCssExtractPlugin = require('mini-css-extract-plugin'); |
|||
const root = path.resolve($config.modules_root, $config.modules_dir); |
|||
module.exports = { |
|||
mode: "production", |
|||
// 你想要打包的模块的数组
|
|||
entry: $config.modules, |
|||
module: { |
|||
rules: [{ |
|||
test: /\.css$/, |
|||
use: [{ |
|||
loader: MiniCssExtractPlugin.loader, |
|||
options: { |
|||
esModule: true, |
|||
}, |
|||
}, |
|||
'css-loader', |
|||
] |
|||
}] |
|||
}, |
|||
output: { |
|||
path: root, // 打包后文件输出的位置
|
|||
filename: '[name].[hash:8].dll.js', |
|||
library: '[name]_library' |
|||
// vendor.dll.js中暴露出的全局变量名。
|
|||
// 主要是给DllPlugin中的name使用,
|
|||
// 故这里需要和webpack.DllPlugin中的`name: '[name]_library',`保持一致。
|
|||
}, |
|||
plugins: [ |
|||
new MiniCssExtractPlugin({ |
|||
// Options similar to the same options in webpackOptions.output
|
|||
// all options are optional
|
|||
filename: '[name].[hash:8].dll.css', |
|||
chunkFilename: '[id].css', |
|||
ignoreOrder: false, // Enable to remove warnings about conflicting order
|
|||
}), |
|||
new webpack.DllPlugin({ |
|||
path: path.join(root, '[name]-manifest.json'), |
|||
name: '[name]_library', |
|||
context: process.cwd() |
|||
}), |
|||
] |
|||
}; |
File diff suppressed because it is too large
@ -0,0 +1,49 @@ |
|||
{ |
|||
"name": "VisiblePlatform", |
|||
"version": "0.1.0", |
|||
"private": true, |
|||
"scripts": { |
|||
"dll": "webpack --config ./build/webpack.dll.config.js", |
|||
"dev": "vue-cli-service serve", |
|||
"build": "vue-cli-service build", |
|||
"zip": "node ./scripts/zip.js", |
|||
"lint": "vue-cli-service lint" |
|||
}, |
|||
"dependencies": { |
|||
"core-js": "^3.3.2", |
|||
"vue": "^2.6.10", |
|||
"vue-router": "^3.1.3", |
|||
"vuex": "^3.0.1", |
|||
"lodash": "^4.17.15", |
|||
"ant-design-vue": "^1.3.8", |
|||
"babel-plugin-import": "^1.13.0" |
|||
}, |
|||
"devDependencies": { |
|||
"@vue/cli-plugin-babel": "^4.0.0", |
|||
"@vue/cli-plugin-eslint": "^4.0.0", |
|||
"@vue/cli-plugin-router": "^4.0.0", |
|||
"@vue/cli-plugin-vuex": "^4.0.0", |
|||
"@vue/cli-service": "^4.0.0", |
|||
"babel-eslint": "^10.0.3", |
|||
"eslint": "^5.16.0", |
|||
"eslint-plugin-vue": "^5.0.0", |
|||
"normalize.css": "^8.0.1", |
|||
"node-sass": "^4.12.0", |
|||
"sass-loader": "^8.0.0", |
|||
"vue-template-compiler": "^2.6.10", |
|||
"webpack-bundle-analyzer": "^3.4.1", |
|||
"compression-webpack-plugin": "^3.0.0", |
|||
"webpack-cli": "^3.3.10", |
|||
"add-asset-html-webpack-plugin": "^3.1.3", |
|||
"compressing": "^1.5.0", |
|||
"vue-router-invoke-webpack-plugin": "^0.4.3", |
|||
"vuex-persistedstate": "^2.7.0", |
|||
"axios": "^0.19.0", |
|||
"mini-css-extract-plugin": "^0.9.0", |
|||
"vue-cli-plugin-qrcode": "*", |
|||
"qrcode-terminal": "^0.12.0", |
|||
"chalk": "^2.4.2", |
|||
"internal-ip": "^4.2.0" |
|||
}, |
|||
"externals": {} |
|||
} |
@ -0,0 +1,5 @@ |
|||
module.exports = { |
|||
plugins: { |
|||
autoprefixer: {}, |
|||
} |
|||
} |
After Width: | Height: | Size: 4.2 KiB |
@ -0,0 +1,47 @@ |
|||
<!DOCTYPE html> |
|||
<html lang="en"> |
|||
|
|||
<head> |
|||
<meta charset="utf-8"> |
|||
<meta http-equiv="X-UA-Compatible" content="IE=edge"> |
|||
<meta name="viewport" content="width=device-width,initial-scale=1.0"> |
|||
<!-- 是否启动webapp功能,会删除默认的苹果工具栏和菜单栏。 --> |
|||
<meta name="apple-mobile-web-app-capable" content="yes" /> |
|||
<!-- 这个主要是根据实际的页面设计的主体色为搭配来进行设置。 --> |
|||
<meta name="apple-mobile-web-app-status-bar-style" content="black" /> |
|||
<!-- 忽略页面中的数字识别为电话号码,email识别 --> |
|||
<meta name="format-detection" content="telephone=no, email=no" /> |
|||
<!-- 启用360浏览器的极速模式(webkit) --> |
|||
<meta name="renderer" content="webkit"> |
|||
<!-- 避免IE使用兼容模式 --> |
|||
<meta http-equiv="X-UA-Compatible" content="IE=edge"> |
|||
<!-- 针对手持设备优化,主要是针对一些老的不识别viewport的浏览器,比如黑莓 --> |
|||
<meta name="HandheldFriendly" content="true"> |
|||
<!-- 微软的老式浏览器 --> |
|||
<meta name="MobileOptimized" content="320"> |
|||
<!-- uc强制竖屏 --> |
|||
<meta name="screen-orientation" content="portrait"> |
|||
<!-- UC强制全屏 --> |
|||
<meta name="full-screen" content="yes"> |
|||
<!-- QQ强制全屏 --> |
|||
<meta name="x5-fullscreen" content="true"> |
|||
<!-- UC应用模式 --> |
|||
<meta name="browsermode" content="application"> |
|||
<!-- QQ应用模式 --> |
|||
<meta name="x5-page-mode" content="app"> |
|||
<!-- windows phone 点击无高光 --> |
|||
<meta name="msapplication-tap-highlight" content="no"> |
|||
<link rel="icon" href="favicon.ico"> |
|||
<title>可视化页面布局</title> |
|||
</head> |
|||
|
|||
<body> |
|||
<noscript> |
|||
<strong>We're sorry but VisiblePlatform doesn't work properly without JavaScript enabled. Please enable it to |
|||
continue.</strong> |
|||
</noscript> |
|||
<div id="app"></div> |
|||
<!-- built files will be auto injected --> |
|||
</body> |
|||
|
|||
</html> |
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -0,0 +1 @@ |
|||
{"name":"css_library","content":{"./node_modules/ant-design-vue/dist/antd.css":{"id":970,"buildMeta":{"providedExports":true}}}} |
File diff suppressed because it is too large
@ -0,0 +1 @@ |
|||
var css_library=function(e){var r={};function t(n){if(r[n])return r[n].exports;var o=r[n]={i:n,l:!1,exports:{}};return e[n].call(o.exports,o,o.exports,t),o.l=!0,o.exports}return t.m=e,t.c=r,t.d=function(e,r,n){t.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:n})},t.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},t.t=function(e,r){if(1&r&&(e=t(e)),8&r)return e;if(4&r&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(t.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var o in e)t.d(n,o,function(r){return e[r]}.bind(null,o));return n},t.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(r,"a",r),r},t.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},t.p="",t(t.s=969)}({969:function(e,r,t){e.exports=t},970:function(e,r,t){}}); |
@ -0,0 +1 @@ |
|||
{"name":"plugins_library","content":{"./node_modules/webpack/buildin/global.js":{"id":51,"buildMeta":{"providedExports":true}},"./node_modules/axios/lib/utils.js":{"id":59,"buildMeta":{"providedExports":true}},"./node_modules/process/browser.js":{"id":95,"buildMeta":{"providedExports":true}},"./node_modules/webpack/buildin/module.js":{"id":156,"buildMeta":{"providedExports":true}},"./node_modules/axios/lib/helpers/bind.js":{"id":607,"buildMeta":{"providedExports":true}},"./node_modules/axios/lib/helpers/buildURL.js":{"id":608,"buildMeta":{"providedExports":true}},"./node_modules/axios/lib/cancel/isCancel.js":{"id":609,"buildMeta":{"providedExports":true}},"./node_modules/axios/lib/defaults.js":{"id":610,"buildMeta":{"providedExports":true}},"./node_modules/axios/lib/adapters/xhr.js":{"id":611,"buildMeta":{"providedExports":true}},"./node_modules/axios/lib/core/createError.js":{"id":612,"buildMeta":{"providedExports":true}},"./node_modules/axios/lib/core/mergeConfig.js":{"id":613,"buildMeta":{"providedExports":true}},"./node_modules/axios/lib/cancel/Cancel.js":{"id":614,"buildMeta":{"providedExports":true}},"./node_modules/lodash/lodash.js":{"id":976,"buildMeta":{"providedExports":true}},"./node_modules/axios/index.js":{"id":977,"buildMeta":{"providedExports":true}},"./node_modules/axios/lib/axios.js":{"id":978,"buildMeta":{"providedExports":true}},"./node_modules/axios/lib/core/Axios.js":{"id":979,"buildMeta":{"providedExports":true}},"./node_modules/axios/lib/core/InterceptorManager.js":{"id":980,"buildMeta":{"providedExports":true}},"./node_modules/axios/lib/core/dispatchRequest.js":{"id":981,"buildMeta":{"providedExports":true}},"./node_modules/axios/lib/core/transformData.js":{"id":982,"buildMeta":{"providedExports":true}},"./node_modules/axios/lib/helpers/normalizeHeaderName.js":{"id":983,"buildMeta":{"providedExports":true}},"./node_modules/axios/lib/core/settle.js":{"id":984,"buildMeta":{"providedExports":true}},"./node_modules/axios/lib/core/enhanceError.js":{"id":985,"buildMeta":{"providedExports":true}},"./node_modules/axios/lib/core/buildFullPath.js":{"id":986,"buildMeta":{"providedExports":true}},"./node_modules/axios/lib/helpers/isAbsoluteURL.js":{"id":987,"buildMeta":{"providedExports":true}},"./node_modules/axios/lib/helpers/combineURLs.js":{"id":988,"buildMeta":{"providedExports":true}},"./node_modules/axios/lib/helpers/parseHeaders.js":{"id":989,"buildMeta":{"providedExports":true}},"./node_modules/axios/lib/helpers/isURLSameOrigin.js":{"id":990,"buildMeta":{"providedExports":true}},"./node_modules/axios/lib/helpers/cookies.js":{"id":991,"buildMeta":{"providedExports":true}},"./node_modules/axios/lib/cancel/CancelToken.js":{"id":992,"buildMeta":{"providedExports":true}},"./node_modules/axios/lib/helpers/spread.js":{"id":993,"buildMeta":{"providedExports":true}},"./node_modules/vuex-persistedstate/dist/vuex-persistedstate.es.js":{"id":994,"buildMeta":{"exportsType":"namespace","providedExports":["default"]}}}} |
File diff suppressed because one or more lines are too long
@ -0,0 +1 @@ |
|||
{"name":"vue_library","content":{"./node_modules/vue/dist/vue.runtime.esm.js":{"id":21,"buildMeta":{"exportsType":"namespace","providedExports":["default"]}},"./node_modules/webpack/buildin/global.js":{"id":51,"buildMeta":{"providedExports":true}},"./node_modules/process/browser.js":{"id":95,"buildMeta":{"providedExports":true}},"./node_modules/timers-browserify/main.js":{"id":299,"buildMeta":{"providedExports":true}},"./node_modules/setimmediate/setImmediate.js":{"id":300,"buildMeta":{"providedExports":true}}}} |
File diff suppressed because one or more lines are too long
@ -0,0 +1 @@ |
|||
{"name":"vuerouter_library","content":{"./node_modules/vue-router/dist/vue-router.esm.js":{"id":974,"buildMeta":{"exportsType":"namespace","providedExports":["default"]}}}} |
File diff suppressed because one or more lines are too long
@ -0,0 +1 @@ |
|||
{"name":"vuex_library","content":{"./node_modules/webpack/buildin/global.js":{"id":51,"buildMeta":{"providedExports":true}},"./node_modules/vuex/dist/vuex.esm.js":{"id":972,"buildMeta":{"exportsType":"namespace","providedExports":["default","Store","createLogger","createNamespacedHelpers","install","mapActions","mapGetters","mapMutations","mapState"]}}}} |
File diff suppressed because one or more lines are too long
@ -0,0 +1,14 @@ |
|||
* 克隆拖拽 |
|||
* 拖拽位置不变 |
|||
* 快速拖动不会失去焦点 |
|||
* 拖动节点时不触发点击事件 |
|||
|
|||
## 预想 |
|||
* 框选部分组件 |
|||
* 导出代码 |
|||
* 删除组件 |
|||
* 组件插槽以及布局 |
|||
|
|||
|
|||
https://woai3c.github.io/visual-drag-demo |
|||
https://github.com/woai3c/visual-drag-demo |
@ -0,0 +1,22 @@ |
|||
const compressing = require('compressing'); |
|||
var path = require('path') |
|||
//格式化data时间
|
|||
function timeStamp2String(time) { |
|||
var datetime = new Date(); |
|||
datetime.setTime(time); |
|||
var year = datetime.getFullYear(); |
|||
var month = datetime.getMonth() + 1 < 10 ? "0" + (datetime.getMonth() + 1) : datetime.getMonth() + 1; |
|||
var date = datetime.getDate() < 10 ? "0" + datetime.getDate() : datetime.getDate(); |
|||
var hour = datetime.getHours() < 10 ? "0" + datetime.getHours() : datetime.getHours(); |
|||
var minute = datetime.getMinutes() < 10 ? "0" + datetime.getMinutes() : datetime.getMinutes(); |
|||
var second = datetime.getSeconds() < 10 ? "0" + datetime.getSeconds() : datetime.getSeconds(); |
|||
return year + "-" + month + "-" + date + "_" + hour + "-" + minute + "-" + second; |
|||
} |
|||
let name = `dist_${timeStamp2String(new Date())}.zip`; |
|||
compressing.zip.compressDir('dist', name) |
|||
.then(() => { |
|||
console.log('success'); |
|||
}) |
|||
.catch(err => { |
|||
console.error(err); |
|||
}); |
@ -0,0 +1,12 @@ |
|||
<template> |
|||
<div id="app"> |
|||
<keep-alive> |
|||
<router-view v-if="$route.meta.alive" /> |
|||
</keep-alive> |
|||
<router-view v-if="!$route.meta.alive" /> |
|||
</div> |
|||
</template> |
|||
|
|||
<style lang="scss"> |
|||
@import "~@/assets/style/global.scss"; |
|||
</style> |
@ -0,0 +1,56 @@ |
|||
import axios from "axios"; |
|||
import qs from "qs"; |
|||
import toast from "@/components/toast"; |
|||
import config from "@/config"; |
|||
|
|||
const checkERR = (err, type) => { |
|||
console.log(err); |
|||
switch (type) { |
|||
case 0: |
|||
// 来自执行的错误
|
|||
break; |
|||
case 1: |
|||
//来自于接口的错误
|
|||
break; |
|||
} |
|||
}; |
|||
|
|||
// 静默执行
|
|||
export function request_(type = "GET", url, data = {},contentType=0,sendToken = true) { |
|||
var __data = {}; |
|||
if (type.toUpperCase() == "GET") { |
|||
__data.params = data; |
|||
} |
|||
if (type.toUpperCase() == "POST") { |
|||
__data.data = data; // qs.stringify(data)
|
|||
} |
|||
return new Promise((resolve, reject) => { |
|||
var reg = /^([hH][tT]{2}[pP]:\/\/|[hH][tT]{2}[pP][sS]:\/\/)/; |
|||
var realUrl = url; |
|||
if (!reg.test(url)) { |
|||
realUrl = config.BASEURL + url; |
|||
} |
|||
axios({ |
|||
url: realUrl, |
|||
method: type, |
|||
...__data, |
|||
headers:{ |
|||
$type: contentType, |
|||
$token: sendToken, |
|||
}, |
|||
responseType: "json", |
|||
}) |
|||
.then((res) => { |
|||
if (res.status == 200 && res.data && res.data.error == 0) { |
|||
resolve(res.data); |
|||
} else { |
|||
checkERR(res, 1); |
|||
reject(res); |
|||
} |
|||
}) |
|||
.catch((err) => { |
|||
checkERR(err, 0); |
|||
reject(err); |
|||
}); |
|||
}); |
|||
} |
@ -0,0 +1,33 @@ |
|||
import axios from "axios"; |
|||
import store from "@/store"; |
|||
|
|||
axios.interceptors.request.use( |
|||
(config) => { |
|||
if (!config.headers["Content-Type"]) { |
|||
config.headers = { |
|||
Accept: "application/json", |
|||
"Content-Type": "application/x-www-form-urlencoded", |
|||
}; |
|||
} |
|||
// if (store.getters.token) {
|
|||
// config.headers = {
|
|||
// 'Authorization': store.getters.token
|
|||
// }
|
|||
// }
|
|||
return config; |
|||
}, |
|||
(error) => { |
|||
return Promise.reject(error); |
|||
} |
|||
); |
|||
|
|||
// 添加响应拦截器
|
|||
axios.interceptors.response.use( |
|||
(res) => { |
|||
//对错误代码做处理
|
|||
return res; |
|||
}, |
|||
(error) => { |
|||
return Promise.reject(error); |
|||
} |
|||
); |
After Width: | Height: | Size: 6.7 KiB |
@ -0,0 +1,3 @@ |
|||
html,body,#app{ |
|||
height: 100%; |
|||
} |
@ -0,0 +1,23 @@ |
|||
// 引入vue 及 lodash
|
|||
import Vue from 'vue' |
|||
import { |
|||
upperFirst |
|||
} from 'lodash' // 首字线大写
|
|||
import { |
|||
camelCase |
|||
} from 'lodash' // 驼峰命名大法
|
|||
|
|||
// 把 /component/base/ 下的所有 vue 组件 require 进来
|
|||
// path: 要引入的组件所在相对路径(相对于当前文件)
|
|||
// deep: 是否检索子文件夹
|
|||
// matchFile: 匹配的文件名称
|
|||
// require.context(path, deep, matchFile)
|
|||
const requireComponent = require.context('./', true, /base-[\w-]+\.vue$/) |
|||
|
|||
// 遍历 require 进来的组件并注册
|
|||
requireComponent.keys().forEach((fileName) => { |
|||
const componentConfig = requireComponent(fileName) |
|||
const componentName = upperFirst(camelCase(fileName.replace(/^\.\/_/, '').replace(/\.\w+$/, ''))) |
|||
// 全局注册组件
|
|||
Vue.component(componentName, componentConfig.default || componentConfig) |
|||
}) |
@ -0,0 +1,26 @@ |
|||
import Toast from "./toast.vue"; |
|||
import Vue from "vue"; |
|||
function createToast(text, duration = 2000) { |
|||
let VueToast = Vue.extend({ |
|||
render(h) { |
|||
let props = { |
|||
text: text, |
|||
}; |
|||
return h(Toast, { |
|||
props, |
|||
}); |
|||
}, |
|||
}); |
|||
let newToast = new VueToast(); |
|||
let vm = newToast.$mount(); |
|||
document.body.append(vm.$el); |
|||
setTimeout(() => { |
|||
vm.$el.remove(); |
|||
vm = null; |
|||
}, duration); |
|||
} |
|||
|
|||
createToast.install = function(Vue, defaultOptions = {}) { |
|||
Vue.prototype.$toast = createToast; |
|||
}; |
|||
export default createToast; |
@ -0,0 +1,29 @@ |
|||
<template> |
|||
<div class="base-toast"> |
|||
{{ text }} |
|||
</div> |
|||
</template> |
|||
|
|||
<script> |
|||
export default { |
|||
props: ['text'], |
|||
methods: { |
|||
show() {} |
|||
} |
|||
} |
|||
</script> |
|||
<style lang="scss" scoped> |
|||
.base-toast { |
|||
position: fixed; |
|||
max-width: 60%; |
|||
text-align: center; |
|||
top: 50%; |
|||
left: 50%; |
|||
transform: translate(-50%, -50%); |
|||
background-color: rgba(0, 0, 0, 0.733); |
|||
color: white; |
|||
padding: 10px; |
|||
border-radius: 10px; |
|||
z-index: 999; |
|||
} |
|||
</style> |
@ -0,0 +1,3 @@ |
|||
module.exports = { |
|||
BASEURL: '' |
|||
} |
@ -0,0 +1,19 @@ |
|||
let isProd = process.env.NODE_ENV == "production"; |
|||
|
|||
let config = {} |
|||
|
|||
if (isProd) { |
|||
const prodConfig = require("./production"); |
|||
config = { |
|||
...config, |
|||
...prodConfig |
|||
} |
|||
} |
|||
if (!isProd) { |
|||
const devConfig = require("./development"); |
|||
config = { |
|||
...config, |
|||
...devConfig |
|||
} |
|||
} |
|||
export default config; |
@ -0,0 +1,3 @@ |
|||
module.exports = { |
|||
BASEURL: '' |
|||
} |
@ -0,0 +1,130 @@ |
|||
let list = [ |
|||
{ |
|||
name: "a-button", |
|||
describe: "按钮", |
|||
type: "component", |
|||
slot: [{ type: "text", text: "按钮" }], |
|||
props: {}, |
|||
id: "customID", |
|||
style: { |
|||
left: 0, |
|||
top: 0, |
|||
width: 80, |
|||
height: 32, |
|||
}, |
|||
}, |
|||
{ |
|||
name: "a-avatar", |
|||
describe: "图片", |
|||
type: "component", |
|||
slot: [], |
|||
props: { |
|||
icon: "user", |
|||
}, |
|||
id: "badgeID", |
|||
style: { |
|||
left: 0, |
|||
top: 0, |
|||
width: 32, |
|||
height: 32, |
|||
}, |
|||
}, |
|||
{ |
|||
name: "a-input", |
|||
describe: "输入框", |
|||
type: "component", |
|||
slot: [], |
|||
props: {}, |
|||
id: "inputID", |
|||
style: { |
|||
height: 32, |
|||
width: 120, |
|||
left: 0, |
|||
top: 0, |
|||
}, |
|||
}, |
|||
{ |
|||
name: "a-tooltip", |
|||
describe: "tooltip", |
|||
type: "component", |
|||
slot: [ |
|||
{ |
|||
name: "a-button", |
|||
type: "component", |
|||
slot: [{ type: "text", text: "阿萨大" }], |
|||
props: {}, |
|||
style: { |
|||
left: 0, |
|||
top: 0, |
|||
width: 80, |
|||
height: 32, |
|||
}, |
|||
}, |
|||
], |
|||
props: { placement: "topLeft", title: "Prompt Text" }, |
|||
id: "tooltipID", |
|||
style: { |
|||
height: 32, |
|||
width: 80, |
|||
left: 0, |
|||
top: 0, |
|||
}, |
|||
}, |
|||
{ |
|||
name: "a-cascader", |
|||
describe: "级联选择", |
|||
type: "component", |
|||
slot: [], |
|||
props: { |
|||
options: [ |
|||
{ |
|||
value: "zhejiang", |
|||
label: "Zhejiang", |
|||
children: [ |
|||
{ |
|||
value: "hangzhou", |
|||
label: "Hangzhou", |
|||
children: [ |
|||
{ |
|||
value: "xihu", |
|||
label: "West Lake", |
|||
}, |
|||
], |
|||
}, |
|||
], |
|||
}, |
|||
{ |
|||
value: "jiangsu", |
|||
label: "Jiangsu", |
|||
children: [ |
|||
{ |
|||
value: "nanjing", |
|||
label: "Nanjing", |
|||
children: [ |
|||
{ |
|||
value: "zhonghuamen", |
|||
label: "Zhong Hua Men", |
|||
}, |
|||
], |
|||
}, |
|||
], |
|||
}, |
|||
], |
|||
placeholder: "Please select", |
|||
}, |
|||
methods: { |
|||
onChange(value) { |
|||
console.log(value); |
|||
}, |
|||
}, |
|||
id: "cascaderID", |
|||
style: { |
|||
width: 120, |
|||
height: 32, |
|||
left: 0, |
|||
top: 0, |
|||
}, |
|||
}, |
|||
]; |
|||
|
|||
export default list; |
@ -0,0 +1,35 @@ |
|||
import "normalize.css" |
|||
import Vue from 'vue' |
|||
import App from '@/App.vue' |
|||
import router from '@/router'; |
|||
import store from '@/store' |
|||
import '@/components/base' |
|||
import toast from '@/components/toast' |
|||
import '@/vendor/ant' |
|||
import {cloneDeep} from "lodash" |
|||
//http://kazupon.github.io/vue-i18n/api/#constructor-options
|
|||
Vue.prototype.$event = new Vue() |
|||
Vue.config.productionTip = process.env.NODE_ENV === 'production' ? true : false; |
|||
|
|||
Vue.use(toast); |
|||
|
|||
Vue.prototype.$computeStyle= function(style){ |
|||
let myStyle = cloneDeep(style); |
|||
let attr = ["left", "width", "height", "top", "right", "bottom"]; |
|||
let attrStr = ["backgroundColor", "color","position",'zIndex']; |
|||
Object.keys(style).forEach((v) => { |
|||
if (attr.indexOf(v) != -1 && typeof style[v] == "number") { |
|||
myStyle[v] = style[v] + "px"; |
|||
} |
|||
if (attrStr.indexOf(v) != -1) { |
|||
myStyle[v] = style[v]; |
|||
} |
|||
}); |
|||
return myStyle |
|||
}, |
|||
|
|||
new Vue({ |
|||
router, |
|||
store, |
|||
render: h => h(App) |
|||
}).$mount('#app') |
@ -0,0 +1,32 @@ |
|||
import Vue from 'vue'; |
|||
import Router from 'vue-router';; |
|||
Vue.use(Router); |
|||
export const routes = [{ |
|||
component: () => import('@/views/about/index.vue'), |
|||
name: 'about', |
|||
meta: { |
|||
alive: true, |
|||
}, |
|||
path: '/about', |
|||
}, |
|||
{ |
|||
component: () => import('@/views/home/index.vue'), |
|||
name: 'home', |
|||
path: '/home', |
|||
}, |
|||
{ |
|||
path: '/', |
|||
redirect: '/home' |
|||
}, |
|||
|
|||
{ |
|||
name: 'notFound', |
|||
path: '*', |
|||
component: () => import('@/views/NotFound.vue') |
|||
}, |
|||
]; |
|||
const router = new Router({ |
|||
mode: 'hash', |
|||
routes, |
|||
}); |
|||
export default router; |
@ -0,0 +1,33 @@ |
|||
// import Vue from 'vue'
|
|||
// import VueRouter from 'vue-router'
|
|||
// import Home from '../views/home'
|
|||
|
|||
// Vue.use(VueRouter)
|
|||
|
|||
// const routes = [{
|
|||
// path: '/home',
|
|||
// alias:'/',
|
|||
// name: 'home',
|
|||
// meta:{alive:true},
|
|||
// component: Home
|
|||
// },{
|
|||
// path: '/about',
|
|||
// name: 'about',
|
|||
// meta:{alive:true},
|
|||
// component: ()=>import('@/views/about')
|
|||
// }
|
|||
// ]
|
|||
|
|||
// const router = new VueRouter({
|
|||
// mode: 'history',
|
|||
// base: process.env.BASE_URL,
|
|||
// routes
|
|||
// })
|
|||
|
|||
// export default router
|
|||
import router from './.invoke/router'; |
|||
router.beforeEach((to, from, next) => { |
|||
next(); |
|||
}) |
|||
export default router; |
|||
// https://zhuanlan.zhihu.com/p/63079674
|
@ -0,0 +1,33 @@ |
|||
import Vue from 'vue' |
|||
import Vuex from 'vuex' |
|||
import createPersistedState from 'vuex-persistedstate' |
|||
import root from './root' |
|||
|
|||
|
|||
Vue.use(Vuex); |
|||
const modules = {}; |
|||
const local = []; |
|||
const requireComponent = require.context('./modules/', true, /^(.*?).js$/); |
|||
// 遍历 require 进来的组件并注册
|
|||
requireComponent.keys().forEach((fileName) => { |
|||
const key = fileName.replace(/^\.\//, '').replace(/\.\w+$/, ''); |
|||
const componentConfig = requireComponent(fileName) |
|||
const module = componentConfig.default || componentConfig; |
|||
modules[key] = module; |
|||
if (module.local == true) { |
|||
local.push(key); |
|||
} |
|||
if (Array.isArray(module.local) && module.local.length > 0) { |
|||
module.local.forEach(v => { |
|||
local.push(key + '.' + v); |
|||
}) |
|||
} |
|||
}); |
|||
const vuexStore = new Vuex.Store({ |
|||
modules, |
|||
...root, |
|||
plugins: [createPersistedState({ |
|||
paths: local |
|||
})] |
|||
}); |
|||
export default vuexStore |
@ -0,0 +1,41 @@ |
|||
const state = { |
|||
components: [], |
|||
}; |
|||
const getters = { |
|||
components(state) { |
|||
return state.components; |
|||
}, |
|||
}; |
|||
|
|||
const mutations = { |
|||
addComponent(state, payload) { |
|||
state.components.push(payload); |
|||
}, |
|||
editComponent(state, payload, index) { |
|||
state.components.splice(index, 1, payload); |
|||
}, |
|||
replaceComponents(state,payload){ |
|||
state.components = payload |
|||
}, |
|||
}; |
|||
|
|||
const actions = { |
|||
addComponent({ commit }, payload) { |
|||
commit("addComponent", payload); |
|||
}, |
|||
editComponent({ commit }, payload, index) { |
|||
commit("editComponent", payload, index); |
|||
}, |
|||
replaceComponents({ commit }, payload) { |
|||
commit("replaceComponents", payload); |
|||
}, |
|||
}; |
|||
|
|||
export default { |
|||
namespaced: true, |
|||
local: false, |
|||
state, |
|||
getters, |
|||
mutations, |
|||
actions, |
|||
}; |
@ -0,0 +1,30 @@ |
|||
const state = { |
|||
token: "", |
|||
userinfo: {} |
|||
} |
|||
const getters = { |
|||
token(state) { |
|||
return state.token |
|||
}, |
|||
userinfo(state) { |
|||
return state.userinfo |
|||
}, |
|||
} |
|||
|
|||
|
|||
const mutations = { |
|||
|
|||
} |
|||
|
|||
const actions = { |
|||
|
|||
} |
|||
|
|||
export default { |
|||
namespaced: true, |
|||
local: true, |
|||
state, |
|||
getters, |
|||
mutations, |
|||
actions |
|||
} |
@ -0,0 +1,10 @@ |
|||
export default { |
|||
state: {}, |
|||
getters: { |
|||
components(state){ |
|||
return state.components.components |
|||
} |
|||
}, |
|||
mutations: {}, |
|||
actions: {} |
|||
} |
@ -0,0 +1,54 @@ |
|||
<template> |
|||
<div class="detail"> |
|||
<div v-if="currentDetail"> |
|||
<a-input v-model.number="currentDetail.style.left" @blur="blur" /> |
|||
<a-input v-model.number="currentDetail.style.top" @blur="blur" /> |
|||
</div> |
|||
{{ currentDetail }}--{{ currentIndex }} |
|||
</div> |
|||
</template> |
|||
|
|||
<script> |
|||
import {cloneDeep} from "lodash" |
|||
export default { |
|||
data() { |
|||
return { |
|||
currentDetail: null, |
|||
currentIndex: -1, |
|||
}; |
|||
}, |
|||
created() { |
|||
// 激活详情页 |
|||
this.$event.$on("@editor:publish:activeOne", (item, index) => { |
|||
this.currentDetail = cloneDeep(item); |
|||
this.currentIndex = index; |
|||
}); |
|||
// 获取编辑器更新的数据 |
|||
this.$event.$on("@editor:notice:update", (item, index) => { |
|||
// 编辑器更新节点数据 |
|||
this.currentDetail = cloneDeep(item); |
|||
this.currentIndex = index; |
|||
}); |
|||
}, |
|||
beforeDestroy(){ |
|||
this.$event.$off("@editor:publish:activeOne") |
|||
this.$event.$off("@editor:notice:update") |
|||
}, |
|||
methods: { |
|||
blur() { |
|||
console.log(this.currentDetail,this.currentIndex); |
|||
this.$event.$emit("@editor:publish:updateOne",this.currentDetail,this.currentIndex) |
|||
}, |
|||
}, |
|||
}; |
|||
</script> |
|||
|
|||
<style lang="scss" scoped> |
|||
.detail { |
|||
height: 100%; |
|||
width: 200px; |
|||
border-left: 1px solid blue; |
|||
border-top: 1px solid blue; |
|||
padding: 10px; |
|||
} |
|||
</style> |
@ -0,0 +1,184 @@ |
|||
<template> |
|||
<div class="makeline"> |
|||
<slot></slot> |
|||
<div |
|||
v-for="(item, index) in lineTop" |
|||
:key="index" |
|||
:style="$computeStyle(item)" |
|||
></div> |
|||
<div |
|||
v-for="(item, index) in lineLeft" |
|||
:key="index" |
|||
:style="$computeStyle(item)" |
|||
></div> |
|||
</div> |
|||
</template> |
|||
|
|||
<script> |
|||
import { cloneDeep } from "lodash"; |
|||
let value = 4; |
|||
let lines = []; |
|||
let curIndex = 0; |
|||
let temp1 = { |
|||
id: "", |
|||
position: "absolute", |
|||
left: 0, |
|||
right: 0, |
|||
top: 0, |
|||
height: 1, |
|||
backgroundColor: "red", |
|||
}; |
|||
let temp2 = { |
|||
id: "", |
|||
position: "absolute", |
|||
left: 0, |
|||
bottom: 0, |
|||
top: 0, |
|||
width: 1, |
|||
backgroundColor: "red", |
|||
}; |
|||
|
|||
export default { |
|||
data() { |
|||
return { |
|||
lineLeft: [], |
|||
lineTop: [], |
|||
}; |
|||
}, |
|||
methods: { |
|||
removeAll(){ |
|||
this.lineLeft = []; |
|||
this.lineTop = []; |
|||
}, |
|||
listenerChange(list, index,cb) { |
|||
const activeStyle = list[index].style; |
|||
list.forEach((item, jndex) => { |
|||
if (jndex !== index) { |
|||
let curStyle = item.style; |
|||
|
|||
let curBottom = curStyle.top+curStyle.height |
|||
let curRight = curStyle.left+curStyle.width |
|||
let actRight = activeStyle.left+activeStyle.width |
|||
let actBottom = activeStyle.top+activeStyle.height |
|||
|
|||
|
|||
this.removeLine(curStyle.top, curStyle.left); |
|||
this.removeLine(curBottom, curRight); |
|||
// TODO 中 中 |
|||
// 上--上 |
|||
if (Math.abs(activeStyle.top - curStyle.top) <= value) { |
|||
activeStyle.top = curStyle.top; |
|||
this.makeTop2Bottom(curStyle.top); |
|||
cb&&cb(list[index],index) |
|||
} |
|||
// 下--上 |
|||
if (Math.abs(actBottom - curBottom) <= value) { |
|||
activeStyle.top = curBottom - activeStyle.height; |
|||
this.makeTop2Bottom(curBottom); |
|||
cb&&cb(list[index],index) |
|||
} |
|||
// 上--下 |
|||
if (Math.abs(activeStyle.top - curBottom) <= value) { |
|||
activeStyle.top = curBottom; |
|||
this.makeTop2Bottom(curBottom); |
|||
cb&&cb(list[index],index) |
|||
} |
|||
// 左--左 |
|||
if (Math.abs(activeStyle.left - curStyle.left) <= value) { |
|||
activeStyle.left = curStyle.left; |
|||
this.makeLeft2Right(curStyle.left); |
|||
cb&&cb(list[index],index) |
|||
} |
|||
// 左--右 |
|||
if (Math.abs(activeStyle.left - curRight) <= value) { |
|||
activeStyle.left = curRight |
|||
this.makeLeft2Right(curRight); |
|||
cb&&cb(list[index],index) |
|||
} |
|||
// 右--右 |
|||
if (Math.abs(actRight - curRight) <= value) { |
|||
activeStyle.left = curRight - activeStyle.width; |
|||
this.makeLeft2Right(curRight); |
|||
cb&&cb(list[index],index) |
|||
} |
|||
// 下--上 |
|||
if (Math.abs(actBottom - curStyle.top) <= value) { |
|||
activeStyle.top = curStyle.top - activeStyle.height; |
|||
this.makeTop2Bottom(curStyle.top); |
|||
cb&&cb(list[index],index) |
|||
} |
|||
// 右--左 |
|||
if (Math.abs(actRight - curStyle.left) <= value) { |
|||
activeStyle.left = curStyle.left - activeStyle.width; |
|||
this.makeLeft2Right(curStyle.left); |
|||
cb&&cb(list[index],index) |
|||
} |
|||
} |
|||
}); |
|||
}, |
|||
removeLine(top, left) { |
|||
let isHave = -1; |
|||
this.lineTop.forEach((line, index) => { |
|||
if (line.top == top) { |
|||
isHave = index; |
|||
} |
|||
}); |
|||
if (isHave != -1) { |
|||
this.lineTop.splice(isHave, 1); |
|||
} |
|||
let isHaveLeft = -1; |
|||
this.lineLeft.forEach((line, index) => { |
|||
if (line.left == left) { |
|||
isHaveLeft = index; |
|||
} |
|||
}); |
|||
if (isHaveLeft != -1) { |
|||
this.lineLeft.splice(isHaveLeft, 1); |
|||
} |
|||
}, |
|||
// 制作从左到右的线 |
|||
makeTop2Bottom(top) { |
|||
const line = cloneDeep(temp1); |
|||
curIndex++; |
|||
line.top = top; |
|||
line.id = curIndex; |
|||
let isHave = false; |
|||
this.lineTop.forEach((line) => { |
|||
if (Math.abs(line.top - top) <= value) { |
|||
isHave = true; |
|||
} |
|||
}); |
|||
if (!isHave) { |
|||
this.lineTop.push(line); |
|||
} |
|||
}, |
|||
// 制作从上到下的线 |
|||
makeLeft2Right(left) { |
|||
const line = cloneDeep(temp2); |
|||
curIndex++; |
|||
line.id = curIndex; |
|||
line.left = left; |
|||
let isHave = false; |
|||
this.lineLeft.forEach((line) => { |
|||
if (Math.abs(line.left - left) <= value) { |
|||
isHave = true; |
|||
} |
|||
}); |
|||
if (!isHave) { |
|||
this.lineLeft.push(line); |
|||
} |
|||
}, |
|||
}, |
|||
}; |
|||
</script> |
|||
|
|||
<style lang="scss" scoped> |
|||
.makeline { |
|||
position: absolute; |
|||
top: 0; |
|||
left: 0; |
|||
right: 0; |
|||
bottom: 0; |
|||
pointer-events: none; |
|||
} |
|||
</style> |
@ -0,0 +1,24 @@ |
|||
<template> |
|||
<div class="shape"> |
|||
<slot></slot> |
|||
</div> |
|||
</template> |
|||
<style lang="scss" scoped> |
|||
.shape { |
|||
position: absolute; |
|||
transition: none; |
|||
&.moving { |
|||
z-index: 99; |
|||
pointer-events: none; |
|||
user-select: none; |
|||
} |
|||
&.staying { |
|||
pointer-events: none; |
|||
user-select: none; |
|||
} |
|||
> .component{ |
|||
width: 100%; |
|||
height: 100%; |
|||
} |
|||
} |
|||
</style> |
@ -0,0 +1,31 @@ |
|||
<template> |
|||
<component |
|||
class="component" |
|||
:is="detail.name" |
|||
v-if="detail.type == 'component'" |
|||
v-bind="detail.props" |
|||
v-on="detail.methods" |
|||
> |
|||
<template v-for="(child, childIndex) in detail.slot"> |
|||
<template v-if="child.type == 'text'"> {{ child.text }}</template> |
|||
<customComponent v-if="child.type == 'component'" :detail="child" :key="childIndex" /> |
|||
</template> |
|||
</component> |
|||
</template> |
|||
<script> |
|||
export default { |
|||
name: "customComponent", |
|||
props: { |
|||
detail: { |
|||
type: Object, |
|||
default: () => {}, |
|||
}, |
|||
}, |
|||
}; |
|||
</script> |
|||
<style lang="scss" scoped> |
|||
.component { |
|||
// width: 100%; |
|||
// height: 100%; |
|||
} |
|||
</style> |
@ -0,0 +1,146 @@ |
|||
<template> |
|||
<div |
|||
class="editor" |
|||
:class="[isEnter ? 'dragging' : '']" |
|||
@dragenter="dragenter" |
|||
@dragleave="dragleave" |
|||
@dragover="dragover" |
|||
@drop="drop" |
|||
> |
|||
<Shape |
|||
v-for="(item, index) in componentList" |
|||
ref="shape" |
|||
@mousedown.native="mousedown($event, index)" |
|||
:class="[ |
|||
movingIndex == index ? 'moving' : '', |
|||
movingIndex != -1 && movingIndex != index ? 'staying' : '', |
|||
]" |
|||
:key="index" |
|||
:style="[$computeStyle(item.style)]" |
|||
> |
|||
<customComponent :detail="item" :key="index" /> |
|||
</Shape> |
|||
<MakeLine ref="makeline"> </MakeLine> |
|||
</div> |
|||
</template> |
|||
|
|||
<script> |
|||
import componentList from "@/data/component-list"; |
|||
import { cloneDeep } from "lodash"; |
|||
import Shape from "./Shape"; |
|||
import customComponent from "./customComponent"; |
|||
import MakeLine from "./MakeLine"; |
|||
export default { |
|||
components: { |
|||
Shape, |
|||
customComponent, |
|||
MakeLine, |
|||
}, |
|||
data() { |
|||
return { |
|||
isEnter: false, |
|||
movingIndex: -1, |
|||
activeIndex: -1, |
|||
componentList: [], |
|||
}; |
|||
}, |
|||
created () { |
|||
// 更新了一个组件 |
|||
this.$event.$on("@editor:publish:updateOne",(item,index)=>{ |
|||
this.componentList.splice(index, 1, item); |
|||
// 绑定历史记录 |
|||
// 更新了组件状态 |
|||
this.$event.$emit("@editor:notice:update",item,index); |
|||
}) |
|||
}, |
|||
beforeDestroy(){ |
|||
this.$event.$off("@editor:publish:updateOne") |
|||
}, |
|||
methods: { |
|||
mousedown(ev, index) { |
|||
let startX = ev.clientX; |
|||
let startY = ev.clientY; |
|||
let startW = this.$refs.shape[index].$el.offsetLeft; |
|||
let startH = this.$refs.shape[index].$el.offsetTop; |
|||
this.activeIndex = index |
|||
this.$event.$emit("@editor:publish:activeOne",this.componentList[index],index); |
|||
let isMoving = false; |
|||
document.onmousemove = (ex) => { |
|||
if (!isMoving && Math.abs(ex.clientX - startX) > 1) { |
|||
isMoving = true; |
|||
} |
|||
if (isMoving) { |
|||
this.$refs.shape[index].$el.onmousemove = null; |
|||
// 移动的时候才算是移动状态 |
|||
this.movingIndex = index; |
|||
let el = this.componentList[index]; |
|||
let offsetX = ex.clientX - startX + startW; |
|||
let offsetY = ex.clientY - startY + startH; |
|||
el.style.left = offsetX; |
|||
el.style.top = offsetY; |
|||
this.componentList.splice(index, 1, el); |
|||
// 派发组件更新事件 |
|||
this.$event.$emit("@editor:notice:update",el,index); |
|||
this.$refs.makeline.listenerChange(this.componentList, index,(item,index)=>{ |
|||
this.$event.$emit("@editor:notice:update",item,index); |
|||
}); |
|||
} |
|||
}; |
|||
document.onmouseup = (ex) => { |
|||
// 绑定历史记录 |
|||
this.$refs.makeline.removeAll(); |
|||
isMoving = false; |
|||
this.movingIndex = -1; |
|||
document.onmousemove = null; |
|||
document.onmouseup = null; |
|||
}; |
|||
}, |
|||
drag(e) { |
|||
console.log(e); |
|||
}, |
|||
dragenter() { |
|||
this.isEnter = true; |
|||
}, |
|||
dragleave() { |
|||
this.isEnter = false; |
|||
}, |
|||
drop(ev) { |
|||
ev.stopPropagation(); |
|||
try { |
|||
this.isEnter = false; |
|||
var { id, x, y } = JSON.parse( |
|||
decodeURIComponent(ev.dataTransfer.getData("component-transfer")) |
|||
); |
|||
let offsetX = ev.offsetX - x; |
|||
let offsetY = ev.offsetY - y; |
|||
let el = cloneDeep(componentList.filter((v) => v.id === id)[0]); |
|||
el.style.left = offsetX; |
|||
el.style.top = offsetY; |
|||
this.componentList.push(el); |
|||
} catch (error) { |
|||
console.warn(error); |
|||
} |
|||
}, |
|||
dragover(ev) { |
|||
ev.preventDefault(); |
|||
}, |
|||
}, |
|||
}; |
|||
</script> |
|||
|
|||
<style lang="scss" scoped> |
|||
.editor { |
|||
width: 200px; |
|||
height: 100%; |
|||
border-top: 1px solid blue; |
|||
position: relative; |
|||
overflow: hidden; |
|||
&.dragging { |
|||
border-color: red; |
|||
* { |
|||
pointer-events: none; |
|||
user-select: none; |
|||
} |
|||
} |
|||
} |
|||
</style> |
@ -0,0 +1,5 @@ |
|||
<template> |
|||
<header> |
|||
header |
|||
</header> |
|||
</template> |
@ -0,0 +1,37 @@ |
|||
<template> |
|||
<div class="panel"> |
|||
<a-button v-for="(item,index) in componentList" :key="index" @dragstart="drag($event,item)" @drag="drago" draggable>{{item.describe}}</a-button> |
|||
</div> |
|||
</template> |
|||
|
|||
<script> |
|||
import data from "@/data/component-list" |
|||
export default { |
|||
data () { |
|||
return { |
|||
componentList : data |
|||
} |
|||
}, |
|||
methods: { |
|||
drago(event){ |
|||
|
|||
}, |
|||
drag(event,row){ |
|||
event.dataTransfer.setData("component-transfer",encodeURIComponent(JSON.stringify({id:row.id,x:event.layerX,y:event.layerY}))); |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style lang="scss" scoped> |
|||
.panel{ |
|||
height: 100%; |
|||
width: 200px; |
|||
border-right: 1px solid blue; |
|||
border-top: 1px solid blue; |
|||
padding: 10px; |
|||
>*{ |
|||
margin: 5px 10px; |
|||
} |
|||
} |
|||
</style> |
@ -0,0 +1,7 @@ |
|||
import Vue from 'vue'; |
|||
// 通用采用了按需引入
|
|||
import Antd from 'ant-design-vue'; |
|||
import 'ant-design-vue/dist/antd.css'; |
|||
|
|||
console.log(Antd) |
|||
Vue.use(Antd); |
@ -0,0 +1,5 @@ |
|||
<template> |
|||
<div> |
|||
404 |
|||
</div> |
|||
</template> |
@ -0,0 +1,19 @@ |
|||
<template> |
|||
<div> |
|||
<router-link to="/home" replace>Go to home</router-link> |
|||
<input type="text" /> |
|||
</div> |
|||
</template> |
|||
<script> |
|||
export default { |
|||
beforeRouteLeave(to, from, next) { |
|||
next(); |
|||
}, |
|||
beforeRouteEnter(to, from, next) { |
|||
next(vw => { |
|||
// 去除缓存 |
|||
vw.$route.meta.alive = false; |
|||
}); |
|||
} |
|||
}; |
|||
</script> |
@ -0,0 +1,2 @@ |
|||
meta: |
|||
- alive: true |
@ -0,0 +1,49 @@ |
|||
<template> |
|||
<div class="home"> |
|||
<x-header></x-header> |
|||
<div class="layout"> |
|||
<panel class="panel"></panel> |
|||
<editor class="editor"></editor> |
|||
<detail class="detail"></detail> |
|||
</div> |
|||
</div> |
|||
</template> |
|||
|
|||
<script> |
|||
import detail from "@/ui/detail/detail"; |
|||
import editor from "@/ui/editor/editor"; |
|||
import header from "@/ui/header/header"; |
|||
import panel from "@/ui/panel/panel"; |
|||
export default { |
|||
name: "home", |
|||
components: { |
|||
detail, |
|||
editor, |
|||
panel, |
|||
xHeader:header, |
|||
}, |
|||
mounted () { |
|||
setTimeout(() => { |
|||
this.$toast("哈哈") |
|||
}, 200); |
|||
} |
|||
}; |
|||
</script> |
|||
<style lang="scss" scoped> |
|||
.home{ |
|||
height: 100%; |
|||
display: flex; |
|||
flex-direction: column; |
|||
overflow: hidden; |
|||
} |
|||
.layout{ |
|||
flex: 1; |
|||
display: flex; |
|||
align-content: center; |
|||
height: 100%; |
|||
.editor{ |
|||
flex: 1; |
|||
width: 0; |
|||
} |
|||
} |
|||
</style> |
@ -0,0 +1,123 @@ |
|||
const path = require('path') |
|||
const webpack = require('webpack'); |
|||
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin |
|||
const CompressionPlugin = require("compression-webpack-plugin"); |
|||
const AddAssetHtmlPlugin = require("add-asset-html-webpack-plugin"); |
|||
const VueRouterInvokeWebpackPlugin = require('vue-router-invoke-webpack-plugin'); |
|||
const $config = require("./build/config") |
|||
const root = path.resolve($config.modules_root, $config.modules_dir); |
|||
const isProd = () => { |
|||
return process.env.NODE_ENV === 'production'; |
|||
}; |
|||
|
|||
let plugins = []; |
|||
plugins.push( |
|||
// 将 dll 注入到 生成的 html 模板中
|
|||
new AddAssetHtmlPlugin({ |
|||
// dll文件位置
|
|||
filepath: path.resolve(root, '*.js'), |
|||
// dll 引用路径
|
|||
publicPath: $config.modules_dir, |
|||
// dll最终输出的目录
|
|||
outputPath: $config.modules_dir |
|||
}) |
|||
) |
|||
plugins.push( |
|||
// 将 dll 注入到 生成的 html 模板中
|
|||
new AddAssetHtmlPlugin({ |
|||
// dll文件位置
|
|||
filepath: path.resolve(root, '*.css'), |
|||
// dll 引用路径
|
|||
publicPath: $config.modules_dir, |
|||
// dll最终输出的目录
|
|||
typeOfAsset: 'css', |
|||
outputPath: $config.modules_dir |
|||
}) |
|||
) |
|||
let modules = Object.keys($config.modules); |
|||
modules.forEach(module => { |
|||
plugins.push(new webpack.DllReferencePlugin({ |
|||
context: process.cwd(), |
|||
manifest: require(path.resolve(root, `${module}-manifest.json`)) |
|||
}), ) |
|||
}) |
|||
if (isProd()) { |
|||
plugins.push(new BundleAnalyzerPlugin()) |
|||
plugins.push(new CompressionPlugin({ |
|||
test: /\.js$|\.html$|\.css/, |
|||
threshold: 10240, |
|||
deleteOriginalAssets: false |
|||
})); |
|||
} |
|||
|
|||
|
|||
module.exports = { |
|||
publicPath: process.env.NODE_ENV === 'production' ? |
|||
'./' : '/', |
|||
assetsDir: 'static', |
|||
productionSourceMap: false, |
|||
// 以下是必须的,如果你要编译完成之后打印二维码的话
|
|||
devServer: { |
|||
open: true, |
|||
host: '0.0.0.0', |
|||
// 要保证不会冲突,否则二维码不对
|
|||
port: 3200, |
|||
https: false, |
|||
compress: true, |
|||
hotOnly: false, |
|||
overlay: { |
|||
warnings: false, |
|||
errors: true |
|||
} |
|||
}, |
|||
css: { |
|||
// 是否使用css分离插件 ExtractTextPlugin
|
|||
extract: isProd() ? true : false, |
|||
// 开启 CSS source maps?
|
|||
sourceMap: isProd() ? true : false, |
|||
// css预设器配置项
|
|||
loaderOptions: { |
|||
scss: { |
|||
prependData: ` |
|||
@import "~@/assets/style/utils.scss"; |
|||
` |
|||
}, |
|||
}, |
|||
// requireModuleExtension: false,
|
|||
}, |
|||
chainWebpack: config => {}, |
|||
configureWebpack: (config) => { |
|||
return { |
|||
resolve: { |
|||
alias: { |
|||
'@': path.resolve('src') |
|||
} |
|||
}, |
|||
plugins: [ |
|||
new webpack.ProvidePlugin({ |
|||
// _: "lodash",
|
|||
// $: "jquery"
|
|||
}), |
|||
new webpack.BannerPlugin('文件头声明'), |
|||
// https://github.com/cklwblove/vue-preset/blob/master/generator/index.js
|
|||
// https://zhuanlan.zhihu.com/p/63079674
|
|||
new VueRouterInvokeWebpackPlugin({ |
|||
dir: 'src/views', |
|||
alias: '@/views', |
|||
mode: 'hash', |
|||
base: process.env.BASE_URL, |
|||
ignore: ['images', 'components', 'NotFound.vue', /\.scss$/], |
|||
notFound: '@/views/NotFound.vue', |
|||
|
|||
routerDir: 'src/router', |
|||
redirect: [{ |
|||
redirect: '/home', |
|||
path: '/' |
|||
}] |
|||
}), |
|||
...plugins |
|||
], |
|||
externals: {} |
|||
} |
|||
} |
|||
} |
Loading…
Reference in new issue