|
@@ -1,10 +1,15 @@
|
|
|
<script>
|
|
<script>
|
|
|
import Vue from 'vue'
|
|
import Vue from 'vue'
|
|
|
import domtoimage from 'dom-to-image-more'
|
|
import domtoimage from 'dom-to-image-more'
|
|
|
-import * as monaco from 'monaco-editor'
|
|
|
|
|
|
|
+import hugerte from 'hugerte'
|
|
|
|
|
+import 'hugerte/models/dom'
|
|
|
|
|
+import 'hugerte/icons/default'
|
|
|
|
|
+import 'hugerte/themes/silver'
|
|
|
|
|
+import 'hugerte/skins/ui/oxide/skin.js'
|
|
|
|
|
+import 'hugerte/skins/ui/oxide/content.js'
|
|
|
|
|
+import 'hugerte/skins/content/default/content.js'
|
|
|
import { upload } from '@/api/system'
|
|
import { upload } from '@/api/system'
|
|
|
import { saveTemplate } from '@/api/template'
|
|
import { saveTemplate } from '@/api/template'
|
|
|
-import { getExpoList } from '@/api/expo'
|
|
|
|
|
export default Vue.extend({
|
|
export default Vue.extend({
|
|
|
name: 'Index',
|
|
name: 'Index',
|
|
|
components: {
|
|
components: {
|
|
@@ -280,29 +285,6 @@ export default Vue.extend({
|
|
|
</div>
|
|
</div>
|
|
|
</body>
|
|
</body>
|
|
|
</html>`,
|
|
</html>`,
|
|
|
- viewCode: '',
|
|
|
|
|
- exhibitorSetting: {},
|
|
|
|
|
- userSetting: {
|
|
|
|
|
- first_name: '张',
|
|
|
|
|
- last_name: '三',
|
|
|
|
|
- full_name: '张三',
|
|
|
|
|
- id_type: '中国居民身份证',
|
|
|
|
|
- id_number: '430111200001011111',
|
|
|
|
|
- mobile: '18888888888',
|
|
|
|
|
- mobile_country_code: '+86',
|
|
|
|
|
- email: 'zhansan@gmail.com',
|
|
|
|
|
- company: '北京某某有限公司',
|
|
|
|
|
- department: '销售部门',
|
|
|
|
|
- position: '客户专员',
|
|
|
|
|
- country: '中国',
|
|
|
|
|
- province: '北京市',
|
|
|
|
|
- city: '北京',
|
|
|
|
|
- address: '烟袋斜街',
|
|
|
|
|
- interested_products: '预制菜',
|
|
|
|
|
- business_type: '销售'
|
|
|
|
|
- },
|
|
|
|
|
- expoList: [],
|
|
|
|
|
- expoId: 0,
|
|
|
|
|
timer: null,
|
|
timer: null,
|
|
|
editor: null,
|
|
editor: null,
|
|
|
templateInfo: {
|
|
templateInfo: {
|
|
@@ -326,134 +308,53 @@ export default Vue.extend({
|
|
|
this.templateInfo.name = templateInfo.name
|
|
this.templateInfo.name = templateInfo.name
|
|
|
this.templateInfo.description = templateInfo.description
|
|
this.templateInfo.description = templateInfo.description
|
|
|
this.code = templateInfo.content
|
|
this.code = templateInfo.content
|
|
|
|
|
+ hugerte.init({
|
|
|
|
|
+ selector: '#editor-creat',
|
|
|
|
|
+ plugins: [
|
|
|
|
|
+ "advlist", "anchor", "autolink", "charmap", "code", "fullscreen",
|
|
|
|
|
+ "help", "image", "insertdatetime", "link", "lists", "media",
|
|
|
|
|
+ "preview", "searchreplace", "table", "visualblocks",
|
|
|
|
|
+ ],
|
|
|
|
|
+ toolbar: "undo redo | styles | bold italic underline strikethrough | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | link image",
|
|
|
|
|
+ skin_url: 'default',
|
|
|
|
|
+ content_css: 'default',
|
|
|
|
|
+ statusbar: false,
|
|
|
|
|
+ height: '100%',
|
|
|
|
|
+ width: '100%'
|
|
|
|
|
+ }).then(editor => {
|
|
|
|
|
+ hugerte.activeEditor.setContent(this.code)
|
|
|
|
|
+ }).catch(err => {
|
|
|
|
|
+ })
|
|
|
this.loading = false
|
|
this.loading = false
|
|
|
}
|
|
}
|
|
|
- getExpoList(1, 1000).then(res => {
|
|
|
|
|
- this.expoList = res.data.data
|
|
|
|
|
- for (var i = 0; i < this.expoList.length; i++) {
|
|
|
|
|
- if (this.expoList[i].status === 0) {
|
|
|
|
|
- this.expoId = 0
|
|
|
|
|
- this.exhibitorSetting = this.expoList[i]
|
|
|
|
|
- break
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- this.initEditor()
|
|
|
|
|
- this.parseCode()
|
|
|
|
|
- }).catch(err => {
|
|
|
|
|
- this.loading = false
|
|
|
|
|
- })
|
|
|
|
|
- },
|
|
|
|
|
- changeExpo(index) {
|
|
|
|
|
- this.exhibitorSetting = this.expoList[index]
|
|
|
|
|
- this.parseCode()
|
|
|
|
|
},
|
|
},
|
|
|
saveTemp() {
|
|
saveTemp() {
|
|
|
if (this.loading) { return }
|
|
if (this.loading) { return }
|
|
|
this.loading = true
|
|
this.loading = true
|
|
|
- domtoimage
|
|
|
|
|
- .toPng(document.querySelector('#invitation-viewer'))
|
|
|
|
|
- .then((dataUrl) => {
|
|
|
|
|
- var arr = dataUrl.split(','); var mime = arr[0].match(/:(.*?);/)[1]
|
|
|
|
|
- var bstr = atob(arr[1]); var n = bstr.length; var u8arr = new Uint8Array(n)
|
|
|
|
|
- while (n--) {
|
|
|
|
|
- u8arr[n] = bstr.charCodeAt(n)
|
|
|
|
|
- }
|
|
|
|
|
- const picFile = new File([u8arr], 'invitation.png', { type: mime })
|
|
|
|
|
- upload(picFile).then(res => {
|
|
|
|
|
- saveTemplate(this.templateInfo.id, this.templateInfo.name, this.templateInfo.description, this.code, res.data.file)
|
|
|
|
|
- .then(res => {
|
|
|
|
|
- this.$router.push('/invitation/list')
|
|
|
|
|
- this.loading = false
|
|
|
|
|
- }).catch(err => {
|
|
|
|
|
- this.loading = false
|
|
|
|
|
- })
|
|
|
|
|
- }).catch(err => {
|
|
|
|
|
- this.loading = false
|
|
|
|
|
- })
|
|
|
|
|
- })
|
|
|
|
|
- },
|
|
|
|
|
- parseCode() {
|
|
|
|
|
- if (this.timer) { clearTimeout(this.timer) }
|
|
|
|
|
- this.timer = setTimeout(() => {
|
|
|
|
|
- let processedCode = this.code
|
|
|
|
|
- const regex = /\{\{([^}]+)\}\}/g
|
|
|
|
|
- processedCode = processedCode.replace(regex, (match, key) => {
|
|
|
|
|
- const trimmedKey = key.trim()
|
|
|
|
|
- if (Object.prototype.hasOwnProperty.call(this.exhibitorSetting, trimmedKey)) {
|
|
|
|
|
- return this.exhibitorSetting[trimmedKey]
|
|
|
|
|
- }
|
|
|
|
|
- if (Object.prototype.hasOwnProperty.call(this.userSetting, trimmedKey)) {
|
|
|
|
|
- return this.userSetting[trimmedKey]
|
|
|
|
|
- }
|
|
|
|
|
- return match
|
|
|
|
|
- })
|
|
|
|
|
- this.viewCode = processedCode
|
|
|
|
|
- }, 500)
|
|
|
|
|
- },
|
|
|
|
|
- initEditor() {
|
|
|
|
|
- if (this.$getIsInit() === false) {
|
|
|
|
|
- this.$setIsInit()
|
|
|
|
|
- monaco.languages.registerCompletionItemProvider('html', {
|
|
|
|
|
- triggerCharacters: ['{'],
|
|
|
|
|
- provideCompletionItems: (model, position) => {
|
|
|
|
|
- const textUntilPosition = model.getValueInRange({
|
|
|
|
|
- startLineNumber: position.lineNumber,
|
|
|
|
|
- startColumn: 1,
|
|
|
|
|
- endLineNumber: position.lineNumber,
|
|
|
|
|
- endColumn: position.column
|
|
|
|
|
- })
|
|
|
|
|
- const match = textUntilPosition.match(/\{\{$/)
|
|
|
|
|
- if (!match) {
|
|
|
|
|
- return { suggestions: [] }
|
|
|
|
|
|
|
+ this.code = hugerte.activeEditor.getContent()
|
|
|
|
|
+ setTimeout(() => {
|
|
|
|
|
+ domtoimage
|
|
|
|
|
+ .toPng(document.querySelector('#shot'))
|
|
|
|
|
+ .then((dataUrl) => {
|
|
|
|
|
+ var arr = dataUrl.split(','); var mime = arr[0].match(/:(.*?);/)[1]
|
|
|
|
|
+ var bstr = atob(arr[1]); var n = bstr.length; var u8arr = new Uint8Array(n)
|
|
|
|
|
+ while (n--) {
|
|
|
|
|
+ u8arr[n] = bstr.charCodeAt(n)
|
|
|
}
|
|
}
|
|
|
- const suggestions = Object.keys(this.exhibitorSetting).map(key => {
|
|
|
|
|
- return {
|
|
|
|
|
- label: key,
|
|
|
|
|
- kind: monaco.languages.CompletionItemKind.Field,
|
|
|
|
|
- insertText: key,
|
|
|
|
|
- detail: `插入展会数据: ${key}`,
|
|
|
|
|
- range: {
|
|
|
|
|
- startLineNumber: position.lineNumber,
|
|
|
|
|
- startColumn: position.column,
|
|
|
|
|
- endLineNumber: position.lineNumber,
|
|
|
|
|
- endColumn: position.column
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- })
|
|
|
|
|
- const suggestions_2 = Object.keys(this.userSetting).map(key => {
|
|
|
|
|
- return {
|
|
|
|
|
- label: key,
|
|
|
|
|
- kind: monaco.languages.CompletionItemKind.Field,
|
|
|
|
|
- insertText: key,
|
|
|
|
|
- detail: `插入用户信息: ${key}`,
|
|
|
|
|
- range: {
|
|
|
|
|
- startLineNumber: position.lineNumber,
|
|
|
|
|
- startColumn: position.column,
|
|
|
|
|
- endLineNumber: position.lineNumber,
|
|
|
|
|
- endColumn: position.column
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ const picFile = new File([u8arr], 'invitation.png', { type: mime })
|
|
|
|
|
+ upload(picFile).then(res => {
|
|
|
|
|
+ saveTemplate(this.templateInfo.id, this.templateInfo.name, this.templateInfo.description, this.code, res.data.file)
|
|
|
|
|
+ .then(res => {
|
|
|
|
|
+ this.$router.push('/invitation/list')
|
|
|
|
|
+ this.loading = false
|
|
|
|
|
+ }).catch(err => {
|
|
|
|
|
+ this.loading = false
|
|
|
|
|
+ })
|
|
|
|
|
+ }).catch(err => {
|
|
|
|
|
+ this.loading = false
|
|
|
})
|
|
})
|
|
|
- return { suggestions: [...suggestions, ...suggestions_2] }
|
|
|
|
|
- }
|
|
|
|
|
- })
|
|
|
|
|
- }
|
|
|
|
|
- this.editor = monaco.editor.create(document.getElementById('editor'), {
|
|
|
|
|
- value: this.code,
|
|
|
|
|
- language: 'html',
|
|
|
|
|
- theme: 'vs-light',
|
|
|
|
|
- automaticLayout: true,
|
|
|
|
|
- minimap: {
|
|
|
|
|
- enabled: false
|
|
|
|
|
- }
|
|
|
|
|
- })
|
|
|
|
|
- this.editor.onDidChangeModelContent(() => {
|
|
|
|
|
- this.code = this.editor.getValue()
|
|
|
|
|
- this.parseCode()
|
|
|
|
|
- })
|
|
|
|
|
- },
|
|
|
|
|
- popoverOpen() {
|
|
|
|
|
-
|
|
|
|
|
|
|
+ })
|
|
|
|
|
+ }, 500)
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
})
|
|
})
|
|
@@ -463,10 +364,7 @@ export default Vue.extend({
|
|
|
<div v-loading="loading" class="main-box">
|
|
<div v-loading="loading" class="main-box">
|
|
|
<div class="head">
|
|
<div class="head">
|
|
|
<div class="head-left">
|
|
<div class="head-left">
|
|
|
- <el-input v-permission="'invitation.rename'" v-model="templateInfo.name" class="name" />
|
|
|
|
|
- <el-select v-permission="'invitation.select'" v-model="expoId" @change="changeExpo">
|
|
|
|
|
- <el-option v-for="(expo,index) in expoList" :key="expo.id" :label="expo.expo_name" :value="index" />
|
|
|
|
|
- </el-select>
|
|
|
|
|
|
|
+ <el-input v-model="templateInfo.name" v-permission="'invitation.rename'" class="name" />
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
<el-popover
|
|
<el-popover
|
|
@@ -474,13 +372,12 @@ export default Vue.extend({
|
|
|
placement="left-start"
|
|
placement="left-start"
|
|
|
width="500"
|
|
width="500"
|
|
|
trigger="click"
|
|
trigger="click"
|
|
|
- @after-enter="popoverOpen"
|
|
|
|
|
>
|
|
>
|
|
|
<div class="body">
|
|
<div class="body">
|
|
|
<span class="label">模板名称</span>
|
|
<span class="label">模板名称</span>
|
|
|
- <el-input v-permission="'invitation.rename'" v-model="templateInfo.name" placeholder="请输入模板名称" />
|
|
|
|
|
|
|
+ <el-input v-model="templateInfo.name" v-permission="'invitation.rename'" placeholder="请输入模板名称" />
|
|
|
<span class="label">模板描述</span>
|
|
<span class="label">模板描述</span>
|
|
|
- <el-input v-permission="'invitation.desc'" v-model="templateInfo.description" type="textarea" rows="6" placeholder="请输入模板描述" />
|
|
|
|
|
|
|
+ <el-input v-model="templateInfo.description" v-permission="'invitation.desc'" type="textarea" rows="6" placeholder="请输入模板描述" />
|
|
|
<el-button v-permission="'invitation.save'" class="button" type="primary" @click="saveTemp()">保存</el-button>
|
|
<el-button v-permission="'invitation.save'" class="button" type="primary" @click="saveTemp()">保存</el-button>
|
|
|
</div>
|
|
</div>
|
|
|
<el-button slot="reference" icon="el-icon-plus" type="primary">保存模板</el-button>
|
|
<el-button slot="reference" icon="el-icon-plus" type="primary">保存模板</el-button>
|
|
@@ -488,12 +385,12 @@ export default Vue.extend({
|
|
|
|
|
|
|
|
</div>
|
|
</div>
|
|
|
<div class="body">
|
|
<div class="body">
|
|
|
- <div class="editor">
|
|
|
|
|
- <div v-permission="'invitation.editor'" id="editor" class="editor-box" />
|
|
|
|
|
- </div>
|
|
|
|
|
<div class="viewer">
|
|
<div class="viewer">
|
|
|
|
|
+ <div class="shot-viwer">
|
|
|
|
|
+ <div id="shot" v-html="code" />
|
|
|
|
|
+ </div>
|
|
|
<div class="viewer-box">
|
|
<div class="viewer-box">
|
|
|
- <div id="invitation-viewer" v-html="viewCode" />
|
|
|
|
|
|
|
+ <div id="editor-creat" />
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
@@ -512,26 +409,13 @@ export default Vue.extend({
|
|
|
position: relative;
|
|
position: relative;
|
|
|
display: grid;
|
|
display: grid;
|
|
|
grid-gap: 16px;
|
|
grid-gap: 16px;
|
|
|
- grid-template-columns: 1fr 1fr;
|
|
|
|
|
- .editor{
|
|
|
|
|
- border: 1px lightgray solid;
|
|
|
|
|
- border-radius: 8px;
|
|
|
|
|
- overflow: hidden;
|
|
|
|
|
- position: relative;
|
|
|
|
|
- .editor-box{
|
|
|
|
|
- position: absolute;
|
|
|
|
|
- top: 0;
|
|
|
|
|
- left: 0;
|
|
|
|
|
- height: 100%;
|
|
|
|
|
- width: 100%;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ grid-template-columns: 1fr;
|
|
|
.viewer{
|
|
.viewer{
|
|
|
overflow: hidden;
|
|
overflow: hidden;
|
|
|
border: 1px lightgray solid;
|
|
border: 1px lightgray solid;
|
|
|
border-radius: 8px;
|
|
border-radius: 8px;
|
|
|
position: relative;
|
|
position: relative;
|
|
|
- .viewer-box{
|
|
|
|
|
|
|
+ .viewer-box,.shot-viwer{
|
|
|
overflow: hidden;
|
|
overflow: hidden;
|
|
|
overflow-y: auto;
|
|
overflow-y: auto;
|
|
|
position: absolute;
|
|
position: absolute;
|