添加vue-canvas-poster

This commit is contained in:
熊丽君
2021-09-03 10:49:11 +08:00
parent 34c33fc2a4
commit 7cdaf34735
39 changed files with 4047 additions and 175 deletions

View File

@ -40,12 +40,16 @@
"url": "https://gitee.com/y_project/RuoYi-Vue.git" "url": "https://gitee.com/y_project/RuoYi-Vue.git"
}, },
"dependencies": { "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", "@riophae/vue-treeselect": "0.4.0",
"axios": "0.18.1", "axios": "0.18.1",
"clipboard": "2.0.4", "clipboard": "2.0.4",
"core-js": "3.6.5",
"echarts": "4.2.1", "echarts": "4.2.1",
"element-ui": "2.13.2",
"file-saver": "2.0.1", "file-saver": "2.0.1",
"fuse.js": "3.4.4", "fuse.js": "3.4.4",
"js-beautify": "1.10.2", "js-beautify": "1.10.2",
@ -57,44 +61,43 @@
"quill": "1.3.7", "quill": "1.3.7",
"screenfull": "4.2.0", "screenfull": "4.2.0",
"sortablejs": "1.8.4", "sortablejs": "1.8.4",
"vue": "2.6.10",
"vue-count-to": "1.0.13", "vue-count-to": "1.0.13",
"vue-cropper": "0.4.9", "vue-cropper": "0.4.9",
"vue-qr": "^2.5.0", "vue-qr": "^2.5.0",
"vue-router": "3.0.2", "vue-router": "3.0.2",
"vue-splitpane": "1.0.4", "vue-splitpane": "1.0.4",
"vuedraggable": "2.20.0",
"vuex": "3.1.0" "vuex": "3.1.0"
}, },
"devDependencies": { "devDependencies": {
"@vue/cli-plugin-babel": "4.4.4", "@vue/cli-plugin-babel": "^4.1.0",
"@vue/cli-plugin-eslint": "4.4.4", "@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-plugin-unit-jest": "4.4.4",
"@vue/cli-service": "4.4.4",
"@vue/test-utils": "1.0.0-beta.29", "@vue/test-utils": "1.0.0-beta.29",
"autoprefixer": "9.5.1", "autoprefixer": "9.5.1",
"babel-eslint": "10.1.0",
"babel-jest": "23.6.0", "babel-jest": "23.6.0",
"babel-plugin-dynamic-import-node": "2.3.3", "babel-plugin-dynamic-import-node": "2.3.3",
"chalk": "2.4.2", "chalk": "2.4.2",
"chokidar": "2.1.5", "chokidar": "2.1.5",
"connect": "3.6.6", "connect": "3.6.6",
"eslint": "6.7.2",
"eslint-plugin-vue": "6.2.2",
"html-webpack-plugin": "3.2.0", "html-webpack-plugin": "3.2.0",
"husky": "1.3.1", "husky": "1.3.1",
"lint-staged": "8.1.5", "lint-staged": "8.1.5",
"mockjs": "1.0.1-beta3", "mockjs": "1.0.1-beta3",
"plop": "2.3.0", "plop": "2.3.0",
"runjs": "4.3.2", "runjs": "4.3.2",
"node-sass": "4.14.1",
"sass-loader": "8.0.2",
"script-ext-html-webpack-plugin": "2.1.3", "script-ext-html-webpack-plugin": "2.1.3",
"script-loader": "0.7.2", "script-loader": "0.7.2",
"serve-static": "1.13.2", "serve-static": "1.13.2",
"svg-sprite-loader": "4.1.3", "svg-sprite-loader": "4.1.3",
"svgo": "1.2.0", "svgo": "1.2.0"
"vue-template-compiler": "2.6.10"
}, },
"engines": { "engines": {
"node": ">=8.9", "node": ">=8.9",

View File

@ -1,5 +1,13 @@
import request from '@/utils/request'; import request from '@/utils/request';
// 修改活动
export function qrUpdate(data) {
return request({
url: '/qr/update',
method: 'post',
data
});
}
// 获取订单列表 // 获取订单列表
export function orderList(data, params) { export function orderList(data, params) {
return request({ return request({
@ -63,14 +71,6 @@ export function qrCreate(data) {
data data
}); });
} }
// 修改二维码
export function qrUpdate(data) {
return request({
url: '/qr/update',
method: 'post',
data
});
}
// 删除二维码 // 删除二维码
export function qrDelete(params) { export function qrDelete(params) {
return request({ return request({

133
src/assets/common.scss Normal file
View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

BIN
src/assets/image/2.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

BIN
src/assets/image/3.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 203 KiB

BIN
src/assets/image/4.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

BIN
src/assets/image/5.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

BIN
src/assets/image/6.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

BIN
src/assets/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

246
src/assets/reset.css Normal file
View 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;
}

View File

@ -1,91 +1,100 @@
<template>  <template>
<div> <div style="width:100%">
<el-upload <el-upload
:action="useOss?ossUploadUrl:minioUploadUrl" :action="useOss ? ossUploadUrl : minioUploadUrl"
:data="useOss?dataObj:null" :data="useOss ? dataObj : null"
list-type="picture" list-type="picture"
:multiple="false" :show-file-list="showFileList" :multiple="false"
:show-file-list="showFileList"
:file-list="fileList" :file-list="fileList"
:before-upload="beforeUpload" :before-upload="beforeUpload"
:on-remove="handleRemove" :on-remove="handleRemove"
:on-success="handleUploadSuccess" :on-success="handleUploadSuccess"
:on-preview="handlePreview"> :on-preview="handlePreview"
<el-button size="small" type="primary">点击上传</el-button> >
<div slot="tip" class="el-upload__tip">只能上传jpg/png文件且不超过10MB</div> <el-button size="mini" style="width:100%">添加图片</el-button>
<!-- <div slot="tip" class="el-upload__tip">
只能上传jpg/png文件且不超过10MB
</div> -->
</el-upload> </el-upload>
<el-dialog :visible.sync="dialogVisible"> <!-- <el-dialog :visible.sync="dialogVisible">
<img width="100%" :src="fileList[0].url" alt=""> <img width="100%" :src="fileList[0].url" alt="" />
</el-dialog> </el-dialog> -->
</div> </div>
</template> </template>
<script> <script>
import {policy} from '@/api/oss' import { policy } from '@/api/oss';
export default { export default {
name: 'singleUpload', name: 'singleUpload',
props: { props: {
value: String value: String
},
computed: {
imageUrl() {
return this.value;
}, },
computed: { imageName() {
imageUrl() { if (this.value != null && this.value !== '') {
return this.value; return this.value.substr(this.value.lastIndexOf('/') + 1);
}, } else {
imageName() { return null;
if (this.value != null && this.value !== '') {
return this.value.substr(this.value.lastIndexOf("/") + 1);
} else {
return null;
}
},
fileList() {
return [{
name: this.imageName,
url: this.imageUrl
}]
},
showFileList: {
get: function () {
return this.value !== null && this.value !== ''&& this.value!==undefined;
},
set: function (newValue) {
}
} }
}, },
data() { fileList() {
return { return [
dataObj: { {
policy: '', name: this.imageName,
signature: '', url: this.imageUrl
key: '',
ossaccessKeyId: '',
dir: '',
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',
};
},
methods: {
emitInput(val) {
this.$emit('input', val)
},
handleRemove(file, fileList) {
this.emitInput('');
},
handlePreview(file) {
this.dialogVisible = true;
},
beforeUpload(file) {
let _self = this;
if(!this.useOss){
//不使用oss不需要获取策略
return true;
} }
return new Promise((resolve, reject) => { ];
policy().then(response => { },
showFileList: {
get: function() {
return (
this.value !== null && this.value !== '' && this.value !== undefined
);
},
set: function(newValue) {}
}
},
data() {
return {
dataObj: {
policy: '',
signature: '',
key: '',
ossaccessKeyId: '',
dir: '',
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',
minioUploadUrl: process.env.VUE_APP_BASE_API + '/minio/upload'
};
},
methods: {
emitInput(val) {
this.$emit('input', val);
},
handleRemove(file, fileList) {
this.emitInput('');
},
handlePreview(file) {
this.dialogVisible = true;
},
beforeUpload(file) {
let _self = this;
if (!this.useOss) {
//不使用oss不需要获取策略
return true;
}
return new Promise((resolve, reject) => {
policy()
.then(response => {
_self.dataObj.policy = response.data.policy; _self.dataObj.policy = response.data.policy;
_self.dataObj.signature = response.data.signature; _self.dataObj.signature = response.data.signature;
_self.dataObj.ossaccessKeyId = response.data.accessKeyId; _self.dataObj.ossaccessKeyId = response.data.accessKeyId;
@ -93,29 +102,26 @@
_self.dataObj.dir = response.data.dir; _self.dataObj.dir = response.data.dir;
_self.dataObj.host = response.data.host; _self.dataObj.host = response.data.host;
// _self.dataObj.callback = response.data.callback; // _self.dataObj.callback = response.data.callback;
resolve(true) resolve(true);
}).catch(err => {
console.log(err)
reject(false)
}) })
}) .catch(err => {
}, console.log(err);
handleUploadSuccess(res, file) { reject(false);
this.showFileList = true; });
this.fileList.pop(); });
let url = this.dataObj.host + '/' + this.dataObj.dir + '/' + file.name; },
if(!this.useOss){ handleUploadSuccess(res, file) {
//不使用oss直接获取图片路径 this.showFileList = true;
url = res.data.url; this.fileList.pop();
} let url = this.dataObj.host + '/' + this.dataObj.dir + '/' + file.name;
this.fileList.push({name: file.name, url: url}); if (!this.useOss) {
this.emitInput(this.fileList[0].url); //不使用oss直接获取图片路径
url = res.data.url;
} }
this.fileList.push({ name: file.name, url: url });
this.emitInput(this.fileList[0].url);
} }
} }
};
</script> </script>
<style> <style></style>
</style>

View 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>

View 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

View 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>

View 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>

View 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;
}

View 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">&#xe86b;</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">&#xe65b;</span>
<div class="name">右对齐</div>
<div class="code-name">&amp;#xe65b;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe64b;</span>
<div class="name">下对齐</div>
<div class="code-name">&amp;#xe64b;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe600;</span>
<div class="name">水平居中对齐</div>
<div class="code-name">&amp;#xe600;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe67f;</span>
<div class="name">左对齐</div>
<div class="code-name">&amp;#xe67f;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe632;</span>
<div class="name">居中对齐</div>
<div class="code-name">&amp;#xe632;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe64e;</span>
<div class="name">上对齐</div>
<div class="code-name">&amp;#xe64e;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe650;</span>
<div class="name">左对齐</div>
<div class="code-name">&amp;#xe650;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe60e;</span>
<div class="name">右对齐</div>
<div class="code-name">&amp;#xe60e;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe60f;</span>
<div class="name">左对齐</div>
<div class="code-name">&amp;#xe60f;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe603;</span>
<div class="name">undo</div>
<div class="code-name">&amp;#xe603;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe670;</span>
<div class="name">垂直居中对齐</div>
<div class="code-name">&amp;#xe670;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe651;</span>
<div class="name">undo</div>
<div class="code-name">&amp;#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"
>&lt;span class="iconfont"&gt;&amp;#x33;&lt;/span&gt;
</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">&lt;link rel="stylesheet" href="./iconfont.css"&gt;
</code></pre>
<h3 id="-">第二步:挑选相应图标并获取类名,应用于页面:</h3>
<pre><code class="language-html">&lt;span class="iconfont icon-xxx"&gt;&lt;/span&gt;
</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">&lt;script src="./iconfont.js"&gt;&lt;/script&gt;
</code></pre>
<h3 id="-css-">第二步:加入通用 CSS 代码(引入一次就行):</h3>
<pre><code class="language-html">&lt;style&gt;
.icon {
width: 1em;
height: 1em;
vertical-align: -0.15em;
fill: currentColor;
overflow: hidden;
}
&lt;/style&gt;
</code></pre>
<h3 id="-">第三步:挑选相应图标并获取类名,应用于页面:</h3>
<pre><code class="language-html">&lt;svg class="icon" aria-hidden="true"&gt;
&lt;use xlink:href="#icon-xxx"&gt;&lt;/use&gt;
&lt;/svg&gt;
</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>

View 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";
}

Binary file not shown.

File diff suppressed because one or more lines are too long

View 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
}
]
}

View 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="&#58971;" 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="&#58955;" d="M896 96v-48H128v48h768zM848 528V144h-240V528h240zM464 672v-480H272v480h192m48 48H224V144H512V720z" horiz-adv-x="1024" />
<glyph glyph-name="shuipingjuzhongduiqi" unicode="&#58880;" 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="&#59007;" 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="&#58930;" 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="&#58958;" 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="&#58960;" 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="&#58894;" 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="&#58895;" 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="&#58883;" 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="&#58992;" 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="&#58961;" 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

Binary file not shown.

Binary file not shown.

Binary file not shown.

View 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>

View 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>

View 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

View 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>

View 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>

View 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>

View 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
}

View 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>

View File

@ -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 Element from 'element-ui';
import './assets/styles/element-variables.scss' import './assets/styles/element-variables.scss';
import './assets/common.scss';
import './assets/reset.css';
import '@/assets/styles/index.scss' // global css import '@/assets/styles/index.scss'; // global css
import '@/assets/styles/ruoyi.scss' // ruoyi css import '@/assets/styles/ruoyi.scss'; // ruoyi css
import App from './App' import App from './App';
import store from './store' import store from './store';
import router from './router' import router from './router';
import permission from './directive/permission' import permission from './directive/permission';
import './assets/icons' // icon import './assets/icons'; // icon
import './permission' // permission control import './permission'; // permission control
import { getDicts } from "@/api/system/dict/data"; import { getDicts } from '@/api/system/dict/data';
import { getConfigKey } from "@/api/system/config"; import { getConfigKey } from '@/api/system/config';
import { parseTime, resetForm, addDateRange, selectDictLabel, selectDictLabels, download, handleTree } from "@/utils/ruoyi"; import {
import Pagination from "@/components/Pagination"; 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.getDicts = getDicts;
Vue.prototype.getConfigKey = getConfigKey Vue.prototype.getConfigKey = getConfigKey;
Vue.prototype.parseTime = parseTime Vue.prototype.parseTime = parseTime;
Vue.prototype.resetForm = resetForm Vue.prototype.resetForm = resetForm;
Vue.prototype.addDateRange = addDateRange Vue.prototype.addDateRange = addDateRange;
Vue.prototype.selectDictLabel = selectDictLabel Vue.prototype.selectDictLabel = selectDictLabel;
Vue.prototype.selectDictLabels = selectDictLabels Vue.prototype.selectDictLabels = selectDictLabels;
Vue.prototype.download = download Vue.prototype.download = download;
Vue.prototype.handleTree = handleTree Vue.prototype.handleTree = handleTree;
Vue.prototype.msgSuccess = function (msg) { Vue.prototype.msgSuccess = function(msg) {
this.$message({ showClose: true, message: msg, type: "success" }); this.$message({ showClose: true, message: msg, type: 'success' });
} };
Vue.prototype.msgError = function (msg) { Vue.prototype.msgError = function(msg) {
this.$message({ showClose: true, message: msg, type: "error" }); this.$message({ showClose: true, message: msg, type: 'error' });
} };
Vue.prototype.msgInfo = function (msg) { Vue.prototype.msgInfo = function(msg) {
this.$message.info(msg); this.$message.info(msg);
} };
// 全局组件挂载 // 全局组件挂载
Vue.component('Pagination', Pagination) Vue.component('Pagination', Pagination);
Vue.component('RightToolbar', RightToolbar) Vue.component('RightToolbar', RightToolbar);
Vue.use(permission) Vue.use(permission);
/** /**
* If you don't want to use mock-server * If you don't want to use mock-server
@ -63,13 +73,13 @@ Vue.use(permission)
Vue.use(Element, { Vue.use(Element, {
size: Cookies.get('size') || 'medium' // set element-ui default size size: Cookies.get('size') || 'medium' // set element-ui default size
}) });
Vue.config.productionTip = false Vue.config.productionTip = false;
new Vue({ new Vue({
el: '#app', el: '#app',
router, router,
store, store,
render: h => h(App) render: h => h(App)
}) });

51
src/utils/assist.js Normal file
View 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
}

View File

@ -274,10 +274,11 @@ export default {
// this.$router.push({ path: '/index1', query: { id: row.id } }); // this.$router.push({ path: '/index1', query: { id: row.id } });
}, },
handlePage2(row) { handlePage2(row) {
this.$router.push({ // this.$router.push({
path: '/index3', // path: '/index3',
query: { row: JSON.stringify(row) } // query: { row: JSON.stringify(row) }
}); // });
this.$router.push({ path: '/index3', query: { id: row.id } });
}, },
/** 删除按钮操作 */ /** 删除按钮操作 */
handleDelete(id) { handleDelete(id) {

View File

@ -1,27 +1,453 @@
<template> <template>
<div class="app-container"> <div id="app-container" class="app-container">
{{ query }} <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> </div>
</template> </template>
<script> <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 { export default {
name: 'Index3', components: {
posterEditor,
blankDialog,
imageSelection,
widgetView,
singleUpload
},
data() { data() {
return { return {
query: {} index: 1,
textColor: '',
imgSrc: '',
bgImgSrc: '',
showImgSelection: false,
editorList: [],
activeEditorIndex: -1
}; };
}, },
methods: {}, methods: {
created() { clickParent() {
const { row } = this.$route.query; this.$refs.child[0].addWidget('text');
const query = JSON.parse(row); },
if (!query || !query.id) { checkImg(e) {
this.msgError('无ID'); const a = {
this.$router.go(-1); // active: false,
return; // 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
}
},
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 || [];
});
} }
console.log(query); },
this.query = query; created() {
this.getEditorList();
const { id } = this.$route.query;
this.id = id;
} }
}; };
</script> </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
View 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>