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