Commit 4ad99be5 authored by yyh's avatar yyh

更新模板管理

parent bbb096e2
import { importAll } from '@/utils/utils'; import { importAll } from '@/utils/utils';
const components = importAll(require.context('@/components/', true, /\.vue$/ )); const components = importAll(require.context('@/components/', true, /\.vue$/ ));
import { List, Image, Empty, Field, Dialog, GridItem, Grid, Button, Popup } from 'vant'; import { List, Image, Empty, Field, Dialog, GridItem, Grid, Button, Popup, NavBar, SwipeCell } from 'vant';
export default { export default {
install(Vue: any) { install(Vue: any) {
Vue.component(List.name, List); Vue.component(List.name, List);
...@@ -12,6 +12,8 @@ export default { ...@@ -12,6 +12,8 @@ export default {
Vue.component(Button.name, Button); Vue.component(Button.name, Button);
Vue.component(Popup.name, Popup); Vue.component(Popup.name, Popup);
Vue.component(Dialog.Component.name, Dialog.Component); Vue.component(Dialog.Component.name, Dialog.Component);
Vue.component(NavBar.name, NavBar);
Vue.component(SwipeCell.name, SwipeCell);
Object.keys(components).forEach((key: string) => { Object.keys(components).forEach((key: string) => {
Vue.component(key, components[key]); Vue.component(key, components[key]);
}); });
......
<template> <template>
<div class="template"> <div class="template">
<base-info @next="next" @cancel="cancelCreateTempl"></base-info> <base-info :showBaseInfo.sync="showBaseInfo" @next="baseInfoNext" @cancel="cancelCreateTempl"></base-info>
<div v-for="(item,index) in data" :key="index" class="template-item van-hairline--top"> <words-manager :data.sync="words" @show-preview="showPreview= true;" @confirm-words="confirm"></words-manager>
<div v-if="item.type === 3"> <transition name="van-slide-up">
<div style="display:flex;flex-direction:row;justify-content:space-between;" class="data-item van-hairline--bottom"> <preview-template :data="words" v-if="showPreview" @cancel="showPreview = false" @employ="employ"></preview-template>
<div style="color:#353535;font-size:18px;font-weight:500;">{{item.label}}</div> </transition>
<div style="width: 30px;text-align:center;">
<common-svg name="cunzhengliebiao-gengduo" @click.native="showActionHandler(index)" width="20px" height="20px"></common-svg>
</div>
<!-- <div @click="preAddWord(2,item)" style="color:#3F79FE;font-size:16px;">添加字段</div> -->
</div>
<div v-for="(item, index2) in item.data" :key="index2" class="data-item van-hairline--bottom" style="margin-top:10px;">
<div style="font-size:15px;color:#353535;">{{item.label}}</div>
<div style="font-size:10px;color:#818181;">字段类型: {{typeObj[item.type]}}</div>
<common-svg name="cunzhengliebiao-gengduo" class="icon" width="20px" height="20px" @click.native="showActionHandler(index2, index)"></common-svg>
</div>
<div @click="preAddWord(2,item)" style="color:#B6B5BA;font-size:16px;text-align:center;padding:12px;background:#FFFFFF;"><common-svg name="tianjia1" width="16px" height="16px" style="margin-right:5px"></common-svg>添加下一级</div>
</div>
<div v-else>
出错了
</div>
</div>
<div class="add-word"
@click="preAddWord(1)">
<common-svg name="tianjia" style="margin-right:10px;"></common-svg>添加一级标题
</div>
<p class="tip">比如:基本信息、出厂信息、图片描述……</p>
<div class="btn-group">
<div role="button" v-show="data.length > 0" class="preview" @click="preview = true">预览</div>
<div role="button" class="slc-btn-common confirm" @click="confirm">确定</div>
</div>
<van-dialog
v-model="show"
:before-close="addWord"
:title="levelObj.title"
show-cancel-button>
<van-field v-model="wordName" label="" :placeholder="levelObj.placeholder"/>
<div
v-if="level === 2"
@click="show2 = true;"
style="text-align:left;display:flex;justify-content:space-between;padding:16px;color:#999999;font-size:14px;"><span>字段类型</span> <span>{{typeObj[wordType]}}</span></div>
</van-dialog>
<transition name="van-slide-up">
<add-option v-if="show2" @back="show2=false" @confirmWordType="confirmWordType"></add-option>
</transition>
<transition name="van-slide-up">
<preview-template :data="data" v-if="preview" @cancel="preview = false" @employ="employ"></preview-template>
</transition>
<more-action :show.sync="showAction" :menuList="menuList"></more-action>
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts">
import { Component, Prop, Vue } from 'vue-property-decorator'; import { Component, Prop, Vue } from 'vue-property-decorator';
import { List, Image, Field, Dialog, GridItem, Grid, Button, Popup } from 'vant';
import { TEMPLATETYPE, DataType } from '@/const/enum'; import { TEMPLATETYPE, DataType } from '@/const/enum';
import BaseInfo from './components/BaseInfo.vue'; import BaseInfo from './components/BaseInfo.vue';
import AddOption from './components/AddOption.vue';
import PreviewTemplate from './components/PreviewTemplate.vue'; import PreviewTemplate from './components/PreviewTemplate.vue';
// class Type { import WordsManager from './components/WordsManager.vue';
// private label: string;
// private type: DataType;
// private data: DataType1;
// private options: string[];
// constructor(label: string, type: number, data: any, options: []) {
// this.label = label;
// this.type = type;
// this.data = data;
// this.options = options;
// }
// }
// class DataType1 {
// private type: string;
// private format: string;
// private value: any;
// constructor(type: string, format: string, value: any) {
// this.type = type;
// this.format = format;
// this.value = value;
// }
// protected toString() {
// return `{type: ${this.type}, format: ${this.format}, value: ${this.value}}`;
// }
// }
// class BaseType {
// protected label: string = '';
// protected key: string = '';
// protected type: number = -1;
// protected data: any = {};
// constructor(type: number, label: string, key: string) {
// this.label = label;
// this.key = key;
// this.type = type;
// }
// protected initData(val: any) {
// this.data = val;
// }
// protected createDataUnit(dataUnit: any) {
// throw new Error('未实现错误');
// }
// protected toString() {
// return `{label: ${this.label},key: ${this.key},type: ${this.type},data: ${this.data.toString()}}`;
// }
// }
// class TypeZero extends BaseType {
// constructor(label: string, key: string) {
// super(0, label, key);
// this.initData('');
// }
// protected createDataUnit(value: string) {
// this.data = new DataType1('text', 'string', value);
// return this;
// }
// }
// class TypeOne extends BaseType {
// constructor(label: string, key: string) {
// super(1, label, key);
// this.initData([]);
// }
// protected createDataUnit(value: string) {
// this.data.push(new DataType1('image', 'hash', value));
// return this;
// }
// }
// class TypeTwo extends BaseType {
// constructor(label: string, key: string) {
// super(2, label, key);
// this.initData([]);
// }
// protected createDataUnit(value: string) {
// this.data.push(new DataType1('file', 'hash', value));
// return this;
// }
// }
// class TypeThree extends BaseType {
// constructor(label: string, key: string) {
// super(3, label, key);
// this.initData([]);
// }
// protected createDataUnit(value: any) {
// this.data.push(createType(value.type, value.label, value.key));
// return this;
// }
// }
// class TypeFive extends BaseType {
// private options: string [] = [];
// constructor(label: string, key: string, options: string[] = []) {
// super(5, label, key);
// this.options = options;
// this.initData([]);
// }
// protected createDataUnit(value: any) {
// this.data.push(new DataType1('text', 'string', value));
// return this;
// }
// protected toString() {
// return `{label: ${this.label},key: ${this.key},type:
// ${this.type},data: ${this.data.toString()},options: [${this.options}]}`;
// }
// }
// class TypeSix extends BaseType {
// constructor(label: string, key: string) {
// super(6, label, key);
// this.initData('');
// }
// protected createDataUnit(value: any) {
// this.data.push(new DataType1('data', 'utc', value));
// return this;
// }
// }
// class TypeEight extends BaseType {
// constructor(label: string, key: string) {
// super(8, label, key);
// this.initData([]);
// }
// protected createDataUnit(value: string) {
// this.data.push(new DataType1('media', 'hash', value));
// return this;
// }
// }
// class TypeNine extends BaseType {
// constructor(label: string, key: string) {
// super(9, label, key);
// this.initData([]);
// }
// protected createDataUnit(value: string) {
// this.data.push(new DataType1('media', 'hash', value));
// return this;
// }
// }
// function createType(type: number, label: string, key: string, options: string[] = []) {
// switch (type) {
// case 0: return new TypeZero(label, key);
// case 1: return new TypeOne(label, key);
// case 2: return new TypeTwo(label, key);
// case 3: return new TypeThree(label, key);
// // case 4: return new TypeTwo(label, key);
// case 5: return new TypeFive(label, key, options);
// case 6: return new TypeSix(label, key);
// case 8: return new TypeEight(label, key);
// case 9: return new TypeNine(label, key);
// default: throw new Error('为定义数据类型');
// }
// }
// console.log(createType(0,'3','4').createDataUnit('123').toString());
// console.log(createType(1,'图片','图片').createDataUnit('0xfjdkfjkd').toString());
// console.log(createType(2,'3','4').createDataUnit('0xfjdkfjk').toString());
// console.log(createType(3,'123','3333').createDataUnit({type:1,label:'文本',key:''}).toString());
// console.log(createType(5,'222','4444',['r','f']).createDataUnit('r').createDataUnit('f').toString());
// console.log(createType(6,'3','4'));
// console.log(createType(8,'3','4'));
// console.log(createType(9,'3','4'));
@Component({ @Component({
components: { components: {
BaseInfo, BaseInfo,
AddOption,
PreviewTemplate, PreviewTemplate,
[Field.name]: Field, WordsManager,
[Dialog.Component.name]: Dialog.Component,
[Grid.name]: Grid,
[GridItem.name]: GridItem,
[Button.name]: Button,
[Popup.name]: Popup,
}, },
}) })
export default class Add extends Vue { export default class Add extends Vue {
private templateName: string = ''; private showBaseInfo: boolean = true;
private thumb: [] = []; private baseInfo: any = {
private folder: any = {}; templateName: '',
private imgUrl: string = ''; folder: {},
imgUrl: '',
private show: boolean = false;
private show2: boolean = false;
private level: number = 1;
private wordName: string = '';
private wordType: number = 0;
private options: string[] = [];
private preview: boolean = false;
private typeObj: any = {
0: '单行文本',
1: '图片',
2: 'pdf文件',
3: '一级标题',
5: '选择',
6: '日期',
8: '视频',
9: '音频',
};
private typeValue: any = {
0: {
data : {
type : 'text',
format : 'string',
value : '',
},
type: 0,
key: '',
label: '单行文本',
},
1: {
data : {
type : 'image',
format : 'hash',
value : [],
},
type: 1,
key: '',
label: '图片',
},
2: {
data : {
type : 'file',
format : 'hash',
value : [],
},
type: 2,
key: '',
label: 'pdf',
},
3: {
data : [],
type: 3,
key: '',
label: '一级标题',
},
5: {
data : {
type : 'text',
format : 'string',
value : '',
},
type: 5,
key: '',
options: [],
label: '选择',
},
6: {
data : {
type : 'date',
format : 'utc',
value : 0,
},
type: 6,
key: '',
label: '日期',
},
8: {
data : {
type : 'video',
format : 'hash',
value : [],
},
type: 8,
key: '',
label: '视频',
},
9: {
data : {
type : 'audio',
format : 'hash',
value : [],
},
type: 9,
key: '',
label: '音频',
},
}; };
private data: any[] = [];
private tempData: any[] = [];
get levelObj(): any { private showWordsManager: boolean = false;
const obj1 = { private words: any[] = [];
title: '添加标题',
placeholder: '一级标题名称', private showPreview: boolean = false;
toast: '标题至少2个汉字',
};
const obj2 = {
title: '添加字段',
placeholder: '字段名称',
toast: '字段至少2个汉字',
};
const obj: any = {obj1, obj2};
return obj[`obj${this.level}`];
}
private showAction: boolean = false;
private menuList: any[] = [
{
icon: 'zaishangtianjia',
text: '在上添加',
action: this.addBefore.bind(this),
},
{
icon: 'zaixiatianjia',
text: '在下添加',
action: this.addAfter.bind(this),
},
{
icon: 'shanchu4',
text: '删除',
action: this.delWord.bind(this),
},
];
private currentIndex: number = -1;
private parentIndex: number = -1;
private flag: string = '';
get templateData() { get templateData() {
const {templateName , data, folder } = this; const {baseInfo , words } = this;
const { templateName, folder } = baseInfo;
return { return {
templateName, templateName,
data, words,
folder, folder,
}; };
} }
private deepClone(data: any) { private baseInfoNext(data: any) {
return JSON.parse(JSON.stringify(data)); this.baseInfo = {...data};
}
private next(data: any) {
this.templateName = data.templateName;
this.thumb = data.fileList;
this.folder = data.folder;
this.imgUrl = data.imgUrl;
} }
private cancelCreateTempl() { private cancelCreateTempl() {
this.$router.back(); this.$router.back();
} }
private showActionHandler(index: number, parentIndex: number = -1) {
this.showAction = true;
this.currentIndex = index;
this.parentIndex = parentIndex;
}
private addBefore() {
this.flag = 'before';
const { parentIndex, currentIndex } = this;
if (parentIndex === -1) {
this.preAddWord(1);
return;
}
this.preAddWord(2, this.data[parentIndex]);
}
private addAfter() {
this.flag = 'after';
const { parentIndex, currentIndex } = this;
if (parentIndex === -1) {
this.preAddWord(1);
return;
}
this.preAddWord(2, this.data[parentIndex]);
}
private delWord() {
const { parentIndex, currentIndex } = this;
if (parentIndex === -1) {
this.data.splice(currentIndex, 1);
return;
}
this.data[parentIndex].data.splice(currentIndex, 1);
this.$toast({
message: '删除成功',
duration: 3000,
});
this.showAction = false;
}
private preAddWord(level: number, item?: any) {
this.level = level;
this.show = true;
this.wordType = level === 1 ? DataType.Unit : DataType.Input;
this.tempData = item ? item.data : this.data;
}
private addWord(action: string, done: any) {
if (action === 'confirm') {
if (this.wordName === '') {
this.$toast(this.levelObj.toast);
done(false);
return;
}
let tempData = {...this.deepClone((this.typeValue as any)[this.wordType]), label: this.wordName};
if (this.wordType === DataType.Select) {
tempData = { ...tempData, options: this.options };
}
if ( this.flag !== '') {
if ( this.flag === 'after') {
this.currentIndex++;
}
this.tempData.splice(this.currentIndex, 0 , tempData);
} else {
this.tempData.push(tempData);
}
this.flag = '';
this.wordName = '';
this.wordType = 0;
this.showAction = false;
done();
} else {
done();
}
}
private confirmWordType(data: any) {
this.show2 = false;
this.wordType = data.type;
this.options = data.type === DataType.Select ? data.options : [];
}
private commitTemplate() { private commitTemplate() {
if (this.data.length <= 0) { if (this.words.length <= 0) {
return Promise.reject('请添加字段'); return Promise.reject('请添加字段');
} }
return this.$api.template.customize(this.templateName, this.data, this.folder.id, { return this.$api.template.customize(this.baseInfo.templateName, this.words, this.baseInfo.folder.id, {
s_image_url: this.imgUrl, s_image_url: this.baseInfo.imgUrl,
}); });
} }
private confirm() { private confirm() {
...@@ -483,71 +69,12 @@ export default class Add extends Vue { ...@@ -483,71 +69,12 @@ export default class Add extends Vue {
} }
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
.template{ .template{
position: relative; position: relative;
// padding: 17px; .tip{
.template-item{ font-size: 12px;
text-align: left; color: #737582;
display: flex; margin-bottom: 100px;
flex-direction: column;
font-size: 10px;
line-height: 1.5;
color: #818181;
background: #FFFFFF;
padding: 0 17px;
margin-bottom: 20px;
.data-item{
display: grid;
grid-template-columns: auto 30px;
grid-template-rows: repeat(2, 20px);
grid-auto-flow: column;
align-items: center;
padding: 10px 0;
.icon{
grid-row: span 2;
place-self: center;
}
}
}
.add-word{
padding: 17px;
margin: 0 20px;
margin-top: 20px;
font-size: 16px;
color: #3F79FE;
border-radius:6px;
border:1px dashed rgba(63,121,254,1);
}
.tip{
font-size: 12px;
color: #737582;
margin-bottom: 100px;
}
.btn-group{
.preview{
align-self: center;
color: #353535;
background: #F0F1F5;
border-radius:4px;
width: 107px;
padding: 9px;
margin-right: 10px;
}
.confirm{
flex: auto;
align-self: center;
width: 228px;
padding: 9px;
}
}
}
.more-action{
padding: 20px;
box-sizing: border-box;
.content{
display: grid;
grid-gap: 10px;
grid-template-columns: repeat(3,100px);
}
} }
}
</style> </style>
<template> <template>
<van-popup v-model="show" :style="{'height': '100%', 'width': '100%'}" :overlay="false" get-container="body" > <van-popup v-model="show" :style="{'height': '100%', 'width': '100%'}" :overlay="false" get-container="body" >
<div class="add-word1"> <div class="add-word1">
<van-nav-bar <van-nav-bar
title="选择字段类型" title="选择字段类型"
left-arrow left-arrow
@click-left="onClickLeft"></van-nav-bar> @click-left="onClickLeft"></van-nav-bar>
<div <div
v-for="(item, index) in data" v-for="(item, index) in data"
:key="index" :key="index"
class="item van-hairline--bottom van-hairline--top" class="item van-hairline--bottom van-hairline--top"
@click="activeName = item.type"> @click="activeName = item.type">
...@@ -39,29 +39,24 @@ ...@@ -39,29 +39,24 @@
</div> </div>
<div class="btn-group"> <div class="btn-group">
<div class="slc-btn-common right" @click="confirmWordType">确定</div> <div class="slc-btn-common right" @click="confirmWordType">确定</div>
<!-- <van-button block color="#5D7BF6" type="primary" @click="confirmWordType">确定</van-button> -->
</div> </div>
</div> </div>
</van-popup> </van-popup>
</template> </template>
<script lang="ts"> <script lang="ts">
import { Component, Prop, Vue, Emit } from 'vue-property-decorator'; import { Component, PropSync, Vue, Emit } from 'vue-property-decorator';
import { Collapse, CollapseItem, Field, Button, NavBar, SwipeCell, Popup } from 'vant'; import { TEMPLATETYPE, DataType } from '@/const/enum';
import { TEMPLATETYPE } from '@/const/enum';
@Component({ @Component({
components: { components: {
[Collapse.name]: Collapse,
[CollapseItem.name]: CollapseItem,
[Field.name]: Field,
[Button.name]: Button,
[NavBar.name]: NavBar,
[SwipeCell.name]: SwipeCell,
[Popup.name]: Popup,
}, },
}) })
export default class AddOption extends Vue { export default class AddOption extends Vue {
@PropSync('showOptions', {
type: Boolean,
required: true,
})
private show!: boolean;
private activeName: number = 0; private activeName: number = 0;
private show: boolean = true;
private data = [ private data = [
{ {
title: '单行文本', title: '单行文本',
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
<div style="position:relative;margin-bottom:26px;font-size:18px;"> <div style="position:relative;margin-bottom:26px;font-size:18px;">
<common-svg <common-svg
@click.native="cancel" @click.native="cancel"
name="shanchu5" name="shanchu5"
style="position:absolute;left:17px;"></common-svg> style="position:absolute;left:17px;"></common-svg>
<span>创建模板</span> <span>创建模板</span>
</div> </div>
...@@ -31,7 +31,7 @@ ...@@ -31,7 +31,7 @@
<div @click="next" class="slc-btn-common next">下一步</div> <div @click="next" class="slc-btn-common next">下一步</div>
</div> </div>
<van-popup <van-popup
v-model="show2" v-model="showSelectFolder"
position="bottom" > position="bottom" >
<van-picker <van-picker
show-toolbar show-toolbar
...@@ -45,7 +45,7 @@ ...@@ -45,7 +45,7 @@
</van-popup> </van-popup>
</template> </template>
<script lang="ts"> <script lang="ts">
import { Component, Prop, Vue, Emit } from 'vue-property-decorator'; import { Component, Prop, PropSync, Vue, Emit } from 'vue-property-decorator';
import { Collapse, CollapseItem, Field, Button, NavBar, SwipeCell, Popup, Uploader, Picker , Image } from 'vant'; import { Collapse, CollapseItem, Field, Button, NavBar, SwipeCell, Popup, Uploader, Picker , Image } from 'vant';
import { TEMPLATETYPE } from '@/const/enum'; import { TEMPLATETYPE } from '@/const/enum';
@Component({ @Component({
...@@ -63,8 +63,12 @@ import { TEMPLATETYPE } from '@/const/enum'; ...@@ -63,8 +63,12 @@ import { TEMPLATETYPE } from '@/const/enum';
}, },
}) })
export default class BaseInfo extends Vue { export default class BaseInfo extends Vue {
private show: boolean = true; @PropSync('showBaseInfo', {
private show2: boolean = false; type: Boolean,
required: true,
})
private show!: boolean;
private showSelectFolder: boolean = false;
private folderList: [] = []; private folderList: [] = [];
private templateName: string = ''; private templateName: string = '';
private imgUrl: string = ''; private imgUrl: string = '';
...@@ -73,14 +77,16 @@ export default class BaseInfo extends Vue { ...@@ -73,14 +77,16 @@ export default class BaseInfo extends Vue {
private defaultFolderIndex: number = 0; private defaultFolderIndex: number = 0;
private defaultFolderName: string = '未分类文件夹'; private defaultFolderName: string = '未分类文件夹';
private mounted() { private mounted() {
this.$api.template.folderList({ this.getFolders();
template_type: TEMPLATETYPE.USER, }
}).then((res: any) => { private async getFolders() {
this.folderList = res.results || []; const { results = []} = await this.$api.template.folderList({
const findDefaultFolder = (folder: any) => folder.name === this.defaultFolderName; template_type: TEMPLATETYPE.USER,
this.folder = this.folderList.find( findDefaultFolder ); });
this.defaultFolderIndex = this.folderList.findIndex( findDefaultFolder ); this.folderList = results;
}); const findDefaultFolder = (folder: any) => folder.name === this.defaultFolderName;
this.folder = this.folderList.find( findDefaultFolder );
this.defaultFolderIndex = this.folderList.findIndex( findDefaultFolder );
} }
private next() { private next() {
const { templateName, folder, imgUrl} = this; const { templateName, folder, imgUrl} = this;
...@@ -104,14 +110,13 @@ export default class BaseInfo extends Vue { ...@@ -104,14 +110,13 @@ export default class BaseInfo extends Vue {
this.show = false; this.show = false;
} }
private onConfirm(value: any , index: number) { private onConfirm(value: any , index: number) {
this.show2 = false; this.showSelectFolder = false;
this.folder = value; this.folder = value;
} }
private handleFileChange(e: any) { private handleFileChange(e: any) {
const input = e.target; const [file] = e.tartet.files;
const files = input.files; if (file) {
if (files && files[0]) { this.$api.file.upload(file).then((res: any) => {
this.$api.file.upload(files[0]).then((res: any) => {
this.imgUrl = res.url; this.imgUrl = res.url;
}); });
} }
......
<template>
<div>
<div v-if="word.type === 3">
<div style="display:flex;flex-direction:row;justify-content:space-between;" class="data-item van-hairline--bottom">
<div style="color:#353535;font-size:18px;font-weight:500;">{{word.label}}</div>
<div style="width: 30px;text-align:center;">
<common-svg name="cunzhengliebiao-gengduo" width="20px" height="20px" @click.native="showActionHandler(wordIndex)"></common-svg>
</div>
</div>
<div v-for="(item, index2) in word.data" :key="index2" class="data-item van-hairline--bottom" style="margin-top:10px;">
<div style="font-size:15px;color:#353535;">{{item.label}}</div>
<div style="font-size:10px;color:#818181;">字段类型: {{typeObj[item.type]}}</div>
<common-svg name="cunzhengliebiao-gengduo" class="icon" width="20px" height="20px" @click.native="showActionHandler(index2, wordIndex)"></common-svg>
</div>
<div
@click="preAddWord(DataType.Input,word)"
style="color:#B6B5BA;font-size:16px;text-align:center;padding:12px;background:#FFFFFF;">
<common-svg name="tianjia1" width="16px" height="16px" style="margin-right:5px"></common-svg>添加下一级</div>
</div>
<div v-else>
出错了
</div>
</div>
</template>
<script lang="ts">
import { Component, Prop, Vue, Emit } from 'vue-property-decorator';
import { Collapse, CollapseItem, Field, Button, NavBar, SwipeCell, Popup } from 'vant';
import { TEMPLATETYPE, DataType } from '@/const/enum';
@Component({
components: {
[Collapse.name]: Collapse,
[CollapseItem.name]: CollapseItem,
[Field.name]: Field,
[Button.name]: Button,
[NavBar.name]: NavBar,
[SwipeCell.name]: SwipeCell,
[Popup.name]: Popup,
},
})
export default class WordItem extends Vue {
@Prop({
required: true,
type: Object,
})
private word!: any;
@Prop()
private wordIndex!: number;
private typeObj: any = {
0: '单行文本',
1: '图片',
2: 'pdf文件',
3: '一级标题',
5: '选择',
6: '日期',
8: '视频',
9: '音频',
};
private DataType: any = DataType;
@Emit('show-action-handler')
private showActionHandler(index: number, parentIndex: number = -1) {
return {
index,
parentIndex,
};
}
@Emit('pre-add-word')
private preAddWord(level: number, word: any) {
return {
wordType: level,
word,
};
}
}
</script>
<style scoped lang="scss">
.data-item{
display: grid;
grid-template-columns: auto 30px;
grid-template-rows: repeat(2, 20px);
grid-auto-flow: column;
align-items: center;
padding: 10px 0;
.icon{
grid-row: span 2;
place-self: center;
}
}
</style>
\ No newline at end of file
<template>
<div class="template">
<word-item
v-for="(item,index) in words"
:word="item" :word-index="index"
@show-action-handler="showActionHandler"
:key="index"
@pre-add-word="preAddWord"
class="template-item van-hairline--top"></word-item>
<div class="add-word"
@click="preAddWord({wordType: DataType.Unit})">
<common-svg name="tianjia" style="margin-right:10px;"></common-svg>添加一级标题
</div>
<p class="tip">比如:基本信息、出厂信息、图片描述……</p>
<div class="btn-group">
<div role="button" v-show="words.length > 0" class="preview" @click="$emit('show-preview')">预览</div>
<div role="button" class="slc-btn-common confirm" @click="$emit('confirm-words')">确定</div>
</div>
<van-dialog
v-model="show"
:before-close="addWord"
:title="levelObj.title"
show-cancel-button>
<van-field v-model="wordName" label="" :placeholder="levelObj.placeholder"/>
<div
v-if="wordType !== DataType.Unit"
@click="showOptions = true;"
style="text-align:left;display:flex;justify-content:space-between;padding:16px;color:#999999;font-size:14px;"><span>字段类型</span> <span>{{typeObj[wordType]}}</span></div>
</van-dialog>
<add-option :showOptions.sync="showOptions" @back="showOptions=false" @confirmWordType="confirmWordType"></add-option>
<more-action :show.sync="showMoreAction" :menuList="menuList"></more-action>
</div>
</template>
<script lang="ts">
import { Component, Prop, PropSync, Vue } from 'vue-property-decorator';
import { List, Image, Field, Dialog, GridItem, Grid, Button, Popup } from 'vant';
import { TEMPLATETYPE, DataType } from '@/const/enum';
import WordItem from './WordItem.vue';
import AddOption from './AddOption.vue';
@Component({
components: {
WordItem,
AddOption,
},
})
export default class Add extends Vue {
@PropSync('data', {
required: true,
type: Array,
})
private words!: any[];
private wordType: number = DataType.Unit;
private DataType: any = DataType;
private wordName: string = '';
private flag: string = '';
private options: string[] = [];
private currentWords: any[] = [];
private showOptions: boolean = false;
private show: boolean = false;
private showMoreAction: boolean = false;
private currentAction: any = {
index: -1,
parentIndex: -1,
};
private menuList: any[] = [
{
icon: 'zaishangtianjia',
text: '在上添加',
action: this.addBefore.bind(this),
},
{
icon: 'zaixiatianjia',
text: '在下添加',
action: this.addAfter.bind(this),
},
{
icon: 'shanchu4',
text: '删除',
action: this.delWord.bind(this),
},
];
private typeObj: any = {
0: '单行文本',
1: '图片',
2: 'pdf文件',
3: '一级标题',
5: '选择',
6: '日期',
8: '视频',
9: '音频',
};
private typeValue: any = {
0: {
data : {
type : 'text',
format : 'string',
value : '',
},
type: 0,
key: '',
label: '单行文本',
},
1: {
data : {
type : 'image',
format : 'hash',
value : [],
},
type: 1,
key: '',
label: '图片',
},
2: {
data : {
type : 'file',
format : 'hash',
value : [],
},
type: 2,
key: '',
label: 'pdf',
},
3: {
data : [],
type: 3,
key: '',
label: '一级标题',
},
5: {
data : {
type : 'text',
format : 'string',
value : '',
},
type: 5,
key: '',
options: [],
label: '选择',
},
6: {
data : {
type : 'date',
format : 'utc',
value : 0,
},
type: 6,
key: '',
label: '日期',
},
8: {
data : {
type : 'video',
format : 'hash',
value : [],
},
type: 8,
key: '',
label: '视频',
},
9: {
data : {
type : 'audio',
format : 'hash',
value : [],
},
type: 9,
key: '',
label: '音频',
},
};
private showActionHandler({ index, parentIndex}: any) {
this.showMoreAction = true;
this.currentAction = {index, parentIndex};
}
private addBefore() {
const { index, parentIndex } = this.currentAction;
const wordType = parentIndex === -1 ? DataType.Unit : DataType.Input;
const word = parentIndex !== -1 ? this.words[parentIndex] : null;
this.preAddWord({ wordType, word}, 'before');
}
private addAfter() {
const { index, parentIndex } = this.currentAction;
const wordType = parentIndex === -1 ? DataType.Unit : DataType.Input;
const word = parentIndex !== -1 ? this.words[parentIndex] : null;
this.preAddWord({ wordType, word }, 'after');
}
private delWord() {
const { index, parentIndex } = this.currentAction;
const tempWords = parentIndex === -1 ? this.words : this.words[parentIndex].data;
tempWords.splice(index, 1);
}
private confirmWordType({type = DataType.Input, options}: any) {
this.wordType = type;
this.options = options;
this.showOptions = false;
}
get levelObj(): any {
if (this.wordType === DataType.Unit) {
return {
title: '添加标题',
placeholder: '一级标题名称',
toast: '标题至少2个汉字',
};
}
return {
title: '添加字段',
placeholder: '字段名称',
toast: '字段至少2个汉字',
};
}
private preAddWord({ wordType, word }: any, flag: string = '') {
this.show = true;
this.wordType = wordType;
this.currentWords = word ? word.data : this.words;
this.flag = flag;
}
private deepClone(data: any) {
return JSON.parse(JSON.stringify(data));
}
private addWord(action: string, done: any) {
if (action !== 'confirm') { return done(); }
if (this.wordName === '') {
this.$toast(this.levelObj.toast);
return done(false);
}
let tempData = {...this.deepClone((this.typeValue as any)[this.wordType]), label: this.wordName};
if (this.wordType === DataType.Select) {
tempData = { ...tempData, options: this.options};
}
if (this.flag === 'before') {
this.currentWords.splice( this.currentAction.index , 0 , tempData);
} else if (this.flag === 'after') {
let { index } = this.currentAction;
this.currentWords.splice( ++index , 0 , tempData);
} else {
this.currentWords.push(tempData);
}
this.wordName = '';
this.wordType = DataType.Unit;
this.flag = '';
done();
}
}
</script>
<style scoped lang="scss">
.template-item{
text-align: left;
display: flex;
flex-direction: column;
font-size: 10px;
line-height: 1.5;
color: #818181;
background: #FFFFFF;
padding: 0 17px;
margin-bottom: 20px;
}
.add-word{
padding: 17px;
margin: 0 20px;
margin-top: 20px;
font-size: 16px;
color: #3F79FE;
border-radius:6px;
border:1px dashed rgba(63,121,254,1);
}
.btn-group{
.preview{
align-self: center;
color: #353535;
background: #F0F1F5;
border-radius:4px;
width: 107px;
padding: 9px;
margin-right: 10px;
}
.confirm{
flex: auto;
align-self: center;
width: 228px;
padding: 9px;
}
}
</style>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment