添加vue-canvas-poster
31
package.json
@ -40,12 +40,16 @@
|
||||
"url": "https://gitee.com/y_project/RuoYi-Vue.git"
|
||||
},
|
||||
"dependencies": {
|
||||
"element-ui": "^2.13.0",
|
||||
"vue": "^2.6.10",
|
||||
"vuedraggable": "^2.23.2",
|
||||
"core-js": "^3.4.4",
|
||||
"html2canvas": "^1.0.0-rc.5",
|
||||
"uuid": "^3.3.3",
|
||||
"@riophae/vue-treeselect": "0.4.0",
|
||||
"axios": "0.18.1",
|
||||
"clipboard": "2.0.4",
|
||||
"core-js": "3.6.5",
|
||||
"echarts": "4.2.1",
|
||||
"element-ui": "2.13.2",
|
||||
"file-saver": "2.0.1",
|
||||
"fuse.js": "3.4.4",
|
||||
"js-beautify": "1.10.2",
|
||||
@ -57,44 +61,43 @@
|
||||
"quill": "1.3.7",
|
||||
"screenfull": "4.2.0",
|
||||
"sortablejs": "1.8.4",
|
||||
"vue": "2.6.10",
|
||||
"vue-count-to": "1.0.13",
|
||||
"vue-cropper": "0.4.9",
|
||||
"vue-qr": "^2.5.0",
|
||||
"vue-router": "3.0.2",
|
||||
"vue-splitpane": "1.0.4",
|
||||
"vuedraggable": "2.20.0",
|
||||
"vuex": "3.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vue/cli-plugin-babel": "4.4.4",
|
||||
"@vue/cli-plugin-eslint": "4.4.4",
|
||||
"@vue/cli-plugin-babel": "^4.1.0",
|
||||
"@vue/cli-plugin-eslint": "^4.1.0",
|
||||
"@vue/cli-service": "^4.1.0",
|
||||
"@vue/eslint-config-standard": "^4.0.0",
|
||||
"babel-eslint": "^10.0.3",
|
||||
"eslint": "^5.16.0",
|
||||
"eslint-plugin-vue": "^5.0.0",
|
||||
"node-sass": "^4.13.0",
|
||||
"sass-loader": "^8.0.1",
|
||||
"vue-template-compiler": "^2.6.10",
|
||||
"@vue/cli-plugin-unit-jest": "4.4.4",
|
||||
"@vue/cli-service": "4.4.4",
|
||||
"@vue/test-utils": "1.0.0-beta.29",
|
||||
"autoprefixer": "9.5.1",
|
||||
"babel-eslint": "10.1.0",
|
||||
"babel-jest": "23.6.0",
|
||||
"babel-plugin-dynamic-import-node": "2.3.3",
|
||||
"chalk": "2.4.2",
|
||||
"chokidar": "2.1.5",
|
||||
"connect": "3.6.6",
|
||||
"eslint": "6.7.2",
|
||||
"eslint-plugin-vue": "6.2.2",
|
||||
"html-webpack-plugin": "3.2.0",
|
||||
"husky": "1.3.1",
|
||||
"lint-staged": "8.1.5",
|
||||
"mockjs": "1.0.1-beta3",
|
||||
"plop": "2.3.0",
|
||||
"runjs": "4.3.2",
|
||||
"node-sass": "4.14.1",
|
||||
"sass-loader": "8.0.2",
|
||||
"script-ext-html-webpack-plugin": "2.1.3",
|
||||
"script-loader": "0.7.2",
|
||||
"serve-static": "1.13.2",
|
||||
"svg-sprite-loader": "4.1.3",
|
||||
"svgo": "1.2.0",
|
||||
"vue-template-compiler": "2.6.10"
|
||||
"svgo": "1.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8.9",
|
||||
|
@ -1,5 +1,13 @@
|
||||
import request from '@/utils/request';
|
||||
|
||||
// 修改活动
|
||||
export function qrUpdate(data) {
|
||||
return request({
|
||||
url: '/qr/update',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
// 获取订单列表
|
||||
export function orderList(data, params) {
|
||||
return request({
|
||||
@ -63,14 +71,6 @@ export function qrCreate(data) {
|
||||
data
|
||||
});
|
||||
}
|
||||
// 修改二维码
|
||||
export function qrUpdate(data) {
|
||||
return request({
|
||||
url: '/qr/update',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
// 删除二维码
|
||||
export function qrDelete(params) {
|
||||
return request({
|
||||
|
133
src/assets/common.scss
Normal file
@ -0,0 +1,133 @@
|
||||
.width-250{
|
||||
width: 250px !important;
|
||||
}
|
||||
.width-200{
|
||||
width: 200px !important;
|
||||
}
|
||||
.width-220{
|
||||
width: 220px !important;
|
||||
}
|
||||
.addTitle {
|
||||
text-align: left;
|
||||
font-size: 24px;
|
||||
height: 60px;
|
||||
line-height: 60px;
|
||||
}
|
||||
.card-part{
|
||||
padding: 25px;
|
||||
background: white;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.single-row-ellipsis{
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.two-row-ellipsis{
|
||||
overflow : hidden;
|
||||
text-overflow: ellipsis;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
}
|
||||
.float-left{
|
||||
float: left;
|
||||
}
|
||||
.float-right{
|
||||
float: right;
|
||||
}
|
||||
.m-t-20{
|
||||
margin-top: 20px !important;
|
||||
}
|
||||
.m-t-16{
|
||||
margin-top: 16px !important;
|
||||
}
|
||||
.m-b-20{
|
||||
margin-bottom: 20px !important;
|
||||
}
|
||||
.m-b-16{
|
||||
margin-bottom: 16px !important;
|
||||
}
|
||||
.p-b-20{
|
||||
padding-bottom: 20px !important;
|
||||
}
|
||||
.m-r-20{
|
||||
margin-right: 20px !important;
|
||||
}
|
||||
.m-r-16{
|
||||
margin-right: 16px !important;
|
||||
}
|
||||
.m-l-16{
|
||||
margin-left: 16px !important;
|
||||
}
|
||||
.m-l-24{
|
||||
margin-left: 24px !important;
|
||||
}
|
||||
.m-l-20{
|
||||
margin-left: 20px !important;
|
||||
}
|
||||
.m-t-10{
|
||||
margin-top: 10px !important;
|
||||
}
|
||||
.m-l-8{
|
||||
margin-left: 8px !important;
|
||||
}
|
||||
.m-r-8{
|
||||
margin-right: 8px !important;
|
||||
}
|
||||
.m-b-8{
|
||||
margin-bottom: 8px !important;
|
||||
}
|
||||
.p-b-24{
|
||||
padding-bottom: 24px !important;
|
||||
}
|
||||
.p-t-24{
|
||||
padding-top: 24px !important;
|
||||
}
|
||||
.el-main{
|
||||
background: #f1f3f9 !important;
|
||||
}
|
||||
.el-tabs--border-card {
|
||||
box-shadow: 0 2px 4px 0 rgba(0,0,0,.05) !important, 0 0 6px 0 rgba(0,0,0,.04) !important;
|
||||
}
|
||||
.el-tabs{
|
||||
background: #fff;
|
||||
}
|
||||
.el-tabs__nav{
|
||||
margin-left: 24px !important;
|
||||
}
|
||||
.el-tabs__header{
|
||||
margin-bottom: 24px !important;
|
||||
}
|
||||
.el-tabs__nav-wrap::after{
|
||||
height: 1px !important;
|
||||
}
|
||||
.el-tabs__item{
|
||||
height: 48px !important;
|
||||
line-height: 48px !important;
|
||||
}
|
||||
.el-dialog__header>.el-dialog__title{
|
||||
color: #fff!important;
|
||||
}
|
||||
/*数字或英文单词强制换行*/
|
||||
.el-tooltip__popper{
|
||||
word-wrap: break-word;
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
.toolWrap{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.c-b{
|
||||
color: #000;
|
||||
}
|
||||
.cursor-p{
|
||||
cursor: pointer;
|
||||
}
|
||||
.left-right-layout{
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
BIN
src/assets/image/1.jpg
Normal file
After Width: | Height: | Size: 40 KiB |
BIN
src/assets/image/2.jpg
Normal file
After Width: | Height: | Size: 22 KiB |
BIN
src/assets/image/3.jpg
Normal file
After Width: | Height: | Size: 203 KiB |
BIN
src/assets/image/4.jpg
Normal file
After Width: | Height: | Size: 42 KiB |
BIN
src/assets/image/5.jpg
Normal file
After Width: | Height: | Size: 44 KiB |
BIN
src/assets/image/6.jpg
Normal file
After Width: | Height: | Size: 7.1 KiB |
BIN
src/assets/logo.png
Normal file
After Width: | Height: | Size: 6.7 KiB |
246
src/assets/reset.css
Normal file
@ -0,0 +1,246 @@
|
||||
/*
|
||||
2017/7/22 v1.0*/
|
||||
html, body, div, span, applet, object, iframe,
|
||||
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
|
||||
a, abbr, acronym, address, big, cite, code,
|
||||
del, dfn, em, font, img, ins, kbd, q, s, samp,
|
||||
small, strike, strong, sub, sup, tt, var,
|
||||
dl, dt, dd, ol, ul, li,
|
||||
fieldset, form, label, legend,
|
||||
table, caption, tbody, tfoot, thead, tr, td {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
outline: 0;
|
||||
font-weight: inherit;
|
||||
font-style: inherit;
|
||||
font-size: 100%;
|
||||
font-family: "Helvetica Neue", "Luxi Sans", "DejaVu Sans", Tahoma, "Hiragino Sans GB", STHeiti, "Microsoft YaHei";
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
:focus, input, button, select, textarea {
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
html {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
/*line-height: 1;*/
|
||||
color: #333333;
|
||||
background: white;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
ol, ul, li {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
table {
|
||||
border-collapse: separate;
|
||||
border-spacing: 0;
|
||||
}
|
||||
|
||||
textarea {
|
||||
resize: none; /*必须添加overflow此属性才能生效*/
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
header, section, footer, aside, nav, main, article, figure {
|
||||
display: block;
|
||||
}
|
||||
|
||||
caption, td {
|
||||
text-align: left;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
blockquote:before, blockquote:after,
|
||||
q:before, q:after {
|
||||
content: "";
|
||||
}
|
||||
|
||||
blockquote, q {
|
||||
quotes: "" "";
|
||||
}
|
||||
|
||||
a, a:hover.a:link, a:visited, a:active {
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
#app {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/* 清除浮动*/
|
||||
.fix {
|
||||
*zoom: 1;
|
||||
}
|
||||
|
||||
.fix:after {
|
||||
display: table;
|
||||
content: '';
|
||||
clear: both;
|
||||
}
|
||||
|
||||
/*单行文字溢出虚点显示*/
|
||||
.ell {
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/*适用于父元素高度确定,本身高度不确定的元素*/
|
||||
.vm3 {
|
||||
position: relative;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
|
||||
/*本项目公共CSS*/
|
||||
.favorite {
|
||||
color: #f00;
|
||||
}
|
||||
.success,.error,.info,.warning{
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
/*.success {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
z-index: 999999999;
|
||||
height: 60px;
|
||||
width: 100%;
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
background-color: rgb(19,206,102);
|
||||
box-shadow: rgba(0, 0, 0, 0.5) 0 0 10px 0;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
.error {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
z-index: 999999999;
|
||||
height: 60px;
|
||||
width: 100%;
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
background-color: rgb(255,73,73);
|
||||
box-shadow: rgba(0, 0, 0, 0.5) 0 0 10px 0;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
.info {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
z-index: 999999999;
|
||||
height: 60px;
|
||||
width: 100%;
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
background-color: rgb(80,191,255);
|
||||
box-shadow: rgba(0, 0, 0, 0.5) 0 0 10px 0;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
.warning {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
z-index: 999999999;
|
||||
height: 60px;
|
||||
width: 100%;
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
background-color: rgb(247,186,42);
|
||||
box-shadow: rgba(0, 0, 0, 0.5) 0 0 10px 0;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
.success>div>p, .error>div>p, .info>div>p, .warning>div>p {
|
||||
margin: 15px auto 0;
|
||||
color: rgb(255, 255, 255);
|
||||
font-size: 24px;
|
||||
}
|
||||
.success>img, .error>img, .info>img, .warning>img {
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
}*/
|
||||
|
||||
|
||||
/*项目中公用CSS*/
|
||||
.vux-pop-out-enter-active,
|
||||
.vux-pop-out-leave-active,
|
||||
.vux-pop-in-enter-active,
|
||||
.vux-pop-in-leave-active {
|
||||
will-change: transform;
|
||||
transition: all 500ms;
|
||||
height: 100%;
|
||||
top: 46px;
|
||||
position: absolute;
|
||||
backface-visibility: hidden;
|
||||
perspective: 1000;
|
||||
}
|
||||
.vux-pop-out-enter {
|
||||
opacity: 0;
|
||||
transform: translate3d(-100%, 0, 0);
|
||||
}
|
||||
.vux-pop-out-leave-active {
|
||||
opacity: 0;
|
||||
transform: translate3d(100%, 0, 0);
|
||||
}
|
||||
.vux-pop-in-enter {
|
||||
opacity: 0;
|
||||
transform: translate3d(100%, 0, 0);
|
||||
}
|
||||
.vux-pop-in-leave-active {
|
||||
opacity: 0;
|
||||
transform: translate3d(-100%, 0, 0);
|
||||
}
|
||||
|
||||
|
||||
.weui-search-bar__label{
|
||||
line-height: 26px!important;/*no*/
|
||||
}
|
||||
/*定义滚动条高宽及背景 高宽分别对应横竖滚动条的尺寸*/
|
||||
::-webkit-scrollbar
|
||||
{
|
||||
width: 12px;
|
||||
height: 10px;
|
||||
background-color: #F5F5F5;
|
||||
}
|
||||
/*修改ele 的菜单组件下拉框*/
|
||||
.el-aside::-webkit-scrollbar
|
||||
{
|
||||
width: 4px;
|
||||
height: 10px;
|
||||
background-color: #F5F5F5;
|
||||
}
|
||||
/*定义滚动条轨道 内阴影+圆角*/
|
||||
::-webkit-scrollbar-track
|
||||
{
|
||||
/*-webkit-box-shadow: inset 0 0 6px rgba(0,0,0,0.3);*/
|
||||
/*border-radius: 10px;*/
|
||||
background-color: #d7dde4;
|
||||
}
|
||||
|
||||
/*定义滑块 内阴影+圆角*/
|
||||
::-webkit-scrollbar-thumb
|
||||
{
|
||||
/* border-radius: 10px;
|
||||
-webkit-box-shadow: inset 0 0 6px rgba(0,0,0,.3);*/
|
||||
/*background-color: grey;*/
|
||||
}
|
||||
/*去掉input[type=number]的箭头*/
|
||||
input::-webkit-outer-spin-button,
|
||||
input::-webkit-inner-spin-button {
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
input[type="number"] {
|
||||
-moz-appearance: textfield;
|
||||
}
|
@ -1,27 +1,31 @@
|
||||
<template>
|
||||
<div>
|
||||
<template>
|
||||
<div style="width:100%">
|
||||
<el-upload
|
||||
:action="useOss?ossUploadUrl:minioUploadUrl"
|
||||
:data="useOss?dataObj:null"
|
||||
:action="useOss ? ossUploadUrl : minioUploadUrl"
|
||||
:data="useOss ? dataObj : null"
|
||||
list-type="picture"
|
||||
:multiple="false" :show-file-list="showFileList"
|
||||
:multiple="false"
|
||||
:show-file-list="showFileList"
|
||||
:file-list="fileList"
|
||||
:before-upload="beforeUpload"
|
||||
:on-remove="handleRemove"
|
||||
:on-success="handleUploadSuccess"
|
||||
:on-preview="handlePreview">
|
||||
<el-button size="small" type="primary">点击上传</el-button>
|
||||
<div slot="tip" class="el-upload__tip">只能上传jpg/png文件,且不超过10MB</div>
|
||||
:on-preview="handlePreview"
|
||||
>
|
||||
<el-button size="mini" style="width:100%">添加图片</el-button>
|
||||
<!-- <div slot="tip" class="el-upload__tip">
|
||||
只能上传jpg/png文件,且不超过10MB
|
||||
</div> -->
|
||||
</el-upload>
|
||||
<el-dialog :visible.sync="dialogVisible">
|
||||
<img width="100%" :src="fileList[0].url" alt="">
|
||||
</el-dialog>
|
||||
<!-- <el-dialog :visible.sync="dialogVisible">
|
||||
<img width="100%" :src="fileList[0].url" alt="" />
|
||||
</el-dialog> -->
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import {policy} from '@/api/oss'
|
||||
import { policy } from '@/api/oss';
|
||||
|
||||
export default {
|
||||
export default {
|
||||
name: 'singleUpload',
|
||||
props: {
|
||||
value: String
|
||||
@ -32,23 +36,26 @@
|
||||
},
|
||||
imageName() {
|
||||
if (this.value != null && this.value !== '') {
|
||||
return this.value.substr(this.value.lastIndexOf("/") + 1);
|
||||
return this.value.substr(this.value.lastIndexOf('/') + 1);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
fileList() {
|
||||
return [{
|
||||
return [
|
||||
{
|
||||
name: this.imageName,
|
||||
url: this.imageUrl
|
||||
}]
|
||||
}
|
||||
];
|
||||
},
|
||||
showFileList: {
|
||||
get: function () {
|
||||
return this.value !== null && this.value !== ''&& this.value!==undefined;
|
||||
get: function() {
|
||||
return (
|
||||
this.value !== null && this.value !== '' && this.value !== undefined
|
||||
);
|
||||
},
|
||||
set: function (newValue) {
|
||||
}
|
||||
set: function(newValue) {}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
@ -59,18 +66,19 @@
|
||||
key: '',
|
||||
ossaccessKeyId: '',
|
||||
dir: '',
|
||||
host: '',
|
||||
host: ''
|
||||
// callback:'',
|
||||
},
|
||||
dialogVisible: false,
|
||||
useOss:false, //使用oss->true;使用MinIO->false
|
||||
ossUploadUrl:'http://macro-oss.oss-cn-shenzhen.aliyuncs.com',
|
||||
minioUploadUrl:'http://192.168.99.239:1818/minio/upload',
|
||||
useOss: false, //使用oss->true;使用MinIO->false
|
||||
ossUploadUrl: 'http://macro-oss.oss-cn-shenzhen.aliyuncs.com',
|
||||
// minioUploadUrl:'http://192.168.99.239:1818/minio/upload',
|
||||
minioUploadUrl: process.env.VUE_APP_BASE_API + '/minio/upload'
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
emitInput(val) {
|
||||
this.$emit('input', val)
|
||||
this.$emit('input', val);
|
||||
},
|
||||
handleRemove(file, fileList) {
|
||||
this.emitInput('');
|
||||
@ -80,12 +88,13 @@
|
||||
},
|
||||
beforeUpload(file) {
|
||||
let _self = this;
|
||||
if(!this.useOss){
|
||||
if (!this.useOss) {
|
||||
//不使用oss不需要获取策略
|
||||
return true;
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
policy().then(response => {
|
||||
policy()
|
||||
.then(response => {
|
||||
_self.dataObj.policy = response.data.policy;
|
||||
_self.dataObj.signature = response.data.signature;
|
||||
_self.dataObj.ossaccessKeyId = response.data.accessKeyId;
|
||||
@ -93,29 +102,26 @@
|
||||
_self.dataObj.dir = response.data.dir;
|
||||
_self.dataObj.host = response.data.host;
|
||||
// _self.dataObj.callback = response.data.callback;
|
||||
resolve(true)
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
reject(false)
|
||||
})
|
||||
resolve(true);
|
||||
})
|
||||
.catch(err => {
|
||||
console.log(err);
|
||||
reject(false);
|
||||
});
|
||||
});
|
||||
},
|
||||
handleUploadSuccess(res, file) {
|
||||
this.showFileList = true;
|
||||
this.fileList.pop();
|
||||
let url = this.dataObj.host + '/' + this.dataObj.dir + '/' + file.name;
|
||||
if(!this.useOss){
|
||||
if (!this.useOss) {
|
||||
//不使用oss直接获取图片路径
|
||||
url = res.data.url;
|
||||
}
|
||||
this.fileList.push({name: file.name, url: url});
|
||||
this.fileList.push({ name: file.name, url: url });
|
||||
this.emitInput(this.fileList[0].url);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style>
|
||||
|
||||
</style>
|
||||
|
||||
|
||||
<style></style>
|
||||
|
109
src/components/blankDialog/blankDialog.vue
Normal file
@ -0,0 +1,109 @@
|
||||
<template>
|
||||
<div class="blankDialog-comp-ct">
|
||||
<el-dialog
|
||||
@close="handleClose"
|
||||
:width="contentWidth+'px'"
|
||||
:visible.sync="showDialog">
|
||||
<div
|
||||
class="content"
|
||||
:style="{
|
||||
background: bg,
|
||||
height: contentHeight + 'px',
|
||||
contentWidth: contentWidth + 'px',
|
||||
padding: padding + 'px'
|
||||
}"
|
||||
>
|
||||
<div class="title">{{ dTitle }}</div>
|
||||
<slot></slot>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
name: 'blankDialog',
|
||||
props: {
|
||||
bg: {
|
||||
type: String,
|
||||
default: '#fff'
|
||||
},
|
||||
contentWidth: {
|
||||
type: Number,
|
||||
default: 720
|
||||
},
|
||||
padding: {
|
||||
type: Number,
|
||||
default: 48
|
||||
},
|
||||
contentHeight: {
|
||||
type: Number,
|
||||
default: 500
|
||||
},
|
||||
value: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
dTitle: {
|
||||
type: String
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
showDialog: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleClose () {
|
||||
this.$emit('close')
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value (val) {
|
||||
if (val !== this.showDialog) {
|
||||
this.showDialog = val
|
||||
}
|
||||
},
|
||||
showDialog (val) {
|
||||
if (val !== this.value) {
|
||||
this.$emit('input', val)
|
||||
}
|
||||
}
|
||||
},
|
||||
created () {
|
||||
this.showDialog = this.value
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss">
|
||||
.blankDialog-comp-ct{
|
||||
.el-dialog{
|
||||
/* width: 800px;*/
|
||||
.el-dialog__header{
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
.el-dialog__headerbtn .el-dialog__close{
|
||||
color: #282828;
|
||||
}
|
||||
}
|
||||
.el-dialog__body{
|
||||
padding: 0;
|
||||
height: auto;
|
||||
width: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<style lang="scss" scoped>
|
||||
.blankDialog-comp-ct {
|
||||
.content{
|
||||
box-sizing: border-box;
|
||||
overflow: auto;
|
||||
/*padding: 48px;*/
|
||||
.title{
|
||||
margin-bottom: 16px;
|
||||
font-size: 24px;
|
||||
color: rgba(33, 33, 33, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
6
src/components/blankDialog/index.js
Normal file
@ -0,0 +1,6 @@
|
||||
import blankDialog from './blankDialog.vue'
|
||||
import Vue from 'vue'
|
||||
blankDialog.install = function () {
|
||||
Vue.component(blankDialog.name, blankDialog)
|
||||
}
|
||||
export default blankDialog
|
239
src/components/imageSelection.vue
Normal file
@ -0,0 +1,239 @@
|
||||
<template>
|
||||
<div class="image-selection-comp">
|
||||
<div class="header-ct m-b-16">
|
||||
<div class="title">选择图片</div>
|
||||
<div class="right">
|
||||
<el-input
|
||||
clearable
|
||||
v-model="search"
|
||||
placeholader="搜索内容"
|
||||
class="width-200" size="mini" suffix-icon="el-icon-search">
|
||||
<el-button slot="append">搜索</el-button>
|
||||
</el-input>
|
||||
</div>
|
||||
</div>
|
||||
<div class="main">
|
||||
<div class="right">
|
||||
<div
|
||||
@click="handleClickImageItem(i, item)"
|
||||
class="list-item cursor-p"
|
||||
:key="item.uid"
|
||||
v-for="(item, i) in imageList"
|
||||
:style="{background: item.bg}">
|
||||
<el-checkbox class="check-ct" :value="item.active"></el-checkbox>
|
||||
<img :src="item.path" class="img"/>
|
||||
<div class="text single-row-ellipsis">
|
||||
{{item.name}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bottom-ct">
|
||||
<el-button @click="cancel">取消</el-button>
|
||||
<el-button @click="submit" type="primary">确定</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import uuid from 'uuid'
|
||||
export default {
|
||||
props: {
|
||||
// 是否单选
|
||||
isSingle: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
value: {
|
||||
type: Object
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
search: '',
|
||||
imageList: [
|
||||
{
|
||||
uid: uuid(),
|
||||
name: '钢铁侠',
|
||||
path: require('../assets/image/1.jpg')
|
||||
},
|
||||
{
|
||||
uid: uuid(),
|
||||
name: '蜘蛛侠',
|
||||
path: require('../assets/image/2.jpg')
|
||||
},
|
||||
{
|
||||
uid: uuid(),
|
||||
name: '美国队长',
|
||||
path: require('../assets/image/3.jpg')
|
||||
},
|
||||
{
|
||||
uid: uuid(),
|
||||
name: '奇异博士',
|
||||
path: require('../assets/image/4.jpg')
|
||||
},
|
||||
{
|
||||
uid: uuid(),
|
||||
name: '蚁人',
|
||||
path: require('../assets/image/5.jpg')
|
||||
},
|
||||
{
|
||||
uid: uuid(),
|
||||
name: '钢铁侠2',
|
||||
path: require('../assets/image/6.jpg')
|
||||
}
|
||||
],
|
||||
activeIndex: -1,
|
||||
selectionImage: [],
|
||||
selectionImageUids: [],
|
||||
systemMaterialList: [],
|
||||
materialList: [],
|
||||
tagSelect: ''
|
||||
// systemMaterialListCount: 0,
|
||||
// materialListCount: 0
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleClickImageItem (i, item) {
|
||||
this.isSingle ? this.singleModeClick(i, item) : this.multipleModeClick(i, item)
|
||||
},
|
||||
singleModeClick (i, item) {
|
||||
if (i === this.activeIndex) {
|
||||
this.$set(this.imageList, i, { ...item, active: false })
|
||||
this.activeIndex = -1
|
||||
} else {
|
||||
let oldItem = this.imageList[this.activeIndex]
|
||||
this.$set(this.imageList, this.activeIndex, { ...oldItem, active: false })
|
||||
this.activeIndex = i
|
||||
this.$set(this.imageList, this.activeIndex, { ...item, active: true })
|
||||
}
|
||||
},
|
||||
multipleModeClick (i, item) {
|
||||
this.$set(this.imageList, i, { ...item, active: !item.active })
|
||||
},
|
||||
reset () {
|
||||
this.imageList.forEach(v => {
|
||||
v.active = false
|
||||
})
|
||||
},
|
||||
async submit () {
|
||||
let imageSelection = this.imageList.filter(v => v.active)
|
||||
if (imageSelection.length === 0) {
|
||||
this.$message.error('请选择图片!')
|
||||
return false
|
||||
}
|
||||
this.$emit('success', this.isSingle ? imageSelection[0] : imageSelection)
|
||||
this.reset()
|
||||
},
|
||||
cancel () {
|
||||
this.$emit('cancel')
|
||||
this.reset()
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value (val) {
|
||||
if (val) {
|
||||
// this.findBackground()
|
||||
}
|
||||
}
|
||||
},
|
||||
async created () {
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.image-selection-comp{
|
||||
position: relative;
|
||||
.header-ct{
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
.title {
|
||||
font-size: 27px;
|
||||
font-weight: 400;
|
||||
color: rgba(44, 62, 80, 1);
|
||||
}
|
||||
.right{
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
.main{
|
||||
display: flex;
|
||||
.left{
|
||||
box-sizing: border-box;
|
||||
width: 200px;
|
||||
height: 400px;
|
||||
border-right: 1px solid #E4E8EB;
|
||||
.type-child-item{
|
||||
position: relative;
|
||||
padding-left: 24px;
|
||||
height: 32px;
|
||||
line-height: 32px;
|
||||
&.active{
|
||||
background: #409EFF;
|
||||
color: white;
|
||||
&:before{
|
||||
background: white;
|
||||
}
|
||||
}
|
||||
&:before{
|
||||
content: '';
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 13px;
|
||||
left: 8px;
|
||||
background: black;
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
overflow: hidden;
|
||||
border-radius: 50%;
|
||||
z-index: 9;
|
||||
}
|
||||
}
|
||||
}
|
||||
.right{
|
||||
flex: 1;
|
||||
display: grid;
|
||||
flex-wrap: wrap;
|
||||
grid-template-columns: repeat(auto-fill, 144px);
|
||||
grid-template-rows: repeat(auto-fill, 144px);
|
||||
grid-row-gap: 8px;
|
||||
grid-column-gap: 8px;
|
||||
margin-left: 16px;
|
||||
.list-item{
|
||||
position: relative;
|
||||
width: 144px;
|
||||
height: 144px;
|
||||
border: 1px solid #e1e1e1;
|
||||
overflow: hidden;
|
||||
.check-ct{
|
||||
position: absolute;
|
||||
top: 8px;
|
||||
left: 8px;
|
||||
}
|
||||
.img{
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100.8px;
|
||||
}
|
||||
}
|
||||
.text{
|
||||
height: 43px;
|
||||
line-height: 43px;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
.bottom-ct{
|
||||
/*position: absolute;
|
||||
left: 0;
|
||||
bottom: 0;*/
|
||||
margin-top: 32px;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
93
src/components/poster-editor/alignSelect.vue
Normal file
@ -0,0 +1,93 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-tooltip
|
||||
:key="item.icon"
|
||||
v-for="item in iconList"
|
||||
effect="dark"
|
||||
:content="item.label"
|
||||
placement="top-start">
|
||||
<Icon
|
||||
:active="(item.dir === 'x' && item.value === align.x) || (item.dir === 'y' && item.value === align.y)"
|
||||
@click.native="handleClick(item)" :icon="item.icon"></Icon>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import _ from 'lodash'
|
||||
import Icon from './icon'
|
||||
export default {
|
||||
components: {
|
||||
Icon
|
||||
},
|
||||
props: {
|
||||
value: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
align: {
|
||||
x: 2,
|
||||
y: 2
|
||||
},
|
||||
iconList: [
|
||||
{
|
||||
icon: 'zuoduiqi2',
|
||||
label: '左对齐',
|
||||
value: 1,
|
||||
dir: 'x'
|
||||
},
|
||||
{
|
||||
icon: 'shuipingjuzhongduiqi',
|
||||
label: '水平居中对齐',
|
||||
value: 2,
|
||||
dir: 'x'
|
||||
},
|
||||
{
|
||||
icon: 'youduiqi',
|
||||
label: '右对齐',
|
||||
value: 3,
|
||||
dir: 'x'
|
||||
},
|
||||
{
|
||||
icon: 'shangduiqi',
|
||||
label: '上对齐',
|
||||
value: 1,
|
||||
dir: 'y'
|
||||
},
|
||||
{
|
||||
icon: 'chuizhijuzhongduiqi',
|
||||
label: '垂直居中对齐',
|
||||
value: 2,
|
||||
dir: 'y'
|
||||
},
|
||||
{
|
||||
icon: 'xiaduiqi',
|
||||
label: '下对齐',
|
||||
value: 3,
|
||||
dir: 'y'
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleClick (item) {
|
||||
this.align[item.dir] = item.value
|
||||
this.$emit('input', this.align)
|
||||
}
|
||||
},
|
||||
created () {
|
||||
this.align = { ...this.value }
|
||||
},
|
||||
watch: {
|
||||
value (val) {
|
||||
if (!_.isEqual(val, this.align)) {
|
||||
this.align = { ...val }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss">
|
||||
</style>
|
539
src/components/poster-editor/icon/demo.css
Normal file
@ -0,0 +1,539 @@
|
||||
/* Logo 字体 */
|
||||
@font-face {
|
||||
font-family: "iconfont logo";
|
||||
src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834');
|
||||
src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834#iefix') format('embedded-opentype'),
|
||||
url('https://at.alicdn.com/t/font_985780_km7mi63cihi.woff?t=1545807318834') format('woff'),
|
||||
url('https://at.alicdn.com/t/font_985780_km7mi63cihi.ttf?t=1545807318834') format('truetype'),
|
||||
url('https://at.alicdn.com/t/font_985780_km7mi63cihi.svg?t=1545807318834#iconfont') format('svg');
|
||||
}
|
||||
|
||||
.logo {
|
||||
font-family: "iconfont logo";
|
||||
font-size: 160px;
|
||||
font-style: normal;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
/* tabs */
|
||||
.nav-tabs {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.nav-tabs .nav-more {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
height: 42px;
|
||||
line-height: 42px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
#tabs {
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
||||
#tabs li {
|
||||
cursor: pointer;
|
||||
width: 100px;
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
text-align: center;
|
||||
font-size: 16px;
|
||||
border-bottom: 2px solid transparent;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
margin-bottom: -1px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
|
||||
#tabs .active {
|
||||
border-bottom-color: #f00;
|
||||
color: #222;
|
||||
}
|
||||
|
||||
.tab-container .content {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* 页面布局 */
|
||||
.main {
|
||||
padding: 30px 100px;
|
||||
width: 960px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.main .logo {
|
||||
color: #333;
|
||||
text-align: left;
|
||||
margin-bottom: 30px;
|
||||
line-height: 1;
|
||||
height: 110px;
|
||||
margin-top: -50px;
|
||||
overflow: hidden;
|
||||
*zoom: 1;
|
||||
}
|
||||
|
||||
.main .logo a {
|
||||
font-size: 160px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.helps {
|
||||
margin-top: 40px;
|
||||
}
|
||||
|
||||
.helps pre {
|
||||
padding: 20px;
|
||||
margin: 10px 0;
|
||||
border: solid 1px #e7e1cd;
|
||||
background-color: #fffdef;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.icon_lists {
|
||||
width: 100% !important;
|
||||
overflow: hidden;
|
||||
*zoom: 1;
|
||||
}
|
||||
|
||||
.icon_lists li {
|
||||
width: 100px;
|
||||
margin-bottom: 10px;
|
||||
margin-right: 20px;
|
||||
text-align: center;
|
||||
list-style: none !important;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.icon_lists li .code-name {
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.icon_lists .icon {
|
||||
display: block;
|
||||
height: 100px;
|
||||
line-height: 100px;
|
||||
font-size: 42px;
|
||||
margin: 10px auto;
|
||||
color: #333;
|
||||
-webkit-transition: font-size 0.25s linear, width 0.25s linear;
|
||||
-moz-transition: font-size 0.25s linear, width 0.25s linear;
|
||||
transition: font-size 0.25s linear, width 0.25s linear;
|
||||
}
|
||||
|
||||
.icon_lists .icon:hover {
|
||||
font-size: 100px;
|
||||
}
|
||||
|
||||
.icon_lists .svg-icon {
|
||||
/* 通过设置 font-size 来改变图标大小 */
|
||||
width: 1em;
|
||||
/* 图标和文字相邻时,垂直对齐 */
|
||||
vertical-align: -0.15em;
|
||||
/* 通过设置 color 来改变 SVG 的颜色/fill */
|
||||
fill: currentColor;
|
||||
/* path 和 stroke 溢出 viewBox 部分在 IE 下会显示
|
||||
normalize.css 中也包含这行 */
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.icon_lists li .name,
|
||||
.icon_lists li .code-name {
|
||||
color: #666;
|
||||
}
|
||||
|
||||
/* markdown 样式 */
|
||||
.markdown {
|
||||
color: #666;
|
||||
font-size: 14px;
|
||||
line-height: 1.8;
|
||||
}
|
||||
|
||||
.highlight {
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.markdown img {
|
||||
vertical-align: middle;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.markdown h1 {
|
||||
color: #404040;
|
||||
font-weight: 500;
|
||||
line-height: 40px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.markdown h2,
|
||||
.markdown h3,
|
||||
.markdown h4,
|
||||
.markdown h5,
|
||||
.markdown h6 {
|
||||
color: #404040;
|
||||
margin: 1.6em 0 0.6em 0;
|
||||
font-weight: 500;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.markdown h1 {
|
||||
font-size: 28px;
|
||||
}
|
||||
|
||||
.markdown h2 {
|
||||
font-size: 22px;
|
||||
}
|
||||
|
||||
.markdown h3 {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.markdown h4 {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.markdown h5 {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.markdown h6 {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.markdown hr {
|
||||
height: 1px;
|
||||
border: 0;
|
||||
background: #e9e9e9;
|
||||
margin: 16px 0;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.markdown p {
|
||||
margin: 1em 0;
|
||||
}
|
||||
|
||||
.markdown>p,
|
||||
.markdown>blockquote,
|
||||
.markdown>.highlight,
|
||||
.markdown>ol,
|
||||
.markdown>ul {
|
||||
width: 80%;
|
||||
}
|
||||
|
||||
.markdown ul>li {
|
||||
list-style: circle;
|
||||
}
|
||||
|
||||
.markdown>ul li,
|
||||
.markdown blockquote ul>li {
|
||||
margin-left: 20px;
|
||||
padding-left: 4px;
|
||||
}
|
||||
|
||||
.markdown>ul li p,
|
||||
.markdown>ol li p {
|
||||
margin: 0.6em 0;
|
||||
}
|
||||
|
||||
.markdown ol>li {
|
||||
list-style: decimal;
|
||||
}
|
||||
|
||||
.markdown>ol li,
|
||||
.markdown blockquote ol>li {
|
||||
margin-left: 20px;
|
||||
padding-left: 4px;
|
||||
}
|
||||
|
||||
.markdown code {
|
||||
margin: 0 3px;
|
||||
padding: 0 5px;
|
||||
background: #eee;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.markdown strong,
|
||||
.markdown b {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.markdown>table {
|
||||
border-collapse: collapse;
|
||||
border-spacing: 0px;
|
||||
empty-cells: show;
|
||||
border: 1px solid #e9e9e9;
|
||||
width: 95%;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.markdown>table th {
|
||||
white-space: nowrap;
|
||||
color: #333;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.markdown>table th,
|
||||
.markdown>table td {
|
||||
border: 1px solid #e9e9e9;
|
||||
padding: 8px 16px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.markdown>table th {
|
||||
background: #F7F7F7;
|
||||
}
|
||||
|
||||
.markdown blockquote {
|
||||
font-size: 90%;
|
||||
color: #999;
|
||||
border-left: 4px solid #e9e9e9;
|
||||
padding-left: 0.8em;
|
||||
margin: 1em 0;
|
||||
}
|
||||
|
||||
.markdown blockquote p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.markdown .anchor {
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s ease;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.markdown .waiting {
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
.markdown h1:hover .anchor,
|
||||
.markdown h2:hover .anchor,
|
||||
.markdown h3:hover .anchor,
|
||||
.markdown h4:hover .anchor,
|
||||
.markdown h5:hover .anchor,
|
||||
.markdown h6:hover .anchor {
|
||||
opacity: 1;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.markdown>br,
|
||||
.markdown>p>br {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
|
||||
.hljs {
|
||||
display: block;
|
||||
background: white;
|
||||
padding: 0.5em;
|
||||
color: #333333;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.hljs-comment,
|
||||
.hljs-meta {
|
||||
color: #969896;
|
||||
}
|
||||
|
||||
.hljs-string,
|
||||
.hljs-variable,
|
||||
.hljs-template-variable,
|
||||
.hljs-strong,
|
||||
.hljs-emphasis,
|
||||
.hljs-quote {
|
||||
color: #df5000;
|
||||
}
|
||||
|
||||
.hljs-keyword,
|
||||
.hljs-selector-tag,
|
||||
.hljs-type {
|
||||
color: #a71d5d;
|
||||
}
|
||||
|
||||
.hljs-literal,
|
||||
.hljs-symbol,
|
||||
.hljs-bullet,
|
||||
.hljs-attribute {
|
||||
color: #0086b3;
|
||||
}
|
||||
|
||||
.hljs-section,
|
||||
.hljs-name {
|
||||
color: #63a35c;
|
||||
}
|
||||
|
||||
.hljs-tag {
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.hljs-title,
|
||||
.hljs-attr,
|
||||
.hljs-selector-id,
|
||||
.hljs-selector-class,
|
||||
.hljs-selector-attr,
|
||||
.hljs-selector-pseudo {
|
||||
color: #795da3;
|
||||
}
|
||||
|
||||
.hljs-addition {
|
||||
color: #55a532;
|
||||
background-color: #eaffea;
|
||||
}
|
||||
|
||||
.hljs-deletion {
|
||||
color: #bd2c00;
|
||||
background-color: #ffecec;
|
||||
}
|
||||
|
||||
.hljs-link {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
/* 代码高亮 */
|
||||
/* PrismJS 1.15.0
|
||||
https://prismjs.com/download.html#themes=prism&languages=markup+css+clike+javascript */
|
||||
/**
|
||||
* prism.js default theme for JavaScript, CSS and HTML
|
||||
* Based on dabblet (http://dabblet.com)
|
||||
* @author Lea Verou
|
||||
*/
|
||||
code[class*="language-"],
|
||||
pre[class*="language-"] {
|
||||
color: black;
|
||||
background: none;
|
||||
text-shadow: 0 1px white;
|
||||
font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
|
||||
text-align: left;
|
||||
white-space: pre;
|
||||
word-spacing: normal;
|
||||
word-break: normal;
|
||||
word-wrap: normal;
|
||||
line-height: 1.5;
|
||||
|
||||
-moz-tab-size: 4;
|
||||
-o-tab-size: 4;
|
||||
tab-size: 4;
|
||||
|
||||
-webkit-hyphens: none;
|
||||
-moz-hyphens: none;
|
||||
-ms-hyphens: none;
|
||||
hyphens: none;
|
||||
}
|
||||
|
||||
pre[class*="language-"]::-moz-selection,
|
||||
pre[class*="language-"] ::-moz-selection,
|
||||
code[class*="language-"]::-moz-selection,
|
||||
code[class*="language-"] ::-moz-selection {
|
||||
text-shadow: none;
|
||||
background: #b3d4fc;
|
||||
}
|
||||
|
||||
pre[class*="language-"]::selection,
|
||||
pre[class*="language-"] ::selection,
|
||||
code[class*="language-"]::selection,
|
||||
code[class*="language-"] ::selection {
|
||||
text-shadow: none;
|
||||
background: #b3d4fc;
|
||||
}
|
||||
|
||||
@media print {
|
||||
|
||||
code[class*="language-"],
|
||||
pre[class*="language-"] {
|
||||
text-shadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* Code blocks */
|
||||
pre[class*="language-"] {
|
||||
padding: 1em;
|
||||
margin: .5em 0;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
:not(pre)>code[class*="language-"],
|
||||
pre[class*="language-"] {
|
||||
background: #f5f2f0;
|
||||
}
|
||||
|
||||
/* Inline code */
|
||||
:not(pre)>code[class*="language-"] {
|
||||
padding: .1em;
|
||||
border-radius: .3em;
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
.token.comment,
|
||||
.token.prolog,
|
||||
.token.doctype,
|
||||
.token.cdata {
|
||||
color: slategray;
|
||||
}
|
||||
|
||||
.token.punctuation {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.namespace {
|
||||
opacity: .7;
|
||||
}
|
||||
|
||||
.token.property,
|
||||
.token.tag,
|
||||
.token.boolean,
|
||||
.token.number,
|
||||
.token.constant,
|
||||
.token.symbol,
|
||||
.token.deleted {
|
||||
color: #905;
|
||||
}
|
||||
|
||||
.token.selector,
|
||||
.token.attr-name,
|
||||
.token.string,
|
||||
.token.char,
|
||||
.token.builtin,
|
||||
.token.inserted {
|
||||
color: #690;
|
||||
}
|
||||
|
||||
.token.operator,
|
||||
.token.entity,
|
||||
.token.url,
|
||||
.language-css .token.string,
|
||||
.style .token.string {
|
||||
color: #9a6e3a;
|
||||
background: hsla(0, 0%, 100%, .5);
|
||||
}
|
||||
|
||||
.token.atrule,
|
||||
.token.attr-value,
|
||||
.token.keyword {
|
||||
color: #07a;
|
||||
}
|
||||
|
||||
.token.function,
|
||||
.token.class-name {
|
||||
color: #DD4A68;
|
||||
}
|
||||
|
||||
.token.regex,
|
||||
.token.important,
|
||||
.token.variable {
|
||||
color: #e90;
|
||||
}
|
||||
|
||||
.token.important,
|
||||
.token.bold {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.token.italic {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.token.entity {
|
||||
cursor: help;
|
||||
}
|
446
src/components/poster-editor/icon/demo_index.html
Normal file
@ -0,0 +1,446 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>IconFont Demo</title>
|
||||
<link rel="shortcut icon" href="https://gtms04.alicdn.com/tps/i4/TB1_oz6GVXXXXaFXpXXJDFnIXXX-64-64.ico" type="image/x-icon"/>
|
||||
<link rel="stylesheet" href="https://g.alicdn.com/thx/cube/1.3.2/cube.min.css">
|
||||
<link rel="stylesheet" href="demo.css">
|
||||
<link rel="stylesheet" href="iconfont.css">
|
||||
<script src="iconfont.js"></script>
|
||||
<!-- jQuery -->
|
||||
<script src="https://a1.alicdn.com/oss/uploads/2018/12/26/7bfddb60-08e8-11e9-9b04-53e73bb6408b.js"></script>
|
||||
<!-- 代码高亮 -->
|
||||
<script src="https://a1.alicdn.com/oss/uploads/2018/12/26/a3f714d0-08e6-11e9-8a15-ebf944d7534c.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="main">
|
||||
<h1 class="logo"><a href="https://www.iconfont.cn/" title="iconfont 首页" target="_blank"></a></h1>
|
||||
<div class="nav-tabs">
|
||||
<ul id="tabs" class="dib-box">
|
||||
<li class="dib active"><span>Unicode</span></li>
|
||||
<li class="dib"><span>Font class</span></li>
|
||||
<li class="dib"><span>Symbol</span></li>
|
||||
</ul>
|
||||
|
||||
<a href="https://www.iconfont.cn/manage/index?manage_type=myprojects&projectId=1577785" target="_blank" class="nav-more">查看项目</a>
|
||||
|
||||
</div>
|
||||
<div class="tab-container">
|
||||
<div class="content unicode" style="display: block;">
|
||||
<ul class="icon_lists dib-box">
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">右对齐</div>
|
||||
<div class="code-name">&#xe65b;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">下对齐</div>
|
||||
<div class="code-name">&#xe64b;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">水平居中对齐</div>
|
||||
<div class="code-name">&#xe600;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">左对齐</div>
|
||||
<div class="code-name">&#xe67f;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">居中对齐</div>
|
||||
<div class="code-name">&#xe632;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">上对齐</div>
|
||||
<div class="code-name">&#xe64e;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">左对齐</div>
|
||||
<div class="code-name">&#xe650;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">右对齐</div>
|
||||
<div class="code-name">&#xe60e;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">左对齐</div>
|
||||
<div class="code-name">&#xe60f;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">undo</div>
|
||||
<div class="code-name">&#xe603;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">垂直居中对齐</div>
|
||||
<div class="code-name">&#xe670;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">undo</div>
|
||||
<div class="code-name">&#xe651;</div>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
<div class="article markdown">
|
||||
<h2 id="unicode-">Unicode 引用</h2>
|
||||
<hr>
|
||||
|
||||
<p>Unicode 是字体在网页端最原始的应用方式,特点是:</p>
|
||||
<ul>
|
||||
<li>兼容性最好,支持 IE6+,及所有现代浏览器。</li>
|
||||
<li>支持按字体的方式去动态调整图标大小,颜色等等。</li>
|
||||
<li>但是因为是字体,所以不支持多色。只能使用平台里单色的图标,就算项目里有多色图标也会自动去色。</li>
|
||||
</ul>
|
||||
<blockquote>
|
||||
<p>注意:新版 iconfont 支持多色图标,这些多色图标在 Unicode 模式下将不能使用,如果有需求建议使用symbol 的引用方式</p>
|
||||
</blockquote>
|
||||
<p>Unicode 使用步骤如下:</p>
|
||||
<h3 id="-font-face">第一步:拷贝项目下面生成的 <code>@font-face</code></h3>
|
||||
<pre><code class="language-css"
|
||||
>@font-face {
|
||||
font-family: 'iconfont';
|
||||
src: url('iconfont.eot');
|
||||
src: url('iconfont.eot?#iefix') format('embedded-opentype'),
|
||||
url('iconfont.woff2') format('woff2'),
|
||||
url('iconfont.woff') format('woff'),
|
||||
url('iconfont.ttf') format('truetype'),
|
||||
url('iconfont.svg#iconfont') format('svg');
|
||||
}
|
||||
</code></pre>
|
||||
<h3 id="-iconfont-">第二步:定义使用 iconfont 的样式</h3>
|
||||
<pre><code class="language-css"
|
||||
>.iconfont {
|
||||
font-family: "iconfont" !important;
|
||||
font-size: 16px;
|
||||
font-style: normal;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
</code></pre>
|
||||
<h3 id="-">第三步:挑选相应图标并获取字体编码,应用于页面</h3>
|
||||
<pre>
|
||||
<code class="language-html"
|
||||
><span class="iconfont">&#x33;</span>
|
||||
</code></pre>
|
||||
<blockquote>
|
||||
<p>"iconfont" 是你项目下的 font-family。可以通过编辑项目查看,默认是 "iconfont"。</p>
|
||||
</blockquote>
|
||||
</div>
|
||||
</div>
|
||||
<div class="content font-class">
|
||||
<ul class="icon_lists dib-box">
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-youduiqi"></span>
|
||||
<div class="name">
|
||||
右对齐
|
||||
</div>
|
||||
<div class="code-name">.icon-youduiqi
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-xiaduiqi"></span>
|
||||
<div class="name">
|
||||
下对齐
|
||||
</div>
|
||||
<div class="code-name">.icon-xiaduiqi
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-shuipingjuzhongduiqi"></span>
|
||||
<div class="name">
|
||||
水平居中对齐
|
||||
</div>
|
||||
<div class="code-name">.icon-shuipingjuzhongduiqi
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-zuoduiqi"></span>
|
||||
<div class="name">
|
||||
左对齐
|
||||
</div>
|
||||
<div class="code-name">.icon-zuoduiqi
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-card-text-center"></span>
|
||||
<div class="name">
|
||||
居中对齐
|
||||
</div>
|
||||
<div class="code-name">.icon-card-text-center
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-shangduiqi"></span>
|
||||
<div class="name">
|
||||
上对齐
|
||||
</div>
|
||||
<div class="code-name">.icon-shangduiqi
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-zuoduiqi2"></span>
|
||||
<div class="name">
|
||||
左对齐
|
||||
</div>
|
||||
<div class="code-name">.icon-zuoduiqi2
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-youduiqi1"></span>
|
||||
<div class="name">
|
||||
右对齐
|
||||
</div>
|
||||
<div class="code-name">.icon-youduiqi1
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-zuoduiqi1"></span>
|
||||
<div class="name">
|
||||
左对齐
|
||||
</div>
|
||||
<div class="code-name">.icon-zuoduiqi1
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-Undo"></span>
|
||||
<div class="name">
|
||||
undo
|
||||
</div>
|
||||
<div class="code-name">.icon-Undo
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-chuizhijuzhongduiqi"></span>
|
||||
<div class="name">
|
||||
垂直居中对齐
|
||||
</div>
|
||||
<div class="code-name">.icon-chuizhijuzhongduiqi
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-ic_undo"></span>
|
||||
<div class="name">
|
||||
undo
|
||||
</div>
|
||||
<div class="code-name">.icon-ic_undo
|
||||
</div>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
<div class="article markdown">
|
||||
<h2 id="font-class-">font-class 引用</h2>
|
||||
<hr>
|
||||
|
||||
<p>font-class 是 Unicode 使用方式的一种变种,主要是解决 Unicode 书写不直观,语意不明确的问题。</p>
|
||||
<p>与 Unicode 使用方式相比,具有如下特点:</p>
|
||||
<ul>
|
||||
<li>兼容性良好,支持 IE8+,及所有现代浏览器。</li>
|
||||
<li>相比于 Unicode 语意明确,书写更直观。可以很容易分辨这个 icon 是什么。</li>
|
||||
<li>因为使用 class 来定义图标,所以当要替换图标时,只需要修改 class 里面的 Unicode 引用。</li>
|
||||
<li>不过因为本质上还是使用的字体,所以多色图标还是不支持的。</li>
|
||||
</ul>
|
||||
<p>使用步骤如下:</p>
|
||||
<h3 id="-fontclass-">第一步:引入项目下面生成的 fontclass 代码:</h3>
|
||||
<pre><code class="language-html"><link rel="stylesheet" href="./iconfont.css">
|
||||
</code></pre>
|
||||
<h3 id="-">第二步:挑选相应图标并获取类名,应用于页面:</h3>
|
||||
<pre><code class="language-html"><span class="iconfont icon-xxx"></span>
|
||||
</code></pre>
|
||||
<blockquote>
|
||||
<p>"
|
||||
iconfont" 是你项目下的 font-family。可以通过编辑项目查看,默认是 "iconfont"。</p>
|
||||
</blockquote>
|
||||
</div>
|
||||
</div>
|
||||
<div class="content symbol">
|
||||
<ul class="icon_lists dib-box">
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-youduiqi"></use>
|
||||
</svg>
|
||||
<div class="name">右对齐</div>
|
||||
<div class="code-name">#icon-youduiqi</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-xiaduiqi"></use>
|
||||
</svg>
|
||||
<div class="name">下对齐</div>
|
||||
<div class="code-name">#icon-xiaduiqi</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-shuipingjuzhongduiqi"></use>
|
||||
</svg>
|
||||
<div class="name">水平居中对齐</div>
|
||||
<div class="code-name">#icon-shuipingjuzhongduiqi</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-zuoduiqi"></use>
|
||||
</svg>
|
||||
<div class="name">左对齐</div>
|
||||
<div class="code-name">#icon-zuoduiqi</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-card-text-center"></use>
|
||||
</svg>
|
||||
<div class="name">居中对齐</div>
|
||||
<div class="code-name">#icon-card-text-center</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-shangduiqi"></use>
|
||||
</svg>
|
||||
<div class="name">上对齐</div>
|
||||
<div class="code-name">#icon-shangduiqi</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-zuoduiqi2"></use>
|
||||
</svg>
|
||||
<div class="name">左对齐</div>
|
||||
<div class="code-name">#icon-zuoduiqi2</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-youduiqi1"></use>
|
||||
</svg>
|
||||
<div class="name">右对齐</div>
|
||||
<div class="code-name">#icon-youduiqi1</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-zuoduiqi1"></use>
|
||||
</svg>
|
||||
<div class="name">左对齐</div>
|
||||
<div class="code-name">#icon-zuoduiqi1</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-Undo"></use>
|
||||
</svg>
|
||||
<div class="name">undo</div>
|
||||
<div class="code-name">#icon-Undo</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-chuizhijuzhongduiqi"></use>
|
||||
</svg>
|
||||
<div class="name">垂直居中对齐</div>
|
||||
<div class="code-name">#icon-chuizhijuzhongduiqi</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-ic_undo"></use>
|
||||
</svg>
|
||||
<div class="name">undo</div>
|
||||
<div class="code-name">#icon-ic_undo</div>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
<div class="article markdown">
|
||||
<h2 id="symbol-">Symbol 引用</h2>
|
||||
<hr>
|
||||
|
||||
<p>这是一种全新的使用方式,应该说这才是未来的主流,也是平台目前推荐的用法。相关介绍可以参考这篇<a href="">文章</a>
|
||||
这种用法其实是做了一个 SVG 的集合,与另外两种相比具有如下特点:</p>
|
||||
<ul>
|
||||
<li>支持多色图标了,不再受单色限制。</li>
|
||||
<li>通过一些技巧,支持像字体那样,通过 <code>font-size</code>, <code>color</code> 来调整样式。</li>
|
||||
<li>兼容性较差,支持 IE9+,及现代浏览器。</li>
|
||||
<li>浏览器渲染 SVG 的性能一般,还不如 png。</li>
|
||||
</ul>
|
||||
<p>使用步骤如下:</p>
|
||||
<h3 id="-symbol-">第一步:引入项目下面生成的 symbol 代码:</h3>
|
||||
<pre><code class="language-html"><script src="./iconfont.js"></script>
|
||||
</code></pre>
|
||||
<h3 id="-css-">第二步:加入通用 CSS 代码(引入一次就行):</h3>
|
||||
<pre><code class="language-html"><style>
|
||||
.icon {
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
vertical-align: -0.15em;
|
||||
fill: currentColor;
|
||||
overflow: hidden;
|
||||
}
|
||||
</style>
|
||||
</code></pre>
|
||||
<h3 id="-">第三步:挑选相应图标并获取类名,应用于页面:</h3>
|
||||
<pre><code class="language-html"><svg class="icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-xxx"></use>
|
||||
</svg>
|
||||
</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
$(document).ready(function () {
|
||||
$('.tab-container .content:first').show()
|
||||
|
||||
$('#tabs li').click(function (e) {
|
||||
var tabContent = $('.tab-container .content')
|
||||
var index = $(this).index()
|
||||
|
||||
if ($(this).hasClass('active')) {
|
||||
return
|
||||
} else {
|
||||
$('#tabs li').removeClass('active')
|
||||
$(this).addClass('active')
|
||||
|
||||
tabContent.hide().eq(index).fadeIn()
|
||||
}
|
||||
})
|
||||
})
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
65
src/components/poster-editor/icon/iconfont.css
Normal file
@ -0,0 +1,65 @@
|
||||
@font-face {font-family: "iconfont";
|
||||
src: url('iconfont.eot?t=1578558136402'); /* IE9 */
|
||||
src: url('iconfont.eot?t=1578558136402#iefix') format('embedded-opentype'), /* IE6-IE8 */
|
||||
url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAYYAAsAAAAADMwAAAXLAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHEIGVgCEUgqKGId2ATYCJAM0CxwABCAFhG0HgUobxAojEaaMjp/sLxLMk0wHLVmncXb21PWwcny3xMGD4l8oJema4IHozH+zMxEOmwJO8uFUzpXOpgByNXhX++8HkAQIyENwg7lbAW7qrO0j3q7pt+DY9WnHNeDIsULKy3Kpdy10UV+stfpu03VfDAuFGt/EENXQIBMShCLi8eciHiqEQsmdxbya7IRiUma8ej8E4CS/skS1GvWaYEfBDxOA6Nkt3hF7JIDKYQvsbitxrnpXzMaCXZuiZQGznN8X3xDMDhoWCX9Q1KjehYiJOUYmkgnayEwy24sBt4tAAmWBArGXTGxHem1lSZwfZBQJIMBPYjUxpekzg2Zds4GZYbY2+5pjkkkY3tssOhdPCAICnxOb20Ff/+bZcWBBw4oNNwIXCoQVdLhh2epI0EQBEkwVnGB6VbCD6VPBAWZQBQuYdVXQwGygghXMqAo2MDMM/ThbgwoCzL4quMAcYzrRDo91Tm5QAaIriBNwVxdHWaGwYDWKLWhYEArRbnPg8kMgn///AtcIl9XZ3aO5O+IVfbyabwT+BmhO2UzVprlbeToKF62kS2smPVoHH95mmk+2dynnAM0rewkv7epZ4/JWsqNw0zxt2onEcWfq1JOZp+K+AwHG62+VQnHC/oMOTz7G3dJepNClzRZDOS2d+xnZ1GlzxdzS06UPdKvTR7BkSzHfpm15xNKtxb3rN7eIolKGC5G51emfujkmbTn/PJdvGmpof0hscaVNK24fEYr4ptXFa/q/Hpwty+qbNyC17/pgsTTQQi3uP24mFi1dn95lfkKPLdgkxL1XhUgs0MWiRaL/1PlgxMMnue49anr/dWsXzs4e3M0Xw9VuMt+2h+gSkxE8eNVSPPg6FModCoeyspay1Ihlkx0MGiGD9+lQkYo3diV+TMya9fL0yx8qFIwU5PNc8XA8tGRpeGlWEIO9MbIJJkNGiCVZoaWha0HfF0w20DNnPQNjTK6kNRa+eSibWim1CxtV44VrFxpSdXCuoi0wqhqFWrx2MVoVzSg0uOpgMpiWZLUvHPcVbg83H0K7w+Eq4Z5311jW9OzpvOVUoQ/6f8DFgnfy3Z6KPXo6bjnCaid9X4OgEj0ZOaYcVCR+7Yv3rqgjUT2oq+2PpGc1sgSOWSpZ6NNxj0Al/XigiiiiZ+UE8+lVXgRpDSulXwtcDByTldSjF6NXP6gkRc9Hjyo8OXBefxK0dS29kn41Oj9yVS+X1ecVibq2irFA6YjxhSJEcMa+f+PJ5+ObFs93PwsOTI09azI+p9PZ1NSHA4VtcQc6LLb7q9qW2LbvCO1I22FfbLtl//3nXV+1kWMJbBz16ajfP/nzU/Fav32086NRDuNf48NPhr8ezptJf745+OaQc6A7O7GweHnLapXTZcyfQNKUcZkbQCa0m1F8mB3XskVjylOZYK/Rvmt5IXvJQhhPqLlUIZmp5sDefyghcwHIH+OvvNR5mamz/obdi1Y8L3wf/zZcCoDHn8jDaGh/fCZgR4PSRn00RT2B5F/q6d3Mgx71nkUtfxKg5yyfHXWeaLMdDxF0RYsGOEUGkpBJ9bwfl156IAjs6AFo2MgHEjtF0QpfFiy4CIMVO5+BkzJqXuwiTX8kQjmA0iYwEPjtAg0vp0Dil41W+GdgIeA7sOJPwgOX/nq65pWX6PJVsFJsoN4DGZ6sDOulO/gL2zBUEjY7/D8UT2XI4jTe+YkTShsn+E2bq1qwwiN8sIfhMDDMwh0ajZ3qvE4SSyEUGx4Xl14JVFGoAWp7gBhsYq311WXi57+gVjCoSM6sn6Z/kPBo8yATS0ugPx1TqVnHMtLbaOUUWyxgbwPZCHxgDA2ijYGZ3quDDBVzNVpmawmuZss64v5d4zHfADhx5DyFrKiabpiW7bheOp9DE+ifzndUudFn7+z6M01tFw6OjTncQ+Cd700lTaS408jgTnTIpXdVAcJFOL9o0ZMlR2THv1PDT6bhNQdHl5YzMkWg/YsF') format('woff2'),
|
||||
url('iconfont.woff?t=1578558136402') format('woff'),
|
||||
url('iconfont.ttf?t=1578558136402') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+ */
|
||||
url('iconfont.svg?t=1578558136402#iconfont') format('svg'); /* iOS 4.1- */
|
||||
}
|
||||
|
||||
.iconfont {
|
||||
font-family: "iconfont" !important;
|
||||
font-size: 16px;
|
||||
font-style: normal;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.icon-youduiqi:before {
|
||||
content: "\e65b";
|
||||
}
|
||||
|
||||
.icon-xiaduiqi:before {
|
||||
content: "\e64b";
|
||||
}
|
||||
|
||||
.icon-shuipingjuzhongduiqi:before {
|
||||
content: "\e600";
|
||||
}
|
||||
|
||||
.icon-zuoduiqi:before {
|
||||
content: "\e67f";
|
||||
}
|
||||
|
||||
.icon-card-text-center:before {
|
||||
content: "\e632";
|
||||
}
|
||||
|
||||
.icon-shangduiqi:before {
|
||||
content: "\e64e";
|
||||
}
|
||||
|
||||
.icon-zuoduiqi2:before {
|
||||
content: "\e650";
|
||||
}
|
||||
|
||||
.icon-youduiqi1:before {
|
||||
content: "\e60e";
|
||||
}
|
||||
|
||||
.icon-zuoduiqi1:before {
|
||||
content: "\e60f";
|
||||
}
|
||||
|
||||
.icon-Undo:before {
|
||||
content: "\e603";
|
||||
}
|
||||
|
||||
.icon-chuizhijuzhongduiqi:before {
|
||||
content: "\e670";
|
||||
}
|
||||
|
||||
.icon-ic_undo:before {
|
||||
content: "\e651";
|
||||
}
|
||||
|
BIN
src/components/poster-editor/icon/iconfont.eot
Normal file
2
src/components/poster-editor/icon/iconfont.js
Normal file
93
src/components/poster-editor/icon/iconfont.json
Normal file
@ -0,0 +1,93 @@
|
||||
{
|
||||
"id": "1577785",
|
||||
"name": "classcard",
|
||||
"font_family": "iconfont",
|
||||
"css_prefix_text": "icon-",
|
||||
"description": "",
|
||||
"glyphs": [
|
||||
{
|
||||
"icon_id": "657594",
|
||||
"name": "右对齐",
|
||||
"font_class": "youduiqi",
|
||||
"unicode": "e65b",
|
||||
"unicode_decimal": 58971
|
||||
},
|
||||
{
|
||||
"icon_id": "2404492",
|
||||
"name": "下对齐",
|
||||
"font_class": "xiaduiqi",
|
||||
"unicode": "e64b",
|
||||
"unicode_decimal": 58955
|
||||
},
|
||||
{
|
||||
"icon_id": "2684435",
|
||||
"name": "水平居中对齐",
|
||||
"font_class": "shuipingjuzhongduiqi",
|
||||
"unicode": "e600",
|
||||
"unicode_decimal": 58880
|
||||
},
|
||||
{
|
||||
"icon_id": "2791240",
|
||||
"name": "左对齐",
|
||||
"font_class": "zuoduiqi",
|
||||
"unicode": "e67f",
|
||||
"unicode_decimal": 59007
|
||||
},
|
||||
{
|
||||
"icon_id": "4297547",
|
||||
"name": "居中对齐",
|
||||
"font_class": "card-text-center",
|
||||
"unicode": "e632",
|
||||
"unicode_decimal": 58930
|
||||
},
|
||||
{
|
||||
"icon_id": "4875059",
|
||||
"name": "上对齐",
|
||||
"font_class": "shangduiqi",
|
||||
"unicode": "e64e",
|
||||
"unicode_decimal": 58958
|
||||
},
|
||||
{
|
||||
"icon_id": "4875066",
|
||||
"name": "左对齐",
|
||||
"font_class": "zuoduiqi2",
|
||||
"unicode": "e650",
|
||||
"unicode_decimal": 58960
|
||||
},
|
||||
{
|
||||
"icon_id": "5003035",
|
||||
"name": "右对齐",
|
||||
"font_class": "youduiqi1",
|
||||
"unicode": "e60e",
|
||||
"unicode_decimal": 58894
|
||||
},
|
||||
{
|
||||
"icon_id": "5003037",
|
||||
"name": "左对齐",
|
||||
"font_class": "zuoduiqi1",
|
||||
"unicode": "e60f",
|
||||
"unicode_decimal": 58895
|
||||
},
|
||||
{
|
||||
"icon_id": "8074641",
|
||||
"name": "undo",
|
||||
"font_class": "Undo",
|
||||
"unicode": "e603",
|
||||
"unicode_decimal": 58883
|
||||
},
|
||||
{
|
||||
"icon_id": "8229624",
|
||||
"name": "垂直居中对齐",
|
||||
"font_class": "chuizhijuzhongduiqi",
|
||||
"unicode": "e670",
|
||||
"unicode_decimal": 58992
|
||||
},
|
||||
{
|
||||
"icon_id": "8756570",
|
||||
"name": "undo",
|
||||
"font_class": "ic_undo",
|
||||
"unicode": "e651",
|
||||
"unicode_decimal": 58961
|
||||
}
|
||||
]
|
||||
}
|
62
src/components/poster-editor/icon/iconfont.svg
Normal file
@ -0,0 +1,62 @@
|
||||
<?xml version="1.0" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
|
||||
<!--
|
||||
2013-9-30: Created.
|
||||
-->
|
||||
<svg>
|
||||
<metadata>
|
||||
Created by iconfont
|
||||
</metadata>
|
||||
<defs>
|
||||
|
||||
<font id="iconfont" horiz-adv-x="1024" >
|
||||
<font-face
|
||||
font-family="iconfont"
|
||||
font-weight="500"
|
||||
font-stretch="normal"
|
||||
units-per-em="1024"
|
||||
ascent="896"
|
||||
descent="-128"
|
||||
/>
|
||||
<missing-glyph />
|
||||
|
||||
<glyph glyph-name="youduiqi" unicode="" d="M819.2 793.6l51.2 0 0-819.2-51.2 0 0 819.2ZM358.4 691.2l409.6 0 0-256-409.6 0 0 256ZM716.8 332.8l0-204.8L204.8 128l0 204.8L716.8 332.8M768 384 153.6 384l0-307.2 614.4 0L768 384 768 384z" horiz-adv-x="1024" />
|
||||
|
||||
|
||||
<glyph glyph-name="xiaduiqi" unicode="" d="M896 96v-48H128v48h768zM848 528V144h-240V528h240zM464 672v-480H272v480h192m48 48H224V144H512V720z" horiz-adv-x="1024" />
|
||||
|
||||
|
||||
<glyph glyph-name="shuipingjuzhongduiqi" unicode="" d="M486.40000000000015-25.600000000000136l-5.684341886080802e-14 102.4 51.2 2.842170943040401e-14 5.684341886080802e-14-102.4-51.2-2.842170943040401e-14ZM486.40000000000003 383.9999999999999l-2.2737367544323206e-13 409.6 51.2 2.842170943040401e-14 2.2737367544323206e-13-409.6-51.2-2.842170943040401e-14ZM332.79999999999995 435.19999999999993l-1.1368683772161603e-13 256 358.4 1.7053025658242404e-13 1.1368683772161603e-13-256-358.4-1.7053025658242404e-13ZM256.00000000000006 332.79999999999995l512 2.2737367544323206e-13L768 128.0000000000001 256.00000000000006 128 256.00000000000006 332.79999999999995M204.79999999999995 384L204.80000000000024 76.79999999999984l614.3999999999999 2.2737367544323206e-13-1.1368683772161603e-13 307.19999999999993L204.79999999999995 384 204.79999999999995 384z" horiz-adv-x="1024" />
|
||||
|
||||
|
||||
<glyph glyph-name="zuoduiqi" unicode="" d="M153.6 716.8h51.2v-665.6h-51.2zM870.4 358.4v-307.2H256V358.4h614.4z m-51.2-256V307.2H307.2v-204.8h512zM281.6 691.2h358.4v-256H281.6zM665.6 716.8v-307.2H256V716.8h409.6z m-51.2-256V665.6H307.2v-204.8h307.2z" horiz-adv-x="1024" />
|
||||
|
||||
|
||||
<glyph glyph-name="card-text-center" unicode="" d="M73.142857 18.285713999999984h1097.142857c43.885714 0 73.142857-29.257143 73.142857-73.142857s-29.257143-73.142857-73.142857-73.142857H73.142857c-43.885714 0-73.142857 29.257143-73.142857 73.142857s29.257143 73.142857 73.142857 73.142857zM321.828571 310.85714299999995h599.771429c43.885714 0 73.142857-29.257143 73.142857-73.142857s-29.257143-73.142857-73.142857-73.142857H321.828571c-43.885714 0-73.142857 29.257143-73.142857 73.142857s29.257143 73.142857 73.142857 73.142857zM197.485714 603.4285709999999h848.457143c43.885714 0 73.142857-29.257143 73.142857-73.142857s-29.257143-73.142857-73.142857-73.142857H197.485714c-43.885714 0-73.142857 29.257143-73.142857 73.142857s29.257143 73.142857 73.142857 73.142857zM446.171429 896h351.085714c43.885714 0 73.142857-29.257143 73.142857-73.142857s-29.257143-73.142857-73.142857-73.142857H446.171429c-43.885714 0-73.142857 29.257143-73.142858 73.142857s29.257143 73.142857 73.142858 73.142857z" horiz-adv-x="1243" />
|
||||
|
||||
|
||||
<glyph glyph-name="shangduiqi" unicode="" d="M95.001 696.75v52.125H929v-52.125H95.001z m469.124-469.125v417H824.75v-416.999H564.125zM459.875 592.5v-521.25h-208.5V592.5h208.5M512 644.625H199.23v-625.499H512V644.625z" horiz-adv-x="1024" />
|
||||
|
||||
|
||||
<glyph glyph-name="zuoduiqi2" unicode="" d="M199.25-32.999h-52.125V800.999h52.125v-833.998z m469.125 469.124h-417V696.75h416.999v-260.625zM303.5 331.875h521.25v-208.5H303.5v208.5M251.375 384v-312.77h625.499V384H251.375z" horiz-adv-x="1024" />
|
||||
|
||||
|
||||
<glyph glyph-name="youduiqi1" unicode="" d="M347.46496 845.91744v-49.81248c0-27.44704 22.29376-49.81376 50.0864-49.81376h701.9904c27.6608 0 50.0864 22.30144 50.0864 49.81376V845.91744c0 27.51232-22.4256 49.81376-50.0864 49.81376h-701.696c-27.70944 0-50.21824-22.25536-50.3808-49.81376zM116.3712 554.67648v-49.81248c0-27.34848 22.29248-49.81376 50.0864-49.81376h952.51712c27.6608 0 50.0864 22.30144 50.0864 49.81376V554.6752c0 27.51232-22.4256 49.81376-50.0864 49.81376H166.4576a49.93152 49.93152 0 0 1-35.50336-14.5024 49.3824 49.3824 0 0 1-14.58304-35.31136z m339.24864-241.42464c-27.73248 0.05376-50.272-22.2336-50.3808-49.81376v-49.81376c0-27.44576 22.4896-49.81376 50.18496-49.81376h651.80544c27.6544 0.05376 50.07616 22.30912 50.18368 49.81376V263.4368c0 27.34848-22.4896 49.81376-50.18368 49.81376h-651.6096zM51.20384 22.01088C23.47008 22.06464 0.92928-0.22144 0.82176-27.8016v-49.81248c0-27.44704 22.4896-49.81376 50.18368-49.81376h1052.88576c27.66208 0 50.0864 22.30144 50.0864 49.81376v49.81248c0 27.51232-22.42432 49.81376-50.0864 49.81376H51.20256z" horiz-adv-x="1169" />
|
||||
|
||||
|
||||
<glyph glyph-name="zuoduiqi1" unicode="" d="M0.832 847.08608v-48.65024c0-26.80576 22.5728-48.6528 50.7136-48.6528h710.76352c28.00768 0 50.71232 21.78304 50.71232 48.6528V847.08608c0 26.86976-22.70464 48.6528-50.71232 48.6528H51.84256C23.78752 895.7376 0.9984 874.00064 0.83328 847.0848z m0.29952-292.288v-48.65152c0-26.70976 22.57152-48.65152 50.71104-48.65152H1016.2688c28.0064 0 50.71232 21.78176 50.71232 48.6528v48.65024c0 26.86848-22.70464 48.65152-50.7136 48.65152H51.84384c-13.47328 0.0768-26.4192-5.02528-35.94624-14.16576C6.368 580.14464 1.0496 567.7248 1.13152 554.79808z m50.71104-243.44704C23.76448 311.40352 0.94208 289.6384 0.83328 262.69952v-48.65024c0-26.80576 22.7712-48.6528 50.81088-48.6528h659.95264c28.00128 0.05376 50.70208 21.78944 50.81216 48.6528v48.65024c0 26.71104-22.7712 48.6528-50.81216 48.6528H51.84256z m0-292.384C23.76448 19.01952 0.94208-2.74688 0.83328-29.68448v-48.65024c0-26.80576 22.7712-48.6528 50.81088-48.6528h1066.048c28.0064 0 50.71104 21.78304 50.71104 48.6528v48.65024c0 26.86976-22.70464 48.65152-50.71104 48.65152H51.84256z" horiz-adv-x="1168" />
|
||||
|
||||
|
||||
<glyph glyph-name="Undo" unicode="" d="M1023.99732 9.762c-4.13 223.3-169.472 551.151-511.966 561.698V759.467a17.067 17.067 0 1 1-26.317 14.37L7.84832 466.637a17.033 17.033 0 0 1-0.683-28.228L485.03132 97.075A17.135 17.135 0 0 1 512.03132 110.933V298.36c252.28-9.898 475.648-235.28 477.833-289.006l0.034-1.093a17.1 17.1 0 0 1 17.066-16.793h0.137a17.067 17.067 0 0 1 16.93 17.066l-0.034 1.23zM494.96432 332.8a17.067 17.067 0 0 1-17.066-17.067v-171.622L47.51032 451.516 477.89832 728.2v-173.534a17.067 17.067 0 0 1 17.066-17.067c279.552 0 447.898-238.763 486.469-447.317C895.48532 193.673 692.63032 332.8 494.96432 332.8z" horiz-adv-x="1024" />
|
||||
|
||||
|
||||
<glyph glyph-name="chuizhijuzhongduiqi" unicode="" d="M0.512 422.7072h125.8496v-61.44H0.512v61.44zM504.0128 422.7072h503.5008v-61.44H504.0128v61.44zM566.9376 607.0272h314.6752v-430.08h-314.6752zM441.0368 699.1872v-614.4H189.2864v614.4h251.7504z m62.976 61.44H126.3616v-737.28h377.6512v737.28z" horiz-adv-x="1024" />
|
||||
|
||||
|
||||
<glyph glyph-name="ic_undo" unicode="" d="M484.308604 648.193618v247.831671L0 456.655142 484.308604 17.209128V282.945372c134.562481 0 550.186308 0 729.560802-410.920083v45.646547c0 182.636768-179.399783 769.390892-729.560802 730.521782z" horiz-adv-x="1213" />
|
||||
|
||||
|
||||
|
||||
|
||||
</font>
|
||||
</defs></svg>
|
After Width: | Height: | Size: 7.0 KiB |
BIN
src/components/poster-editor/icon/iconfont.ttf
Normal file
BIN
src/components/poster-editor/icon/iconfont.woff
Normal file
BIN
src/components/poster-editor/icon/iconfont.woff2
Normal file
35
src/components/poster-editor/icon/index.vue
Normal file
@ -0,0 +1,35 @@
|
||||
<template>
|
||||
<svg
|
||||
:class="{active: active}"
|
||||
class="icon el-icon cursor-p" aria-hidden="true">
|
||||
<use :xlink:href="'#icon-' + icon"></use>
|
||||
</svg>
|
||||
</template>
|
||||
<script>
|
||||
import './iconfont.js'
|
||||
export default {
|
||||
props: {
|
||||
icon: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
active: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss">
|
||||
.icon{
|
||||
font-size: 18px;
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
vertical-align: -0.15em;
|
||||
fill: rgba(0,0,0,0.5);
|
||||
overflow: hidden;
|
||||
&.active{
|
||||
fill: rgba(0,0,0,1);
|
||||
}
|
||||
}
|
||||
</style>
|
39
src/components/poster-editor/imgWidget.vue
Normal file
@ -0,0 +1,39 @@
|
||||
<template>
|
||||
<img
|
||||
v-if="value"
|
||||
:style="{
|
||||
transform: `scale(${needScale ? dZoom : 1})`
|
||||
}"
|
||||
class="img-widget-ct"
|
||||
:src="value"
|
||||
/>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
value: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
// 解决chrome字体无法更小的问题
|
||||
needScale: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
dZoom: {
|
||||
type: Number,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
created() {
|
||||
console.log(this.value);
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style lang="scss">
|
||||
.img-widget-ct {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
user-select: none;
|
||||
}
|
||||
</style>
|
11
src/components/poster-editor/index.js
Normal file
@ -0,0 +1,11 @@
|
||||
import 'element-ui/lib/theme-chalk/index.css'
|
||||
import Vue from 'vue'
|
||||
import ElementUI from 'element-ui'
|
||||
import './assets/reset.css'
|
||||
import './assets/common.scss'
|
||||
import PosterEditor from './index.vue'
|
||||
Vue.use(ElementUI)
|
||||
PosterEditor.install = function (Vue) {
|
||||
Vue.component(PosterEditor.name, PosterEditor)
|
||||
}
|
||||
export default PosterEditor
|
750
src/components/poster-editor/index.vue
Normal file
@ -0,0 +1,750 @@
|
||||
<template>
|
||||
<div class="poster-editor-comp" :id="editorUid">
|
||||
<div class="main-pane" :style="{ minHeight: 720 * dZoom + 58 + 'px' }">
|
||||
<div class="header-ct">
|
||||
<div class="left">
|
||||
<el-tooltip effect="dark" content="撤销" placement="top-start">
|
||||
<el-button
|
||||
:disabled="!canClickPrev"
|
||||
@click="getHistory(-1)"
|
||||
type="text"
|
||||
>
|
||||
<Icon :active="canClickPrev" class="m-r-16" icon="Undo"></Icon>
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip effect="dark" content="前进" placement="top-start">
|
||||
<el-button
|
||||
:disabled="!canClickNext"
|
||||
@click="getHistory(1)"
|
||||
type="text"
|
||||
>
|
||||
<Icon :active="canClickNext" class="mirror" icon="Undo"></Icon>
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
<div class="center">
|
||||
轻点元素开始编辑
|
||||
</div>
|
||||
<div class="right">
|
||||
<el-button @click="submitImg">提交</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<draggable
|
||||
ref="posterContent"
|
||||
v-bind="getOptions()"
|
||||
@end="isDragging = false"
|
||||
@start="isDragging = true"
|
||||
:style="{
|
||||
width: dZoom * page.width + 'px',
|
||||
height: dZoom * page.height + 'px',
|
||||
backgroundColor: value.background.color
|
||||
}"
|
||||
class="poster-content m-b-16"
|
||||
tag="div"
|
||||
>
|
||||
<img class="bg-puppet" :src="value && value.background.img" />
|
||||
<widget
|
||||
:class="{ disabled: !item.active }"
|
||||
@click.native="handleActiveWidget(item)"
|
||||
@dragstart.native="dragStart($event, item)"
|
||||
@dragend.native="dragEnd($event, item, index)"
|
||||
@modify="pushHistory"
|
||||
:widget-data="item"
|
||||
:order="index"
|
||||
:page="page"
|
||||
:d-zoom="dZoom"
|
||||
:key="item.uuid"
|
||||
v-for="(item, index) in widgetList"
|
||||
>
|
||||
</widget>
|
||||
</draggable>
|
||||
</div>
|
||||
<div class="right-pane">
|
||||
<el-tabs>
|
||||
<el-tab-pane label="当前组件">
|
||||
<div v-if="activeWidget" class="style-setting-ct">
|
||||
<el-collapse v-model="activeNames">
|
||||
<el-collapse-item
|
||||
v-if="activeWidget.setDataRange.content.show"
|
||||
title="文本内容"
|
||||
name="1"
|
||||
>
|
||||
<div class="style-config m-r-16">
|
||||
<el-input
|
||||
v-model="activeWidget.content"
|
||||
type="textarea"
|
||||
></el-input>
|
||||
</div>
|
||||
</el-collapse-item>
|
||||
<el-collapse-item
|
||||
v-if="activeWidget.setDataRange.box.show"
|
||||
title="组件样式"
|
||||
name="2"
|
||||
>
|
||||
<div class="style-config">
|
||||
<span class="label">
|
||||
X
|
||||
</span>
|
||||
<el-input-number
|
||||
:precision="0"
|
||||
v-model="activeWidget.style.left"
|
||||
class="w-60"
|
||||
:controls="false"
|
||||
size="mini"
|
||||
></el-input-number>
|
||||
<span class="label">
|
||||
Y
|
||||
</span>
|
||||
<el-input-number
|
||||
:precision="0"
|
||||
v-model="activeWidget.style.top"
|
||||
class="w-60"
|
||||
:controls="false"
|
||||
size="mini"
|
||||
></el-input-number>
|
||||
</div>
|
||||
<div class="style-config">
|
||||
<span class="label">
|
||||
宽度
|
||||
</span>
|
||||
<el-input-number
|
||||
v-model="activeWidget.style.width"
|
||||
class="w-60"
|
||||
:controls="false"
|
||||
size="mini"
|
||||
></el-input-number>
|
||||
<span class="label">
|
||||
高度
|
||||
</span>
|
||||
<el-input-number
|
||||
v-model="activeWidget.style.height"
|
||||
class="w-60"
|
||||
:controls="false"
|
||||
size="mini"
|
||||
></el-input-number>
|
||||
</div>
|
||||
<div class="style-config">
|
||||
<template
|
||||
v-if="activeWidget.setDataRange.box.detail.background"
|
||||
>
|
||||
<span class="label">
|
||||
背景色
|
||||
</span>
|
||||
<el-color-picker
|
||||
v-model="activeWidget.style.background"
|
||||
class="w-60"
|
||||
show-alpha
|
||||
size="mini"
|
||||
></el-color-picker>
|
||||
</template>
|
||||
<span class="label">
|
||||
层级
|
||||
</span>
|
||||
<el-input-number
|
||||
disabled
|
||||
:value="activeWidget.style.zIndex"
|
||||
class="w-60"
|
||||
:controls="false"
|
||||
size="mini"
|
||||
></el-input-number>
|
||||
</div>
|
||||
<div class="style-config">
|
||||
<!-- <span class="label">
|
||||
对齐
|
||||
</span>
|
||||
{{activeWidget.style.textAlign}}-->
|
||||
</div>
|
||||
</el-collapse-item>
|
||||
<el-collapse-item
|
||||
v-if="activeWidget.setDataRange.innerStyle.show"
|
||||
title="字体样式"
|
||||
name="3"
|
||||
>
|
||||
<div class="style-config">
|
||||
<span class="label">
|
||||
大小
|
||||
</span>
|
||||
<el-input-number
|
||||
v-model="activeWidget.style.fontSize"
|
||||
class="w-60"
|
||||
:controls="false"
|
||||
size="mini"
|
||||
></el-input-number>
|
||||
<span class="label">
|
||||
行高
|
||||
</span>
|
||||
<el-input-number
|
||||
v-model="activeWidget.style.lineHeight"
|
||||
class="w-60"
|
||||
:controls="false"
|
||||
size="mini"
|
||||
></el-input-number>
|
||||
</div>
|
||||
<div class="style-config">
|
||||
<span class="label">
|
||||
字距
|
||||
</span>
|
||||
<el-input-number
|
||||
v-model="activeWidget.style.letterSpacing"
|
||||
class="w-60"
|
||||
:controls="false"
|
||||
size="mini"
|
||||
></el-input-number>
|
||||
<span class="label">
|
||||
颜色
|
||||
</span>
|
||||
<el-color-picker
|
||||
v-model="activeWidget.style.color"
|
||||
class="w-60"
|
||||
show-alpha
|
||||
size="mini"
|
||||
></el-color-picker>
|
||||
</div>
|
||||
<div class="style-config">
|
||||
<!--<span class="label">
|
||||
对齐
|
||||
</span>
|
||||
<el-input-number class="w-60" :controls="false" size="mini"></el-input-number>-->
|
||||
<!-- <span class="label">
|
||||
字体
|
||||
</span>
|
||||
<el-input-number class="w-60" :controls="false" size="mini"></el-input-number>-->
|
||||
</div>
|
||||
<div class="style-config">
|
||||
<span class="label">
|
||||
对齐
|
||||
</span>
|
||||
<align-select
|
||||
v-model="activeWidget.style.align"
|
||||
></align-select>
|
||||
</div>
|
||||
<div class="style-config">
|
||||
<span class="label">其他</span>
|
||||
<span
|
||||
:class="{ active: activeWidget.style.isBold }"
|
||||
@click="handleOtherClick('isBold')"
|
||||
class="cursor-p other"
|
||||
><b>B</b></span
|
||||
>
|
||||
<span
|
||||
:class="{ active: activeWidget.style.isOblique }"
|
||||
@click="handleOtherClick('isOblique')"
|
||||
class="m-l-8 cursor-p other"
|
||||
><i>I</i></span
|
||||
>
|
||||
<span></span>
|
||||
</div>
|
||||
</el-collapse-item>
|
||||
</el-collapse>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import _ from 'lodash';
|
||||
import draggable from 'vuedraggable';
|
||||
import uuid from 'uuid';
|
||||
import html2canvas from 'html2canvas';
|
||||
import { Loading } from 'element-ui';
|
||||
import alignSelect from './alignSelect';
|
||||
import widget from './widget';
|
||||
import blankDialog from '@/components/blankDialog';
|
||||
import imageSelection from '@/components/imageSelection';
|
||||
import { setDataRangeMap, defaultDataMap } from './widgetInitDataMap';
|
||||
import Icon from './icon/index';
|
||||
import { qrUpdate } from '@/api/index';
|
||||
import VueQr from 'vue-qr';
|
||||
|
||||
let zIndex = 0;
|
||||
|
||||
export default {
|
||||
name: 'posterEditor',
|
||||
props: {
|
||||
order: {
|
||||
type: Number,
|
||||
required: true
|
||||
},
|
||||
id: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
value: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
page: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {
|
||||
width: 375,
|
||||
height: 620
|
||||
};
|
||||
}
|
||||
}
|
||||
},
|
||||
components: {
|
||||
widget,
|
||||
draggable,
|
||||
alignSelect,
|
||||
blankDialog,
|
||||
imageSelection,
|
||||
Icon,
|
||||
VueQr
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
editorUid: 'uid' + uuid(),
|
||||
dZoom: 0.52,
|
||||
isDragging: false,
|
||||
activeNames: ['1', '2', '3', '4', '5', '6'],
|
||||
widgetList: [],
|
||||
dragStartPosition: null,
|
||||
widgetDragStart: null, // 当前拖的是哪个文本图层 便于后期交互值
|
||||
handleResizeFn: null,
|
||||
handleActiveWidgetChange: null,
|
||||
showImageSelection: false,
|
||||
showImageSelectionWidget: false,
|
||||
widgetHistory: [], // 记录历史记录变动的
|
||||
historyIndex: -1
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
submitImg() {
|
||||
// const
|
||||
console.log(this.id);
|
||||
// qrUpdate(this.value).then(res => {
|
||||
// console.log(res);
|
||||
// });
|
||||
},
|
||||
beforeAddImgWidget(imgObj) {
|
||||
this.addWidget('img', imgObj.path);
|
||||
this.showImageSelectionWidget = false;
|
||||
},
|
||||
addWidget(type, path) {
|
||||
zIndex++;
|
||||
const typeLabelMap = {
|
||||
text: '文本',
|
||||
img: '图片'
|
||||
};
|
||||
const item = {
|
||||
label: typeLabelMap[type],
|
||||
type,
|
||||
content: type === 'text' ? '文本' : path,
|
||||
uuid: uuid(),
|
||||
active: false,
|
||||
setDataRange: setDataRangeMap[type],
|
||||
style: {
|
||||
...defaultDataMap[type],
|
||||
zIndex
|
||||
}
|
||||
};
|
||||
this.widgetList.push(item);
|
||||
this.$nextTick(() => {
|
||||
this.handleActiveWidget(item);
|
||||
});
|
||||
console.log(this.value);
|
||||
this.$emit('input', {
|
||||
background: this.value.background,
|
||||
widgetList: this.widgetList
|
||||
});
|
||||
this.pushHistory('add', item, this.widgetList.length - 1);
|
||||
},
|
||||
handleActiveWidget(item) {
|
||||
this.widgetList.forEach(w => {
|
||||
if (item.uuid === w.uuid) {
|
||||
w.active = !item.active;
|
||||
} else {
|
||||
w.active = false;
|
||||
}
|
||||
});
|
||||
},
|
||||
getOptions() {
|
||||
return {
|
||||
filter: '.disabled'
|
||||
};
|
||||
},
|
||||
dragStart(e) {
|
||||
this.dragStartPosition = {
|
||||
pageX: e.pageX,
|
||||
pageY: e.pageY
|
||||
};
|
||||
},
|
||||
dragEnd(e, item, index) {
|
||||
item.history = {
|
||||
content: item.content,
|
||||
style: _.cloneDeep(item.style)
|
||||
};
|
||||
let target = item.style;
|
||||
let left = (e.pageX - this.dragStartPosition.pageX) / this.dZoom;
|
||||
let top = (e.pageY - this.dragStartPosition.pageY) / this.dZoom;
|
||||
target.left = Math.max(
|
||||
Math.min(left + target.left, this.page.width - target.width),
|
||||
0
|
||||
);
|
||||
target.top = Math.max(
|
||||
Math.min(top + target.top, this.page.height - target.height),
|
||||
0
|
||||
);
|
||||
this.pushHistory('modify', item, index);
|
||||
},
|
||||
deleteWidget(item) {
|
||||
let index = this.widgetList.findIndex(v => v.uuid === item.uuid);
|
||||
if (index > -1) {
|
||||
this.$confirm(
|
||||
`此操作将永久删除该${item.label}图层, 是否继续?`,
|
||||
'警告',
|
||||
{
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}
|
||||
).then(() => {
|
||||
this.widgetList.splice(index, 1);
|
||||
this.pushHistory('delete', item, index);
|
||||
});
|
||||
}
|
||||
},
|
||||
handleOtherClick(key) {
|
||||
this.activeWidget.style[key] = !this.activeWidget.style[key];
|
||||
},
|
||||
dragZIndexStart(item) {
|
||||
this.widgetDragStart = item;
|
||||
},
|
||||
dragZIndexEnd(item) {
|
||||
let tmp = item.style.zIndex;
|
||||
item.style.zIndex = this.widgetDragStart.style.zIndex;
|
||||
this.widgetDragStart.style.zIndex = tmp;
|
||||
this.widgetDragStart = null;
|
||||
},
|
||||
getDZoom() {
|
||||
// const currentEditorBox = document.querySelector(`#${this.editorUid}`).getBoundingClientRect()
|
||||
// fix v-show隐藏元素宽度太小的问题
|
||||
const currentEditorBox = document
|
||||
.querySelector(`.poster-editor-comp`)
|
||||
.getBoundingClientRect();
|
||||
let width = currentEditorBox.width - 32 - 295;
|
||||
if (width >= 480) {
|
||||
this.dZoom = Number((width / 960).toFixed(2));
|
||||
}
|
||||
if (width >= 960) {
|
||||
this.dZoom = 1;
|
||||
}
|
||||
},
|
||||
// 上传当前poster截图
|
||||
uploadCurrentPosterShot(cb) {
|
||||
let instance = Loading.service({
|
||||
text: '保存中,请稍候!',
|
||||
background: 'rgba(0, 0, 0, 0.5)'
|
||||
});
|
||||
console.time('1');
|
||||
this.widgetList.forEach(w => {
|
||||
w.active = false;
|
||||
});
|
||||
return new Promise(resolve => {
|
||||
// 激活当前编辑器 方便截图
|
||||
this.$emit('active');
|
||||
this.$nextTick(() => {
|
||||
html2canvas(
|
||||
document
|
||||
.querySelector(`#${this.editorUid}`)
|
||||
.querySelector('.poster-content'),
|
||||
{
|
||||
useCORS: true,
|
||||
// 编辑器缩放保持 960 * 720 的尺寸
|
||||
scale: 1 / this.dZoom
|
||||
}
|
||||
).then(async canvas => {
|
||||
console.timeEnd('1');
|
||||
console.time('2');
|
||||
let file = canvas
|
||||
.toDataURL('image/jpeg', 1)
|
||||
.replace('data:image/jpeg;base64,', '');
|
||||
// base64的图片
|
||||
console.timeEnd('2');
|
||||
console.time('3');
|
||||
if (cb) {
|
||||
await cb(file, resolve);
|
||||
console.timeEnd('3');
|
||||
instance.close();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
changeBackground(imgUrl) {
|
||||
console.log((this.value.background.img = imgUrl));
|
||||
// this.value.background = imgUrl;
|
||||
// this.value.material_uid = imgObj.uid;
|
||||
// this.value.materialType = imgObj.expandType;
|
||||
this.showImageSelection = false;
|
||||
},
|
||||
changeTextColor(color) {
|
||||
this.value.background.color = color;
|
||||
console.log(this.value.background);
|
||||
},
|
||||
pushHistory(type, item, index) {
|
||||
const obj = {
|
||||
uuid: item.uuid,
|
||||
item,
|
||||
index,
|
||||
type
|
||||
};
|
||||
if (this.canClickNext) {
|
||||
// 已经执行过撤销的操作了 再添加历史记录把后面的删除掉
|
||||
this.widgetHistory.length = this.historyIndex + 1;
|
||||
}
|
||||
this.widgetHistory.push(obj);
|
||||
this.historyIndex = this.widgetHistory.length - 1;
|
||||
/* switch (type) {
|
||||
case 'add':
|
||||
obj.type = 'add'
|
||||
|
||||
this.historyIndex = this.widgetHistory.length - 1
|
||||
break
|
||||
case 'delete':
|
||||
obj.type = 'delete'
|
||||
this.widgetHistory.push(obj)
|
||||
this.historyIndex = this.widgetHistory.length
|
||||
break
|
||||
case 'modify':
|
||||
break
|
||||
} */
|
||||
},
|
||||
getHistory(num) {
|
||||
if ((num < 0 && this.canClickPrev) || (num > 0 && this.canClickNext)) {
|
||||
if (num > 0) this.historyIndex += num;
|
||||
let historyObj = this.widgetHistory[this.historyIndex];
|
||||
switch (historyObj.type) {
|
||||
case 'add':
|
||||
// 撤回操作
|
||||
if (num < 0) {
|
||||
let index = this.widgetList.findIndex(
|
||||
v => v.uuid === historyObj.uuid
|
||||
);
|
||||
if (index > -1) {
|
||||
this.widgetList.splice(index, 1);
|
||||
}
|
||||
} else {
|
||||
this.widgetList.push(historyObj.item);
|
||||
}
|
||||
break;
|
||||
case 'delete':
|
||||
if (num < 0) {
|
||||
this.widgetList.splice(historyObj.index, 0, historyObj.item);
|
||||
} else {
|
||||
this.widgetList.splice(historyObj.index, 1);
|
||||
}
|
||||
break;
|
||||
case 'modify':
|
||||
if (num < 0) {
|
||||
this.$set(this.widgetList, historyObj.index, {
|
||||
...historyObj.item,
|
||||
style: historyObj.item.history.style,
|
||||
content: historyObj.item.history.content
|
||||
});
|
||||
} else {
|
||||
this.$set(this.widgetList, historyObj.index, {
|
||||
...historyObj.item
|
||||
});
|
||||
}
|
||||
}
|
||||
if (num < 0) {
|
||||
this.historyIndex += num;
|
||||
}
|
||||
// this.widgetHistory[this.historyIndex - 1]
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
activeWidget() {
|
||||
return this.widgetList.find(w => w.active);
|
||||
},
|
||||
sortWidgetList() {
|
||||
return this.widgetList
|
||||
.slice(0)
|
||||
.sort((a, b) => b.style.zIndex - a.style.zIndex);
|
||||
},
|
||||
canClickPrev() {
|
||||
// 是否能点击上一步
|
||||
return this.historyIndex > -1;
|
||||
},
|
||||
canClickNext() {
|
||||
return (
|
||||
this.historyIndex < this.widgetHistory.length - 1 &&
|
||||
this.widgetHistory.length > 0
|
||||
);
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
widgetList: {
|
||||
handler(val) {
|
||||
zIndex = val.length;
|
||||
val.forEach((v, i) => {
|
||||
v.style.zIndex = i + 1;
|
||||
});
|
||||
this.$emit('input', {
|
||||
background: this.value.background,
|
||||
widgetList: this.widgetList
|
||||
});
|
||||
},
|
||||
deep: true
|
||||
},
|
||||
'value.widgetList': {
|
||||
handler(val) {
|
||||
if (val !== this.widgetList) {
|
||||
this.widgetList = val;
|
||||
}
|
||||
},
|
||||
deep: true
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.getDZoom();
|
||||
this.handleResizeFn = _.throttle(this.getDZoom, 100);
|
||||
window.addEventListener('resize', this.handleResizeFn);
|
||||
},
|
||||
beforeDestroy() {
|
||||
window.removeEventListener('resize', this.handleResizeFn);
|
||||
},
|
||||
created() {
|
||||
this.widgetList = this.value.widgetList || [];
|
||||
this.widgetList.forEach(v => {
|
||||
v.history = {
|
||||
content: v.content,
|
||||
style: _.cloneDeep(v.style)
|
||||
};
|
||||
});
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.w-60 {
|
||||
width: 60px !important;
|
||||
}
|
||||
.poster-editor-comp {
|
||||
display: flex;
|
||||
min-width: 830px;
|
||||
.main-pane {
|
||||
position: relative;
|
||||
flex: 1;
|
||||
padding: 0 16px;
|
||||
.header-ct {
|
||||
display: flex;
|
||||
height: 40px;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
.left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.mirror {
|
||||
transform: rotateY(180deg);
|
||||
}
|
||||
}
|
||||
.center {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
.poster-content {
|
||||
top: 40px;
|
||||
left: 50%;
|
||||
position: absolute;
|
||||
/*min-width: 480px;
|
||||
min-height: 360px;
|
||||
max-width: 960px;
|
||||
max-height: 720px;*/
|
||||
width: 960px;
|
||||
height: 720px;
|
||||
/* margin: 0 auto;*/
|
||||
transform: translateX(-50%);
|
||||
&:hover {
|
||||
outline: 1px solid grey;
|
||||
}
|
||||
transform-origin: 0 0;
|
||||
background-size: 100% 100% !important;
|
||||
.bg-puppet {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
.right-pane {
|
||||
box-sizing: border-box;
|
||||
width: 295px;
|
||||
padding-left: 16px;
|
||||
border: 1px solid #e4e4e4;
|
||||
background: #fff;
|
||||
z-index: 900;
|
||||
.header-ct {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
height: 40px;
|
||||
}
|
||||
.style-setting-ct {
|
||||
.style-config {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 8px;
|
||||
padding-left: 16px;
|
||||
.label {
|
||||
display: inline-block;
|
||||
margin-left: 8px;
|
||||
width: 48px;
|
||||
text-align: left;
|
||||
}
|
||||
.other {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
line-height: 20px;
|
||||
text-align: center;
|
||||
font-size: 18px;
|
||||
color: rgba(0, 0, 0, 0.8);
|
||||
&.active {
|
||||
background: rgba(0, 0, 0, 0.8);
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.widget-zIndex-config-ct {
|
||||
.widget-list-item {
|
||||
cursor: move;
|
||||
}
|
||||
.item {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 40px;
|
||||
border-bottom: 1px solid #e1e1e1;
|
||||
font-size: 14px;
|
||||
.delete-btn {
|
||||
position: absolute;
|
||||
display: none;
|
||||
transition: all 200ms linear;
|
||||
}
|
||||
&:hover .delete-btn {
|
||||
display: block;
|
||||
}
|
||||
.view {
|
||||
margin-left: 40px;
|
||||
width: 24px;
|
||||
height: 18px;
|
||||
background-size: 100% 100% !important;
|
||||
border: 1px solid rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
.desc {
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
.bg {
|
||||
.view {
|
||||
margin-left: 24px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
33
src/components/poster-editor/textWidget.vue
Normal file
@ -0,0 +1,33 @@
|
||||
<template>
|
||||
<div
|
||||
:style="{
|
||||
transform: `scale(${needScale ? dZoom : 1})`
|
||||
}"
|
||||
class="text-widget-ct">
|
||||
{{value}}
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
value: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
// 解决chrome字体无法更小的问题
|
||||
needScale: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
dZoom: {
|
||||
type: Number,
|
||||
required: true
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss">
|
||||
.text-widget-ct {
|
||||
user-select: none;
|
||||
}
|
||||
</style>
|
95
src/components/poster-editor/widget.vue
Normal file
@ -0,0 +1,95 @@
|
||||
<template>
|
||||
<div
|
||||
:style="{
|
||||
...getStyleRaw
|
||||
}"
|
||||
class="widget-ct cursor-p"
|
||||
>
|
||||
<widgetWrapper
|
||||
:d-zoom="dZoom"
|
||||
:page="page"
|
||||
:show-wrapper="showWrapper"
|
||||
:widget-data="widgetData"
|
||||
:widget-name="widgetData.type"
|
||||
>
|
||||
<vue-qr text="123" :size="140"></vue-qr>
|
||||
</widgetWrapper>
|
||||
<widgetWrapper
|
||||
:d-zoom="dZoom"
|
||||
:page="page"
|
||||
:show-wrapper="showWrapper"
|
||||
:widget-data="widgetData"
|
||||
:widget-name="widgetData.type"
|
||||
>
|
||||
<component
|
||||
:need-scale="widgetData.type === 'text' && !showWrapper"
|
||||
:d-zoom="dZoom"
|
||||
v-model="widgetData.content"
|
||||
:is="childName"
|
||||
></component>
|
||||
</widgetWrapper>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import VueQr from 'vue-qr';
|
||||
import widgetWrapper from './widgetWrapper';
|
||||
import textWidget from './textWidget';
|
||||
import imgWidget from './imgWidget';
|
||||
export default {
|
||||
created() {
|
||||
console.log(this.dZoom);
|
||||
console.log(this.widgetData);
|
||||
console.log(this.showWrapper);
|
||||
console.log(this.page);
|
||||
},
|
||||
props: {
|
||||
dZoom: {
|
||||
type: Number,
|
||||
required: true
|
||||
},
|
||||
widgetData: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
order: {
|
||||
type: Number
|
||||
},
|
||||
showWrapper: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
page: {
|
||||
type: Object
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
getStyleRaw() {
|
||||
let obj = {};
|
||||
let style = this.widgetData.style;
|
||||
let zIndex = style.zIndex;
|
||||
for (let key in style) {
|
||||
obj[key] =
|
||||
typeof style[key] === 'number'
|
||||
? style[key] * this.dZoom + 'px'
|
||||
: style[key];
|
||||
}
|
||||
obj.zIndex = zIndex;
|
||||
return obj;
|
||||
},
|
||||
childName() {
|
||||
return this.widgetData.type + 'Widget';
|
||||
}
|
||||
},
|
||||
components: {
|
||||
widgetWrapper,
|
||||
textWidget,
|
||||
imgWidget,
|
||||
VueQr
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style lang="scss">
|
||||
.widget-ct {
|
||||
position: absolute;
|
||||
}
|
||||
</style>
|
94
src/components/poster-editor/widgetInitDataMap.js
Normal file
@ -0,0 +1,94 @@
|
||||
const setDataRangeMap = {
|
||||
text: {
|
||||
box: {
|
||||
show: true,
|
||||
detail: {
|
||||
top: true,
|
||||
left: true,
|
||||
height: true,
|
||||
width: true,
|
||||
background: true,
|
||||
zIndex: true
|
||||
}
|
||||
},
|
||||
content: {
|
||||
show: true
|
||||
},
|
||||
innerStyle: {
|
||||
show: true,
|
||||
detail: {
|
||||
align: true,
|
||||
lineHeight: true,
|
||||
fontSize: true,
|
||||
color: true,
|
||||
letterSpacing: true,
|
||||
isOblique: true,
|
||||
isBold: true
|
||||
}
|
||||
}
|
||||
},
|
||||
img: {
|
||||
box: {
|
||||
show: true,
|
||||
detail: {
|
||||
top: true,
|
||||
left: true,
|
||||
height: true,
|
||||
width: true,
|
||||
background: false,
|
||||
zIndex: true
|
||||
}
|
||||
},
|
||||
content: {
|
||||
show: false
|
||||
},
|
||||
innerStyle: {
|
||||
show: false,
|
||||
detail: {
|
||||
align: true,
|
||||
lineHeight: true,
|
||||
fontSize: true,
|
||||
color: true,
|
||||
letterSpacing: true,
|
||||
isOblique: true,
|
||||
isBold: true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const defaultDataMap = {
|
||||
text: {
|
||||
top: 0,
|
||||
left: 0,
|
||||
height: 40,
|
||||
width: 100,
|
||||
align: {
|
||||
x: 2,
|
||||
y: 2
|
||||
},
|
||||
// textAlign: 'center',
|
||||
lineHeight: 14,
|
||||
fontSize: 14,
|
||||
background: 'rgba(0, 0, 0, 0)',
|
||||
color: 'rgba(0, 0, 0, 1)',
|
||||
letterSpacing: 0,
|
||||
isOblique: false,
|
||||
isBold: false
|
||||
},
|
||||
img: {
|
||||
top: 0,
|
||||
left: 0,
|
||||
height: 40,
|
||||
width: 100,
|
||||
align: {
|
||||
x: 2,
|
||||
y: 2
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
setDataRangeMap,
|
||||
defaultDataMap
|
||||
}
|
240
src/components/poster-editor/widgetWrapper.vue
Normal file
@ -0,0 +1,240 @@
|
||||
<template>
|
||||
<div
|
||||
:style="{
|
||||
'font-weight': widgetData.style.isBold ? 'bold' : 'normal',
|
||||
'font-style': widgetData.style.isOblique ? 'oblique' : 'normal',
|
||||
'justify-content': alignMap[widgetData.style.align.x],
|
||||
'align-items': alignMap[widgetData.style.align.y]
|
||||
}"
|
||||
:class="{active: showWrapper && widgetData.active}"
|
||||
class="widget-wrapper">
|
||||
<template v-if="showWrapper && widgetData.active">
|
||||
<div
|
||||
@mousedown.stop="handleMouseDown($event, item.cursor)"
|
||||
:style="item"
|
||||
:key="i"
|
||||
v-for="(item, i) in sizeControls"
|
||||
class="square"></div>
|
||||
</template>
|
||||
<slot></slot>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
widgetData: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
showWrapper: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
dZoom: {
|
||||
type: Number,
|
||||
required: true
|
||||
},
|
||||
page: {
|
||||
type: Object
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
// 调整尺寸用的
|
||||
sizeControls: [
|
||||
// 左上
|
||||
{
|
||||
top: 0,
|
||||
left: 0,
|
||||
cursor: 'nw-resize'
|
||||
},
|
||||
{
|
||||
top: 0,
|
||||
left: '50%',
|
||||
cursor: 'n-resize'
|
||||
},
|
||||
{
|
||||
top: 0,
|
||||
left: '100%',
|
||||
cursor: 'ne-resize'
|
||||
},
|
||||
{
|
||||
top: '50%',
|
||||
left: '100%',
|
||||
cursor: 'e-resize'
|
||||
},
|
||||
{
|
||||
top: '100%',
|
||||
left: '100%',
|
||||
cursor: 'se-resize'
|
||||
},
|
||||
{
|
||||
top: '100%',
|
||||
left: '50%',
|
||||
cursor: 's-resize'
|
||||
},
|
||||
{
|
||||
top: '100%',
|
||||
left: 0,
|
||||
cursor: 'sw-resize'
|
||||
},
|
||||
{
|
||||
top: '50%',
|
||||
left: 0,
|
||||
cursor: 'w-resize'
|
||||
}
|
||||
],
|
||||
dMouseXY: {},
|
||||
dActiveWidgetXY: {},
|
||||
dResizeWH: {},
|
||||
dirs: [],
|
||||
// 对齐方式
|
||||
alignMap: {
|
||||
1: 'flex-start',
|
||||
2: 'center',
|
||||
3: 'flex-end'
|
||||
},
|
||||
isMoving: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleMouseDown (e, cursor) {
|
||||
const dirMap = {
|
||||
n: 'top',
|
||||
s: 'bottom',
|
||||
e: 'right',
|
||||
w: 'left'
|
||||
}
|
||||
let first = cursor[0]
|
||||
let second = cursor[1]
|
||||
this.dirs = []
|
||||
this.dirs.push(dirMap[first])
|
||||
if (/[swen]/.test(second)) {
|
||||
this.dirs.push(dirMap[second])
|
||||
}
|
||||
e.stopPropagation()
|
||||
this.initDResize({
|
||||
startX: e.pageX,
|
||||
startY: e.pageY,
|
||||
originX: this.widgetData.style.left,
|
||||
originY: this.widgetData.style.top,
|
||||
width: this.widgetData.style.width,
|
||||
height: this.widgetData.style.height
|
||||
})
|
||||
document.addEventListener('mousemove', this.handleMouseMove, true)
|
||||
document.addEventListener('mouseup', this.handleMouseUp, true)
|
||||
},
|
||||
initDResize (payload) {
|
||||
let mouseXY = this.dMouseXY
|
||||
let widgetXY = this.dActiveWidgetXY
|
||||
let resizeWH = this.dResizeWH
|
||||
mouseXY.x = payload.startX
|
||||
mouseXY.y = payload.startY
|
||||
widgetXY.x = payload.originX
|
||||
widgetXY.y = payload.originY
|
||||
resizeWH.width = payload.width
|
||||
resizeWH.height = payload.height
|
||||
},
|
||||
// 更新组件宽高
|
||||
dResize ({ x, y, dirs }) {
|
||||
let page = this.page
|
||||
let target = this.widgetData.style
|
||||
let mouseXY = this.dMouseXY
|
||||
let widgetXY = this.dActiveWidgetXY
|
||||
let resizeWH = this.dResizeWH
|
||||
let parent = this.$parent
|
||||
|
||||
if (target.parent !== '-1') {
|
||||
// parent = store.state.dWidgets.find(item => item.uuid === target.parent)
|
||||
}
|
||||
|
||||
let dx = x - mouseXY.x
|
||||
let dy = y - mouseXY.y
|
||||
|
||||
let left = 0
|
||||
let top = 0
|
||||
const minWidth = 20
|
||||
for (let i = 0; i < dirs.length; ++i) {
|
||||
let dir = dirs[i]
|
||||
|
||||
switch (dir) {
|
||||
case 'top':
|
||||
let t = widgetXY.y + Math.floor(dy / this.dZoom)
|
||||
top = Math.max(t, 0)
|
||||
top = Math.min(widgetXY.y + resizeWH.height - target.fontSize, top)
|
||||
target.height += (target.top - top)
|
||||
target.height = Math.max(target.height, target.fontSize)
|
||||
target.top = top
|
||||
break
|
||||
case 'bottom':
|
||||
top = Math.floor(dy / this.dZoom)
|
||||
target.height = Math.max(Math.min(resizeWH.height + top, page.height - target.top), 40)
|
||||
break
|
||||
case 'left':
|
||||
let tLeft = widgetXY.x + Math.floor(dx / this.dZoom)
|
||||
left = Math.max(tLeft, 0)
|
||||
target.width += (target.left - left)
|
||||
target.width = Math.max(target.width, minWidth)
|
||||
left = Math.min(widgetXY.x + resizeWH.width - minWidth, left)
|
||||
target.left = left
|
||||
break
|
||||
case 'right':
|
||||
left = Math.floor(dx / this.dZoom) // 实际偏移量
|
||||
target.width = Math.max(Math.min(resizeWH.width + left, page.width - target.left), minWidth)
|
||||
break
|
||||
}
|
||||
}
|
||||
if (parent.uuid !== '-1') {
|
||||
// store.dispatch('updateGroupSize', parent.uuid)
|
||||
}
|
||||
|
||||
// store.dispatch('reChangeCanvas')
|
||||
},
|
||||
// 组件调整结束
|
||||
stopDResize (store) {
|
||||
/* if (store.state.dResizeing) {
|
||||
store.dispatch('pushHistory')
|
||||
}
|
||||
store.state.dResizeing = false */
|
||||
},
|
||||
handleMouseMove (e) {
|
||||
e.stopPropagation()
|
||||
e.preventDefault()
|
||||
|
||||
this.dResize({
|
||||
x: e.pageX,
|
||||
y: e.pageY,
|
||||
dirs: this.dirs
|
||||
})
|
||||
},
|
||||
|
||||
handleMouseUp () {
|
||||
document.removeEventListener('mousemove', this.handleMouseMove, true)
|
||||
document.removeEventListener('mouseup', this.handleMouseUp, true)
|
||||
this.stopDResize()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss">
|
||||
.widget-wrapper{
|
||||
display: flex;
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
&.active, &.active:hover{
|
||||
border: 1px dashed;
|
||||
}
|
||||
.square{
|
||||
box-sizing: border-box;
|
||||
position: absolute;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
z-index: 999;
|
||||
background-color: #fff;
|
||||
outline: 1px solid #3b74f1;
|
||||
transform: translateX(-50%) translateY(-50%);
|
||||
}
|
||||
}
|
||||
</style>
|
92
src/main.js
@ -1,56 +1,66 @@
|
||||
import Vue from 'vue'
|
||||
import Vue from 'vue';
|
||||
|
||||
import Cookies from 'js-cookie'
|
||||
import Cookies from 'js-cookie';
|
||||
|
||||
import 'normalize.css/normalize.css' // a modern alternative to CSS resets
|
||||
import 'normalize.css/normalize.css'; // a modern alternative to CSS resets
|
||||
|
||||
import Element from 'element-ui'
|
||||
import './assets/styles/element-variables.scss'
|
||||
import Element from 'element-ui';
|
||||
import './assets/styles/element-variables.scss';
|
||||
import './assets/common.scss';
|
||||
import './assets/reset.css';
|
||||
|
||||
import '@/assets/styles/index.scss' // global css
|
||||
import '@/assets/styles/ruoyi.scss' // ruoyi css
|
||||
import App from './App'
|
||||
import store from './store'
|
||||
import router from './router'
|
||||
import permission from './directive/permission'
|
||||
import '@/assets/styles/index.scss'; // global css
|
||||
import '@/assets/styles/ruoyi.scss'; // ruoyi css
|
||||
import App from './App';
|
||||
import store from './store';
|
||||
import router from './router';
|
||||
import permission from './directive/permission';
|
||||
|
||||
import './assets/icons' // icon
|
||||
import './permission' // permission control
|
||||
import { getDicts } from "@/api/system/dict/data";
|
||||
import { getConfigKey } from "@/api/system/config";
|
||||
import { parseTime, resetForm, addDateRange, selectDictLabel, selectDictLabels, download, handleTree } from "@/utils/ruoyi";
|
||||
import Pagination from "@/components/Pagination";
|
||||
import './assets/icons'; // icon
|
||||
import './permission'; // permission control
|
||||
import { getDicts } from '@/api/system/dict/data';
|
||||
import { getConfigKey } from '@/api/system/config';
|
||||
import {
|
||||
parseTime,
|
||||
resetForm,
|
||||
addDateRange,
|
||||
selectDictLabel,
|
||||
selectDictLabels,
|
||||
download,
|
||||
handleTree
|
||||
} from '@/utils/ruoyi';
|
||||
import Pagination from '@/components/Pagination';
|
||||
//自定义表格工具扩展
|
||||
import RightToolbar from "@/components/RightToolbar"
|
||||
import RightToolbar from '@/components/RightToolbar';
|
||||
|
||||
// 全局方法挂载
|
||||
Vue.prototype.getDicts = getDicts
|
||||
Vue.prototype.getConfigKey = getConfigKey
|
||||
Vue.prototype.parseTime = parseTime
|
||||
Vue.prototype.resetForm = resetForm
|
||||
Vue.prototype.addDateRange = addDateRange
|
||||
Vue.prototype.selectDictLabel = selectDictLabel
|
||||
Vue.prototype.selectDictLabels = selectDictLabels
|
||||
Vue.prototype.download = download
|
||||
Vue.prototype.handleTree = handleTree
|
||||
Vue.prototype.getDicts = getDicts;
|
||||
Vue.prototype.getConfigKey = getConfigKey;
|
||||
Vue.prototype.parseTime = parseTime;
|
||||
Vue.prototype.resetForm = resetForm;
|
||||
Vue.prototype.addDateRange = addDateRange;
|
||||
Vue.prototype.selectDictLabel = selectDictLabel;
|
||||
Vue.prototype.selectDictLabels = selectDictLabels;
|
||||
Vue.prototype.download = download;
|
||||
Vue.prototype.handleTree = handleTree;
|
||||
|
||||
Vue.prototype.msgSuccess = function (msg) {
|
||||
this.$message({ showClose: true, message: msg, type: "success" });
|
||||
}
|
||||
Vue.prototype.msgSuccess = function(msg) {
|
||||
this.$message({ showClose: true, message: msg, type: 'success' });
|
||||
};
|
||||
|
||||
Vue.prototype.msgError = function (msg) {
|
||||
this.$message({ showClose: true, message: msg, type: "error" });
|
||||
}
|
||||
Vue.prototype.msgError = function(msg) {
|
||||
this.$message({ showClose: true, message: msg, type: 'error' });
|
||||
};
|
||||
|
||||
Vue.prototype.msgInfo = function (msg) {
|
||||
Vue.prototype.msgInfo = function(msg) {
|
||||
this.$message.info(msg);
|
||||
}
|
||||
};
|
||||
|
||||
// 全局组件挂载
|
||||
Vue.component('Pagination', Pagination)
|
||||
Vue.component('RightToolbar', RightToolbar)
|
||||
Vue.component('Pagination', Pagination);
|
||||
Vue.component('RightToolbar', RightToolbar);
|
||||
|
||||
Vue.use(permission)
|
||||
Vue.use(permission);
|
||||
|
||||
/**
|
||||
* If you don't want to use mock-server
|
||||
@ -63,13 +73,13 @@ Vue.use(permission)
|
||||
|
||||
Vue.use(Element, {
|
||||
size: Cookies.get('size') || 'medium' // set element-ui default size
|
||||
})
|
||||
});
|
||||
|
||||
Vue.config.productionTip = false
|
||||
Vue.config.productionTip = false;
|
||||
|
||||
new Vue({
|
||||
el: '#app',
|
||||
router,
|
||||
store,
|
||||
render: h => h(App)
|
||||
})
|
||||
});
|
||||
|
51
src/utils/assist.js
Normal file
@ -0,0 +1,51 @@
|
||||
// 向上找到制定的父组件
|
||||
function findComponentUpward (context, componentName) {
|
||||
let parent = context.$parent
|
||||
let name = parent.$options.name
|
||||
while (parent && (!name || [componentName].indexOf(name) < 0)) {
|
||||
parent = parent.$parent
|
||||
if (parent) name = parent.$options.name
|
||||
}
|
||||
return parent
|
||||
}
|
||||
// 向下找到指定组件
|
||||
function findComponentDownward (context, componentName) {
|
||||
const childrens = context.$children
|
||||
let children = null
|
||||
if (childrens.length) {
|
||||
for (const child of childrens) {
|
||||
const name = child.$options.name
|
||||
if (name === componentName) {
|
||||
children = child
|
||||
break
|
||||
} else {
|
||||
children = findComponentDownward(child, componentName)
|
||||
if (children) break
|
||||
}
|
||||
}
|
||||
}
|
||||
return children
|
||||
}
|
||||
// 向下找到所有符合条件的组件
|
||||
function findComponentsDownward (context, componentName) {
|
||||
return context.$children.reduce((components, child) => {
|
||||
if (child.$options.name === componentName) components.push(child)
|
||||
const foundChilds = findComponentsDownward(child, componentName)
|
||||
return components.concat(foundChilds)
|
||||
}, [])
|
||||
}
|
||||
// 找到所有符合条件的兄弟组件
|
||||
function findBrothersComponents (context, componentName, exceptMe = true) {
|
||||
let res = context.$parent.$children.filter(item => {
|
||||
return item.$options.name === componentName
|
||||
})
|
||||
let index = res.findIndex(item => item._uid === context._uid)
|
||||
if (exceptMe) res.splice(index, 1)
|
||||
return res
|
||||
}
|
||||
export {
|
||||
findComponentDownward,
|
||||
findComponentUpward,
|
||||
findComponentsDownward,
|
||||
findBrothersComponents
|
||||
}
|
@ -274,10 +274,11 @@ export default {
|
||||
// this.$router.push({ path: '/index1', query: { id: row.id } });
|
||||
},
|
||||
handlePage2(row) {
|
||||
this.$router.push({
|
||||
path: '/index3',
|
||||
query: { row: JSON.stringify(row) }
|
||||
});
|
||||
// this.$router.push({
|
||||
// path: '/index3',
|
||||
// query: { row: JSON.stringify(row) }
|
||||
// });
|
||||
this.$router.push({ path: '/index3', query: { id: row.id } });
|
||||
},
|
||||
/** 删除按钮操作 */
|
||||
handleDelete(id) {
|
||||
|
@ -1,27 +1,453 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
{{ query }}
|
||||
<div id="app-container" class="app-container">
|
||||
<el-menu
|
||||
default-active="1"
|
||||
:collapse="true"
|
||||
style="background-color:#e8e8e8;border-right:1px solid #e0e0e0"
|
||||
>
|
||||
<el-menu-item index="1" @click="index = 1">
|
||||
<i class="el-icon-menu"></i>
|
||||
<span slot="title">模板</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="2" @click="index = 2">
|
||||
<i class="el-icon-picture"></i>
|
||||
<span slot="title">图片</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="3" @click="index = 3">
|
||||
<i class="el-icon-document"></i>
|
||||
<span slot="title">文本</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="4" @click="index = 4">
|
||||
<i class="el-icon-setting"></i>
|
||||
<span slot="title">背景色</span>
|
||||
</el-menu-item>
|
||||
</el-menu>
|
||||
<div class="left-pane">
|
||||
<ul class="poster-ct" v-if="index == 1">
|
||||
<li
|
||||
@click="handleEditorClick(i)"
|
||||
v-for="(item, i) in editorList"
|
||||
:key="i"
|
||||
class="poster-item"
|
||||
>
|
||||
<div class="poster-order">
|
||||
{{ i + 1 }}
|
||||
</div>
|
||||
<div :class="{ active: item.active }" class="poster-img cursor-p">
|
||||
<widget-view
|
||||
:background="item.content.background"
|
||||
:widget-list="item.content.widgetList"
|
||||
>
|
||||
</widget-view>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
<single-upload
|
||||
v-model="imgSrc"
|
||||
@input="checkImg"
|
||||
v-if="index == 2"
|
||||
></single-upload>
|
||||
<el-button
|
||||
style="width:80%"
|
||||
size="mini"
|
||||
v-if="index == 3"
|
||||
@click="clickParent"
|
||||
>
|
||||
添加文本
|
||||
</el-button>
|
||||
<div v-if="index == 4">
|
||||
<div style="display:flex;align-items: center;padding-left: 20px">
|
||||
<span style="margin-right:20px">背景色</span>
|
||||
<el-color-picker
|
||||
v-model="textColor"
|
||||
@change="checkTextCol"
|
||||
class="w-60"
|
||||
show-alpha
|
||||
size="mini"
|
||||
></el-color-picker>
|
||||
</div>
|
||||
<div style="margin-top:20px">
|
||||
<div style="float:left;padding-left: 20px;margin-bottom:10px">
|
||||
背景图片
|
||||
</div>
|
||||
<single-upload v-model="bgImgSrc" @input="checkBgImg"></single-upload>
|
||||
</div>
|
||||
</div>
|
||||
<!-- <div class="bottom-ct">
|
||||
<el-button @click="showImgSelection = true" size="mini"
|
||||
><i class="el-icon-plus"></i> 新建</el-button
|
||||
>
|
||||
<el-button size="mini">取消</el-button>
|
||||
<el-button @click="saveAllTheme" size="mini">保存</el-button>
|
||||
</div> -->
|
||||
</div>
|
||||
<div class="right-pane">
|
||||
<template v-for="(item, i) in editorList">
|
||||
<posterEditor
|
||||
ref="child"
|
||||
:order="i"
|
||||
:id="id"
|
||||
v-model="item.content"
|
||||
v-show="item.active"
|
||||
:key="i"
|
||||
:page="
|
||||
i === 0 ? { width: 375, height: 620 } : { width: 620, height: 375 }
|
||||
"
|
||||
@active="handleEditorClick(i, 'active')"
|
||||
></posterEditor>
|
||||
</template>
|
||||
</div>
|
||||
<!-- <blankDialog :padding="24" :contentHeight="520" v-model="showImgSelection">
|
||||
<imageSelection
|
||||
:is-single="false"
|
||||
@cancel="showImgSelection = false"
|
||||
@success="handleImageSelection"
|
||||
></imageSelection>
|
||||
</blankDialog> -->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import posterEditor from '@/components/poster-editor/index.vue';
|
||||
import blankDialog from '@/components/blankDialog';
|
||||
import imageSelection from '@/components/imageSelection';
|
||||
import widgetView from '../widgetView';
|
||||
import { findComponentsDownward } from '@/utils/assist';
|
||||
import singleUpload from '@/components/Upload/singleUpload';
|
||||
export default {
|
||||
name: 'Index3',
|
||||
components: {
|
||||
posterEditor,
|
||||
blankDialog,
|
||||
imageSelection,
|
||||
widgetView,
|
||||
singleUpload
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
query: {}
|
||||
index: 1,
|
||||
textColor: '',
|
||||
imgSrc: '',
|
||||
bgImgSrc: '',
|
||||
showImgSelection: false,
|
||||
editorList: [],
|
||||
activeEditorIndex: -1
|
||||
};
|
||||
},
|
||||
methods: {},
|
||||
created() {
|
||||
const { row } = this.$route.query;
|
||||
const query = JSON.parse(row);
|
||||
if (!query || !query.id) {
|
||||
this.msgError('无ID');
|
||||
this.$router.go(-1);
|
||||
return;
|
||||
methods: {
|
||||
clickParent() {
|
||||
this.$refs.child[0].addWidget('text');
|
||||
},
|
||||
checkImg(e) {
|
||||
const a = {
|
||||
// active: false,
|
||||
// name: +new Date(),
|
||||
path: e
|
||||
// uid: 'a04c' + +new Date()
|
||||
};
|
||||
this.$refs.child[0].beforeAddImgWidget(a);
|
||||
},
|
||||
checkBgImg(e) {
|
||||
this.$refs.child[0].changeBackground(e);
|
||||
},
|
||||
checkTextCol(e) {
|
||||
this.$refs.child[0].changeTextColor(e);
|
||||
},
|
||||
fn() {},
|
||||
getEditorList() {
|
||||
this.editorList = [
|
||||
{
|
||||
name: '蜘蛛侠',
|
||||
active: false,
|
||||
content: {
|
||||
background: {
|
||||
color: '#fff',
|
||||
img: ''
|
||||
},
|
||||
material_uid: '79b5ff3f-ab9a-4384-9904-aa96338202d2',
|
||||
materialType: '__vue_devtool_undefined__',
|
||||
widgetList: [
|
||||
{
|
||||
label: '图片',
|
||||
type: 'img',
|
||||
content: require('@/assets/image/6.jpg'),
|
||||
uuid: '32a73470-cfc1-4b1a-9daf-c1f59275c022',
|
||||
active: false,
|
||||
style: {
|
||||
top: 10,
|
||||
left: 49,
|
||||
height: 160,
|
||||
width: 240,
|
||||
align: { x: 2, y: 2 },
|
||||
lineHeight: 14,
|
||||
fontSize: 14,
|
||||
background: 'rgba(0, 0, 0, 0)',
|
||||
color: 'rgba(0, 0, 0, 1)',
|
||||
letterSpacing: 0,
|
||||
isOblique: false,
|
||||
isBold: false,
|
||||
zIndex: 1
|
||||
},
|
||||
setDataRange: {
|
||||
box: {
|
||||
show: true,
|
||||
detail: {
|
||||
top: true,
|
||||
left: true,
|
||||
height: true,
|
||||
width: true,
|
||||
background: false,
|
||||
zIndex: true
|
||||
}
|
||||
console.log(query);
|
||||
this.query = query;
|
||||
},
|
||||
content: {
|
||||
show: false
|
||||
},
|
||||
innerStyle: {
|
||||
show: false,
|
||||
detail: {
|
||||
align: true,
|
||||
lineHeight: true,
|
||||
fontSize: true,
|
||||
color: true,
|
||||
letterSpacing: true,
|
||||
isOblique: true,
|
||||
isBold: true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
name: '美国队长',
|
||||
active: false,
|
||||
content: {
|
||||
background: {
|
||||
color: '#fff',
|
||||
img: ''
|
||||
},
|
||||
material_uid: 'a32c6558-1130-4e45-8d0d-efc6f887be50',
|
||||
materialType: '__vue_devtool_undefined__',
|
||||
widgetList: []
|
||||
}
|
||||
}
|
||||
];
|
||||
this.handleEditorClick(0);
|
||||
},
|
||||
handleEditorClick(i) {
|
||||
if (this.activeEditorIndex > -1) {
|
||||
let oldItem = this.editorList[this.activeEditorIndex];
|
||||
this.$set(this.editorList, this.activeEditorIndex, {
|
||||
...oldItem,
|
||||
active: false
|
||||
});
|
||||
}
|
||||
let newItem = this.editorList[i];
|
||||
this.$set(this.editorList, i, { ...newItem, active: true });
|
||||
this.activeEditorIndex = i;
|
||||
},
|
||||
deleteEditor(i, item) {
|
||||
this.$confirm('确认删除该海报?').then(async () => {
|
||||
if (item.uid) {
|
||||
await global.dataFactory.deleteThemePosterItem(item.uid);
|
||||
}
|
||||
this.editorList.splice(i, 1);
|
||||
});
|
||||
},
|
||||
// handleImageSelection(data) {
|
||||
// this.editorList.push(
|
||||
// ...data.map(v => {
|
||||
// return {
|
||||
// name: v.name,
|
||||
// active: false,
|
||||
// content: {
|
||||
// background: v.path,
|
||||
// material_uid: v.uid,
|
||||
// materialType: v.expandType,
|
||||
// widgetList: []
|
||||
// }
|
||||
// };
|
||||
// })
|
||||
// );
|
||||
// if (this.activeEditorIndex < 0) {
|
||||
// this.handleEditorClick(0);
|
||||
// }
|
||||
// this.showImgSelection = false;
|
||||
// },
|
||||
async saveAllTheme() {
|
||||
const editors = findComponentsDownward(this, 'posterEditor');
|
||||
const activeEditorIndex = this.activeEditorIndex;
|
||||
const listData = this.editorList.slice(0);
|
||||
const tasks1 = [];
|
||||
editors.forEach(async e => {
|
||||
tasks1.push({
|
||||
fn: e.uploadCurrentPosterShot,
|
||||
index: e.order
|
||||
});
|
||||
});
|
||||
while (tasks1.length) {
|
||||
let current = tasks1.shift();
|
||||
await current
|
||||
.fn((file, done) => {
|
||||
return new Promise(resolve => {
|
||||
// console.log('截图成功', file)
|
||||
done('file');
|
||||
resolve('file');
|
||||
});
|
||||
})
|
||||
.then(path => {
|
||||
listData[current.index].path = path;
|
||||
});
|
||||
}
|
||||
await Promise.all(tasks1);
|
||||
this.editorList = listData;
|
||||
/* const tasks2 = []
|
||||
let fnMap = {
|
||||
update: global.dataFactory.updateThemePoster,
|
||||
create: global.dataFactory.createThemePoster
|
||||
}
|
||||
this.editorList.forEach(e => {
|
||||
const fn = e.uid ? fnMap.update : fnMap.create
|
||||
const args = [{
|
||||
content: e.content,
|
||||
name: e.name,
|
||||
theme: this.theme,
|
||||
path: e.path
|
||||
}]
|
||||
if (e.uid) args.unshift(e.uid)
|
||||
tasks2.push(
|
||||
fn(...args)
|
||||
)
|
||||
console.log(args)
|
||||
})
|
||||
await Promise.all(tasks2)
|
||||
console.log('请求完成') */
|
||||
this.handleEditorClick(activeEditorIndex);
|
||||
},
|
||||
// 获取主题海报
|
||||
getThemePoster() {
|
||||
global.dataFactory
|
||||
.getThemePosterList({
|
||||
theme_id: this.theme
|
||||
})
|
||||
.then(res => {
|
||||
let data = res.data.data.results;
|
||||
data.forEach(v => {
|
||||
v.active = false;
|
||||
});
|
||||
if (data.length) {
|
||||
this.$nextTick(() => this.handleEditorClick(0));
|
||||
}
|
||||
this.editorList = data || [];
|
||||
});
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getEditorList();
|
||||
const { id } = this.$route.query;
|
||||
this.id = id;
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
/deep/.el-upload-list {
|
||||
display: none;
|
||||
}
|
||||
/deep/.el-upload {
|
||||
width: 80%;
|
||||
}
|
||||
#app-container {
|
||||
display: flex;
|
||||
background: #fff;
|
||||
.left-pane {
|
||||
position: relative;
|
||||
padding-top: 15px;
|
||||
width: 276px;
|
||||
min-width: 276px;
|
||||
text-align: center;
|
||||
background: rgb(232, 232, 232);
|
||||
.header {
|
||||
margin: 10px 0 10px;
|
||||
color: #2c2c2c;
|
||||
}
|
||||
.poster-ct {
|
||||
.poster-item {
|
||||
margin-bottom: 15px;
|
||||
position: relative;
|
||||
.remove {
|
||||
display: none;
|
||||
position: absolute;
|
||||
top: 8px;
|
||||
right: 36px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
box-sizing: border-box;
|
||||
border-radius: 50%;
|
||||
background: white;
|
||||
border: 1px solid #f56c6c;
|
||||
color: #f56c6c;
|
||||
z-index: 9;
|
||||
transition: all 0.3s linear;
|
||||
}
|
||||
&:hover {
|
||||
.remove {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
.poster-order {
|
||||
position: absolute;
|
||||
left: 8px;
|
||||
top: 8px;
|
||||
}
|
||||
.poster-img {
|
||||
box-sizing: border-box;
|
||||
margin-left: 24px;
|
||||
width: 240px;
|
||||
height: 180px;
|
||||
&.active {
|
||||
outline: 2px solid #409eff;
|
||||
}
|
||||
}
|
||||
.poster-name {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
margin: 8px 0;
|
||||
height: 40px;
|
||||
text-align: center;
|
||||
.input {
|
||||
display: none;
|
||||
transition: all 0.2s linear;
|
||||
}
|
||||
.text {
|
||||
transition: all 0.2s linear;
|
||||
}
|
||||
&:hover {
|
||||
.text {
|
||||
display: none;
|
||||
}
|
||||
.input {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.bottom-ct {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 276px;
|
||||
height: 48px;
|
||||
background: rgb(223, 223, 223);
|
||||
}
|
||||
}
|
||||
.right-pane {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
45
src/widgetView.vue
Normal file
@ -0,0 +1,45 @@
|
||||
<template>
|
||||
<div
|
||||
class="widgetView-comp-ct"
|
||||
:style="{
|
||||
background: `${background.color} url(${background.img})`
|
||||
}"
|
||||
>
|
||||
<widget
|
||||
:show-wrapper="false"
|
||||
v-for="item in widgetList"
|
||||
:widget-data="item"
|
||||
:d-zoom="0.25"
|
||||
:key="item.uuid"
|
||||
></widget>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import widget from '@/components/poster-editor/widget';
|
||||
export default {
|
||||
name: 'widgetView',
|
||||
props: {
|
||||
background: {
|
||||
type: Object
|
||||
},
|
||||
widgetList: {
|
||||
type: Array,
|
||||
default() {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
},
|
||||
components: {
|
||||
widget
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.widgetView-comp-ct {
|
||||
position: relative;
|
||||
width: 240px;
|
||||
height: 180px;
|
||||
background-size: 100% 100% !important;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
</style>
|