first commit
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
<template>
|
||||
<el-breadcrumb class="app-breadcrumb" separator="/">
|
||||
<transition-group name="breadcrumb">
|
||||
<el-breadcrumb-item v-for="(item,index) in levelList" :key="item.path">
|
||||
<span v-if="item.redirect==='noRedirect'||index==levelList.length-1" class="no-redirect">{{ item.meta.title }}</span>
|
||||
<a v-else @click.prevent="handleLink(item)">{{ item.meta.title }}</a>
|
||||
</el-breadcrumb-item>
|
||||
</transition-group>
|
||||
</el-breadcrumb>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import pathToRegexp from 'path-to-regexp'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
levelList: null
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
$route(route) {
|
||||
// if you go to the redirect page, do not update the breadcrumbs
|
||||
if (route.path.startsWith('/redirect/')) {
|
||||
return
|
||||
}
|
||||
this.getBreadcrumb()
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getBreadcrumb()
|
||||
},
|
||||
methods: {
|
||||
getBreadcrumb() {
|
||||
// only show routes with meta.title
|
||||
let matched = this.$route.matched.filter(item => item.meta && item.meta.title)
|
||||
const first = matched[0]
|
||||
|
||||
if (!this.isDashboard(first)) {
|
||||
matched = [{ path: '/dashboard', meta: { title: '主页' }}].concat(matched)
|
||||
}
|
||||
|
||||
this.levelList = matched.filter(item => item.meta && item.meta.title && item.meta.breadcrumb !== false)
|
||||
},
|
||||
isDashboard(route) {
|
||||
const name = route && route.name
|
||||
if (!name) {
|
||||
return false
|
||||
}
|
||||
return name.trim().toLocaleLowerCase() === 'Dashboard'.toLocaleLowerCase()
|
||||
},
|
||||
pathCompile(path) {
|
||||
// To solve this problem https://github.com/PanJiaChen/vue-element-admin/issues/561
|
||||
const { params } = this.$route
|
||||
var toPath = pathToRegexp.compile(path)
|
||||
return toPath(params)
|
||||
},
|
||||
handleLink(item) {
|
||||
const { redirect, path } = item
|
||||
if (redirect) {
|
||||
this.$router.push(redirect)
|
||||
return
|
||||
}
|
||||
this.$router.push(this.pathCompile(path))
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.app-breadcrumb.el-breadcrumb {
|
||||
display: inline-block;
|
||||
font-size: 14px;
|
||||
line-height: 50px;
|
||||
margin-left: 8px;
|
||||
|
||||
.no-redirect {
|
||||
color: #97a8be;
|
||||
cursor: text;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,193 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-form ref="editPram" :model="editPram" label-width="130px">
|
||||
<el-form-item
|
||||
label="分类名称"
|
||||
prop="name"
|
||||
:rules="[{ required:true,message:'请输入分类名称',trigger:['blur','change'] }]"
|
||||
>
|
||||
<el-input v-model="editPram.name" :maxlength="biztype.value === 1 ? 8 : 20" placeholder="分类名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="URL" v-if="biztype.value!==1 && biztype.value!==3">
|
||||
<el-input v-model="editPram.url" placeholder="URL" />
|
||||
</el-form-item>
|
||||
<el-form-item label="父级" v-if="biztype.value!==3">
|
||||
<el-cascader v-model="editPram.pid" :disabled="isCreate ===1 && editPram.pid ===0" :options="biztype.value === 5 ? allTreeList : parentOptions" :props="categoryProps" style="width:100%" />
|
||||
</el-form-item>
|
||||
<el-form-item label="菜单图标" v-if="biztype.value===5">
|
||||
<el-input placeholder="请选择菜单图标" v-model="editPram.extra">
|
||||
<el-button slot="append" icon="el-icon-circle-plus-outline" @click="addIcon"></el-button>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="分类图标(180*180)" v-if="biztype.value === 1 || biztype.value === 3">
|
||||
<div class="upLoadPicBox" @click="modalPicTap('1')">
|
||||
<div v-if="editPram.extra" class="pictrue">
|
||||
<img :src="editPram.extra">
|
||||
</div>
|
||||
<div v-else class="upLoad">
|
||||
<i class="el-icon-camera cameraIconfont" />
|
||||
</div>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="排序">
|
||||
<el-input-number v-model="editPram.sort" :min="0"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="状态">
|
||||
<el-switch v-model="editPram.status" active-text="显示"
|
||||
inactive-text="隐藏" :active-value="true" :inactive-value="false" />
|
||||
</el-form-item>
|
||||
<el-form-item label="扩展字段" v-if="biztype.value !== 1 && biztype.value !== 3 && biztype.value !== 5">
|
||||
<el-input v-model="editPram.extra" type="textarea" placeholder="扩展字段" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" :loading="loadingBtn" @click="handlerSubmit('editPram')" v-hasPermi="['admin:category:update']">确定</el-button>
|
||||
<el-button @click="close">取消</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
</template>
|
||||
<!--创建和编辑公用一个组件-->
|
||||
<script>
|
||||
import * as categoryApi from '@/api/categoryApi.js'
|
||||
import * as selfUtil from '@/utils/ZBKJIutil.js'
|
||||
export default {
|
||||
// name: "edit"
|
||||
props: {
|
||||
prent: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
isCreate: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
editData: {
|
||||
type: Object
|
||||
},
|
||||
biztype: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
allTreeList: {
|
||||
type: Array
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loadingBtn: false,
|
||||
constants: this.$constants,
|
||||
editPram: {
|
||||
extra: null,
|
||||
name: null,
|
||||
pid: null,
|
||||
sort: 0,
|
||||
status: true,
|
||||
type: this.biztype.value,
|
||||
url: null,
|
||||
id: 0
|
||||
},
|
||||
categoryProps: {
|
||||
value: 'id',
|
||||
label: 'name',
|
||||
children: 'child',
|
||||
expandTrigger: 'hover',
|
||||
checkStrictly: true,
|
||||
emitPath: false
|
||||
},
|
||||
parentOptions: []
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.initEditData()
|
||||
},
|
||||
methods: {
|
||||
// 点击图标
|
||||
addIcon() {
|
||||
const _this = this
|
||||
_this.$modalIcon(function(icon) {
|
||||
_this.editPram.extra = icon
|
||||
})
|
||||
},
|
||||
// 点击商品图
|
||||
modalPicTap (tit, num, i) {
|
||||
const _this = this
|
||||
const attr = []
|
||||
this.$modalUpload(function(img) {
|
||||
if(tit==='1'&& !num){
|
||||
_this.editPram.extra = img[0].sattDir
|
||||
}
|
||||
if(tit==='2'&& !num){
|
||||
img.map((item) => {
|
||||
attr.push(item.attachment_src)
|
||||
_this.formValidate.slider_image.push(item)
|
||||
});
|
||||
}
|
||||
},tit, 'store')
|
||||
},
|
||||
close() {
|
||||
this.$emit('hideEditDialog')
|
||||
},
|
||||
initEditData() {
|
||||
this.parentOptions = [...this.allTreeList]
|
||||
this.addTreeListLabelForCasCard(this.parentOptions, 'child')
|
||||
const { extra, name, pid, sort, status, type, id, url } = this.editData
|
||||
if(this.isCreate === 1){
|
||||
this.editPram.extra = extra
|
||||
this.editPram.name = name
|
||||
this.editPram.pid = pid
|
||||
this.editPram.sort = sort
|
||||
this.editPram.status = status
|
||||
this.editPram.type = type
|
||||
this.editPram.url = url
|
||||
this.editPram.id = id
|
||||
}else{
|
||||
this.editPram.pid = this.prent.id
|
||||
this.editPram.type = this.biztype.value
|
||||
}
|
||||
},
|
||||
addTreeListLabelForCasCard(arr, child) {
|
||||
arr.forEach((o,i) => {
|
||||
if (o.child && o.child.length) {
|
||||
// o.disabled = true
|
||||
o.child.forEach((j) => {
|
||||
j.disabled = true
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
handlerSubmit(formName) {
|
||||
this.$refs[formName].validate((valid) => {
|
||||
if (!valid) return
|
||||
this.handlerSaveOrUpdate(this.isCreate === 0)
|
||||
})
|
||||
},
|
||||
handlerSaveOrUpdate(isSave) {
|
||||
if (isSave) {
|
||||
// this.editPram.pid = this.prent.id
|
||||
this.loadingBtn = true
|
||||
categoryApi.addCategroy(this.editPram).then(data => {
|
||||
this.$emit('hideEditDialog')
|
||||
this.$message.success('创建目录成功')
|
||||
this.loadingBtn = false
|
||||
}).catch(() => {
|
||||
this.loadingBtn = false
|
||||
})
|
||||
} else {
|
||||
this.editPram.pid = Array.isArray(this.editPram.pid) ? this.editPram.pid[0] : this.editPram.pid
|
||||
this.loadingBtn = true
|
||||
categoryApi.updateCategroy(this.editPram).then(data => {
|
||||
this.$emit('hideEditDialog')
|
||||
this.$message.success('更新目录成功')
|
||||
this.loadingBtn = false
|
||||
}).catch(() => {
|
||||
this.loadingBtn = false
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,89 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-tree :data="ddd" :props="defaultProps" @node-click="handleNodeClick" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import * as categoryApi from '@/api/categoryApi.js'
|
||||
export default {
|
||||
// name: "info"
|
||||
props: {
|
||||
id: {
|
||||
type: Number,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
defaultProps: {
|
||||
children: 'children',
|
||||
label: 'label'
|
||||
},
|
||||
ddd: [{
|
||||
label: '一级 1',
|
||||
children: [{
|
||||
label: '二级 1-1',
|
||||
children: [{
|
||||
label: '三级 1-1-1'
|
||||
}]
|
||||
}]
|
||||
}, {
|
||||
label: '一级 2',
|
||||
children: [{
|
||||
label: '二级 2-1',
|
||||
children: [{
|
||||
label: '三级 2-1-1'
|
||||
}]
|
||||
}, {
|
||||
label: '二级 2-2',
|
||||
children: [{
|
||||
label: '三级 2-2-1'
|
||||
}]
|
||||
}]
|
||||
}, {
|
||||
label: '一级 3',
|
||||
children: [{
|
||||
label: '二级 3-1',
|
||||
children: [{
|
||||
label: '三级 3-1-1'
|
||||
}]
|
||||
}, {
|
||||
label: '二级 3-2',
|
||||
children: [{
|
||||
label: '三级 3-2-1'
|
||||
}]
|
||||
}]
|
||||
}],
|
||||
dataList: {// 数据结果
|
||||
page: 0,
|
||||
limit: 0,
|
||||
totalPage: 0,
|
||||
total: 0,
|
||||
list: []
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.handlerGetTreeList(this.id)
|
||||
},
|
||||
methods: {
|
||||
handlerGetTreeList(id) {
|
||||
if (!id) {
|
||||
this.$message.error('当前数据id不正确')
|
||||
return
|
||||
}
|
||||
categoryApi.treeCategroy({ pid: id }).then(data => {
|
||||
this.dataList = data
|
||||
})
|
||||
},
|
||||
handleNodeClick(data) {
|
||||
console.log('data:', data)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,329 @@
|
||||
<template>
|
||||
<div>
|
||||
<template v-if="selectModel">
|
||||
<el-tree
|
||||
ref="tree"
|
||||
:data="treeList"
|
||||
show-checkbox
|
||||
node-key="id"
|
||||
@check="getCurrentNode"
|
||||
:default-checked-keys="selectModelKeysNew"
|
||||
:props="treeProps">
|
||||
</el-tree>
|
||||
</template>
|
||||
<template v-else>
|
||||
<div class="divBox">
|
||||
<el-card class="box-card">
|
||||
<div slot="header" class="clearfix">
|
||||
<div class="container">
|
||||
<el-form inline size="small">
|
||||
<el-form-item label="状态:">
|
||||
<el-select v-model="listPram.status" placeholder="状态" class="selWidth" @change="handlerGetList">
|
||||
<el-option label="全部" :value="-1"></el-option>
|
||||
<el-option label="显示" :value="1"></el-option>
|
||||
<el-option label="不显示" :value="0"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="名称:">
|
||||
<el-input v-model="listPram.name" placeholder="请输入名称" class="selWidth" size="small" clearable>
|
||||
<el-button slot="append" icon="el-icon-search" @click="handlerGetList" size="small"/>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
<el-button size="mini" type="primary" @click="handleAddMenu({id:0,name:'顶层目录'})" v-hasPermi="['admin:category:save']" >新增{{ biztype.name }}</el-button>
|
||||
</div>
|
||||
<el-table
|
||||
ref="treeList"
|
||||
:data="treeList"
|
||||
size="mini"
|
||||
class="table"
|
||||
highlight-current-row
|
||||
row-key="id"
|
||||
:tree-props="{children: 'child', hasChildren: 'hasChildren'}"
|
||||
>
|
||||
<el-table-column prop="name" label="名称" min-width="240">
|
||||
<template slot-scope="scope">
|
||||
{{ scope.row.name }} | {{ scope.row.id }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<template v-if="!selectModel">
|
||||
<el-table-column label="类型" min-width="150">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ scope.row.type | filterCategroyType | filterEmpty }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="分类图标" min-width="80">
|
||||
<template slot-scope="scope">
|
||||
<div class="listPic" v-if=" biztype.value === 5 ">
|
||||
<i :class="'el-icon-' + scope.row.extra" style="font-size: 20px;" />
|
||||
</div>
|
||||
<div class="demo-image__preview" v-else>
|
||||
<el-image
|
||||
style="width: 36px; height: 36px"
|
||||
:src="scope.row.extra"
|
||||
:preview-src-list="[scope.row.extra]"
|
||||
v-if="scope.row.extra"
|
||||
/>
|
||||
<img style="width: 36px; height: 36px" v-else :src="defaultImg" alt="">
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="Url" min-width="250" v-if="biztype.value === 5" key="2">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ scope.row.url }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="排序" prop="sort" min-width="150" />
|
||||
<el-table-column
|
||||
label="状态"
|
||||
min-width="150"
|
||||
>
|
||||
<!-- -->
|
||||
<template slot-scope="scope" v-if="checkPermi(['admin:category:update:status'])">
|
||||
<el-switch
|
||||
v-model="scope.row.status"
|
||||
:active-value="true"
|
||||
:inactive-value="false"
|
||||
active-text="显示"
|
||||
inactive-text="隐藏"
|
||||
@change="onchangeIsShow(scope.row)"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="操作" min-width="200" fixed="right">
|
||||
<template slot-scope="scope">
|
||||
<el-button
|
||||
v-if="(biztype.value === 1 && scope.row.pid === 0) || biztype.value === 5"
|
||||
type="text"
|
||||
size="small"
|
||||
@click="handleAddMenu(scope.row)"
|
||||
>添加子目录</el-button>
|
||||
<el-button type="text" size="small" @click="handleEditMenu(scope.row)" v-hasPermi="['admin:category:info']">编辑</el-button>
|
||||
<el-button type="text" size="small" @click="handleDelMenu(scope.row)" v-hasPermi="['admin:category:delete']">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</template>
|
||||
</el-table>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
<el-dialog
|
||||
:title="(editDialogConfig.isCreate===0?`创建${biztype.name}`:`编辑${biztype.name}`)"
|
||||
:visible.sync="editDialogConfig.visible"
|
||||
destroy-on-close
|
||||
:close-on-click-modal="false"
|
||||
>
|
||||
<edit
|
||||
v-if="editDialogConfig.visible"
|
||||
:prent="editDialogConfig.prent"
|
||||
:is-create="editDialogConfig.isCreate"
|
||||
:edit-data="editDialogConfig.data"
|
||||
:biztype="editDialogConfig.biztype"
|
||||
:all-tree-list="treeList"
|
||||
@hideEditDialog="hideEditDialog"
|
||||
/>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import * as categoryApi from '@/api/categoryApi.js'
|
||||
import info from './info'
|
||||
import edit from './edit'
|
||||
import * as selfUtil from '@/utils/ZBKJIutil.js'
|
||||
import { checkPermi, checkRole } from "@/utils/permission";
|
||||
export default {
|
||||
// name: "list"
|
||||
components: { info, edit },
|
||||
props: {
|
||||
biztype: { // 类型,1 产品分类,2 附件分类,3 文章分类, 4 设置分类, 5 菜单分类
|
||||
type: Object,
|
||||
default: { value: -1 },
|
||||
validator: (obj) => {
|
||||
return obj.value > 0
|
||||
}
|
||||
},
|
||||
pid: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
validator: (value) => {
|
||||
return value >= 0
|
||||
}
|
||||
},
|
||||
selectModel: { // 是否选择模式
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
selectModelKeys: {
|
||||
type: Array
|
||||
},
|
||||
rowSelect: {}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
selectModelKeysNew: this.selectModelKeys,
|
||||
loading: false,
|
||||
constants: this.$constants,
|
||||
treeProps: {
|
||||
label: 'name',
|
||||
children: 'child',
|
||||
// expandTrigger: 'hover',
|
||||
// checkStrictly: false,
|
||||
// emitPath: false
|
||||
},
|
||||
// treeCheckedKeys:[],// 选择模式下的属性结构默认选中
|
||||
multipleSelection: [],
|
||||
editDialogConfig: {
|
||||
visible: false,
|
||||
isCreate: 0, // 0=创建,1=编辑
|
||||
prent: {}, // 父级对象
|
||||
data: {},
|
||||
biztype: this.biztype // 统一主业务中的目录类型
|
||||
},
|
||||
dataList: [],
|
||||
treeList: [],
|
||||
listPram: {
|
||||
pid: this.pid,
|
||||
type: this.biztype.value,
|
||||
status: -1,
|
||||
name: '',
|
||||
page: this.$constants.page.page,
|
||||
limit: this.$constants.page.limit[0]
|
||||
},
|
||||
viewInfoConfig: {
|
||||
data: null,
|
||||
visible: false
|
||||
},
|
||||
defaultImg:require('@/assets/imgs/moren.jpg')
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
/* if(this.biztype.value === 3){
|
||||
this.listPram.pageSize = constants.page.pageSize[4]
|
||||
this.handlerGetList()
|
||||
}else{*/
|
||||
this.handlerGetTreeList()
|
||||
// }
|
||||
},
|
||||
methods: {
|
||||
checkPermi, //权限控制
|
||||
onchangeIsShow(row){
|
||||
categoryApi.categroyUpdateStatus( row.id ).then(() => {
|
||||
this.$message.success('修改成功')
|
||||
this.handlerGetTreeList()
|
||||
}).catch(()=>{
|
||||
row.status = !row.status
|
||||
})
|
||||
},
|
||||
handleEditMenu(rowData) {
|
||||
this.editDialogConfig.isCreate = 1
|
||||
this.editDialogConfig.data = rowData
|
||||
this.editDialogConfig.prent = rowData
|
||||
this.editDialogConfig.visible = true
|
||||
},
|
||||
handleAddMenu(rowData) {
|
||||
this.editDialogConfig.isCreate = 0
|
||||
this.editDialogConfig.prent = rowData
|
||||
this.editDialogConfig.data = {}
|
||||
this.editDialogConfig.biztype = this.biztype
|
||||
this.editDialogConfig.visible = true
|
||||
},
|
||||
getCurrentNode(data) {
|
||||
let node = this.$refs.tree.getNode(data);
|
||||
this.childNodes(node);
|
||||
// this.parentNodes(node);
|
||||
//是否编辑的表示
|
||||
// this.ruleForm.isEditorFlag = true;
|
||||
//编辑时候使用
|
||||
this.$emit('rulesSelect', this.$refs.tree.getCheckedKeys());
|
||||
// this.selectModelKeys = this.$refs.tree.getCheckedKeys();
|
||||
//无论编辑和新增点击了就传到后台这个值
|
||||
// this.$emit('rulesSelect', this.$refs.tree.getCheckedKeys().concat(this.$refs.tree.getHalfCheckedKeys()));
|
||||
// this.ruleForm.menuIdsisEditor = this.$refs.tree.getCheckedKeys().concat(this.$refs.tree.getHalfCheckedKeys());
|
||||
},
|
||||
//具体方法可以看element官网api
|
||||
childNodes(node){
|
||||
let len = node.childNodes.length;
|
||||
for(let i = 0; i < len; i++){
|
||||
node.childNodes[i].checked = node.checked;
|
||||
this.childNodes(node.childNodes[i]);
|
||||
}
|
||||
},
|
||||
parentNodes(node){
|
||||
if(node.parent){
|
||||
for(let key in node){
|
||||
if(key == "parent"){
|
||||
node[key].checked = true;
|
||||
this.parentNodes(node[key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
handleDelMenu(rowData) {
|
||||
this.$confirm('确定删除当前数据?').then(() => {
|
||||
categoryApi.deleteCategroy(rowData).then(data => {
|
||||
this.handlerGetTreeList()
|
||||
this.$message.success('删除成功')
|
||||
})
|
||||
})
|
||||
},
|
||||
handlerGetList() {
|
||||
this.handlerGetTreeList();
|
||||
// categoryApi.listCategroy({status:this.listPram.status, type: 1 }).then(data => {
|
||||
// this.treeList = data.list
|
||||
// })
|
||||
},
|
||||
handlerGetTreeList() {
|
||||
// this.biztype.value === 5 && !this.selectModel) ? -1 : 1
|
||||
// const _pram = { type: this.biztype.value, status: !this.selectModel ? -1 : (this.biztype.value === 5 ? -1 : 1) }
|
||||
const _pram = { type: this.biztype.value, status: this.listPram.status, name: this.listPram.name }
|
||||
this.loading = true
|
||||
this.biztype.value!==3 ? categoryApi.treeCategroy(_pram).then(data => {
|
||||
this.treeList = this.handleAddArrt(data)
|
||||
this.loading = false
|
||||
}).catch(()=>{
|
||||
this.loading = false
|
||||
}) : categoryApi.listCategroy({ type: 3, status: this.listPram.status, pid: this.listPram.pid, name: this.listPram.name}).then(data => {
|
||||
this.treeList = data.list
|
||||
})
|
||||
},
|
||||
handlerGetInfo(id) {
|
||||
this.viewInfoConfig.data = id
|
||||
this.viewInfoConfig.visible = true
|
||||
},
|
||||
handleNodeClick(data) {
|
||||
console.log('data:', data)
|
||||
},
|
||||
handleAddArrt(treeData) {
|
||||
const _result = selfUtil.addTreeListLabel(treeData)
|
||||
return _result
|
||||
},
|
||||
hideEditDialog() {
|
||||
setTimeout(() => {
|
||||
this.editDialogConfig.prent = {}
|
||||
this.editDialogConfig.type = 0
|
||||
this.editDialogConfig.visible = false
|
||||
this.handlerGetTreeList()
|
||||
}, 200)
|
||||
},
|
||||
handleSelectionChange(d1, { checkedNodes, checkedKeys, halfCheckedNodes, halfCheckedKeys }) {
|
||||
// this.multipleSelection = checkedKeys.concat(halfCheckedKeys)
|
||||
this.multipleSelection = checkedKeys
|
||||
this.$emit('rulesSelect', this.multipleSelection)
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.custom-tree-node {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
font-size: 14px;
|
||||
padding-right: 8px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,78 @@
|
||||
<template>
|
||||
<div v-if="errorLogs.length>0">
|
||||
<el-badge :is-dot="true" style="line-height: 25px;margin-top: -5px;" @click.native="dialogTableVisible=true">
|
||||
<el-button style="padding: 8px 10px;" size="small" type="danger">
|
||||
<svg-icon icon-class="bug" />
|
||||
</el-button>
|
||||
</el-badge>
|
||||
|
||||
<el-dialog :visible.sync="dialogTableVisible" width="80%" append-to-body>
|
||||
<div slot="title">
|
||||
<span style="padding-right: 10px;">Error Log</span>
|
||||
<el-button size="mini" type="primary" icon="el-icon-delete" @click="clearAll">Clear All</el-button>
|
||||
</div>
|
||||
<el-table :data="errorLogs" border>
|
||||
<el-table-column label="Message">
|
||||
<template slot-scope="{row}">
|
||||
<div>
|
||||
<span class="message-title">Msg:</span>
|
||||
<el-tag type="danger">
|
||||
{{ row.err.message }}
|
||||
</el-tag>
|
||||
</div>
|
||||
<br>
|
||||
<div>
|
||||
<span class="message-title" style="padding-right: 10px;">Info: </span>
|
||||
<el-tag type="warning">
|
||||
{{ row.vm.$vnode.tag }} error in {{ row.info }}
|
||||
</el-tag>
|
||||
</div>
|
||||
<br>
|
||||
<div>
|
||||
<span class="message-title" style="padding-right: 16px;">Url: </span>
|
||||
<el-tag type="success">
|
||||
{{ row.url }}
|
||||
</el-tag>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="Stack">
|
||||
<template slot-scope="scope">
|
||||
{{ scope.row.err.stack }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'ErrorLog',
|
||||
data() {
|
||||
return {
|
||||
dialogTableVisible: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
errorLogs() {
|
||||
return this.$store.getters.errorLogs
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
clearAll() {
|
||||
this.dialogTableVisible = false
|
||||
this.$store.dispatch('errorLog/clearErrorLog')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.message-title {
|
||||
font-size: 16px;
|
||||
color: #333;
|
||||
font-weight: bold;
|
||||
padding-right: 8px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,73 @@
|
||||
<template>
|
||||
<div
|
||||
v-if="isExternal"
|
||||
:style="styleExternalIcon"
|
||||
class="svg-external-icon svg-icon"
|
||||
v-on="$listeners"
|
||||
/>
|
||||
<svg
|
||||
v-else
|
||||
:class="svgClass"
|
||||
aria-hidden="true"
|
||||
v-on="$listeners"
|
||||
>
|
||||
<use :xlink:href="iconName" />
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// doc: https://panjiachen.github.io/vue-element-admin-site/feature/component/svg-icon.html#usage
|
||||
function isExternal(path) {
|
||||
return /^(https?:|mailto:|tel:)/.test(path)
|
||||
}
|
||||
|
||||
export default {
|
||||
name: 'SvgIcon',
|
||||
props: {
|
||||
iconClass: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
className: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
isExternal() {
|
||||
return isExternal(this.iconClass)
|
||||
},
|
||||
iconName() {
|
||||
return `#icon-${this.iconClass}`
|
||||
},
|
||||
svgClass() {
|
||||
if (this.className) {
|
||||
return `svg-icon ${this.className}`
|
||||
}
|
||||
return 'svg-icon'
|
||||
},
|
||||
styleExternalIcon() {
|
||||
return {
|
||||
mask: `url(${this.iconClass}) no-repeat 50% 50%`,
|
||||
'-webkit-mask': `url(${this.iconClass}) no-repeat 50% 50%`
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.svg-icon {
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
vertical-align: -0.15em;
|
||||
fill: currentColor;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.svg-external-icon {
|
||||
background-color: currentColor;
|
||||
mask-size: cover!important;
|
||||
display: inline-block;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,708 @@
|
||||
import { getToken } from '@/utils/auth'
|
||||
import SettingMer from '@/utils/settingMer'
|
||||
|
||||
// 表单属性【右面板】
|
||||
export const formConf = {
|
||||
formRef: 'elForm',
|
||||
formModel: 'formData',
|
||||
size: 'medium',
|
||||
labelPosition: 'right',
|
||||
labelWidth: 100,
|
||||
formRules: 'rules',
|
||||
gutter: 15,
|
||||
disabled: false,
|
||||
span: 24,
|
||||
formBtns: true
|
||||
}
|
||||
|
||||
// 输入型组件 【左面板】
|
||||
export const inputComponents = [
|
||||
{
|
||||
// 组件的自定义配置
|
||||
__config__: {
|
||||
label: '单行文本',
|
||||
labelWidth: null,
|
||||
showLabel: true,
|
||||
changeTag: true,
|
||||
tag: 'el-input',
|
||||
tagIcon: 'input',
|
||||
defaultValue: undefined,
|
||||
required: true,
|
||||
tips:false, //tooltip描述是否开启
|
||||
tipsDesc:'', //tooltip描述内容
|
||||
tipsIsLink:false,//是否开启描述链接
|
||||
tipsLink:'', //描述链接
|
||||
layout: 'colFormItem',
|
||||
span: 24,
|
||||
document: 'https://element.eleme.cn/#/zh-CN/component/input',
|
||||
// 正则校验规则
|
||||
regList: []
|
||||
},
|
||||
// 组件的插槽属性
|
||||
__slot__: {
|
||||
prepend: '',
|
||||
append: ''
|
||||
},
|
||||
// 其余的为可直接写在组件标签上的属性
|
||||
placeholder: '请输入',
|
||||
style: { width: '95%' },
|
||||
clearable: true,
|
||||
'prefix-icon': '',
|
||||
'suffix-icon': '',
|
||||
maxlength: null,
|
||||
'show-word-limit': false,
|
||||
readonly: false,
|
||||
disabled: false
|
||||
},
|
||||
{
|
||||
__config__: {
|
||||
label: '多行文本',
|
||||
labelWidth: null,
|
||||
showLabel: true,
|
||||
tag: 'el-input',
|
||||
tagIcon: 'textarea',
|
||||
defaultValue: undefined,
|
||||
required: true,
|
||||
tips:false,
|
||||
tipsDesc:'',
|
||||
tipsIsLink:false,
|
||||
tipsLink:'',
|
||||
layout: 'colFormItem',
|
||||
span: 24,
|
||||
regList: [],
|
||||
changeTag: true,
|
||||
document: 'https://element.eleme.cn/#/zh-CN/component/input'
|
||||
},
|
||||
type: 'textarea',
|
||||
placeholder: '请输入',
|
||||
autosize: {
|
||||
minRows: 4,
|
||||
maxRows: 4
|
||||
},
|
||||
style: { width: '95%' },
|
||||
maxlength: null,
|
||||
'show-word-limit': false,
|
||||
readonly: false,
|
||||
disabled: false
|
||||
},
|
||||
{
|
||||
__config__: {
|
||||
label: '密码',
|
||||
showLabel: true,
|
||||
labelWidth: null,
|
||||
changeTag: true,
|
||||
tag: 'el-input',
|
||||
tagIcon: 'password',
|
||||
defaultValue: undefined,
|
||||
layout: 'colFormItem',
|
||||
span: 24,
|
||||
required: true,
|
||||
tips:false,
|
||||
tipsDesc:'',
|
||||
tipsIsLink:false,
|
||||
tipsLink:'',
|
||||
regList: [],
|
||||
document: 'https://element.eleme.cn/#/zh-CN/component/input'
|
||||
},
|
||||
__slot__: {
|
||||
prepend: '',
|
||||
append: ''
|
||||
},
|
||||
placeholder: '请输入',
|
||||
'show-password': true,
|
||||
style: { width: '100%' },
|
||||
clearable: true,
|
||||
'prefix-icon': '',
|
||||
'suffix-icon': '',
|
||||
maxlength: null,
|
||||
'show-word-limit': false,
|
||||
readonly: false,
|
||||
disabled: false
|
||||
},
|
||||
{
|
||||
__config__: {
|
||||
label: '计数器',
|
||||
showLabel: true,
|
||||
changeTag: true,
|
||||
labelWidth: null,
|
||||
tag: 'el-input-number',
|
||||
tagIcon: 'number',
|
||||
defaultValue: undefined,
|
||||
span: 24,
|
||||
layout: 'colFormItem',
|
||||
required: true,
|
||||
tips:false,
|
||||
tipsDesc:'',
|
||||
tipsIsLink:false,
|
||||
tipsLink:'',
|
||||
regList: [],
|
||||
document: 'https://element.eleme.cn/#/zh-CN/component/input-number'
|
||||
},
|
||||
placeholder: '',
|
||||
min: undefined,
|
||||
max: undefined,
|
||||
step: 1,
|
||||
'step-strictly': false,
|
||||
precision: undefined,
|
||||
'controls-position': '',
|
||||
disabled: false
|
||||
}
|
||||
// {
|
||||
// __config__: {
|
||||
// label: '编辑器',
|
||||
// showLabel: true,
|
||||
// changeTag: true,
|
||||
// labelWidth: null,
|
||||
// tag: 'tinymce',
|
||||
// tagIcon: 'rich-text',
|
||||
// defaultValue: null,
|
||||
// span: 24,
|
||||
// layout: 'colFormItem',
|
||||
// required: true,
|
||||
// regList: [],
|
||||
// document: 'http://tinymce.ax-z.cn'
|
||||
// },
|
||||
// height: 300, // 编辑器高度
|
||||
// branding: false // 隐藏右下角品牌烙印
|
||||
// }
|
||||
]
|
||||
|
||||
// 选择型组件 【左面板】
|
||||
export const selectComponents = [
|
||||
{
|
||||
__config__: {
|
||||
label: '下拉选择',
|
||||
showLabel: true,
|
||||
labelWidth: null,
|
||||
tag: 'el-select',
|
||||
tagIcon: 'select',
|
||||
defaultValue: undefined,
|
||||
layout: 'colFormItem',
|
||||
span: 24,
|
||||
required: true,
|
||||
tips:false,
|
||||
tipsDesc:'',
|
||||
tipsIsLink:false,
|
||||
tipsLink:'',
|
||||
regList: [],
|
||||
changeTag: true,
|
||||
document: 'https://element.eleme.cn/#/zh-CN/component/select'
|
||||
},
|
||||
__slot__: {
|
||||
options: [{
|
||||
label: '选项一',
|
||||
value: 1
|
||||
}, {
|
||||
label: '选项二',
|
||||
value: 2
|
||||
}]
|
||||
},
|
||||
placeholder: '请选择',
|
||||
style: { width: '100%' },
|
||||
clearable: true,
|
||||
disabled: false,
|
||||
filterable: false,
|
||||
multiple: false
|
||||
},
|
||||
{
|
||||
__config__: {
|
||||
label: '级联选择',
|
||||
showLabel: true,
|
||||
labelWidth: null,
|
||||
tag: 'el-cascader',
|
||||
tagIcon: 'cascader',
|
||||
layout: 'colFormItem',
|
||||
defaultValue: [],
|
||||
dataType: 'dynamic',
|
||||
span: 24,
|
||||
required: true,
|
||||
tips:false,
|
||||
tipsDesc:'',
|
||||
tipsIsLink:false,
|
||||
tipsLink:'',
|
||||
regList: [],
|
||||
changeTag: true,
|
||||
document: 'https://element.eleme.cn/#/zh-CN/component/cascader'
|
||||
},
|
||||
options: [{
|
||||
id: 1,
|
||||
value: 1,
|
||||
label: '选项1',
|
||||
children: [{
|
||||
id: 2,
|
||||
value: 2,
|
||||
label: '选项1-1'
|
||||
}]
|
||||
}],
|
||||
placeholder: '请选择',
|
||||
style: { width: '100%' },
|
||||
props: {
|
||||
props: {
|
||||
multiple: false,
|
||||
label: 'label',
|
||||
value: 'value',
|
||||
children: 'children'
|
||||
}
|
||||
},
|
||||
'show-all-levels': true,
|
||||
disabled: false,
|
||||
clearable: true,
|
||||
filterable: false,
|
||||
separator: '/'
|
||||
},
|
||||
{
|
||||
__config__: {
|
||||
label: '单选框组',
|
||||
labelWidth: null,
|
||||
showLabel: true,
|
||||
tag: 'el-radio-group',
|
||||
tagIcon: 'radio',
|
||||
changeTag: true,
|
||||
defaultValue: undefined,
|
||||
layout: 'colFormItem',
|
||||
span: 24,
|
||||
optionType: 'default',
|
||||
regList: [],
|
||||
required: true,
|
||||
tips:false,
|
||||
tipsDesc:'',
|
||||
tipsIsLink:false,
|
||||
tipsLink:'',
|
||||
border: false,
|
||||
// bindInput:false, //是否开启绑定输入
|
||||
// bindValve:'', //绑定输入内容
|
||||
document: 'https://element.eleme.cn/#/zh-CN/component/radio'
|
||||
},
|
||||
__slot__: {
|
||||
options: [{
|
||||
label: '选项一',
|
||||
value: 1
|
||||
}, {
|
||||
label: '选项二',
|
||||
value: 2
|
||||
}]
|
||||
},
|
||||
style: {},
|
||||
size: 'medium',
|
||||
disabled: false
|
||||
},
|
||||
{
|
||||
__config__: {
|
||||
label: '多选框组',
|
||||
tag: 'el-checkbox-group',
|
||||
tagIcon: 'checkbox',
|
||||
defaultValue: [],
|
||||
span: 24,
|
||||
showLabel: true,
|
||||
labelWidth: null,
|
||||
layout: 'colFormItem',
|
||||
optionType: 'default',
|
||||
required: true,
|
||||
tips:false,
|
||||
tipsDesc:'',
|
||||
tipsIsLink:false,
|
||||
tipsLink:'',
|
||||
regList: [],
|
||||
changeTag: true,
|
||||
border: false,
|
||||
document: 'https://element.eleme.cn/#/zh-CN/component/checkbox'
|
||||
},
|
||||
__slot__: {
|
||||
options: [{
|
||||
label: '选项一',
|
||||
value: 1
|
||||
}, {
|
||||
label: '选项二',
|
||||
value: 2
|
||||
}]
|
||||
},
|
||||
style: {},
|
||||
size: 'medium',
|
||||
min: null,
|
||||
max: null,
|
||||
disabled: false
|
||||
},
|
||||
{
|
||||
__config__: {
|
||||
label: '开关',
|
||||
tag: 'el-switch',
|
||||
tagIcon: 'switch',
|
||||
defaultValue: false,
|
||||
span: 24,
|
||||
showLabel: true,
|
||||
labelWidth: null,
|
||||
layout: 'colFormItem',
|
||||
required: true,
|
||||
tips:false,
|
||||
tipsDesc:'',
|
||||
tipsIsLink:false,
|
||||
tipsLink:'',
|
||||
regList: [],
|
||||
changeTag: true,
|
||||
document: 'https://element.eleme.cn/#/zh-CN/component/switch'
|
||||
},
|
||||
style: {},
|
||||
disabled: false,
|
||||
'active-text': '',
|
||||
'inactive-text': '',
|
||||
'active-color': null,
|
||||
'inactive-color': null,
|
||||
'active-value': true,
|
||||
'inactive-value': false
|
||||
},
|
||||
{
|
||||
__config__: {
|
||||
label: '滑块',
|
||||
tag: 'el-slider',
|
||||
tagIcon: 'slider',
|
||||
defaultValue: null,
|
||||
span: 24,
|
||||
showLabel: true,
|
||||
layout: 'colFormItem',
|
||||
labelWidth: null,
|
||||
required: true,
|
||||
tips:false,
|
||||
tipsDesc:'',
|
||||
tipsIsLink:false,
|
||||
tipsLink:'',
|
||||
regList: [],
|
||||
changeTag: true,
|
||||
document: 'https://element.eleme.cn/#/zh-CN/component/slider'
|
||||
},
|
||||
disabled: false,
|
||||
min: 0,
|
||||
max: 100,
|
||||
step: 1,
|
||||
'show-stops': false,
|
||||
range: false
|
||||
},
|
||||
{
|
||||
__config__: {
|
||||
label: '时间选择',
|
||||
tag: 'el-time-picker',
|
||||
tagIcon: 'time',
|
||||
defaultValue: null,
|
||||
span: 24,
|
||||
showLabel: true,
|
||||
layout: 'colFormItem',
|
||||
labelWidth: null,
|
||||
required: true,
|
||||
tips:false,
|
||||
tipsDesc:'',
|
||||
tipsIsLink:false,
|
||||
tipsLink:'',
|
||||
regList: [],
|
||||
changeTag: true,
|
||||
document: 'https://element.eleme.cn/#/zh-CN/component/time-picker'
|
||||
},
|
||||
placeholder: '请选择',
|
||||
style: { width: '100%' },
|
||||
disabled: false,
|
||||
clearable: true,
|
||||
'picker-options': {
|
||||
selectableRange: '00:00:00-23:59:59'
|
||||
},
|
||||
format: 'HH:mm:ss',
|
||||
'value-format': 'HH:mm:ss'
|
||||
},
|
||||
{
|
||||
__config__: {
|
||||
label: '时间范围',
|
||||
tag: 'el-time-picker',
|
||||
tagIcon: 'time-range',
|
||||
span: 24,
|
||||
showLabel: true,
|
||||
labelWidth: null,
|
||||
layout: 'colFormItem',
|
||||
defaultValue: null,
|
||||
required: true,
|
||||
tips:false,
|
||||
tipsDesc:'',
|
||||
tipsIsLink:false,
|
||||
tipsLink:'',
|
||||
regList: [],
|
||||
changeTag: true,
|
||||
document: 'https://element.eleme.cn/#/zh-CN/component/time-picker'
|
||||
},
|
||||
style: { width: '100%' },
|
||||
disabled: false,
|
||||
clearable: true,
|
||||
'is-range': true,
|
||||
'range-separator': '至',
|
||||
'start-placeholder': '开始时间',
|
||||
'end-placeholder': '结束时间',
|
||||
format: 'HH:mm:ss',
|
||||
'value-format': 'HH:mm:ss'
|
||||
},
|
||||
{
|
||||
__config__: {
|
||||
label: '固定时间范围',
|
||||
tag: 'time-select',
|
||||
tagIcon: 'time-select',
|
||||
span: 24,
|
||||
showLabel: true,
|
||||
labelWidth: null,
|
||||
layout: 'colFormItem',
|
||||
defaultValue: null,
|
||||
required: true,
|
||||
tips:false,
|
||||
tipsDesc:'',
|
||||
tipsIsLink:false,
|
||||
tipsLink:'',
|
||||
regList: [],
|
||||
changeTag: true,
|
||||
document: 'https://element.eleme.cn/#/zh-CN/component/time-picker'
|
||||
},
|
||||
style: { width: '100%' },
|
||||
disabled: false,
|
||||
clearable: true,
|
||||
placeholder: '请选择',
|
||||
format: 'HH:mm',
|
||||
'value-format': 'HH:mm'
|
||||
},
|
||||
{
|
||||
__config__: {
|
||||
label: '日期选择',
|
||||
tag: 'el-date-picker',
|
||||
tagIcon: 'date',
|
||||
defaultValue: null,
|
||||
showLabel: true,
|
||||
labelWidth: null,
|
||||
span: 24,
|
||||
layout: 'colFormItem',
|
||||
required: true,
|
||||
tips:false,
|
||||
tipsDesc:'',
|
||||
tipsIsLink:false,
|
||||
tipsLink:'',
|
||||
regList: [],
|
||||
changeTag: true,
|
||||
document: 'https://element.eleme.cn/#/zh-CN/component/date-picker'
|
||||
},
|
||||
placeholder: '请选择',
|
||||
type: 'date',
|
||||
style: { width: '100%' },
|
||||
disabled: false,
|
||||
clearable: true,
|
||||
format: 'yyyy-MM-dd',
|
||||
'value-format': 'yyyy-MM-dd',
|
||||
readonly: false
|
||||
},
|
||||
{
|
||||
__config__: {
|
||||
label: '日期范围',
|
||||
tag: 'el-date-picker',
|
||||
tagIcon: 'date-range',
|
||||
defaultValue: null,
|
||||
span: 24,
|
||||
showLabel: true,
|
||||
labelWidth: null,
|
||||
required: true,
|
||||
tips:false,
|
||||
tipsDesc:'',
|
||||
tipsIsLink:false,
|
||||
tipsLink:'',
|
||||
layout: 'colFormItem',
|
||||
regList: [],
|
||||
changeTag: true,
|
||||
document: 'https://element.eleme.cn/#/zh-CN/component/date-picker'
|
||||
},
|
||||
style: { width: '100%' },
|
||||
type: 'daterange',
|
||||
'range-separator': '至',
|
||||
'start-placeholder': '开始日期',
|
||||
'end-placeholder': '结束日期',
|
||||
disabled: false,
|
||||
clearable: true,
|
||||
format: 'yyyy-MM-dd',
|
||||
'value-format': 'yyyy-MM-dd',
|
||||
readonly: false
|
||||
},
|
||||
{
|
||||
__config__: {
|
||||
label: '评分',
|
||||
tag: 'el-rate',
|
||||
tagIcon: 'rate',
|
||||
defaultValue: 0,
|
||||
span: 24,
|
||||
showLabel: true,
|
||||
labelWidth: null,
|
||||
layout: 'colFormItem',
|
||||
required: true,
|
||||
tips:false,
|
||||
tipsDesc:'',
|
||||
tipsIsLink:false,
|
||||
tipsLink:'',
|
||||
regList: [],
|
||||
changeTag: true,
|
||||
document: 'https://element.eleme.cn/#/zh-CN/component/rate'
|
||||
},
|
||||
style: {},
|
||||
max: 5,
|
||||
'allow-half': false,
|
||||
'show-text': false,
|
||||
'show-score': false,
|
||||
disabled: false
|
||||
},
|
||||
{
|
||||
__config__: {
|
||||
label: '颜色选择',
|
||||
tag: 'el-color-picker',
|
||||
tagIcon: 'color',
|
||||
span: 24,
|
||||
defaultValue: null,
|
||||
showLabel: true,
|
||||
labelWidth: null,
|
||||
layout: 'colFormItem',
|
||||
required: true,
|
||||
tips:false,
|
||||
tipsDesc:'',
|
||||
tipsIsLink:false,
|
||||
tipsLink:'',
|
||||
regList: [],
|
||||
changeTag: true,
|
||||
document: 'https://element.eleme.cn/#/zh-CN/component/color-picker'
|
||||
},
|
||||
'show-alpha': false,
|
||||
'color-format': '',
|
||||
disabled: false,
|
||||
size: 'medium'
|
||||
},
|
||||
{
|
||||
__config__: {
|
||||
label: '上传文件',
|
||||
tag: 'upload-file',
|
||||
tagIcon: 'uploadPicture',
|
||||
layout: 'colFormItem',
|
||||
defaultValue: null,
|
||||
showLabel: true,
|
||||
labelWidth: null,
|
||||
required: true,
|
||||
tips:false,
|
||||
tipsDesc:'',
|
||||
tipsIsLink:false,
|
||||
tipsLink:'',
|
||||
span: 24,
|
||||
showTip: false,
|
||||
buttonText: '点击上传',
|
||||
regList: [],
|
||||
changeTag: true,
|
||||
// fileSize: 2,
|
||||
// sizeUnit: 'MB',
|
||||
document: 'https://element.eleme.cn/#/zh-CN/component/upload'
|
||||
},
|
||||
__slot__: {
|
||||
'list-type': true
|
||||
},
|
||||
accept: '',
|
||||
// headers: { 'Authori-zation': getToken() },
|
||||
// data: { model: 'product', pid: 0 },
|
||||
// action: SettingMer.apiBaseURL + 'admin/upload/image?model=product&pid=0',
|
||||
// disabled: false,
|
||||
// accept: '',
|
||||
name: 'upfile',
|
||||
// 'auto-upload': false,
|
||||
// 'list-type': 'text',
|
||||
// multiple: false
|
||||
},
|
||||
{
|
||||
__config__: {
|
||||
label: '自定义上传',
|
||||
tag: 'self-upload',
|
||||
tagIcon: 'selfUpload',
|
||||
layout: 'colFormItem',
|
||||
defaultValue: null,
|
||||
showLabel: true,
|
||||
labelWidth: null,
|
||||
required: true,
|
||||
tips:false,
|
||||
tipsDesc:'',
|
||||
tipsIsLink:false,
|
||||
tipsLink:'',
|
||||
span: 24,
|
||||
showTip: false,
|
||||
buttonText: '',
|
||||
regList: [],
|
||||
changeTag: true,
|
||||
// fileSize: 2,
|
||||
// sizeUnit: 'MB',
|
||||
document: 'https://element.eleme.cn/#/zh-CN/component/upload'
|
||||
},
|
||||
__slot__: {
|
||||
'list-type': true
|
||||
},
|
||||
// action: 'https://jsonplaceholder.typicode.com/posts/',
|
||||
disabled: true,
|
||||
accept: 'image',
|
||||
name: 'file',
|
||||
// 'auto-upload': true,
|
||||
// 'list-type': 'text',
|
||||
multiple: false
|
||||
},
|
||||
// {
|
||||
// __config__: {
|
||||
// label: '富文本编辑器',
|
||||
// tag: 'tinymce',
|
||||
// tagIcon: 'rich-text',
|
||||
// layout: 'colFormItem',
|
||||
// defaultValue: null,
|
||||
// showLabel: true,
|
||||
// labelWidth: null,
|
||||
// required: false,
|
||||
// tips:false,
|
||||
// tipsDesc:'',
|
||||
// tipsIsLink:false,
|
||||
// tipsLink:'',
|
||||
// span: 24,
|
||||
// showTip: false,
|
||||
// regList: [],
|
||||
// document: "http://tinymce.ax-z.cn",
|
||||
// renderKey: 1636077154813,
|
||||
// changeTag: true,
|
||||
// },
|
||||
// height: 300, // 编辑器高度
|
||||
// name: 'tinymce',
|
||||
// disabled: false
|
||||
// }
|
||||
]
|
||||
|
||||
// 布局型组件 【左面板】
|
||||
export const layoutComponents = [
|
||||
{
|
||||
__config__: {
|
||||
layout: 'rowFormItem',
|
||||
tagIcon: 'row',
|
||||
label: '行容器',
|
||||
layoutTree: true,
|
||||
children: [],
|
||||
document: 'https://element.eleme.cn/#/zh-CN/component/layout'
|
||||
},
|
||||
type: 'default',
|
||||
justify: 'start',
|
||||
align: 'top'
|
||||
},
|
||||
{
|
||||
__config__: {
|
||||
label: '按钮',
|
||||
showLabel: true,
|
||||
changeTag: true,
|
||||
labelWidth: null,
|
||||
tag: 'el-button',
|
||||
tagIcon: 'button',
|
||||
defaultValue: undefined,
|
||||
span: 24,
|
||||
layout: 'colFormItem',
|
||||
document: 'https://element.eleme.cn/#/zh-CN/component/button'
|
||||
},
|
||||
__slot__: {
|
||||
default: '主要按钮'
|
||||
},
|
||||
type: 'primary',
|
||||
icon: 'el-icon-search',
|
||||
round: false,
|
||||
size: 'medium',
|
||||
plain: false,
|
||||
circle: false,
|
||||
disabled: false
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,18 @@
|
||||
const styles = {
|
||||
'el-rate': '.el-rate{display: inline-block; vertical-align: text-top;}',
|
||||
'el-upload': '.el-upload__tip{line-height: 1.2;}'
|
||||
}
|
||||
|
||||
function addCss(cssList, el) {
|
||||
const css = styles[el.__config__.tag]
|
||||
css && cssList.indexOf(css) === -1 && cssList.push(css)
|
||||
if (el.__config__.children) {
|
||||
el.__config__.children.forEach(el2 => addCss(cssList, el2))
|
||||
}
|
||||
}
|
||||
|
||||
export function makeUpCss(conf) {
|
||||
const cssList = []
|
||||
conf.fields.forEach(el => addCss(cssList, el))
|
||||
return cssList.join('\n')
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
export default [
|
||||
{
|
||||
__config__: {
|
||||
label: '单行文本',
|
||||
labelWidth: null,
|
||||
showLabel: true,
|
||||
changeTag: true,
|
||||
tag: 'el-input',
|
||||
tagIcon: 'input',
|
||||
defaultValue: undefined,
|
||||
required: true,
|
||||
layout: 'colFormItem',
|
||||
span: 24,
|
||||
document: 'https://element.eleme.cn/#/zh-CN/component/input',
|
||||
// 正则校验规则
|
||||
regList: [{
|
||||
pattern: '/^1(3|4|5|7|8|9)\\d{9}$/',
|
||||
message: '手机号格式错误'
|
||||
}]
|
||||
},
|
||||
// 组件的插槽属性
|
||||
__slot__: {
|
||||
prepend: '',
|
||||
append: ''
|
||||
},
|
||||
__vModel__: 'mobile',
|
||||
placeholder: '请输入手机号',
|
||||
style: { width: '100%' },
|
||||
clearable: true,
|
||||
'prefix-icon': 'el-icon-mobile',
|
||||
'suffix-icon': '',
|
||||
maxlength: 11,
|
||||
'show-word-limit': true,
|
||||
readonly: false,
|
||||
disabled: false
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,432 @@
|
||||
/* eslint-disable max-len */
|
||||
import ruleTrigger from './ruleTrigger'
|
||||
import { getToken } from '@/utils/auth'
|
||||
let confGlobal
|
||||
let someSpanIsNot24
|
||||
|
||||
export function dialogWrapper(str) {
|
||||
return `<el-dialog v-bind="$attrs" v-on="$listeners" @open="onOpen" @close="onClose" title="Dialog Titile">
|
||||
${str}
|
||||
<div slot="footer">
|
||||
<el-button @click="close">取消</el-button>
|
||||
<el-button type="primary" @click="handelConfirm">确定</el-button>
|
||||
</div>
|
||||
</el-dialog>`
|
||||
}
|
||||
|
||||
export function vueTemplate(str) {
|
||||
return `<template>
|
||||
<div>
|
||||
${str}
|
||||
</div>
|
||||
</template>`
|
||||
}
|
||||
|
||||
export function vueScript(str) {
|
||||
return `<script>
|
||||
${str}
|
||||
</script>`
|
||||
}
|
||||
|
||||
export function cssStyle(cssStr) {
|
||||
return `<style>
|
||||
${cssStr}
|
||||
</style>`
|
||||
}
|
||||
|
||||
function buildFormTemplate(scheme, child, type) {
|
||||
let labelPosition = ''
|
||||
if (scheme.labelPosition !== 'right') {
|
||||
labelPosition = `label-position="${scheme.labelPosition}"`
|
||||
}
|
||||
const disabled = scheme.disabled ? `:disabled="${scheme.disabled}"` : ''
|
||||
let str = `<el-form ref="${scheme.formRef}" :model="${scheme.formModel}" :rules="${scheme.formRules}" size="${scheme.size}" ${disabled} label-width="${scheme.labelWidth}px" ${labelPosition}>
|
||||
${child}
|
||||
${buildFromBtns(scheme, type)}
|
||||
</el-form>`
|
||||
if (someSpanIsNot24) {
|
||||
str = `<el-row :gutter="${scheme.gutter}">
|
||||
${str}
|
||||
</el-row>`
|
||||
}
|
||||
return str
|
||||
}
|
||||
|
||||
function buildFromBtns(scheme, type) {
|
||||
let str = ''
|
||||
if (scheme.formBtns && type === 'file') {
|
||||
str = `<el-form-item size="large">
|
||||
<el-button type="primary" @click="submitForm">提交</el-button>
|
||||
<el-button @click="resetForm">取消</el-button>
|
||||
</el-form-item>`
|
||||
if (someSpanIsNot24) {
|
||||
str = `<el-col :span="24">
|
||||
${str}
|
||||
</el-col>`
|
||||
}
|
||||
}
|
||||
return str
|
||||
}
|
||||
|
||||
// span不为24的用el-col包裹
|
||||
function colWrapper(scheme, str) {
|
||||
if (someSpanIsNot24 || scheme.__config__.span !== 24) {
|
||||
return `<el-col :span="${scheme.__config__.span}">
|
||||
${str}
|
||||
</el-col>`
|
||||
}
|
||||
return str
|
||||
}
|
||||
|
||||
const layouts = {
|
||||
colFormItem(scheme) {
|
||||
const config = scheme.__config__
|
||||
let labelWidth = ''
|
||||
let label = `label="${config.label}"`
|
||||
if (config.labelWidth && config.labelWidth !== confGlobal.labelWidth) {
|
||||
labelWidth = `label-width="${config.labelWidth}px"`
|
||||
}
|
||||
if (config.showLabel === false) {
|
||||
labelWidth = 'label-width="0"'
|
||||
label = ''
|
||||
}
|
||||
const required = !ruleTrigger[config.tag] && config.required ? 'required' : ''
|
||||
const tagDom = tags[config.tag] ? tags[config.tag](scheme) : null
|
||||
let str = `<el-form-item ${labelWidth} ${label} prop="${scheme.__vModel__}" ${required}>
|
||||
${tagDom}
|
||||
</el-form-item>`
|
||||
str = colWrapper(scheme, str)
|
||||
return str
|
||||
},
|
||||
rowFormItem(scheme) {
|
||||
const config = scheme.__config__
|
||||
const type = scheme.type === 'default' ? '' : `type="${scheme.type}"`
|
||||
const justify = scheme.type === 'default' ? '' : `justify="${scheme.justify}"`
|
||||
const align = scheme.type === 'default' ? '' : `align="${scheme.align}"`
|
||||
const gutter = scheme.gutter ? `:gutter="${scheme.gutter}"` : ''
|
||||
const children = config.children.map(el => layouts[el.__config__.layout](el))
|
||||
let str = `<el-row ${type} ${justify} ${align} ${gutter}>
|
||||
${children.join('\n')}
|
||||
</el-row>`
|
||||
str = colWrapper(scheme, str)
|
||||
return str
|
||||
}
|
||||
}
|
||||
|
||||
const tags = {
|
||||
'el-button': el => {
|
||||
const {
|
||||
tag, disabled
|
||||
} = attrBuilder(el)
|
||||
const type = el.type ? `type="${el.type}"` : ''
|
||||
const icon = el.icon ? `icon="${el.icon}"` : ''
|
||||
const round = el.round ? 'round' : ''
|
||||
const size = el.size ? `size="${el.size}"` : ''
|
||||
const plain = el.plain ? 'plain' : ''
|
||||
const circle = el.circle ? 'circle' : ''
|
||||
let child = buildElButtonChild(el)
|
||||
|
||||
if (child) child = `\n${child}\n` // 换行
|
||||
return `<${tag} ${type} ${icon} ${round} ${size} ${plain} ${disabled} ${circle}>${child}</${tag}>`
|
||||
},
|
||||
'el-input': el => {
|
||||
const {
|
||||
tag, disabled, vModel, clearable, placeholder, width
|
||||
} = attrBuilder(el)
|
||||
const maxlength = el.maxlength ? `:maxlength="${el.maxlength}"` : ''
|
||||
const showWordLimit = el['show-word-limit'] ? 'show-word-limit' : ''
|
||||
const readonly = el.readonly ? 'readonly' : ''
|
||||
const prefixIcon = el['prefix-icon'] ? `prefix-icon='${el['prefix-icon']}'` : ''
|
||||
const suffixIcon = el['suffix-icon'] ? `suffix-icon='${el['suffix-icon']}'` : ''
|
||||
const showPassword = el['show-password'] ? 'show-password' : ''
|
||||
const type = el.type ? `type="${el.type}"` : ''
|
||||
const autosize = el.autosize && el.autosize.minRows
|
||||
? `:autosize="{minRows: ${el.autosize.minRows}, maxRows: ${el.autosize.maxRows}}"`
|
||||
: ''
|
||||
let child = buildElInputChild(el)
|
||||
|
||||
if (child) child = `\n${child}\n` // 换行
|
||||
return `<${tag} ${vModel} ${type} ${placeholder} ${maxlength} ${showWordLimit} ${readonly} ${disabled} ${clearable} ${prefixIcon} ${suffixIcon} ${showPassword} ${autosize} ${width}>${child}</${tag}>`
|
||||
},
|
||||
'el-input-number': el => {
|
||||
const {
|
||||
tag, disabled, vModel, placeholder
|
||||
} = attrBuilder(el)
|
||||
const controlsPosition = el['controls-position'] ? `controls-position=${el['controls-position']}` : ''
|
||||
const min = el.min ? `:min='${el.min}'` : ''
|
||||
const max = el.max ? `:max='${el.max}'` : ''
|
||||
const step = el.step ? `:step='${el.step}'` : ''
|
||||
const stepStrictly = el['step-strictly'] ? 'step-strictly' : ''
|
||||
const precision = el.precision ? `:precision='${el.precision}'` : ''
|
||||
|
||||
return `<${tag} ${vModel} ${placeholder} ${step} ${stepStrictly} ${precision} ${controlsPosition} ${min} ${max} ${disabled}></${tag}>`
|
||||
},
|
||||
'el-select': el => {
|
||||
const {
|
||||
tag, disabled, vModel, clearable, placeholder, width
|
||||
} = attrBuilder(el)
|
||||
const filterable = el.filterable ? 'filterable' : ''
|
||||
const multiple = el.multiple ? 'multiple' : ''
|
||||
let child = buildElSelectChild(el)
|
||||
|
||||
if (child) child = `\n${child}\n` // 换行
|
||||
return `<${tag} ${vModel} ${placeholder} ${disabled} ${multiple} ${filterable} ${clearable} ${width}>${child}</${tag}>`
|
||||
},
|
||||
'el-radio-group': el => {
|
||||
const { tag, disabled, vModel } = attrBuilder(el)
|
||||
const size = `size="${el.size}"`
|
||||
let child = buildElRadioGroupChild(el)
|
||||
|
||||
if (child) child = `\n${child}\n` // 换行
|
||||
return `<${tag} ${vModel} ${size} ${disabled}>${child}</${tag}>`
|
||||
},
|
||||
'el-checkbox-group': el => {
|
||||
const { tag, disabled, vModel } = attrBuilder(el)
|
||||
const size = `size="${el.size}"`
|
||||
const min = el.min ? `:min="${el.min}"` : ''
|
||||
const max = el.max ? `:max="${el.max}"` : ''
|
||||
let child = buildElCheckboxGroupChild(el)
|
||||
|
||||
if (child) child = `\n${child}\n` // 换行
|
||||
return `<${tag} ${vModel} ${min} ${max} ${size} ${disabled}>${child}</${tag}>`
|
||||
},
|
||||
'el-switch': el => {
|
||||
const { tag, disabled, vModel } = attrBuilder(el)
|
||||
const activeText = el['active-text'] ? `active-text="${el['active-text']}"` : ''
|
||||
const inactiveText = el['inactive-text'] ? `inactive-text="${el['inactive-text']}"` : ''
|
||||
const activeColor = el['active-color'] ? `active-color="${el['active-color']}"` : ''
|
||||
const inactiveColor = el['inactive-color'] ? `inactive-color="${el['inactive-color']}"` : ''
|
||||
const activeValue = el['active-value'] !== true ? `:active-value='${JSON.stringify(el['active-value'])}'` : ''
|
||||
const inactiveValue = el['inactive-value'] !== false ? `:inactive-value='${JSON.stringify(el['inactive-value'])}'` : ''
|
||||
|
||||
return `<${tag} ${vModel} ${activeText} ${inactiveText} ${activeColor} ${inactiveColor} ${activeValue} ${inactiveValue} ${disabled}></${tag}>`
|
||||
},
|
||||
'el-cascader': el => {
|
||||
const {
|
||||
tag, disabled, vModel, clearable, placeholder, width
|
||||
} = attrBuilder(el)
|
||||
const options = el.options ? `:options="${el.__vModel__}Options"` : ''
|
||||
const props = el.props ? `:props="${el.__vModel__}Props"` : ''
|
||||
const showAllLevels = el['show-all-levels'] ? '' : ':show-all-levels="false"'
|
||||
const filterable = el.filterable ? 'filterable' : ''
|
||||
const separator = el.separator === '/' ? '' : `separator="${el.separator}"`
|
||||
|
||||
return `<${tag} ${vModel} ${options} ${props} ${width} ${showAllLevels} ${placeholder} ${separator} ${filterable} ${clearable} ${disabled}></${tag}>`
|
||||
},
|
||||
'el-slider': el => {
|
||||
const { tag, disabled, vModel } = attrBuilder(el)
|
||||
const min = el.min ? `:min='${el.min}'` : ''
|
||||
const max = el.max ? `:max='${el.max}'` : ''
|
||||
const step = el.step ? `:step='${el.step}'` : ''
|
||||
const range = el.range ? 'range' : ''
|
||||
const showStops = el['show-stops'] ? `:show-stops="${el['show-stops']}"` : ''
|
||||
|
||||
return `<${tag} ${min} ${max} ${step} ${vModel} ${range} ${showStops} ${disabled}></${tag}>`
|
||||
},
|
||||
'el-time-picker': el => {
|
||||
const {
|
||||
tag, disabled, vModel, clearable, placeholder, width
|
||||
} = attrBuilder(el)
|
||||
const startPlaceholder = el['start-placeholder'] ? `start-placeholder="${el['start-placeholder']}"` : ''
|
||||
const endPlaceholder = el['end-placeholder'] ? `end-placeholder="${el['end-placeholder']}"` : ''
|
||||
const rangeSeparator = el['range-separator'] ? `range-separator="${el['range-separator']}"` : ''
|
||||
const isRange = el['is-range'] ? 'is-range' : ''
|
||||
const format = el.format ? `format="${el.format}"` : ''
|
||||
const valueFormat = el['value-format'] ? `value-format="${el['value-format']}"` : ''
|
||||
const pickerOptions = el['picker-options'] ? `:picker-options='${JSON.stringify(el['picker-options'])}'` : ''
|
||||
|
||||
return `<${tag} ${vModel} ${isRange} ${format} ${valueFormat} ${pickerOptions} ${width} ${placeholder} ${startPlaceholder} ${endPlaceholder} ${rangeSeparator} ${clearable} ${disabled}></${tag}>`
|
||||
},
|
||||
'el-date-picker': el => {
|
||||
const {
|
||||
tag, disabled, vModel, clearable, placeholder, width
|
||||
} = attrBuilder(el)
|
||||
const startPlaceholder = el['start-placeholder'] ? `start-placeholder="${el['start-placeholder']}"` : ''
|
||||
const endPlaceholder = el['end-placeholder'] ? `end-placeholder="${el['end-placeholder']}"` : ''
|
||||
const rangeSeparator = el['range-separator'] ? `range-separator="${el['range-separator']}"` : ''
|
||||
const format = el.format ? `format="${el.format}"` : ''
|
||||
const valueFormat = el['value-format'] ? `value-format="${el['value-format']}"` : ''
|
||||
const type = el.type === 'date' ? '' : `type="${el.type}"`
|
||||
const readonly = el.readonly ? 'readonly' : ''
|
||||
|
||||
return `<${tag} ${type} ${vModel} ${format} ${valueFormat} ${width} ${placeholder} ${startPlaceholder} ${endPlaceholder} ${rangeSeparator} ${clearable} ${readonly} ${disabled}></${tag}>`
|
||||
},
|
||||
'el-rate': el => {
|
||||
const { tag, disabled, vModel } = attrBuilder(el)
|
||||
const max = el.max ? `:max='${el.max}'` : ''
|
||||
const allowHalf = el['allow-half'] ? 'allow-half' : ''
|
||||
const showText = el['show-text'] ? 'show-text' : ''
|
||||
const showScore = el['show-score'] ? 'show-score' : ''
|
||||
|
||||
return `<${tag} ${vModel} ${max} ${allowHalf} ${showText} ${showScore} ${disabled}></${tag}>`
|
||||
},
|
||||
'el-color-picker': el => {
|
||||
const { tag, disabled, vModel } = attrBuilder(el)
|
||||
const size = `size="${el.size}"`
|
||||
const showAlpha = el['show-alpha'] ? 'show-alpha' : ''
|
||||
const colorFormat = el['color-format'] ? `color-format="${el['color-format']}"` : ''
|
||||
|
||||
return `<${tag} ${vModel} ${size} ${showAlpha} ${colorFormat} ${disabled}></${tag}>`
|
||||
},
|
||||
'el-upload': el => {
|
||||
const { tag } = el.__config__
|
||||
const disabled = el.disabled ? ':disabled=\'true\'' : ''
|
||||
const action = el.action ? `:action="${el.__vModel__}Action"` : ''
|
||||
const multiple = el.multiple ? 'multiple' : ''
|
||||
const listType = el['list-type'] !== 'text' ? `list-type="${el['list-type']}"` : ''
|
||||
const accept = el.accept ? `accept="${el.accept}"` : ''
|
||||
const name = el.name !== 'file' ? `name="${el.name}"` : ''
|
||||
const autoUpload = el['auto-upload'] === false ? ':auto-upload="false"' : ''
|
||||
const beforeUpload = `:before-upload="${el.__vModel__}BeforeUpload"`
|
||||
const fileList = `:file-list="${el.__vModel__}fileList"`
|
||||
const ref = `ref="${el.__vModel__}"`
|
||||
const headers = { 'Authori-zation': getToken() }
|
||||
const data = el.data ? 'data' : ''
|
||||
let child = buildElUploadChild(el)
|
||||
|
||||
if (child) child = `\n${child}\n` // 换行
|
||||
return `<${tag} ${headers} ${data} ${ref} ${fileList} ${action} ${autoUpload} ${multiple} ${beforeUpload} ${listType} ${accept} ${name} ${disabled}>${child}</${tag}>`
|
||||
},
|
||||
'self-upload': el => {
|
||||
const { tag, vModel } = attrBuilder(el)
|
||||
const height = el.height ? `:height="${el.height}"` : ''
|
||||
const multiple = el.multiple ? 'multiple' : ''
|
||||
const branding = el.branding ? `:branding="${el.branding}"` : ''
|
||||
return `<${tag} ${vModel} ${height} ${branding} ${multiple}></${tag}>`
|
||||
},
|
||||
'ueditor-from': el => {
|
||||
const { tag, vModel } = attrBuilder(el)
|
||||
const height = el.height ? `:height="${el.height}"` : ''
|
||||
return `<${tag} ${vModel}${height} >`
|
||||
},
|
||||
'upload-file': el => {
|
||||
const { tag, vModel } = attrBuilder(el)
|
||||
const height = el.height ? `:height="${el.height}"` : ''
|
||||
return `<${tag} ${vModel}${height} >`
|
||||
},
|
||||
'time-select': el => {
|
||||
const { tag, vModel } = attrBuilder(el)
|
||||
const height = el.height ? `:height="${el.height}"` : ''
|
||||
return `<${tag} ${vModel}${height} >`
|
||||
},
|
||||
tinymce: el => {
|
||||
const { tag, vModel } = attrBuilder(el)
|
||||
const branding = el.branding ? `:branding="${el.branding}"` : ''
|
||||
return `<${tag} ${vModel} ${branding}></${tag}>`
|
||||
}
|
||||
}
|
||||
|
||||
function attrBuilder(el) {
|
||||
return {
|
||||
tag: el.__config__.tag,
|
||||
vModel: `v-model="${confGlobal.formModel}.${el.__vModel__}"`,
|
||||
clearable: el.clearable ? 'clearable' : '',
|
||||
placeholder: el.placeholder ? `placeholder="${el.placeholder}"` : '',
|
||||
width: el.style && el.style.width ? ':style="{width: \'100%\'}"' : '',
|
||||
disabled: el.disabled ? ':disabled=\'true\'' : ''
|
||||
}
|
||||
}
|
||||
|
||||
// el-buttin 子级
|
||||
function buildElButtonChild(scheme) {
|
||||
const children = []
|
||||
const slot = scheme.__slot__ || {}
|
||||
if (slot.default) {
|
||||
children.push(slot.default)
|
||||
}
|
||||
return children.join('\n')
|
||||
}
|
||||
|
||||
// el-input 子级
|
||||
function buildElInputChild(scheme) {
|
||||
const children = []
|
||||
const slot = scheme.__slot__
|
||||
if (slot && slot.prepend) {
|
||||
children.push(`<template slot="prepend">${slot.prepend}</template>`)
|
||||
}
|
||||
if (slot && slot.append) {
|
||||
children.push(`<template slot="append">${slot.append}</template>`)
|
||||
}
|
||||
return children.join('\n')
|
||||
}
|
||||
|
||||
// el-select 子级
|
||||
function buildElSelectChild(scheme) {
|
||||
const children = []
|
||||
const slot = scheme.__slot__
|
||||
if (slot && slot.options && slot.options.length) {
|
||||
children.push(`<el-option v-for="(item, index) in ${scheme.__vModel__}Options" :key="index" :label="item.label" :value="item.value" :disabled="item.disabled"></el-option>`)
|
||||
}
|
||||
return children.join('\n')
|
||||
}
|
||||
|
||||
// el-radio-group 子级
|
||||
function buildElRadioGroupChild(scheme) {
|
||||
const children = []
|
||||
const slot = scheme.__slot__
|
||||
const config = scheme.__config__
|
||||
if (slot && slot.options && slot.options.length) {
|
||||
const tag = config.optionType === 'button' ? 'el-radio-button' : 'el-radio'
|
||||
const border = config.border ? 'border' : ''
|
||||
children.push(`<${tag} v-for="(item, index) in ${scheme.__vModel__}Options" :key="index" :label="item.value" :disabled="item.disabled" ${border}>{{item.label}}</${tag}>`)
|
||||
}
|
||||
return children.join('\n')
|
||||
}
|
||||
|
||||
// el-checkbox-group 子级
|
||||
function buildElCheckboxGroupChild(scheme) {
|
||||
const children = []
|
||||
const slot = scheme.__slot__
|
||||
const config = scheme.__config__
|
||||
if (slot && slot.options && slot.options.length) {
|
||||
const tag = config.optionType === 'button' ? 'el-checkbox-button' : 'el-checkbox'
|
||||
const border = config.border ? 'border' : ''
|
||||
children.push(`<${tag} v-for="(item, index) in ${scheme.__vModel__}Options" :key="index" :label="item.value" :disabled="item.disabled" ${border}>{{item.label}}</${tag}>`)
|
||||
}
|
||||
return children.join('\n')
|
||||
}
|
||||
|
||||
// el-upload 子级
|
||||
function buildElUploadChild(scheme) {
|
||||
const list = []
|
||||
const config = scheme.__config__
|
||||
if (scheme['list-type'] === 'picture-card') list.push('<i class="el-icon-plus"></i>')
|
||||
else list.push(`<el-button size="small" type="primary" icon="el-icon-upload">${config.buttonText}</el-button>`)
|
||||
if (config.showTip) list.push(`<div slot="tip" class="el-upload__tip">只能上传不超过 ${config.fileSize}${config.sizeUnit} 的${scheme.accept}文件</div>`)
|
||||
return list.join('\n')
|
||||
}
|
||||
|
||||
// el-upload 子级
|
||||
// function buildSelfUploadChild(scheme) {
|
||||
// const list = []
|
||||
// const config = scheme.__config__
|
||||
// if (scheme['list-type'] === 'picture-card') list.push('<i class="el-icon-plus"></i>')
|
||||
// else list.push(`<el-button size="small" type="primary" icon="el-icon-upload">${config.buttonText}</el-button>`)
|
||||
// if (config.showTip) list.push(`<div slot="tip" class="el-upload__tip">只能上传不超过 ${config.fileSize}${config.sizeUnit} 的${scheme.accept}文件</div>`)
|
||||
// return list.join('\n')
|
||||
// }
|
||||
|
||||
/**
|
||||
* 组装html代码。【入口函数】
|
||||
* @param {Object} formConfig 整个表单配置
|
||||
* @param {String} type 生成类型,文件或弹窗等
|
||||
*/
|
||||
export function makeUpHtml(formConfig, type) {
|
||||
const htmlList = []
|
||||
confGlobal = formConfig
|
||||
// 判断布局是否都沾满了24个栅格,以备后续简化代码结构
|
||||
someSpanIsNot24 = formConfig.fields.some(item => item.__config__.span !== 24)
|
||||
// 遍历渲染每个组件成html
|
||||
formConfig.fields.forEach(el => {
|
||||
htmlList.push(layouts[el.__config__.layout](el))
|
||||
})
|
||||
const htmlStr = htmlList.join('\n')
|
||||
// 将组件代码放进form标签
|
||||
let temp = buildFormTemplate(formConfig, htmlStr, type)
|
||||
// dialog标签包裹代码
|
||||
if (type === 'dialog') {
|
||||
temp = dialogWrapper(temp)
|
||||
}
|
||||
confGlobal = null
|
||||
return temp
|
||||
}
|
||||
@@ -0,0 +1,253 @@
|
||||
import { isArray } from 'util'
|
||||
import { exportDefault, titleCase } from '@/utils/index'
|
||||
import ruleTrigger from './ruleTrigger'
|
||||
|
||||
const units = {
|
||||
KB: '1024',
|
||||
MB: '1024 / 1024',
|
||||
GB: '1024 / 1024 / 1024'
|
||||
}
|
||||
let confGlobal
|
||||
const inheritAttrs = {
|
||||
file: '',
|
||||
dialog: 'inheritAttrs: false,'
|
||||
}
|
||||
|
||||
/**
|
||||
* 组装js 【入口函数】
|
||||
* @param {Object} formConfig 整个表单配置
|
||||
* @param {String} type 生成类型,文件或弹窗等
|
||||
*/
|
||||
export function makeUpJs(formConfig, type) {
|
||||
confGlobal = formConfig = JSON.parse(JSON.stringify(formConfig))
|
||||
const dataList = []
|
||||
const ruleList = []
|
||||
const optionsList = []
|
||||
const propsList = []
|
||||
const methodList = mixinMethod(type)
|
||||
const uploadVarList = []
|
||||
|
||||
formConfig.fields.forEach(el => {
|
||||
buildAttributes(el, dataList, ruleList, optionsList, methodList, propsList, uploadVarList)
|
||||
})
|
||||
|
||||
const script = buildexport(
|
||||
formConfig,
|
||||
type,
|
||||
dataList.join('\n'),
|
||||
ruleList.join('\n'),
|
||||
optionsList.join('\n'),
|
||||
uploadVarList.join('\n'),
|
||||
propsList.join('\n'),
|
||||
methodList.join('\n')
|
||||
)
|
||||
confGlobal = null
|
||||
return script
|
||||
}
|
||||
|
||||
// 构建组件属性
|
||||
function buildAttributes(scheme, dataList, ruleList, optionsList, methodList, propsList, uploadVarList) {
|
||||
const config = scheme.__config__
|
||||
const slot = scheme.__slot__
|
||||
buildData(scheme, dataList)
|
||||
buildRules(scheme, ruleList)
|
||||
|
||||
// 特殊处理options属性
|
||||
if (scheme.options || (slot && slot.options && slot.options.length)) {
|
||||
buildOptions(scheme, optionsList)
|
||||
if (config.dataType === 'dynamic') {
|
||||
const model = `${scheme.__vModel__}Options`
|
||||
const options = titleCase(model)
|
||||
buildOptionMethod(`get${options}`, model, methodList)
|
||||
}
|
||||
}
|
||||
|
||||
// 处理props
|
||||
if (scheme.props && scheme.props.props) {
|
||||
buildProps(scheme, propsList)
|
||||
}
|
||||
|
||||
// 处理el-upload的action
|
||||
if (scheme.action && config.tag === 'el-upload') {
|
||||
uploadVarList.push(
|
||||
`${scheme.__vModel__}Action: '${scheme.action}',
|
||||
${scheme.__vModel__}fileList: [],`
|
||||
)
|
||||
methodList.push(buildBeforeUpload(scheme))
|
||||
// 非自动上传时,生成手动上传的函数
|
||||
if (!scheme['auto-upload']) {
|
||||
methodList.push(buildSubmitUpload(scheme))
|
||||
}
|
||||
}
|
||||
|
||||
// 构建子级组件属性
|
||||
if (config.children) {
|
||||
config.children.forEach(item => {
|
||||
buildAttributes(item, dataList, ruleList, optionsList, methodList, propsList, uploadVarList)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 混入处理函数
|
||||
function mixinMethod(type) {
|
||||
const list = []; const
|
||||
minxins = {
|
||||
file: confGlobal.formBtns ? {
|
||||
submitForm: `submitForm() {
|
||||
this.$refs['${confGlobal.formRef}'].validate(valid => {
|
||||
if(!valid) return
|
||||
// TODO 提交表单
|
||||
})
|
||||
},`,
|
||||
resetForm: `resetForm() {
|
||||
this.$refs['${confGlobal.formRef}'].resetFields()
|
||||
},`
|
||||
} : null,
|
||||
dialog: {
|
||||
onOpen: 'onOpen() {},',
|
||||
onClose: `onClose() {
|
||||
this.$refs['${confGlobal.formRef}'].resetFields()
|
||||
},`,
|
||||
close: `close() {
|
||||
this.$emit('update:visible', false)
|
||||
},`,
|
||||
handelConfirm: `handelConfirm() {
|
||||
this.$refs['${confGlobal.formRef}'].validate(valid => {
|
||||
if(!valid) return
|
||||
this.close()
|
||||
})
|
||||
},`
|
||||
}
|
||||
}
|
||||
|
||||
const methods = minxins[type]
|
||||
if (methods) {
|
||||
Object.keys(methods).forEach(key => {
|
||||
list.push(methods[key])
|
||||
})
|
||||
}
|
||||
|
||||
return list
|
||||
}
|
||||
|
||||
// 构建data
|
||||
function buildData(scheme, dataList) {
|
||||
const config = scheme.__config__
|
||||
if (scheme.__vModel__ === undefined) return
|
||||
const defaultValue = JSON.stringify(config.defaultValue)
|
||||
dataList.push(`${scheme.__vModel__}: ${defaultValue},`)
|
||||
}
|
||||
|
||||
// 构建校验规则
|
||||
function buildRules(scheme, ruleList) {
|
||||
const config = scheme.__config__
|
||||
if (scheme.__vModel__ === undefined) return
|
||||
const rules = []
|
||||
if (ruleTrigger[config.tag]) {
|
||||
if (config.required) {
|
||||
const type = isArray(config.defaultValue) ? 'type: \'array\',' : ''
|
||||
let message = isArray(config.defaultValue) ? `请至少选择一个${config.label}` : scheme.placeholder
|
||||
if (message === undefined) message = `${config.label}不能为空`
|
||||
rules.push(`{ required: true, ${type} message: '${message}', trigger: '${ruleTrigger[config.tag]}' }`)
|
||||
}
|
||||
if (config.regList && isArray(config.regList)) {
|
||||
config.regList.forEach(item => {
|
||||
if (item.pattern) {
|
||||
rules.push(
|
||||
`{ pattern: ${eval(item.pattern)}, message: '${item.message}', trigger: '${ruleTrigger[config.tag]}' }`
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
ruleList.push(`${scheme.__vModel__}: [${rules.join(',')}],`)
|
||||
}
|
||||
}
|
||||
|
||||
// 构建options
|
||||
function buildOptions(scheme, optionsList) {
|
||||
if (scheme.__vModel__ === undefined) return
|
||||
// el-cascader直接有options属性,其他组件都是定义在slot中,所以有两处判断
|
||||
let { options } = scheme
|
||||
if (!options) options = scheme.__slot__.options
|
||||
if (scheme.__config__.dataType === 'dynamic') { options = [] }
|
||||
const str = `${scheme.__vModel__}Options: ${JSON.stringify(options)},`
|
||||
optionsList.push(str)
|
||||
}
|
||||
|
||||
function buildProps(scheme, propsList) {
|
||||
const str = `${scheme.__vModel__}Props: ${JSON.stringify(scheme.props.props)},`
|
||||
propsList.push(str)
|
||||
}
|
||||
|
||||
// el-upload的BeforeUpload
|
||||
function buildBeforeUpload(scheme) {
|
||||
const config = scheme.__config__
|
||||
const unitNum = units[config.sizeUnit]; let rightSizeCode = ''; let acceptCode = ''; const
|
||||
returnList = []
|
||||
if (config.fileSize) {
|
||||
rightSizeCode = `let isRightSize = file.size / ${unitNum} < ${config.fileSize}
|
||||
if(!isRightSize){
|
||||
this.$message.error('文件大小超过 ${config.fileSize}${config.sizeUnit}')
|
||||
}`
|
||||
returnList.push('isRightSize')
|
||||
}
|
||||
if (scheme.accept) {
|
||||
acceptCode = `let isAccept = new RegExp('${scheme.accept}').test(file.type)
|
||||
if(!isAccept){
|
||||
this.$message.error('应该选择${scheme.accept}类型的文件')
|
||||
}`
|
||||
returnList.push('isAccept')
|
||||
}
|
||||
const str = `${scheme.__vModel__}BeforeUpload(file) {
|
||||
${rightSizeCode}
|
||||
${acceptCode}
|
||||
return ${returnList.join('&&')}
|
||||
},`
|
||||
return returnList.length ? str : ''
|
||||
}
|
||||
|
||||
// el-upload的submit
|
||||
function buildSubmitUpload(scheme) {
|
||||
const str = `submitUpload() {
|
||||
this.$refs['${scheme.__vModel__}'].submit()
|
||||
},`
|
||||
return str
|
||||
}
|
||||
|
||||
function buildOptionMethod(methodName, model, methodList) {
|
||||
const str = `${methodName}() {
|
||||
// TODO 发起请求获取数据
|
||||
this.${model}
|
||||
},`
|
||||
methodList.push(str)
|
||||
}
|
||||
|
||||
// js整体拼接
|
||||
function buildexport(conf, type, data, rules, selectOptions, uploadVar, props, methods) {
|
||||
const str = `${exportDefault}{
|
||||
${inheritAttrs[type]}
|
||||
components: {},
|
||||
props: [],
|
||||
data () {
|
||||
return {
|
||||
${conf.formModel}: {
|
||||
${data}
|
||||
},
|
||||
${conf.formRules}: {
|
||||
${rules}
|
||||
},
|
||||
${uploadVar}
|
||||
${selectOptions}
|
||||
${props}
|
||||
}
|
||||
},
|
||||
computed: {},
|
||||
watch: {},
|
||||
created () {},
|
||||
mounted () {},
|
||||
methods: {
|
||||
${methods}
|
||||
}
|
||||
}`
|
||||
return str
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
|
||||
/**
|
||||
* 用于生成表单校验,指定正则规则的触发方式。
|
||||
* 未在此处声明无触发方式的组件将不生成rule!!
|
||||
*/
|
||||
export default {
|
||||
'el-input': 'blur',
|
||||
'el-input-number': 'blur',
|
||||
'el-select': 'change',
|
||||
'el-radio-group': 'change',
|
||||
'el-checkbox-group': 'change',
|
||||
'el-cascader': 'change',
|
||||
'el-time-picker': 'change',
|
||||
'el-date-picker': 'change',
|
||||
'el-rate': 'change',
|
||||
tinymce: 'blur',
|
||||
'time-select': 'change'
|
||||
|
||||
}
|
||||
@@ -0,0 +1,235 @@
|
||||
<script>
|
||||
import render from '@/components/FormGenerator/components/render/render.js'
|
||||
|
||||
const ruleTrigger = {
|
||||
'el-input': 'blur',
|
||||
'el-input-number': 'blur',
|
||||
'el-select': 'change',
|
||||
'el-radio-group': 'change',
|
||||
'el-checkbox-group': 'change',
|
||||
'el-cascader': 'change',
|
||||
'el-time-picker': 'change',
|
||||
'el-date-picker': 'change',
|
||||
'el-rate': 'change'
|
||||
}
|
||||
|
||||
function renderFrom(h) {
|
||||
const { formConfCopy } = this
|
||||
return (
|
||||
<el-row gutter={formConfCopy.gutter}>
|
||||
<el-form
|
||||
size={formConfCopy.size}
|
||||
label-position={formConfCopy.labelPosition}
|
||||
disabled={formConfCopy.disabled}
|
||||
label-width={`${formConfCopy.labelWidth}px`}
|
||||
ref={formConfCopy.formRef}
|
||||
// model不能直接赋值 https://github.com/vuejs/jsx/issues/49#issuecomment-472013664
|
||||
props={{ model: this[formConfCopy.formModel] }}
|
||||
rules={this[formConfCopy.formRules]}
|
||||
>
|
||||
{renderFormItem.call(this, h, formConfCopy.fields)}
|
||||
{formConfCopy.formBtns && formBtns.call(this, h)}
|
||||
</el-form>
|
||||
</el-row>
|
||||
)
|
||||
}
|
||||
|
||||
function formBtns(h) {
|
||||
return <el-col>
|
||||
<el-form-item size='mini'>
|
||||
<el-button type='primary' onClick={this.submitForm}>提交</el-button>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
}
|
||||
|
||||
function renderFormItem(h, elementList) {
|
||||
return elementList.map(scheme => {
|
||||
const config = scheme.__config__
|
||||
const layout = layouts[config.layout]
|
||||
if (layout) {
|
||||
return layout.call(this, h, scheme)
|
||||
}
|
||||
throw new Error(`没有与${config.layout}匹配的layout`)
|
||||
})
|
||||
}
|
||||
|
||||
function renderChildren(h, scheme) {
|
||||
const config = scheme.__config__
|
||||
if (!Array.isArray(config.children)) return null
|
||||
return renderFormItem.call(this, h, config.children)
|
||||
}
|
||||
|
||||
function setValue(event, config, scheme) {
|
||||
this.$set(config, 'defaultValue', event)
|
||||
this.$set(this[this.formConf.formModel], scheme.__vModel__, event)
|
||||
}
|
||||
|
||||
function buildListeners(scheme) {
|
||||
const config = scheme.__config__
|
||||
const methods = this.formConf.__methods__ || {}
|
||||
const listeners = {}
|
||||
|
||||
// 给__methods__中的方法绑定this和event
|
||||
Object.keys(methods).forEach(key => {
|
||||
listeners[key] = event => methods[key].call(this, event)
|
||||
})
|
||||
// 响应 render.js 中的 vModel $emit('input', val)
|
||||
listeners.input = event => setValue.call(this, event, config, scheme)
|
||||
|
||||
return listeners
|
||||
}
|
||||
const layouts = {
|
||||
colFormItem(h, scheme) {
|
||||
const config = scheme.__config__
|
||||
const listeners = buildListeners.call(this, scheme)
|
||||
let labelWidth = config.labelWidth ? `${config.labelWidth}px` : null
|
||||
if (config.showLabel === false) labelWidth = '0'
|
||||
if(config.tips && !config.tipsIsLink){
|
||||
return (
|
||||
<el-col span={config.span}>
|
||||
<el-form-item label-width={labelWidth} prop={scheme.__vModel__}
|
||||
label={config.showLabel ? config.label : ''}>
|
||||
<el-tooltip effect="dark" placement="top-start" style="padding:10px 5px 0 0;">
|
||||
<i class="el-icon-warning-outline" />
|
||||
<div slot="content" style="max-width:400px;">{config.tipsDesc}</div>
|
||||
</el-tooltip>
|
||||
<render conf={scheme} {...{ on: listeners }} />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
)
|
||||
}else if(config.tips && config.tipsIsLink){
|
||||
return (
|
||||
<el-col span={config.span}>
|
||||
<el-form-item label-width={labelWidth} prop={scheme.__vModel__}
|
||||
label={config.showLabel ? config.label : ''}>
|
||||
<el-tooltip effect="dark" placement="top-start" style="padding:10px 5px 0 0;">
|
||||
<i class="el-icon-warning-outline" />
|
||||
<div slot="content" style="max-width:400px;">
|
||||
<a href={config.tipsLink} target="_blank">{config.tipsDesc}</a>
|
||||
</div>
|
||||
</el-tooltip>
|
||||
<render conf={scheme} {...{ on: listeners }} />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
)
|
||||
}else{
|
||||
return (
|
||||
<el-col span={config.span}>
|
||||
<el-form-item label-width={labelWidth} prop={scheme.__vModel__}
|
||||
label={config.showLabel ? config.label : ''}>
|
||||
<render conf={scheme} {...{ on: listeners }} />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
)
|
||||
}
|
||||
|
||||
},
|
||||
rowFormItem(h, scheme) {
|
||||
let child = renderChildren.apply(this, arguments)
|
||||
if (scheme.type === 'flex') {
|
||||
child = <el-row type={scheme.type} justify={scheme.justify} align={scheme.align}>
|
||||
{child}
|
||||
</el-row>
|
||||
}
|
||||
return (
|
||||
<el-col span={scheme.span}>
|
||||
<el-row gutter={scheme.gutter}>
|
||||
{child}
|
||||
</el-row>
|
||||
</el-col>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
components: {
|
||||
render
|
||||
},
|
||||
props: {
|
||||
formConf: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
formEditData: {
|
||||
type: Object
|
||||
},
|
||||
isEdit: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data() {
|
||||
if (this.isEdit) { // 初始化待编辑数据
|
||||
this.formConf.fields.forEach(conf => {
|
||||
// 设置现有的数据
|
||||
const hasValueForEdit = this.formEditData[conf.__vModel__]
|
||||
if(hasValueForEdit){
|
||||
conf.__config__.defaultValue = hasValueForEdit
|
||||
}
|
||||
// 如果是el-select标签 判断数据后改变实现默认选中效果
|
||||
if(conf.__config__.tag === 'el-select' || conf.__config__.tag === 'el-radio-group'){
|
||||
const perValue = conf.__slot__.options.filter(option => option.value == this.formEditData[conf.__vModel__])
|
||||
if(perValue.length > 0){ // 有表单数据
|
||||
conf.__config__.defaultValue = perValue[0].value
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
const data = {
|
||||
formConfCopy: JSON.parse(JSON.stringify(this.formConf)),
|
||||
[this.formConf.formModel]: {},
|
||||
[this.formConf.formRules]: {}
|
||||
}
|
||||
this.initFormData(data.formConfCopy.fields, data[this.formConf.formModel])
|
||||
this.buildRules(data.formConfCopy.fields, data[this.formConf.formRules])
|
||||
return data
|
||||
},
|
||||
methods: {
|
||||
initFormData(componentList, formData) {
|
||||
componentList.forEach(cur => {
|
||||
const config = cur.__config__
|
||||
if (cur.__vModel__) formData[cur.__vModel__] = config.defaultValue
|
||||
if (config.children) this.initFormData(config.children, formData)
|
||||
})
|
||||
},
|
||||
buildRules(componentList, rules) {
|
||||
componentList.forEach(cur => {
|
||||
const config = cur.__config__
|
||||
if (Array.isArray(config.regList)) {
|
||||
if (config.required) {
|
||||
const required = { required: config.required, message: cur.placeholder }
|
||||
if (Array.isArray(config.defaultValue)) {
|
||||
required.type = 'array'
|
||||
required.message = `请至少选择一个${config.label}`
|
||||
}
|
||||
required.message === undefined && (required.message = `${config.label}不能为空`)
|
||||
config.regList.push(required)
|
||||
}
|
||||
rules[cur.__vModel__] = config.regList.map(item => {
|
||||
item.pattern && (item.pattern = eval(item.pattern))
|
||||
item.trigger = ruleTrigger && ruleTrigger[config.tag]
|
||||
return item
|
||||
})
|
||||
}
|
||||
if (config.children) this.buildRules(config.children, rules)
|
||||
})
|
||||
},
|
||||
resetForm() {
|
||||
this.$emit('resetForm', this.formConf)
|
||||
this.formConfCopy = JSON.parse(JSON.stringify(this.formConf))
|
||||
this.$refs[this.formConf.formRef].resetFields()
|
||||
},
|
||||
submitForm() {
|
||||
this.$refs[this.formConf.formRef].validate(valid => {
|
||||
if (!valid) return false
|
||||
// 触发sumit事件
|
||||
this.$emit('submit', this[this.formConf.formModel])
|
||||
return true
|
||||
})
|
||||
}
|
||||
},
|
||||
render(h) {
|
||||
return renderFrom.call(this, h)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,17 @@
|
||||
## form-generator JSON 解析器
|
||||
>用于将form-generator导出的JSON解析成一个表单。
|
||||
|
||||
### 安装组件
|
||||
```
|
||||
npm i form-gen-parser
|
||||
```
|
||||
或者
|
||||
```
|
||||
yarn add form-gen-parser
|
||||
```
|
||||
|
||||
### 使用示例
|
||||
> [查看在线示例](https://mrhj.gitee.io/form-generator/#/parser)
|
||||
|
||||
示例代码:
|
||||
> [src\components\parser\example\Index.vue](https://github.com/JakHuang/form-generator/blob/dev/src/components/parser/example/Index.vue)
|
||||
@@ -0,0 +1,74 @@
|
||||
<template>
|
||||
<div>
|
||||
<parser
|
||||
v-if="formConf.fields.length > 0"
|
||||
v-loading="loading"
|
||||
:is-edit="isCreate === 1"
|
||||
:form-conf="formConf"
|
||||
:form-edit-data="editData"
|
||||
@submit="handlerSubmit"
|
||||
@resetForm="resetForm"
|
||||
/>
|
||||
<!-- editData:{{ editData }}-->
|
||||
<!-- formConf:{{ formConf }}-->
|
||||
<!-- isCreate:{{ isCreate }}-->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/**
|
||||
* 注意:和Parser唯一的区别就是这里仅仅传入表单配置id即可自动加载已配置的表单
|
||||
* 数据后渲染表单,
|
||||
* 其他业务和Parser保持一致
|
||||
*/
|
||||
import * as systemFormConfigApi from '@/api/systemFormConfig.js'
|
||||
import parser from '@/components/FormGenerator/components/parser/Parser'
|
||||
export default {
|
||||
// name: "ZBParser"
|
||||
components: { parser },
|
||||
props: {
|
||||
formId: {
|
||||
type: Number,
|
||||
required: true
|
||||
},
|
||||
isCreate: {
|
||||
type: Number,
|
||||
default: 0 // 0=create 1=edit
|
||||
},
|
||||
editData: {
|
||||
type: Object
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
formConf: { fields: [] }
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.handlerGetFormConfig(this.formId)
|
||||
},
|
||||
methods: {
|
||||
handlerGetFormConfig(formId) { // 获取表单配置后生成table列
|
||||
this.loading = true
|
||||
const _pram = { id: formId }
|
||||
systemFormConfigApi.getFormConfigInfo(_pram).then(data => {
|
||||
this.formConf = JSON.parse(data.content)
|
||||
this.loading = false
|
||||
}).catch(()=>{
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
handlerSubmit(formValue) {
|
||||
this.$emit('submit', formValue)
|
||||
},
|
||||
resetForm(formValue){
|
||||
this.$emit('resetForm', formValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,264 @@
|
||||
<template>
|
||||
<div class="test-form">
|
||||
<parser :form-conf="formConf" @submit="sumbitForm1" />
|
||||
<parser :key="key2" :form-conf="formConf" @submit="sumbitForm2" />
|
||||
<el-button @click="change">
|
||||
change
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Parser from '../Parser'
|
||||
|
||||
// 若parser是通过安装npm方式集成到项目中的,使用此行引入
|
||||
// import Parser from 'form-gen-parser'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Parser
|
||||
},
|
||||
props: {},
|
||||
data() {
|
||||
return {
|
||||
key2: +new Date(),
|
||||
formConf: {
|
||||
fields: [
|
||||
{
|
||||
__config__: {
|
||||
label: '单行文本',
|
||||
labelWidth: null,
|
||||
showLabel: true,
|
||||
changeTag: true,
|
||||
tag: 'el-input',
|
||||
tagIcon: 'input',
|
||||
required: true,
|
||||
layout: 'colFormItem',
|
||||
span: 24,
|
||||
document: 'https://element.eleme.cn/#/zh-CN/component/input',
|
||||
regList: [
|
||||
{
|
||||
pattern: '/^1(3|4|5|7|8|9)\\d{9}$/',
|
||||
message: '手机号格式错误'
|
||||
}
|
||||
]
|
||||
},
|
||||
__slot__: {
|
||||
prepend: '',
|
||||
append: ''
|
||||
},
|
||||
__vModel__: 'mobile',
|
||||
placeholder: '请输入手机号',
|
||||
style: {
|
||||
width: '100%'
|
||||
},
|
||||
clearable: true,
|
||||
'prefix-icon': 'el-icon-mobile',
|
||||
'suffix-icon': '',
|
||||
maxlength: 11,
|
||||
'show-word-limit': true,
|
||||
readonly: false,
|
||||
disabled: false
|
||||
},
|
||||
{
|
||||
__config__: {
|
||||
label: '日期范围',
|
||||
tag: 'el-date-picker',
|
||||
tagIcon: 'date-range',
|
||||
defaultValue: null,
|
||||
span: 24,
|
||||
showLabel: true,
|
||||
labelWidth: null,
|
||||
required: true,
|
||||
layout: 'colFormItem',
|
||||
regList: [],
|
||||
changeTag: true,
|
||||
document:
|
||||
'https://element.eleme.cn/#/zh-CN/component/date-picker',
|
||||
formId: 101,
|
||||
renderKey: 1585980082729
|
||||
},
|
||||
style: {
|
||||
width: '100%'
|
||||
},
|
||||
type: 'daterange',
|
||||
'range-separator': '至',
|
||||
'start-placeholder': '开始日期',
|
||||
'end-placeholder': '结束日期',
|
||||
disabled: false,
|
||||
clearable: true,
|
||||
format: 'yyyy-MM-dd',
|
||||
'value-format': 'yyyy-MM-dd',
|
||||
readonly: false,
|
||||
__vModel__: 'field101'
|
||||
},
|
||||
{
|
||||
__config__: {
|
||||
layout: 'rowFormItem',
|
||||
tagIcon: 'row',
|
||||
label: '行容器',
|
||||
layoutTree: true,
|
||||
children: [
|
||||
{
|
||||
__config__: {
|
||||
label: '评分',
|
||||
tag: 'el-rate',
|
||||
tagIcon: 'rate',
|
||||
defaultValue: 0,
|
||||
span: 24,
|
||||
showLabel: true,
|
||||
labelWidth: null,
|
||||
layout: 'colFormItem',
|
||||
required: true,
|
||||
regList: [],
|
||||
changeTag: true,
|
||||
document: 'https://element.eleme.cn/#/zh-CN/component/rate',
|
||||
formId: 102,
|
||||
renderKey: 1586839671259
|
||||
},
|
||||
style: {},
|
||||
max: 5,
|
||||
'allow-half': false,
|
||||
'show-text': false,
|
||||
'show-score': false,
|
||||
disabled: false,
|
||||
__vModel__: 'field102'
|
||||
}
|
||||
],
|
||||
document: 'https://element.eleme.cn/#/zh-CN/component/layout',
|
||||
formId: 101,
|
||||
span: 24,
|
||||
renderKey: 1586839668999,
|
||||
componentName: 'row101',
|
||||
gutter: 15
|
||||
},
|
||||
type: 'default',
|
||||
justify: 'start',
|
||||
align: 'top'
|
||||
}
|
||||
],
|
||||
formRef: 'elForm',
|
||||
formModel: 'formData',
|
||||
size: 'small',
|
||||
labelPosition: 'right',
|
||||
labelWidth: 100,
|
||||
formRules: 'rules',
|
||||
gutter: 15,
|
||||
disabled: false,
|
||||
span: 24,
|
||||
formBtns: true,
|
||||
unFocusedComponentBorder: false
|
||||
},
|
||||
formConf2: {
|
||||
fields: [
|
||||
{
|
||||
__config__: {
|
||||
label: '单行文本',
|
||||
labelWidth: null,
|
||||
showLabel: true,
|
||||
changeTag: true,
|
||||
tag: 'el-input',
|
||||
tagIcon: 'input',
|
||||
required: true,
|
||||
layout: 'colFormItem',
|
||||
span: 24,
|
||||
document: 'https://element.eleme.cn/#/zh-CN/component/input',
|
||||
regList: [
|
||||
{
|
||||
pattern: '/^1(3|4|5|7|8|9)\\d{9}$/',
|
||||
message: '手机号格式错误'
|
||||
}
|
||||
]
|
||||
},
|
||||
__slot__: {
|
||||
prepend: '',
|
||||
append: ''
|
||||
},
|
||||
__vModel__: 'mobile',
|
||||
placeholder: '请输入手机号',
|
||||
style: {
|
||||
width: '100%'
|
||||
},
|
||||
clearable: true,
|
||||
'prefix-icon': 'el-icon-mobile',
|
||||
'suffix-icon': '',
|
||||
maxlength: 11,
|
||||
'show-word-limit': true,
|
||||
readonly: false,
|
||||
disabled: false
|
||||
},
|
||||
{
|
||||
__config__: {
|
||||
label: '日期范围',
|
||||
tag: 'el-date-picker',
|
||||
tagIcon: 'date-range',
|
||||
defaultValue: null,
|
||||
span: 24,
|
||||
showLabel: true,
|
||||
labelWidth: null,
|
||||
required: true,
|
||||
layout: 'colFormItem',
|
||||
regList: [],
|
||||
changeTag: true,
|
||||
document:
|
||||
'https://element.eleme.cn/#/zh-CN/component/date-picker',
|
||||
formId: 101,
|
||||
renderKey: 1585980082729
|
||||
},
|
||||
style: {
|
||||
width: '100%'
|
||||
},
|
||||
type: 'daterange',
|
||||
'range-separator': '至',
|
||||
'start-placeholder': '开始日期',
|
||||
'end-placeholder': '结束日期',
|
||||
disabled: false,
|
||||
clearable: true,
|
||||
format: 'yyyy-MM-dd',
|
||||
'value-format': 'yyyy-MM-dd',
|
||||
readonly: false,
|
||||
__vModel__: 'field101'
|
||||
}
|
||||
],
|
||||
formRef: 'elForm',
|
||||
formModel: 'formData',
|
||||
size: 'small',
|
||||
labelPosition: 'right',
|
||||
labelWidth: 100,
|
||||
formRules: 'rules',
|
||||
gutter: 15,
|
||||
disabled: false,
|
||||
span: 24,
|
||||
formBtns: true,
|
||||
unFocusedComponentBorder: false
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {},
|
||||
watch: {},
|
||||
created() {},
|
||||
mounted() {},
|
||||
methods: {
|
||||
change() {
|
||||
this.key2 = +new Date()
|
||||
const t = this.formConf
|
||||
this.formConf = this.formConf2
|
||||
this.formConf2 = t
|
||||
},
|
||||
sumbitForm1(data) {
|
||||
console.log('sumbitForm1提交数据:', data)
|
||||
},
|
||||
sumbitForm2(data) {
|
||||
console.log('sumbitForm2提交数据:', data)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.test-form {
|
||||
margin: 15px auto;
|
||||
width: 800px;
|
||||
padding: 15px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,3 @@
|
||||
import Parser from './Parser'
|
||||
|
||||
export default Parser
|
||||
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"name": "form-gen-parser",
|
||||
"version": "1.0.3",
|
||||
"description": "表单json解析器",
|
||||
"main": "lib/form-gen-parser.umd.js",
|
||||
"directories": {
|
||||
"example": "example"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/JakHuang/form-generator.git"
|
||||
},
|
||||
"dependencies": {
|
||||
"form-gen-render": "^1.0.0"
|
||||
},
|
||||
"author": "jakHuang",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/JakHuang/form-generator/issues"
|
||||
},
|
||||
"homepage": "https://github.com/JakHuang/form-generator#readme"
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"name": "form-gen-render",
|
||||
"version": "1.0.4",
|
||||
"description": "表单核心render",
|
||||
"main": "lib/form-gen-render.umd.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/JakHuang/form-generator.git"
|
||||
},
|
||||
"author": "jakhuang",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/JakHuang/form-generator/issues"
|
||||
},
|
||||
"homepage": "https://github.com/JakHuang/form-generator#readme"
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
function vModel(self, dataObject, defaultValue) {
|
||||
dataObject.props.value = defaultValue
|
||||
|
||||
dataObject.on.input = val => {
|
||||
self.$emit('input', val)
|
||||
}
|
||||
}
|
||||
|
||||
const componentChild = {}
|
||||
/**
|
||||
* 将./slots中的文件挂载到对象componentChild上
|
||||
* 文件名为key,对应JSON配置中的__config__.tag
|
||||
* 文件内容为value,解析JSON配置中的__slot__
|
||||
*/
|
||||
const slotsFiles = require.context('./slots', true, /\.js$/)
|
||||
const keys = slotsFiles.keys() || []
|
||||
keys.forEach(key => {
|
||||
const tag = key.replace(/^\.\/(.*)\.\w+$/, '$1')
|
||||
const value = slotsFiles(key).default
|
||||
componentChild[tag] = value
|
||||
})
|
||||
|
||||
export default {
|
||||
render(h) {
|
||||
const dataObject = {
|
||||
attrs: {},
|
||||
props: {},
|
||||
on: {},
|
||||
style: {}
|
||||
}
|
||||
const confClone = JSON.parse(JSON.stringify(this.conf))
|
||||
const children = []
|
||||
|
||||
const childObjs = componentChild[confClone.__config__.tag]
|
||||
if (childObjs) {
|
||||
Object.keys(childObjs).forEach(key => {
|
||||
const childFunc = childObjs[key]
|
||||
if (confClone.__slot__ && confClone.__slot__[key]) {
|
||||
children.push(childFunc(h, confClone, key))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
Object.keys(confClone).forEach(key => {
|
||||
const val = confClone[key]
|
||||
if (key === '__vModel__') {
|
||||
vModel(this, dataObject, confClone.__config__.defaultValue)
|
||||
} else if (dataObject[key]) {
|
||||
dataObject[key] = { ...dataObject[key], ...val }
|
||||
} else {
|
||||
dataObject.attrs[key] = val
|
||||
}
|
||||
})
|
||||
delete dataObject.attrs.__config__
|
||||
delete dataObject.attrs.__slot__
|
||||
return h(this.conf.__config__.tag, dataObject, children)
|
||||
},
|
||||
props: ['conf']
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
export default {
|
||||
default(h, conf, key) {
|
||||
return conf.__slot__[key]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
export default {
|
||||
options(h, conf, key) {
|
||||
const list = []
|
||||
conf.__slot__.options.forEach(item => {
|
||||
if (conf.__config__.optionType === 'button') {
|
||||
list.push(<el-checkbox-button label={item.value}>{item.label}</el-checkbox-button>)
|
||||
} else {
|
||||
list.push(<el-checkbox label={item.value} border={conf.border}>{item.label}</el-checkbox>)
|
||||
}
|
||||
})
|
||||
return list
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
export default {
|
||||
prepend(h, conf, key) {
|
||||
return <template slot='prepend'>{conf.__slot__[key]}</template>
|
||||
},
|
||||
append(h, conf, key) {
|
||||
return <template slot='append'>{conf.__slot__[key]}</template>
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
export default {
|
||||
options(h, conf, key) {
|
||||
const list = []
|
||||
conf.__slot__.options.forEach(item => {
|
||||
if (conf.__config__.optionType === 'button') {
|
||||
list.push(<el-radio-button label={item.value}>{item.label}</el-radio-button>)
|
||||
} else {
|
||||
list.push(<el-radio label={item.value} border={conf.border}>{item.label}</el-radio>)
|
||||
}
|
||||
})
|
||||
return list
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
export default {
|
||||
options(h, conf, key) {
|
||||
const list = []
|
||||
conf.__slot__.options.forEach(item => {
|
||||
list.push(<el-option label={item.label} value={item.value} disabled={item.disabled}></el-option>)
|
||||
})
|
||||
return list
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
export default {
|
||||
'list-type': (h, conf, key) => {
|
||||
const list = []
|
||||
const config = conf.__config__
|
||||
if (conf['list-type'] === 'picture-card') {
|
||||
list.push(<i class='el-icon-plus'></i>)
|
||||
} else {
|
||||
list.push(<el-button size='small' type='primary' icon='el-icon-upload'>{config.buttonText}</el-button>)
|
||||
}
|
||||
if (config.showTip) {
|
||||
list.push(
|
||||
<div slot='tip' class='el-upload__tip'>只能上传不超过 {config.fileSize}{config.sizeUnit} 的{conf.accept}文件</div>
|
||||
)
|
||||
}
|
||||
return list
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
/* eslint-disable max-len */
|
||||
|
||||
export const plugins = [
|
||||
'advlist anchor autolink autosave code codesample directionality emoticons fullscreen hr image imagetools insertdatetime link lists media nonbreaking noneditable pagebreak paste preview print save searchreplace spellchecker tabfocus table template textpattern visualblocks visualchars wordcount'
|
||||
]
|
||||
export const toolbar = [
|
||||
'code searchreplace bold italic underline strikethrough alignleft aligncenter alignright outdent indent blockquote removeformat subscript superscript codesample hr bullist numlist link image charmap preview anchor pagebreak insertdatetime media table emoticons forecolor backcolor fullscreen'
|
||||
]
|
||||
@@ -0,0 +1,3 @@
|
||||
import Index from './index.vue'
|
||||
|
||||
export default Index
|
||||
@@ -0,0 +1,93 @@
|
||||
<template>
|
||||
<textarea :id="tinymceId" class="textarea" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import loadTinymce from '@/components/FormGenerator/utils/loadTinymce'
|
||||
import { plugins, toolbar } from './config'
|
||||
import { debounce } from 'throttle-debounce'
|
||||
|
||||
let num = 1
|
||||
|
||||
export default {
|
||||
props: {
|
||||
id: {
|
||||
type: String,
|
||||
default: () => {
|
||||
num === 10000 && (num = 1)
|
||||
return `tinymce${+new Date()}${num++}`
|
||||
}
|
||||
},
|
||||
value: {
|
||||
type: [String, Number, Boolean],
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
tinymceId: this.id
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
loadTinymce(tinymce => {
|
||||
import('./zh_CN').then(() => {
|
||||
tinymce.init({
|
||||
selector: `#${this.tinymceId}`,
|
||||
language: 'zh_CN',
|
||||
menubar: 'file edit insert view format table',
|
||||
plugins,
|
||||
toolbar,
|
||||
height: this.$attrs.height || 300,
|
||||
branding: this.$attrs.branding || false,
|
||||
object_resizing: false,
|
||||
end_container_on_empty_block: true,
|
||||
powerpaste_word_import: 'clean',
|
||||
code_dialog_height: 450,
|
||||
code_dialog_width: 1000,
|
||||
advlist_bullet_styles: 'square',
|
||||
advlist_number_styles: 'default',
|
||||
default_link_target: '_blank',
|
||||
link_title: false,
|
||||
nonbreaking_force_tab: true,
|
||||
init_instance_callback: editor => {
|
||||
if (this.value) editor.setContent(this.value)
|
||||
this.vModel(editor)
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
},
|
||||
destroyed() {
|
||||
this.destroyTinymce()
|
||||
},
|
||||
methods: {
|
||||
vModel(editor) {
|
||||
// 控制连续写入时setContent的触发频率
|
||||
const debounceSetContent = debounce(250, editor.setContent)
|
||||
this.$watch('value', (val, prevVal) => {
|
||||
if (editor && val !== prevVal && val !== editor.getContent()) {
|
||||
if (typeof val !== 'string') val = val.toString()
|
||||
debounceSetContent.call(editor, val)
|
||||
}
|
||||
})
|
||||
|
||||
editor.on('change keyup undo redo', () => {
|
||||
this.$emit('input', editor.getContent())
|
||||
})
|
||||
},
|
||||
destroyTinymce() {
|
||||
if (!window.tinymce) return
|
||||
const tinymce = window.tinymce.get(this.tinymceId)
|
||||
if (tinymce) {
|
||||
tinymce.destroy()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.textarea{
|
||||
visibility: hidden;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,420 @@
|
||||
/* eslint-disable */
|
||||
tinymce.addI18n('zh_CN',{
|
||||
"Redo": "\u91cd\u505a",
|
||||
"Undo": "\u64a4\u9500",
|
||||
"Cut": "\u526a\u5207",
|
||||
"Copy": "\u590d\u5236",
|
||||
"Paste": "\u7c98\u8d34",
|
||||
"Select all": "\u5168\u9009",
|
||||
"New document": "\u65b0\u6587\u4ef6",
|
||||
"Ok": "\u786e\u5b9a",
|
||||
"Cancel": "\u53d6\u6d88",
|
||||
"Visual aids": "\u7f51\u683c\u7ebf",
|
||||
"Bold": "\u7c97\u4f53",
|
||||
"Italic": "\u659c\u4f53",
|
||||
"Underline": "\u4e0b\u5212\u7ebf",
|
||||
"Strikethrough": "\u5220\u9664\u7ebf",
|
||||
"Superscript": "\u4e0a\u6807",
|
||||
"Subscript": "\u4e0b\u6807",
|
||||
"Clear formatting": "\u6e05\u9664\u683c\u5f0f",
|
||||
"Align left": "\u5de6\u8fb9\u5bf9\u9f50",
|
||||
"Align center": "\u4e2d\u95f4\u5bf9\u9f50",
|
||||
"Align right": "\u53f3\u8fb9\u5bf9\u9f50",
|
||||
"Justify": "\u4e24\u7aef\u5bf9\u9f50",
|
||||
"Bullet list": "\u9879\u76ee\u7b26\u53f7",
|
||||
"Numbered list": "\u7f16\u53f7\u5217\u8868",
|
||||
"Decrease indent": "\u51cf\u5c11\u7f29\u8fdb",
|
||||
"Increase indent": "\u589e\u52a0\u7f29\u8fdb",
|
||||
"Close": "\u5173\u95ed",
|
||||
"Formats": "\u683c\u5f0f",
|
||||
"Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X\/C\/V keyboard shortcuts instead.": "\u4f60\u7684\u6d4f\u89c8\u5668\u4e0d\u652f\u6301\u6253\u5f00\u526a\u8d34\u677f\uff0c\u8bf7\u4f7f\u7528Ctrl+X\/C\/V\u7b49\u5feb\u6377\u952e\u3002",
|
||||
"Headers": "\u6807\u9898",
|
||||
"Header 1": "\u6807\u98981",
|
||||
"Header 2": "\u6807\u98982",
|
||||
"Header 3": "\u6807\u98983",
|
||||
"Header 4": "\u6807\u98984",
|
||||
"Header 5": "\u6807\u98985",
|
||||
"Header 6": "\u6807\u98986",
|
||||
"Headings": "\u6807\u9898",
|
||||
"Heading 1": "\u6807\u98981",
|
||||
"Heading 2": "\u6807\u98982",
|
||||
"Heading 3": "\u6807\u98983",
|
||||
"Heading 4": "\u6807\u98984",
|
||||
"Heading 5": "\u6807\u98985",
|
||||
"Heading 6": "\u6807\u98986",
|
||||
"Preformatted": "\u9884\u5148\u683c\u5f0f\u5316\u7684",
|
||||
"Div": "Div",
|
||||
"Pre": "Pre",
|
||||
"Code": "\u4ee3\u7801",
|
||||
"Paragraph": "\u6bb5\u843d",
|
||||
"Blockquote": "\u5f15\u6587\u533a\u5757",
|
||||
"Inline": "\u6587\u672c",
|
||||
"Blocks": "\u57fa\u5757",
|
||||
"Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.": "\u5f53\u524d\u4e3a\u7eaf\u6587\u672c\u7c98\u8d34\u6a21\u5f0f\uff0c\u518d\u6b21\u70b9\u51fb\u53ef\u4ee5\u56de\u5230\u666e\u901a\u7c98\u8d34\u6a21\u5f0f\u3002",
|
||||
"Fonts": "\u5b57\u4f53",
|
||||
"Font Sizes": "\u5b57\u53f7",
|
||||
"Class": "\u7c7b\u578b",
|
||||
"Browse for an image": "\u6d4f\u89c8\u56fe\u50cf",
|
||||
"OR": "\u6216",
|
||||
"Drop an image here": "\u62d6\u653e\u4e00\u5f20\u56fe\u50cf\u81f3\u6b64",
|
||||
"Upload": "\u4e0a\u4f20",
|
||||
"Block": "\u5757",
|
||||
"Align": "\u5bf9\u9f50",
|
||||
"Default": "\u9ed8\u8ba4",
|
||||
"Circle": "\u7a7a\u5fc3\u5706",
|
||||
"Disc": "\u5b9e\u5fc3\u5706",
|
||||
"Square": "\u65b9\u5757",
|
||||
"Lower Alpha": "\u5c0f\u5199\u82f1\u6587\u5b57\u6bcd",
|
||||
"Lower Greek": "\u5c0f\u5199\u5e0c\u814a\u5b57\u6bcd",
|
||||
"Lower Roman": "\u5c0f\u5199\u7f57\u9a6c\u5b57\u6bcd",
|
||||
"Upper Alpha": "\u5927\u5199\u82f1\u6587\u5b57\u6bcd",
|
||||
"Upper Roman": "\u5927\u5199\u7f57\u9a6c\u5b57\u6bcd",
|
||||
"Anchor...": "\u951a\u70b9...",
|
||||
"Name": "\u540d\u79f0",
|
||||
"Id": "\u6807\u8bc6\u7b26",
|
||||
"Id should start with a letter, followed only by letters, numbers, dashes, dots, colons or underscores.": "\u6807\u8bc6\u7b26\u5e94\u8be5\u4ee5\u5b57\u6bcd\u5f00\u5934\uff0c\u540e\u8ddf\u5b57\u6bcd\u3001\u6570\u5b57\u3001\u7834\u6298\u53f7\u3001\u70b9\u3001\u5192\u53f7\u6216\u4e0b\u5212\u7ebf\u3002",
|
||||
"You have unsaved changes are you sure you want to navigate away?": "\u4f60\u8fd8\u6709\u6587\u6863\u5c1a\u672a\u4fdd\u5b58\uff0c\u786e\u5b9a\u8981\u79bb\u5f00\uff1f",
|
||||
"Restore last draft": "\u6062\u590d\u4e0a\u6b21\u7684\u8349\u7a3f",
|
||||
"Special character...": "\u7279\u6b8a\u5b57\u7b26...",
|
||||
"Source code": "\u6e90\u4ee3\u7801",
|
||||
"Insert\/Edit code sample": "\u63d2\u5165\/\u7f16\u8f91\u4ee3\u7801\u793a\u4f8b",
|
||||
"Language": "\u8bed\u8a00",
|
||||
"Code sample...": "\u793a\u4f8b\u4ee3\u7801...",
|
||||
"Color Picker": "\u9009\u8272\u5668",
|
||||
"R": "R",
|
||||
"G": "G",
|
||||
"B": "B",
|
||||
"Left to right": "\u4ece\u5de6\u5230\u53f3",
|
||||
"Right to left": "\u4ece\u53f3\u5230\u5de6",
|
||||
"Emoticons...": "\u8868\u60c5\u7b26\u53f7...",
|
||||
"Metadata and Document Properties": "\u5143\u6570\u636e\u548c\u6587\u6863\u5c5e\u6027",
|
||||
"Title": "\u6807\u9898",
|
||||
"Keywords": "\u5173\u952e\u8bcd",
|
||||
"Description": "\u63cf\u8ff0",
|
||||
"Robots": "\u673a\u5668\u4eba",
|
||||
"Author": "\u4f5c\u8005",
|
||||
"Encoding": "\u7f16\u7801",
|
||||
"Fullscreen": "\u5168\u5c4f",
|
||||
"Action": "\u64cd\u4f5c",
|
||||
"Shortcut": "\u5feb\u6377\u952e",
|
||||
"Help": "\u5e2e\u52a9",
|
||||
"Address": "\u5730\u5740",
|
||||
"Focus to menubar": "\u79fb\u52a8\u7126\u70b9\u5230\u83dc\u5355\u680f",
|
||||
"Focus to toolbar": "\u79fb\u52a8\u7126\u70b9\u5230\u5de5\u5177\u680f",
|
||||
"Focus to element path": "\u79fb\u52a8\u7126\u70b9\u5230\u5143\u7d20\u8def\u5f84",
|
||||
"Focus to contextual toolbar": "\u79fb\u52a8\u7126\u70b9\u5230\u4e0a\u4e0b\u6587\u83dc\u5355",
|
||||
"Insert link (if link plugin activated)": "\u63d2\u5165\u94fe\u63a5 (\u5982\u679c\u94fe\u63a5\u63d2\u4ef6\u5df2\u6fc0\u6d3b)",
|
||||
"Save (if save plugin activated)": "\u4fdd\u5b58(\u5982\u679c\u4fdd\u5b58\u63d2\u4ef6\u5df2\u6fc0\u6d3b)",
|
||||
"Find (if searchreplace plugin activated)": "\u67e5\u627e(\u5982\u679c\u67e5\u627e\u66ff\u6362\u63d2\u4ef6\u5df2\u6fc0\u6d3b)",
|
||||
"Plugins installed ({0}):": "\u5df2\u5b89\u88c5\u63d2\u4ef6 ({0}):",
|
||||
"Premium plugins:": "\u4f18\u79c0\u63d2\u4ef6\uff1a",
|
||||
"Learn more...": "\u4e86\u89e3\u66f4\u591a...",
|
||||
"You are using {0}": "\u4f60\u6b63\u5728\u4f7f\u7528 {0}",
|
||||
"Plugins": "\u63d2\u4ef6",
|
||||
"Handy Shortcuts": "\u5feb\u6377\u952e",
|
||||
"Horizontal line": "\u6c34\u5e73\u5206\u5272\u7ebf",
|
||||
"Insert\/edit image": "\u63d2\u5165\/\u7f16\u8f91\u56fe\u7247",
|
||||
"Image description": "\u56fe\u7247\u63cf\u8ff0",
|
||||
"Source": "\u5730\u5740",
|
||||
"Dimensions": "\u5927\u5c0f",
|
||||
"Constrain proportions": "\u4fdd\u6301\u7eb5\u6a2a\u6bd4",
|
||||
"General": "\u666e\u901a",
|
||||
"Advanced": "\u9ad8\u7ea7",
|
||||
"Style": "\u6837\u5f0f",
|
||||
"Vertical space": "\u5782\u76f4\u8fb9\u8ddd",
|
||||
"Horizontal space": "\u6c34\u5e73\u8fb9\u8ddd",
|
||||
"Border": "\u8fb9\u6846",
|
||||
"Insert image": "\u63d2\u5165\u56fe\u7247",
|
||||
"Image...": "\u56fe\u7247...",
|
||||
"Image list": "\u56fe\u7247\u5217\u8868",
|
||||
"Rotate counterclockwise": "\u9006\u65f6\u9488\u65cb\u8f6c",
|
||||
"Rotate clockwise": "\u987a\u65f6\u9488\u65cb\u8f6c",
|
||||
"Flip vertically": "\u5782\u76f4\u7ffb\u8f6c",
|
||||
"Flip horizontally": "\u6c34\u5e73\u7ffb\u8f6c",
|
||||
"Edit image": "\u7f16\u8f91\u56fe\u7247",
|
||||
"Image options": "\u56fe\u7247\u9009\u9879",
|
||||
"Zoom in": "\u653e\u5927",
|
||||
"Zoom out": "\u7f29\u5c0f",
|
||||
"Crop": "\u88c1\u526a",
|
||||
"Resize": "\u8c03\u6574\u5927\u5c0f",
|
||||
"Orientation": "\u65b9\u5411",
|
||||
"Brightness": "\u4eae\u5ea6",
|
||||
"Sharpen": "\u9510\u5316",
|
||||
"Contrast": "\u5bf9\u6bd4\u5ea6",
|
||||
"Color levels": "\u989c\u8272\u5c42\u6b21",
|
||||
"Gamma": "\u4f3d\u9a6c\u503c",
|
||||
"Invert": "\u53cd\u8f6c",
|
||||
"Apply": "\u5e94\u7528",
|
||||
"Back": "\u540e\u9000",
|
||||
"Insert date\/time": "\u63d2\u5165\u65e5\u671f\/\u65f6\u95f4",
|
||||
"Date\/time": "\u65e5\u671f\/\u65f6\u95f4",
|
||||
"Insert\/Edit Link": "\u63d2\u5165\/\u7f16\u8f91\u94fe\u63a5",
|
||||
"Insert\/edit link": "\u63d2\u5165\/\u7f16\u8f91\u94fe\u63a5",
|
||||
"Text to display": "\u663e\u793a\u6587\u5b57",
|
||||
"Url": "\u5730\u5740",
|
||||
"Open link in...": "\u94fe\u63a5\u6253\u5f00\u4f4d\u7f6e...",
|
||||
"Current window": "\u5f53\u524d\u7a97\u53e3",
|
||||
"None": "\u65e0",
|
||||
"New window": "\u5728\u65b0\u7a97\u53e3\u6253\u5f00",
|
||||
"Remove link": "\u5220\u9664\u94fe\u63a5",
|
||||
"Anchors": "\u951a\u70b9",
|
||||
"Link...": "\u94fe\u63a5...",
|
||||
"Paste or type a link": "\u7c98\u8d34\u6216\u8f93\u5165\u94fe\u63a5",
|
||||
"The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?": "\u4f60\u6240\u586b\u5199\u7684URL\u5730\u5740\u4e3a\u90ae\u4ef6\u5730\u5740\uff0c\u9700\u8981\u52a0\u4e0amailto:\u524d\u7f00\u5417\uff1f",
|
||||
"The URL you entered seems to be an external link. Do you want to add the required http:\/\/ prefix?": "\u4f60\u6240\u586b\u5199\u7684URL\u5730\u5740\u5c5e\u4e8e\u5916\u90e8\u94fe\u63a5\uff0c\u9700\u8981\u52a0\u4e0ahttp:\/\/:\u524d\u7f00\u5417\uff1f",
|
||||
"Link list": "\u94fe\u63a5\u5217\u8868",
|
||||
"Insert video": "\u63d2\u5165\u89c6\u9891",
|
||||
"Insert\/edit video": "\u63d2\u5165\/\u7f16\u8f91\u89c6\u9891",
|
||||
"Insert\/edit media": "\u63d2\u5165\/\u7f16\u8f91\u5a92\u4f53",
|
||||
"Alternative source": "\u955c\u50cf",
|
||||
"Alternative source URL": "\u66ff\u4ee3\u6765\u6e90\u7f51\u5740",
|
||||
"Media poster (Image URL)": "\u5c01\u9762(\u56fe\u7247\u5730\u5740)",
|
||||
"Paste your embed code below:": "\u5c06\u5185\u5d4c\u4ee3\u7801\u7c98\u8d34\u5728\u4e0b\u9762:",
|
||||
"Embed": "\u5185\u5d4c",
|
||||
"Media...": "\u591a\u5a92\u4f53...",
|
||||
"Nonbreaking space": "\u4e0d\u95f4\u65ad\u7a7a\u683c",
|
||||
"Page break": "\u5206\u9875\u7b26",
|
||||
"Paste as text": "\u7c98\u8d34\u4e3a\u6587\u672c",
|
||||
"Preview": "\u9884\u89c8",
|
||||
"Print...": "\u6253\u5370...",
|
||||
"Save": "\u4fdd\u5b58",
|
||||
"Find": "\u67e5\u627e",
|
||||
"Replace with": "\u66ff\u6362\u4e3a",
|
||||
"Replace": "\u66ff\u6362",
|
||||
"Replace all": "\u5168\u90e8\u66ff\u6362",
|
||||
"Previous": "\u4e0a\u4e00\u4e2a",
|
||||
"Next": "\u4e0b\u4e00\u4e2a",
|
||||
"Find and replace...": "\u67e5\u627e\u5e76\u66ff\u6362...",
|
||||
"Could not find the specified string.": "\u672a\u627e\u5230\u641c\u7d22\u5185\u5bb9.",
|
||||
"Match case": "\u533a\u5206\u5927\u5c0f\u5199",
|
||||
"Find whole words only": "\u5168\u5b57\u5339\u914d",
|
||||
"Spell check": "\u62fc\u5199\u68c0\u67e5",
|
||||
"Ignore": "\u5ffd\u7565",
|
||||
"Ignore all": "\u5168\u90e8\u5ffd\u7565",
|
||||
"Finish": "\u5b8c\u6210",
|
||||
"Add to Dictionary": "\u6dfb\u52a0\u5230\u5b57\u5178",
|
||||
"Insert table": "\u63d2\u5165\u8868\u683c",
|
||||
"Table properties": "\u8868\u683c\u5c5e\u6027",
|
||||
"Delete table": "\u5220\u9664\u8868\u683c",
|
||||
"Cell": "\u5355\u5143\u683c",
|
||||
"Row": "\u884c",
|
||||
"Column": "\u5217",
|
||||
"Cell properties": "\u5355\u5143\u683c\u5c5e\u6027",
|
||||
"Merge cells": "\u5408\u5e76\u5355\u5143\u683c",
|
||||
"Split cell": "\u62c6\u5206\u5355\u5143\u683c",
|
||||
"Insert row before": "\u5728\u4e0a\u65b9\u63d2\u5165",
|
||||
"Insert row after": "\u5728\u4e0b\u65b9\u63d2\u5165",
|
||||
"Delete row": "\u5220\u9664\u884c",
|
||||
"Row properties": "\u884c\u5c5e\u6027",
|
||||
"Cut row": "\u526a\u5207\u884c",
|
||||
"Copy row": "\u590d\u5236\u884c",
|
||||
"Paste row before": "\u7c98\u8d34\u5230\u4e0a\u65b9",
|
||||
"Paste row after": "\u7c98\u8d34\u5230\u4e0b\u65b9",
|
||||
"Insert column before": "\u5728\u5de6\u4fa7\u63d2\u5165",
|
||||
"Insert column after": "\u5728\u53f3\u4fa7\u63d2\u5165",
|
||||
"Delete column": "\u5220\u9664\u5217",
|
||||
"Cols": "\u5217",
|
||||
"Rows": "\u884c",
|
||||
"Width": "\u5bbd",
|
||||
"Height": "\u9ad8",
|
||||
"Cell spacing": "\u5355\u5143\u683c\u5916\u95f4\u8ddd",
|
||||
"Cell padding": "\u5355\u5143\u683c\u5185\u8fb9\u8ddd",
|
||||
"Show caption": "\u663e\u793a\u6807\u9898",
|
||||
"Left": "\u5de6\u5bf9\u9f50",
|
||||
"Center": "\u5c45\u4e2d",
|
||||
"Right": "\u53f3\u5bf9\u9f50",
|
||||
"Cell type": "\u5355\u5143\u683c\u7c7b\u578b",
|
||||
"Scope": "\u8303\u56f4",
|
||||
"Alignment": "\u5bf9\u9f50\u65b9\u5f0f",
|
||||
"H Align": "\u6c34\u5e73\u5bf9\u9f50",
|
||||
"V Align": "\u5782\u76f4\u5bf9\u9f50",
|
||||
"Top": "\u9876\u90e8\u5bf9\u9f50",
|
||||
"Middle": "\u5782\u76f4\u5c45\u4e2d",
|
||||
"Bottom": "\u5e95\u90e8\u5bf9\u9f50",
|
||||
"Header cell": "\u8868\u5934\u5355\u5143\u683c",
|
||||
"Row group": "\u884c\u7ec4",
|
||||
"Column group": "\u5217\u7ec4",
|
||||
"Row type": "\u884c\u7c7b\u578b",
|
||||
"Header": "\u8868\u5934",
|
||||
"Body": "\u8868\u4f53",
|
||||
"Footer": "\u8868\u5c3e",
|
||||
"Border color": "\u8fb9\u6846\u989c\u8272",
|
||||
"Insert template...": "\u63d2\u5165\u6a21\u677f...",
|
||||
"Templates": "\u6a21\u677f",
|
||||
"Template": "\u6a21\u677f",
|
||||
"Text color": "\u6587\u5b57\u989c\u8272",
|
||||
"Background color": "\u80cc\u666f\u8272",
|
||||
"Custom...": "\u81ea\u5b9a\u4e49...",
|
||||
"Custom color": "\u81ea\u5b9a\u4e49\u989c\u8272",
|
||||
"No color": "\u65e0",
|
||||
"Remove color": "\u79fb\u9664\u989c\u8272",
|
||||
"Table of Contents": "\u5185\u5bb9\u5217\u8868",
|
||||
"Show blocks": "\u663e\u793a\u533a\u5757\u8fb9\u6846",
|
||||
"Show invisible characters": "\u663e\u793a\u4e0d\u53ef\u89c1\u5b57\u7b26",
|
||||
"Word count": "\u5b57\u6570",
|
||||
"Count": "\u8ba1\u6570",
|
||||
"Document": "\u6587\u6863",
|
||||
"Selection": "\u9009\u62e9",
|
||||
"Words": "\u5355\u8bcd",
|
||||
"Words: {0}": "\u5b57\u6570\uff1a{0}",
|
||||
"{0} words": "{0} \u5b57",
|
||||
"File": "\u6587\u4ef6",
|
||||
"Edit": "\u7f16\u8f91",
|
||||
"Insert": "\u63d2\u5165",
|
||||
"View": "\u89c6\u56fe",
|
||||
"Format": "\u683c\u5f0f",
|
||||
"Table": "\u8868\u683c",
|
||||
"Tools": "\u5de5\u5177",
|
||||
"Powered by {0}": "\u7531{0}\u9a71\u52a8",
|
||||
"Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help": "\u5728\u7f16\u8f91\u533a\u6309ALT-F9\u6253\u5f00\u83dc\u5355\uff0c\u6309ALT-F10\u6253\u5f00\u5de5\u5177\u680f\uff0c\u6309ALT-0\u67e5\u770b\u5e2e\u52a9",
|
||||
"Image title": "\u56fe\u7247\u6807\u9898",
|
||||
"Border width": "\u8fb9\u6846\u5bbd\u5ea6",
|
||||
"Border style": "\u8fb9\u6846\u6837\u5f0f",
|
||||
"Error": "\u9519\u8bef",
|
||||
"Warn": "\u8b66\u544a",
|
||||
"Valid": "\u6709\u6548",
|
||||
"To open the popup, press Shift+Enter": "\u6309Shitf+Enter\u952e\u6253\u5f00\u5bf9\u8bdd\u6846",
|
||||
"Rich Text Area. Press ALT-0 for help.": "\u7f16\u8f91\u533a\u3002\u6309Alt+0\u952e\u6253\u5f00\u5e2e\u52a9\u3002",
|
||||
"System Font": "\u7cfb\u7edf\u5b57\u4f53",
|
||||
"Failed to upload image: {0}": "\u56fe\u7247\u4e0a\u4f20\u5931\u8d25: {0}",
|
||||
"Failed to load plugin: {0} from url {1}": "\u63d2\u4ef6\u52a0\u8f7d\u5931\u8d25: {0} \u6765\u81ea\u94fe\u63a5 {1}",
|
||||
"Failed to load plugin url: {0}": "\u63d2\u4ef6\u52a0\u8f7d\u5931\u8d25 \u94fe\u63a5: {0}",
|
||||
"Failed to initialize plugin: {0}": "\u63d2\u4ef6\u521d\u59cb\u5316\u5931\u8d25: {0}",
|
||||
"example": "\u793a\u4f8b",
|
||||
"Search": "\u641c\u7d22",
|
||||
"All": "\u5168\u90e8",
|
||||
"Currency": "\u8d27\u5e01",
|
||||
"Text": "\u6587\u5b57",
|
||||
"Quotations": "\u5f15\u7528",
|
||||
"Mathematical": "\u6570\u5b66",
|
||||
"Extended Latin": "\u62c9\u4e01\u8bed\u6269\u5145",
|
||||
"Symbols": "\u7b26\u53f7",
|
||||
"Arrows": "\u7bad\u5934",
|
||||
"User Defined": "\u81ea\u5b9a\u4e49",
|
||||
"dollar sign": "\u7f8e\u5143\u7b26\u53f7",
|
||||
"currency sign": "\u8d27\u5e01\u7b26\u53f7",
|
||||
"euro-currency sign": "\u6b27\u5143\u7b26\u53f7",
|
||||
"colon sign": "\u5192\u53f7",
|
||||
"cruzeiro sign": "\u514b\u9c81\u8d5b\u7f57\u5e01\u7b26\u53f7",
|
||||
"french franc sign": "\u6cd5\u90ce\u7b26\u53f7",
|
||||
"lira sign": "\u91cc\u62c9\u7b26\u53f7",
|
||||
"mill sign": "\u5bc6\u5c14\u7b26\u53f7",
|
||||
"naira sign": "\u5948\u62c9\u7b26\u53f7",
|
||||
"peseta sign": "\u6bd4\u585e\u5854\u7b26\u53f7",
|
||||
"rupee sign": "\u5362\u6bd4\u7b26\u53f7",
|
||||
"won sign": "\u97e9\u5143\u7b26\u53f7",
|
||||
"new sheqel sign": "\u65b0\u8c22\u514b\u5c14\u7b26\u53f7",
|
||||
"dong sign": "\u8d8a\u5357\u76fe\u7b26\u53f7",
|
||||
"kip sign": "\u8001\u631d\u57fa\u666e\u7b26\u53f7",
|
||||
"tugrik sign": "\u56fe\u683c\u91cc\u514b\u7b26\u53f7",
|
||||
"drachma sign": "\u5fb7\u62c9\u514b\u9a6c\u7b26\u53f7",
|
||||
"german penny symbol": "\u5fb7\u56fd\u4fbf\u58eb\u7b26\u53f7",
|
||||
"peso sign": "\u6bd4\u7d22\u7b26\u53f7",
|
||||
"guarani sign": "\u74dc\u62c9\u5c3c\u7b26\u53f7",
|
||||
"austral sign": "\u6fb3\u5143\u7b26\u53f7",
|
||||
"hryvnia sign": "\u683c\u91cc\u592b\u5c3c\u4e9a\u7b26\u53f7",
|
||||
"cedi sign": "\u585e\u5730\u7b26\u53f7",
|
||||
"livre tournois sign": "\u91cc\u5f17\u5f17\u5c14\u7b26\u53f7",
|
||||
"spesmilo sign": "spesmilo\u7b26\u53f7",
|
||||
"tenge sign": "\u575a\u6208\u7b26\u53f7",
|
||||
"indian rupee sign": "\u5370\u5ea6\u5362\u6bd4",
|
||||
"turkish lira sign": "\u571f\u8033\u5176\u91cc\u62c9",
|
||||
"nordic mark sign": "\u5317\u6b27\u9a6c\u514b",
|
||||
"manat sign": "\u9a6c\u7eb3\u7279\u7b26\u53f7",
|
||||
"ruble sign": "\u5362\u5e03\u7b26\u53f7",
|
||||
"yen character": "\u65e5\u5143\u5b57\u6837",
|
||||
"yuan character": "\u4eba\u6c11\u5e01\u5143\u5b57\u6837",
|
||||
"yuan character, in hong kong and taiwan": "\u5143\u5b57\u6837\uff08\u6e2f\u53f0\u5730\u533a\uff09",
|
||||
"yen\/yuan character variant one": "\u5143\u5b57\u6837\uff08\u5927\u5199\uff09",
|
||||
"Loading emoticons...": "\u52a0\u8f7d\u8868\u60c5\u7b26\u53f7...",
|
||||
"Could not load emoticons": "\u4e0d\u80fd\u52a0\u8f7d\u8868\u60c5\u7b26\u53f7",
|
||||
"People": "\u4eba\u7c7b",
|
||||
"Animals and Nature": "\u52a8\u7269\u548c\u81ea\u7136",
|
||||
"Food and Drink": "\u98df\u7269\u548c\u996e\u54c1",
|
||||
"Activity": "\u6d3b\u52a8",
|
||||
"Travel and Places": "\u65c5\u6e38\u548c\u5730\u70b9",
|
||||
"Objects": "\u7269\u4ef6",
|
||||
"Flags": "\u65d7\u5e1c",
|
||||
"Characters": "\u5b57\u7b26",
|
||||
"Characters (no spaces)": "\u5b57\u7b26(\u65e0\u7a7a\u683c)",
|
||||
"{0} characters": "{0} \u4e2a\u5b57\u7b26",
|
||||
"Error: Form submit field collision.": "\u9519\u8bef: \u8868\u5355\u63d0\u4ea4\u5b57\u6bb5\u51b2\u7a81\u3002",
|
||||
"Error: No form element found.": "\u9519\u8bef: \u6ca1\u6709\u8868\u5355\u63a7\u4ef6\u3002",
|
||||
"Update": "\u66f4\u65b0",
|
||||
"Color swatch": "\u989c\u8272\u6837\u672c",
|
||||
"Turquoise": "\u9752\u7eff\u8272",
|
||||
"Green": "\u7eff\u8272",
|
||||
"Blue": "\u84dd\u8272",
|
||||
"Purple": "\u7d2b\u8272",
|
||||
"Navy Blue": "\u6d77\u519b\u84dd",
|
||||
"Dark Turquoise": "\u6df1\u84dd\u7eff\u8272",
|
||||
"Dark Green": "\u6df1\u7eff\u8272",
|
||||
"Medium Blue": "\u4e2d\u84dd\u8272",
|
||||
"Medium Purple": "\u4e2d\u7d2b\u8272",
|
||||
"Midnight Blue": "\u6df1\u84dd\u8272",
|
||||
"Yellow": "\u9ec4\u8272",
|
||||
"Orange": "\u6a59\u8272",
|
||||
"Red": "\u7ea2\u8272",
|
||||
"Light Gray": "\u6d45\u7070\u8272",
|
||||
"Gray": "\u7070\u8272",
|
||||
"Dark Yellow": "\u6697\u9ec4\u8272",
|
||||
"Dark Orange": "\u6df1\u6a59\u8272",
|
||||
"Dark Red": "\u6df1\u7ea2\u8272",
|
||||
"Medium Gray": "\u4e2d\u7070\u8272",
|
||||
"Dark Gray": "\u6df1\u7070\u8272",
|
||||
"Light Green": "\u6d45\u7eff\u8272",
|
||||
"Light Yellow": "\u6d45\u9ec4\u8272",
|
||||
"Light Red": "\u6d45\u7ea2\u8272",
|
||||
"Light Purple": "\u6d45\u7d2b\u8272",
|
||||
"Light Blue": "\u6d45\u84dd\u8272",
|
||||
"Dark Purple": "\u6df1\u7d2b\u8272",
|
||||
"Dark Blue": "\u6df1\u84dd\u8272",
|
||||
"Black": "\u9ed1\u8272",
|
||||
"White": "\u767d\u8272",
|
||||
"Switch to or from fullscreen mode": "\u5207\u6362\u5168\u5c4f\u6a21\u5f0f",
|
||||
"Open help dialog": "\u6253\u5f00\u5e2e\u52a9\u5bf9\u8bdd\u6846",
|
||||
"history": "\u5386\u53f2",
|
||||
"styles": "\u6837\u5f0f",
|
||||
"formatting": "\u683c\u5f0f\u5316",
|
||||
"alignment": "\u5bf9\u9f50",
|
||||
"indentation": "\u7f29\u8fdb",
|
||||
"permanent pen": "\u8bb0\u53f7\u7b14",
|
||||
"comments": "\u5907\u6ce8",
|
||||
"Format Painter": "\u683c\u5f0f\u5237",
|
||||
"Insert\/edit iframe": "\u63d2\u5165\/\u7f16\u8f91\u6846\u67b6",
|
||||
"Capitalization": "\u5927\u5199",
|
||||
"lowercase": "\u5c0f\u5199",
|
||||
"UPPERCASE": "\u5927\u5199",
|
||||
"Title Case": "\u9996\u5b57\u6bcd\u5927\u5199",
|
||||
"Permanent Pen Properties": "\u6c38\u4e45\u7b14\u5c5e\u6027",
|
||||
"Permanent pen properties...": "\u6c38\u4e45\u7b14\u5c5e\u6027...",
|
||||
"Font": "\u5b57\u4f53",
|
||||
"Size": "\u5b57\u53f7",
|
||||
"More...": "\u66f4\u591a...",
|
||||
"Spellcheck Language": "\u62fc\u5199\u68c0\u67e5\u8bed\u8a00",
|
||||
"Select...": "\u9009\u62e9...",
|
||||
"Preferences": "\u9996\u9009\u9879",
|
||||
"Yes": "\u662f",
|
||||
"No": "\u5426",
|
||||
"Keyboard Navigation": "\u952e\u76d8\u6307\u5f15",
|
||||
"Version": "\u7248\u672c",
|
||||
"Anchor": "\u951a\u70b9",
|
||||
"Special character": "\u7279\u6b8a\u7b26\u53f7",
|
||||
"Code sample": "\u4ee3\u7801\u793a\u4f8b",
|
||||
"Color": "\u989c\u8272",
|
||||
"Emoticons": "\u8868\u60c5",
|
||||
"Document properties": "\u6587\u6863\u5c5e\u6027",
|
||||
"Image": "\u56fe\u7247",
|
||||
"Insert link": "\u63d2\u5165\u94fe\u63a5",
|
||||
"Target": "\u6253\u5f00\u65b9\u5f0f",
|
||||
"Link": "\u94fe\u63a5",
|
||||
"Poster": "\u5c01\u9762",
|
||||
"Media": "\u5a92\u4f53",
|
||||
"Print": "\u6253\u5370",
|
||||
"Prev": "\u4e0a\u4e00\u4e2a",
|
||||
"Find and replace": "\u67e5\u627e\u548c\u66ff\u6362",
|
||||
"Whole words": "\u5168\u5b57\u5339\u914d",
|
||||
"Spellcheck": "\u62fc\u5199\u68c0\u67e5",
|
||||
"Caption": "\u6807\u9898",
|
||||
"Insert template": "\u63d2\u5165\u6a21\u677f"
|
||||
});
|
||||
@@ -0,0 +1,110 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-dialog
|
||||
v-bind="$attrs"
|
||||
width="500px"
|
||||
:close-on-click-modal="false"
|
||||
:modal-append-to-body="false"
|
||||
v-on="$listeners"
|
||||
@open="onOpen"
|
||||
@close="onClose"
|
||||
>
|
||||
<el-row :gutter="15">
|
||||
<el-form
|
||||
ref="elForm"
|
||||
:model="formData"
|
||||
:rules="rules"
|
||||
size="medium"
|
||||
label-width="100px"
|
||||
>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="生成类型" prop="type">
|
||||
<el-radio-group v-model="formData.type">
|
||||
<el-radio-button
|
||||
v-for="(item, index) in typeOptions"
|
||||
:key="index"
|
||||
:label="item.value"
|
||||
:disabled="item.disabled"
|
||||
>
|
||||
{{ item.label }}
|
||||
</el-radio-button>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="showFileName" label="文件名" prop="fileName">
|
||||
<el-input v-model="formData.fileName" placeholder="请输入文件名" clearable />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-form>
|
||||
</el-row>
|
||||
|
||||
<div slot="footer">
|
||||
<el-button @click="close">
|
||||
取消
|
||||
</el-button>
|
||||
<el-button type="primary" @click="handelConfirm">
|
||||
确定
|
||||
</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
inheritAttrs: false,
|
||||
props: ['showFileName'],
|
||||
data() {
|
||||
return {
|
||||
formData: {
|
||||
fileName: undefined,
|
||||
type: 'file'
|
||||
},
|
||||
rules: {
|
||||
fileName: [{
|
||||
required: true,
|
||||
message: '请输入文件名',
|
||||
trigger: 'blur'
|
||||
}],
|
||||
type: [{
|
||||
required: true,
|
||||
message: '生成类型不能为空',
|
||||
trigger: 'change'
|
||||
}]
|
||||
},
|
||||
typeOptions: [{
|
||||
label: '页面',
|
||||
value: 'file'
|
||||
}, {
|
||||
label: '弹窗',
|
||||
value: 'dialog'
|
||||
}]
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
},
|
||||
watch: {},
|
||||
mounted() {},
|
||||
methods: {
|
||||
onOpen() {
|
||||
if (this.showFileName) {
|
||||
this.formData.fileName = `${+new Date()}.vue`
|
||||
}
|
||||
},
|
||||
onClose() {
|
||||
},
|
||||
close(e) {
|
||||
this.$emit('update:visible', false)
|
||||
},
|
||||
handelConfirm() {
|
||||
this.$refs.elForm.validate(valid => {
|
||||
if (!valid) return
|
||||
this.$emit('confirm', { ...this.formData })
|
||||
this.close()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,128 @@
|
||||
<script>
|
||||
import draggable from 'vuedraggable'
|
||||
import render from '@/components/FormGenerator/components/render/render'
|
||||
|
||||
const components = {
|
||||
itemBtns(h, element, index, parent) {
|
||||
const { copyItem, deleteItem } = this.$listeners
|
||||
return [
|
||||
<span class='drawing-item-copy' title='复制' onClick={event => {
|
||||
copyItem(element, parent); event.stopPropagation()
|
||||
}}>
|
||||
<i class='el-icon-copy-document' />
|
||||
</span>,
|
||||
<span class='drawing-item-delete' title='删除' onClick={event => {
|
||||
deleteItem(index, parent); event.stopPropagation()
|
||||
}}>
|
||||
<i class='el-icon-delete' />
|
||||
</span>
|
||||
]
|
||||
}
|
||||
}
|
||||
const layouts = {
|
||||
colFormItem(h, element, index, parent) {
|
||||
const { activeItem } = this.$listeners
|
||||
const config = element.__config__
|
||||
let className = this.activeId === config.formId ? 'drawing-item active-from-item' : 'drawing-item'
|
||||
if (this.formConf.unFocusedComponentBorder) className += ' unfocus-bordered'
|
||||
let labelWidth = config.labelWidth ? `${config.labelWidth}px` : null
|
||||
if (config.showLabel === false) labelWidth = '0'
|
||||
if(config.tips == undefined){
|
||||
this.$set(config,'tips',false);//如果以前的表单没有tooltip配置,就赋值一个默认值用来读取
|
||||
}
|
||||
if(config.tips){
|
||||
return (
|
||||
<el-col span={config.span} class={className}
|
||||
nativeOnClick={event => { activeItem(element); event.stopPropagation() }}>
|
||||
<el-form-item label-width={labelWidth}
|
||||
label={config.showLabel ? config.label : ''} required={config.required}>
|
||||
<el-tooltip effect="dark" placement="top-start" style="padding:10px 5px 0 0;">
|
||||
<i class="el-icon-warning-outline" />
|
||||
<div slot="content" style="max-width:400px;">{config.tipsDesc}</div>
|
||||
</el-tooltip>
|
||||
<render key={config.renderKey} conf={element} onInput={ event => {
|
||||
this.$set(config, 'defaultValue', event)
|
||||
}} />
|
||||
</el-form-item>
|
||||
{components.itemBtns.apply(this, arguments)}
|
||||
</el-col>
|
||||
)
|
||||
}else{
|
||||
return (
|
||||
<el-col span={config.span} class={className}
|
||||
nativeOnClick={event => { activeItem(element); event.stopPropagation() }}>
|
||||
<el-form-item label-width={labelWidth}
|
||||
label={config.showLabel ? config.label : ''} required={config.required}>
|
||||
<render key={config.renderKey} conf={element} onInput={ event => {
|
||||
this.$set(config, 'defaultValue', event)
|
||||
}} />
|
||||
</el-form-item>
|
||||
{components.itemBtns.apply(this, arguments)}
|
||||
</el-col>
|
||||
)
|
||||
}
|
||||
},
|
||||
rowFormItem(h, element, index, parent) {
|
||||
const { activeItem } = this.$listeners
|
||||
const className = this.activeId === element.__config__.formId
|
||||
? 'drawing-row-item active-from-item'
|
||||
: 'drawing-row-item'
|
||||
let child = renderChildren.apply(this, arguments)
|
||||
if (element.type === 'flex') {
|
||||
child = <el-row type={element.type} justify={element.justify} align={element.align}>
|
||||
{child}
|
||||
</el-row>
|
||||
}
|
||||
return (
|
||||
<el-col span={element.__config__.span}>
|
||||
<el-row gutter={element.__config__.gutter} class={className}
|
||||
nativeOnClick={event => { activeItem(element); event.stopPropagation() }}>
|
||||
<span class='component-name'>{element.__config__.componentName}</span>
|
||||
<draggable list={element.__config__.children} animation={340} group='componentsGroup' class='drag-wrapper'>
|
||||
{child}
|
||||
</draggable>
|
||||
{components.itemBtns.apply(this, arguments)}
|
||||
</el-row>
|
||||
</el-col>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
function renderChildren(h, element, index, parent) {
|
||||
const config = element.__config__
|
||||
if (!Array.isArray(config.children)) return null
|
||||
return config.children.map((el, i) => {
|
||||
const layout = layouts[el.__config__.layout]
|
||||
if (layout) {
|
||||
return layout.call(this, h, el, i, config.children)
|
||||
}
|
||||
return layoutIsNotFound.call(this)
|
||||
})
|
||||
}
|
||||
|
||||
function layoutIsNotFound() {
|
||||
throw new Error(`没有与${this.element.__config__.layout}匹配的layout`)
|
||||
}
|
||||
|
||||
export default {
|
||||
components: {
|
||||
render,
|
||||
draggable
|
||||
},
|
||||
props: [
|
||||
'element',
|
||||
'index',
|
||||
'drawingList',
|
||||
'activeId',
|
||||
'formConf'
|
||||
],
|
||||
render(h) {
|
||||
const layout = layouts[this.element.__config__.layout]
|
||||
|
||||
if (layout) {
|
||||
return layout.call(this, h, this.element, this.index, this.drawingList)
|
||||
}
|
||||
return layoutIsNotFound.call(this)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,332 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-drawer v-bind="$attrs" v-on="$listeners" @opened="onOpen" @close="onClose">
|
||||
<div style="height:100%">
|
||||
<el-row style="height:100%;overflow:auto">
|
||||
<el-col :md="24" :lg="12" class="left-editor">
|
||||
<div class="setting" title="资源引用" @click="showResource">
|
||||
<el-badge :is-dot="!!resources.length" class="item">
|
||||
<i class="el-icon-setting" />
|
||||
</el-badge>
|
||||
</div>
|
||||
<el-tabs v-model="activeTab" type="card" class="editor-tabs">
|
||||
<el-tab-pane name="html">
|
||||
<span slot="label">
|
||||
<i v-if="activeTab==='html'" class="el-icon-edit" />
|
||||
<i v-else class="el-icon-document" />
|
||||
template
|
||||
</span>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane name="js">
|
||||
<span slot="label">
|
||||
<i v-if="activeTab==='js'" class="el-icon-edit" />
|
||||
<i v-else class="el-icon-document" />
|
||||
script
|
||||
</span>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane name="css">
|
||||
<span slot="label">
|
||||
<i v-if="activeTab==='css'" class="el-icon-edit" />
|
||||
<i v-else class="el-icon-document" />
|
||||
css
|
||||
</span>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
<div v-show="activeTab==='html'" id="editorHtml" class="tab-editor" />
|
||||
<div v-show="activeTab==='js'" id="editorJs" class="tab-editor" />
|
||||
<div v-show="activeTab==='css'" id="editorCss" class="tab-editor" />
|
||||
</el-col>
|
||||
<el-col :md="24" :lg="12" class="right-preview">
|
||||
<div class="action-bar" :style="{'text-align': 'left'}">
|
||||
<span class="bar-btn" @click="runCode">
|
||||
<i class="el-icon-refresh" />
|
||||
刷新
|
||||
</span>
|
||||
<span class="bar-btn" @click="exportFile">
|
||||
<i class="el-icon-download" />
|
||||
导出vue文件
|
||||
</span>
|
||||
<span ref="copyBtn" class="bar-btn copy-btn">
|
||||
<i class="el-icon-document-copy" />
|
||||
复制代码
|
||||
</span>
|
||||
<span class="bar-btn delete-btn" @click="$emit('update:visible', false)">
|
||||
<i class="el-icon-circle-close" />
|
||||
关闭
|
||||
</span>
|
||||
</div>
|
||||
<iframe
|
||||
v-show="isIframeLoaded"
|
||||
ref="previewPage"
|
||||
class="result-wrapper"
|
||||
frameborder="0"
|
||||
src="preview.html"
|
||||
@load="iframeLoad"
|
||||
/>
|
||||
<div v-show="!isIframeLoaded" v-loading="true" class="result-wrapper" />
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</el-drawer>
|
||||
<resource-dialog
|
||||
:visible.sync="resourceVisible"
|
||||
:origin-resource="resources"
|
||||
@save="setResource"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { parse } from '@babel/parser'
|
||||
import ClipboardJS from 'clipboard'
|
||||
import { saveAs } from 'file-saver'
|
||||
import {
|
||||
makeUpHtml, vueTemplate, vueScript, cssStyle
|
||||
} from '@/components/FormGenerator/components/generator/html'
|
||||
import { makeUpJs } from '@/components/FormGenerator/components/generator/js'
|
||||
import { makeUpCss } from '@/components/FormGenerator/components/generator/css'
|
||||
import { exportDefault, beautifierConf, titleCase } from '../utils/index'
|
||||
import ResourceDialog from './ResourceDialog'
|
||||
import loadMonaco from '../utils/loadMonaco'
|
||||
import loadBeautifier from '../utils/loadBeautifier'
|
||||
|
||||
const editorObj = {
|
||||
html: null,
|
||||
js: null,
|
||||
css: null
|
||||
}
|
||||
const mode = {
|
||||
html: 'html',
|
||||
js: 'javascript',
|
||||
css: 'css'
|
||||
}
|
||||
let beautifier
|
||||
let monaco
|
||||
|
||||
export default {
|
||||
components: { ResourceDialog },
|
||||
props: ['formData', 'generateConf'],
|
||||
data() {
|
||||
return {
|
||||
activeTab: 'html',
|
||||
htmlCode: '',
|
||||
jsCode: '',
|
||||
cssCode: '',
|
||||
codeFrame: '',
|
||||
isIframeLoaded: false,
|
||||
isInitcode: false, // 保证open后两个异步只执行一次runcode
|
||||
isRefreshCode: false, // 每次打开都需要重新刷新代码
|
||||
resourceVisible: false,
|
||||
scripts: [],
|
||||
links: [],
|
||||
monaco: null
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
resources() {
|
||||
return this.scripts.concat(this.links)
|
||||
}
|
||||
},
|
||||
watch: {},
|
||||
created() {
|
||||
},
|
||||
mounted() {
|
||||
window.addEventListener('keydown', this.preventDefaultSave)
|
||||
const clipboard = new ClipboardJS('.copy-btn', {
|
||||
text: trigger => {
|
||||
const codeStr = this.generateCode()
|
||||
this.$notify({
|
||||
title: '成功',
|
||||
message: '代码已复制到剪切板,可粘贴。',
|
||||
type: 'success'
|
||||
})
|
||||
return codeStr
|
||||
}
|
||||
})
|
||||
clipboard.on('error', e => {
|
||||
this.$message.error('代码复制失败')
|
||||
})
|
||||
},
|
||||
beforeDestroy() {
|
||||
window.removeEventListener('keydown', this.preventDefaultSave)
|
||||
},
|
||||
methods: {
|
||||
preventDefaultSave(e) {
|
||||
if (e.key === 's' && (e.metaKey || e.ctrlKey)) {
|
||||
e.preventDefault()
|
||||
}
|
||||
},
|
||||
onOpen() {
|
||||
const { type } = this.generateConf
|
||||
this.htmlCode = makeUpHtml(this.formData, type)
|
||||
this.jsCode = makeUpJs(this.formData, type)
|
||||
this.cssCode = makeUpCss(this.formData)
|
||||
|
||||
loadBeautifier(btf => {
|
||||
beautifier = btf
|
||||
this.htmlCode = beautifier.html(this.htmlCode, beautifierConf.html)
|
||||
this.jsCode = beautifier.js(this.jsCode, beautifierConf.js)
|
||||
this.cssCode = beautifier.css(this.cssCode, beautifierConf.html)
|
||||
|
||||
loadMonaco(val => {
|
||||
monaco = val
|
||||
this.setEditorValue('editorHtml', 'html', this.htmlCode)
|
||||
this.setEditorValue('editorJs', 'js', this.jsCode)
|
||||
this.setEditorValue('editorCss', 'css', this.cssCode)
|
||||
if (!this.isInitcode) {
|
||||
this.isRefreshCode = true
|
||||
this.isIframeLoaded && (this.isInitcode = true) && this.runCode()
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
onClose() {
|
||||
this.isInitcode = false
|
||||
this.isRefreshCode = false
|
||||
this.isIframeLoaded = false
|
||||
},
|
||||
iframeLoad() {
|
||||
if (!this.isInitcode) {
|
||||
this.isIframeLoaded = true
|
||||
this.isRefreshCode && (this.isInitcode = true) && this.runCode()
|
||||
}
|
||||
},
|
||||
setEditorValue(id, type, codeStr) {
|
||||
if (editorObj[type]) {
|
||||
editorObj[type].setValue(codeStr)
|
||||
} else {
|
||||
editorObj[type] = monaco.editor.create(document.getElementById(id), {
|
||||
value: codeStr,
|
||||
theme: 'vs-dark',
|
||||
language: mode[type],
|
||||
automaticLayout: true
|
||||
})
|
||||
}
|
||||
// ctrl + s 刷新
|
||||
editorObj[type].onKeyDown(e => {
|
||||
if (e.keyCode === 49 && (e.metaKey || e.ctrlKey)) {
|
||||
this.runCode()
|
||||
}
|
||||
})
|
||||
},
|
||||
runCode() {
|
||||
const jsCodeStr = editorObj.js.getValue()
|
||||
try {
|
||||
const ast = parse(jsCodeStr, { sourceType: 'module' })
|
||||
const astBody = ast.program.body
|
||||
if (astBody.length > 1) {
|
||||
this.$confirm(
|
||||
'js格式不能识别,仅支持修改export default的对象内容',
|
||||
'提示',
|
||||
{
|
||||
type: 'warning'
|
||||
}
|
||||
)
|
||||
return
|
||||
}
|
||||
if (astBody[0].type === 'ExportDefaultDeclaration') {
|
||||
const postData = {
|
||||
type: 'refreshFrame',
|
||||
data: {
|
||||
generateConf: this.generateConf,
|
||||
html: editorObj.html.getValue(),
|
||||
js: jsCodeStr.replace(exportDefault, ''),
|
||||
css: editorObj.css.getValue(),
|
||||
scripts: this.scripts,
|
||||
links: this.links
|
||||
}
|
||||
}
|
||||
|
||||
this.$refs.previewPage.contentWindow.postMessage(
|
||||
postData,
|
||||
location.origin
|
||||
)
|
||||
} else {
|
||||
this.$message.error('请使用export default')
|
||||
}
|
||||
} catch (err) {
|
||||
this.$message.error(`js错误:${err}`)
|
||||
}
|
||||
},
|
||||
generateCode() {
|
||||
const html = vueTemplate(editorObj.html.getValue())
|
||||
const script = vueScript(editorObj.js.getValue())
|
||||
const css = cssStyle(editorObj.css.getValue())
|
||||
return beautifier.html(html + script + css, beautifierConf.html)
|
||||
},
|
||||
exportFile() {
|
||||
this.$prompt('文件名:', '导出文件', {
|
||||
inputValue: `${+new Date()}.vue`,
|
||||
closeOnClickModal: false,
|
||||
inputPlaceholder: '请输入文件名'
|
||||
}).then(({ value }) => {
|
||||
if (!value) value = `${+new Date()}.vue`
|
||||
const codeStr = this.generateCode()
|
||||
const blob = new Blob([codeStr], { type: 'text/plain;charset=utf-8' })
|
||||
saveAs(blob, value)
|
||||
})
|
||||
},
|
||||
showResource() {
|
||||
this.resourceVisible = true
|
||||
},
|
||||
setResource(arr) {
|
||||
const scripts = []; const
|
||||
links = []
|
||||
if (Array.isArray(arr)) {
|
||||
arr.forEach(item => {
|
||||
if (item.endsWith('.css')) {
|
||||
links.push(item)
|
||||
} else {
|
||||
scripts.push(item)
|
||||
}
|
||||
})
|
||||
this.scripts = scripts
|
||||
this.links = links
|
||||
} else {
|
||||
this.scripts = []
|
||||
this.links = []
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '../styles/mixin';
|
||||
.tab-editor {
|
||||
position: absolute;
|
||||
top: 33px;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
font-size: 14px;
|
||||
}
|
||||
.left-editor {
|
||||
position: relative;
|
||||
height: 100%;
|
||||
background: #1e1e1e;
|
||||
overflow: hidden;
|
||||
}
|
||||
.setting{
|
||||
position: absolute;
|
||||
right: 15px;
|
||||
top: 3px;
|
||||
color: #a9f122;
|
||||
font-size: 18px;
|
||||
cursor: pointer;
|
||||
z-index: 1;
|
||||
}
|
||||
.right-preview {
|
||||
height: 100%;
|
||||
.result-wrapper {
|
||||
height: calc(100vh - 33px);
|
||||
width: 100%;
|
||||
overflow: auto;
|
||||
padding: 12px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
}
|
||||
@include action-bar;
|
||||
::v-deep .el-drawer__header {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,494 @@
|
||||
|
||||
<template>
|
||||
<div class="container-FromGen">
|
||||
<div class="left-board">
|
||||
<div class="logo-wrapper">
|
||||
<div class="logo">
|
||||
<span>领淘</span>
|
||||
<!-- <img :src="logo" alt="logo"> Form Generator
|
||||
<a class="github" href="https://github.com/JakHuang/form-generator" target="_blank">
|
||||
<img src="https://github.githubassets.com/pinned-octocat.svg" alt>
|
||||
</a> -->
|
||||
</div>
|
||||
</div>
|
||||
<el-scrollbar class="left-scrollbar">
|
||||
<div class="components-list">
|
||||
<div v-for="(item, listIndex) in leftComponents" :key="listIndex">
|
||||
<div class="components-title">
|
||||
<svg-icon icon-class="component" />
|
||||
{{ item.title }}
|
||||
</div>
|
||||
<draggable
|
||||
class="components-draggable"
|
||||
:list="item.list"
|
||||
:group="{ name: 'componentsGroup', pull: 'clone', put: false }"
|
||||
:clone="cloneComponent"
|
||||
draggable=".components-item"
|
||||
:sort="false"
|
||||
@end="onEnd"
|
||||
>
|
||||
<div
|
||||
v-for="(element, index) in item.list"
|
||||
:key="index"
|
||||
class="components-item"
|
||||
@click="addComponent(element)"
|
||||
>
|
||||
<div class="components-body">
|
||||
<svg-icon :icon-class="element.__config__.tagIcon" />
|
||||
{{ element.__config__.label }}
|
||||
</div>
|
||||
</div>
|
||||
</draggable>
|
||||
</div>
|
||||
</div>
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
|
||||
<div class="center-board">
|
||||
<div class="action-bar">
|
||||
<!-- <el-button icon="el-icon-video-play" type="text" @click="run">-->
|
||||
<!-- 运行-->
|
||||
<!-- </el-button>-->
|
||||
<!-- <el-button icon="el-icon-view" type="text" @click="showJson">-->
|
||||
<!-- 查看json-->
|
||||
<!-- </el-button>-->
|
||||
<!-- <el-button icon="el-icon-download" type="text" @click="download"> -->
|
||||
<!-- 导出vue文件 -->
|
||||
<!-- </el-button> -->
|
||||
<!-- <el-button class="copy-btn-main" icon="el-icon-document-copy" type="text" @click="copy">-->
|
||||
<!-- 复制代码-->
|
||||
<!-- </el-button>-->
|
||||
<!-- <el-button class="delete-btn" icon="el-icon-delete" type="text" @click="empty">-->
|
||||
<!-- 清空-->
|
||||
<!-- </el-button>-->
|
||||
<el-form ref="selfForm" inline :model="selfForm">
|
||||
<el-form-item
|
||||
label="名称"
|
||||
prop="name"
|
||||
:rules="[{ required:true, message:'请填写名称', trigger:['blur','change'] }]"
|
||||
>
|
||||
<el-input v-model="selfForm.name" placeholder="名称" />
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
label="描述"
|
||||
prop="info"
|
||||
:rules="[{ required:true, message:'请填写描述', trigger:['blur','change'] }]"
|
||||
>
|
||||
<el-input v-model="selfForm.info" placeholder="描述" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="handlerSaveJSON('selfForm')" v-hasPermi="['admin:system:form:update']">保存</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
<el-scrollbar class="center-scrollbar">
|
||||
<el-row class="center-board-row" :gutter="formConf.gutter">
|
||||
<el-form
|
||||
:size="formConf.size"
|
||||
:label-position="formConf.labelPosition"
|
||||
:disabled="formConf.disabled"
|
||||
:label-width="formConf.labelWidth + 'px'"
|
||||
>
|
||||
<draggable class="drawing-board" :list="drawingList" :animation="340" group="componentsGroup">
|
||||
<draggable-item
|
||||
v-for="(element, index) in drawingList"
|
||||
:key="element.renderKey"
|
||||
:drawing-list="drawingList"
|
||||
:element="element"
|
||||
:index="index"
|
||||
:active-id="activeId"
|
||||
:form-conf="formConf"
|
||||
@activeItem="activeFormItem"
|
||||
@copyItem="drawingItemCopy"
|
||||
@deleteItem="drawingItemDelete"
|
||||
/>
|
||||
</draggable>
|
||||
<div v-show="!drawingList.length" class="empty-info">
|
||||
从左侧拖入或点选组件进行表单设计
|
||||
</div>
|
||||
</el-form>
|
||||
</el-row>
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
|
||||
<right-panel
|
||||
:active-data="activeData"
|
||||
:form-conf="formConf"
|
||||
:show-field="!!drawingList.length"
|
||||
@tag-change="tagChange"
|
||||
/>
|
||||
|
||||
<form-drawer
|
||||
:visible.sync="drawerVisible"
|
||||
:form-data="formData"
|
||||
size="100%"
|
||||
:generate-conf="generateConf"
|
||||
/>
|
||||
<json-drawer
|
||||
size="60%"
|
||||
:visible.sync="jsonDrawerVisible"
|
||||
:json-str="JSON.stringify(formData)"
|
||||
@refresh="refreshJson"
|
||||
/>
|
||||
<code-type-dialog
|
||||
:visible.sync="dialogVisible"
|
||||
title="选择生成类型"
|
||||
:show-file-name="showFileName"
|
||||
@confirm="generate"
|
||||
/>
|
||||
<input id="copyNode" type="hidden">
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import draggable from 'vuedraggable'
|
||||
import { debounce } from 'throttle-debounce'
|
||||
import { saveAs } from 'file-saver'
|
||||
import ClipboardJS from 'clipboard'
|
||||
import render from '@/components/FormGenerator/components/render/render'
|
||||
import FormDrawer from './FormDrawer'
|
||||
import JsonDrawer from './JsonDrawer'
|
||||
import RightPanel from './RightPanel'
|
||||
import {
|
||||
inputComponents, selectComponents, layoutComponents, formConf
|
||||
} from '@/components/FormGenerator/components/generator/config'
|
||||
import {
|
||||
exportDefault, beautifierConf, isNumberStr, titleCase
|
||||
} from '../utils/index'
|
||||
import {
|
||||
makeUpHtml, vueTemplate, vueScript, cssStyle
|
||||
} from '@/components/FormGenerator/components/generator/html'
|
||||
import { makeUpJs } from '@/components/FormGenerator/components/generator/js'
|
||||
import { makeUpCss } from '@/components/FormGenerator/components/generator/css'
|
||||
import drawingDefalut from '@/components/FormGenerator/components/generator/drawingDefalut'
|
||||
// import logo from '@/assets/logo.png'
|
||||
import CodeTypeDialog from './CodeTypeDialog'
|
||||
import DraggableItem from './DraggableItem'
|
||||
import {
|
||||
getDrawingList, saveDrawingList, getIdGlobal, saveIdGlobal, getFormConf, getFormConfSelf
|
||||
} from '../utils/db'
|
||||
import loadBeautifier from '../utils/loadBeautifier'
|
||||
import {Debounce} from '@/utils/validate'
|
||||
let beautifier
|
||||
const emptyActiveData = { style: {}, autosize: {}}
|
||||
let oldActiveId
|
||||
let tempActiveData
|
||||
const drawingListInDB = getDrawingList()
|
||||
const formConfInDB = getFormConf()
|
||||
const idGlobal = getIdGlobal()
|
||||
|
||||
export default {
|
||||
components: {
|
||||
draggable,
|
||||
render,
|
||||
FormDrawer,
|
||||
JsonDrawer,
|
||||
RightPanel,
|
||||
CodeTypeDialog,
|
||||
DraggableItem
|
||||
},
|
||||
props: {
|
||||
editData: {
|
||||
type: Object,
|
||||
default: {}
|
||||
},
|
||||
isCreate: {
|
||||
type: Number,
|
||||
default: 0 // 0=创建,1=编辑
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// logo,
|
||||
idGlobal,
|
||||
formConf,
|
||||
inputComponents,
|
||||
selectComponents,
|
||||
layoutComponents,
|
||||
labelWidth: 100,
|
||||
drawingList: drawingDefalut,
|
||||
drawingData: {},
|
||||
activeId: drawingDefalut[0].formId,
|
||||
drawerVisible: false,
|
||||
formData: {},
|
||||
dialogVisible: false,
|
||||
jsonDrawerVisible: false,
|
||||
generateConf: null,
|
||||
showFileName: false,
|
||||
activeData: drawingDefalut[0],
|
||||
saveDrawingListDebounce: debounce(340, saveDrawingList),
|
||||
saveIdGlobalDebounce: debounce(340, saveIdGlobal),
|
||||
leftComponents: [
|
||||
{
|
||||
title: '输入型组件',
|
||||
list: inputComponents
|
||||
},
|
||||
{
|
||||
title: '选择型组件',
|
||||
list: selectComponents
|
||||
},
|
||||
{
|
||||
title: '布局型组件',
|
||||
list: layoutComponents
|
||||
}
|
||||
],
|
||||
selfForm: {
|
||||
name: null,
|
||||
info: null,
|
||||
id: null
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
},
|
||||
watch: {
|
||||
// eslint-disable-next-line func-names
|
||||
'activeData.__config__.label': function(val, oldVal) {
|
||||
if (
|
||||
this.activeData.placeholder === undefined ||
|
||||
!this.activeData.__config__.tag ||
|
||||
oldActiveId !== this.activeId
|
||||
) {
|
||||
return
|
||||
}
|
||||
this.activeData.placeholder = this.activeData.placeholder.replace(oldVal, '') + val
|
||||
},
|
||||
activeId: {
|
||||
handler(val) {
|
||||
oldActiveId = val
|
||||
},
|
||||
immediate: true
|
||||
},
|
||||
drawingList: {
|
||||
handler(val) {
|
||||
this.saveDrawingListDebounce(val)
|
||||
if (val.length === 0) this.idGlobal = 100
|
||||
},
|
||||
deep: true
|
||||
},
|
||||
idGlobal: {
|
||||
handler(val) {
|
||||
this.saveIdGlobalDebounce(val)
|
||||
},
|
||||
immediate: true
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
if (this.editData.content) {
|
||||
let { id, name, info, content } = this.editData
|
||||
this.selfForm.name = name
|
||||
this.selfForm.id = id
|
||||
this.selfForm.info = info
|
||||
content = JSON.parse(content)
|
||||
this.drawingList = content.fields
|
||||
const _content = JSON.parse(JSON.stringify(content))
|
||||
delete _content.fields
|
||||
this.formConf = _content
|
||||
}
|
||||
// if (Array.isArray(drawingListInDB) && drawingListInDB.length > 0) {
|
||||
// this.drawingList = drawingListInDB
|
||||
// } else {
|
||||
// this.drawingList = drawingDefalut
|
||||
// }
|
||||
this.activeFormItem(this.drawingList[0])
|
||||
// if (formConfInDB) {
|
||||
// this.formConf = formConfInDB
|
||||
// }
|
||||
loadBeautifier(btf => {
|
||||
beautifier = btf
|
||||
})
|
||||
const clipboard = new ClipboardJS('#copyNode', {
|
||||
text: trigger => {
|
||||
const codeStr = this.generateCode()
|
||||
this.$notify({
|
||||
title: '成功',
|
||||
message: '代码已复制到剪切板,可粘贴。',
|
||||
type: 'success'
|
||||
})
|
||||
return codeStr
|
||||
}
|
||||
})
|
||||
clipboard.on('error', e => {
|
||||
this.$message.error('代码复制失败')
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
activeFormItem(element) {
|
||||
this.activeData = element
|
||||
this.activeId = element.__config__.formId
|
||||
},
|
||||
onEnd(obj) {
|
||||
if (obj.from !== obj.to) {
|
||||
this.activeData = tempActiveData
|
||||
this.activeId = this.idGlobal
|
||||
}
|
||||
},
|
||||
addComponent(item) {
|
||||
const clone = this.cloneComponent(item)
|
||||
this.drawingList.push(clone)
|
||||
this.activeFormItem(clone)
|
||||
},
|
||||
cloneComponent(origin) {
|
||||
const clone = JSON.parse(JSON.stringify(origin))
|
||||
const config = clone.__config__
|
||||
config.formId = ++this.idGlobal
|
||||
config.span = this.formConf.span
|
||||
config.renderKey = +new Date() // 改变renderKey后可以实现强制更新组件
|
||||
if (config.layout === 'colFormItem') {
|
||||
clone.__vModel__ = `field${this.idGlobal}`
|
||||
clone.placeholder !== undefined && (clone.placeholder += config.label)
|
||||
} else if (config.layout === 'rowFormItem') {
|
||||
config.componentName = `row${this.idGlobal}`
|
||||
config.gutter = this.formConf.gutter
|
||||
}
|
||||
tempActiveData = clone
|
||||
return tempActiveData
|
||||
},
|
||||
AssembleFormData() {
|
||||
this.formData = {
|
||||
fields: JSON.parse(JSON.stringify(this.drawingList)),
|
||||
...this.formConf
|
||||
}
|
||||
},
|
||||
generate(data) {
|
||||
const func = this[`exec${titleCase(this.operationType)}`]
|
||||
this.generateConf = data
|
||||
func && func(data)
|
||||
},
|
||||
execRun(data) {
|
||||
this.AssembleFormData()
|
||||
this.drawerVisible = true
|
||||
},
|
||||
execDownload(data) {
|
||||
const codeStr = this.generateCode()
|
||||
const blob = new Blob([codeStr], { type: 'text/plain;charset=utf-8' })
|
||||
saveAs(blob, data.fileName)
|
||||
},
|
||||
execCopy(data) {
|
||||
document.getElementById('copyNode').click()
|
||||
},
|
||||
empty() {
|
||||
this.$confirm('确定要清空所有组件吗?', '提示', { type: 'warning' }).then(
|
||||
() => {
|
||||
this.drawingList = []
|
||||
this.idGlobal = 100
|
||||
}
|
||||
)
|
||||
},
|
||||
drawingItemCopy(item, parent) {
|
||||
let clone = JSON.parse(JSON.stringify(item))
|
||||
clone = this.createIdAndKey(clone)
|
||||
parent.push(clone)
|
||||
this.activeFormItem(clone)
|
||||
},
|
||||
createIdAndKey(item) {
|
||||
const config = item.__config__
|
||||
config.formId = ++this.idGlobal
|
||||
config.renderKey = +new Date()
|
||||
if (config.layout === 'colFormItem') {
|
||||
item.__vModel__ = `field${this.idGlobal}`
|
||||
} else if (config.layout === 'rowFormItem') {
|
||||
config.componentName = `row${this.idGlobal}`
|
||||
}
|
||||
if (Array.isArray(config.children)) {
|
||||
config.children = config.children.map(childItem => this.createIdAndKey(childItem))
|
||||
}
|
||||
return item
|
||||
},
|
||||
drawingItemDelete(index, parent) {
|
||||
parent.splice(index, 1)
|
||||
this.$nextTick(() => {
|
||||
const len = this.drawingList.length
|
||||
if (len) {
|
||||
this.activeFormItem(this.drawingList[len - 1])
|
||||
}
|
||||
})
|
||||
},
|
||||
generateCode() {
|
||||
const { type } = this.generateConf
|
||||
this.AssembleFormData()
|
||||
const script = vueScript(makeUpJs(this.formData, type))
|
||||
const html = vueTemplate(makeUpHtml(this.formData, type))
|
||||
const css = cssStyle(makeUpCss(this.formData))
|
||||
return beautifier.html(html + script + css, beautifierConf.html)
|
||||
},
|
||||
showJson() {
|
||||
this.AssembleFormData()
|
||||
this.jsonDrawerVisible = true
|
||||
},
|
||||
handlerSaveJSON:Debounce(function(form) {
|
||||
// this.AssembleFormData()
|
||||
// loadBeautifier(btf => {
|
||||
// beautifier = btf
|
||||
// let jsonStr = JSON.stringify(this.formData)
|
||||
// this.beautifierJson = beautifier.js(jsonStr, beautifierConf.js)
|
||||
//
|
||||
// })
|
||||
this.$refs[form].validate(result => {
|
||||
if (!result) return
|
||||
const formConfig = getFormConfSelf()
|
||||
if (formConfig.fields.length === 0) {
|
||||
this.$message.error('表单配置数据不能为空')
|
||||
return
|
||||
}
|
||||
this.selfForm.content = JSON.stringify(formConfig)
|
||||
this.$emit('getFormConfigDataResult', this.selfForm)
|
||||
})
|
||||
}),
|
||||
download() {
|
||||
this.dialogVisible = true
|
||||
this.showFileName = true
|
||||
this.operationType = 'download'
|
||||
},
|
||||
run() {
|
||||
this.dialogVisible = true
|
||||
this.showFileName = false
|
||||
this.operationType = 'run'
|
||||
},
|
||||
copy() {
|
||||
this.dialogVisible = true
|
||||
this.showFileName = false
|
||||
this.operationType = 'copy'
|
||||
},
|
||||
tagChange(newTag) {
|
||||
newTag = this.cloneComponent(newTag)
|
||||
const config = newTag.__config__
|
||||
newTag.__vModel__ = this.activeData.__vModel__
|
||||
config.formId = this.activeId
|
||||
config.span = this.activeData.__config__.span
|
||||
this.activeData.__config__.tag = config.tag
|
||||
this.activeData.__config__.tagIcon = config.tagIcon
|
||||
this.activeData.__config__.document = config.document
|
||||
if (typeof this.activeData.__config__.defaultValue === typeof config.defaultValue) {
|
||||
config.defaultValue = this.activeData.__config__.defaultValue
|
||||
}
|
||||
Object.keys(newTag).forEach(key => {
|
||||
if (this.activeData[key] !== undefined) {
|
||||
newTag[key] = this.activeData[key]
|
||||
}
|
||||
})
|
||||
this.activeData = newTag
|
||||
this.updateDrawingList(newTag, this.drawingList)
|
||||
},
|
||||
updateDrawingList(newTag, list) {
|
||||
const index = list.findIndex(item => item.__config__.formId === this.activeId)
|
||||
if (index > -1) {
|
||||
list.splice(index, 1, newTag)
|
||||
} else {
|
||||
list.forEach(item => {
|
||||
if (Array.isArray(item.__config__.children)) this.updateDrawingList(newTag, item.__config__.children)
|
||||
})
|
||||
}
|
||||
},
|
||||
refreshJson(data) {
|
||||
this.drawingList = JSON.parse(JSON.stringify(data.fields))
|
||||
delete data.fields
|
||||
this.formConf = data
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang='scss'>
|
||||
@import '../styles/home';
|
||||
</style>
|
||||
@@ -0,0 +1,123 @@
|
||||
<template>
|
||||
<div class="icon-dialog">
|
||||
<el-dialog
|
||||
v-bind="$attrs"
|
||||
width="980px"
|
||||
:modal-append-to-body="false"
|
||||
v-on="$listeners"
|
||||
@open="onOpen"
|
||||
@close="onClose"
|
||||
>
|
||||
<div slot="title">
|
||||
选择图标
|
||||
<el-input
|
||||
v-model="key"
|
||||
size="mini"
|
||||
:style="{width: '260px'}"
|
||||
placeholder="请输入图标名称"
|
||||
prefix-icon="el-icon-search"
|
||||
clearable
|
||||
/>
|
||||
</div>
|
||||
<ul class="icon-ul">
|
||||
<li
|
||||
v-for="icon in iconList"
|
||||
:key="icon"
|
||||
:class="active===icon?'active-item':''"
|
||||
@click="onSelect(icon)"
|
||||
>
|
||||
<i :class="icon" />
|
||||
<div>{{ icon }}</div>
|
||||
</li>
|
||||
</ul>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import iconList from '../utils/icon.json'
|
||||
|
||||
const originList = iconList.map(name => `el-icon-${name}`)
|
||||
|
||||
export default {
|
||||
inheritAttrs: false,
|
||||
props: ['current'],
|
||||
data() {
|
||||
return {
|
||||
iconList: originList,
|
||||
active: null,
|
||||
key: ''
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
key(val) {
|
||||
if (val) {
|
||||
this.iconList = originList.filter(name => name.indexOf(val) > -1)
|
||||
} else {
|
||||
this.iconList = originList
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onOpen() {
|
||||
this.active = this.current
|
||||
this.key = ''
|
||||
},
|
||||
onClose() {},
|
||||
onSelect(icon) {
|
||||
this.active = icon
|
||||
this.$emit('select', icon)
|
||||
this.$emit('update:visible', false)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.icon-ul {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-size: 0;
|
||||
li {
|
||||
list-style-type: none;
|
||||
text-align: center;
|
||||
font-size: 14px;
|
||||
display: inline-block;
|
||||
width: 16.66%;
|
||||
box-sizing: border-box;
|
||||
height: 108px;
|
||||
padding: 15px 6px 6px 6px;
|
||||
cursor: pointer;
|
||||
overflow: hidden;
|
||||
&:hover {
|
||||
background: #f2f2f2;
|
||||
}
|
||||
&.active-item{
|
||||
background: #e1f3fb;
|
||||
color: #7a6df0
|
||||
}
|
||||
> i {
|
||||
font-size: 30px;
|
||||
line-height: 50px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.icon-dialog {
|
||||
::v-deep .el-dialog {
|
||||
border-radius: 8px;
|
||||
margin-bottom: 0;
|
||||
margin-top: 4vh !important;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
max-height: 92vh;
|
||||
overflow: hidden;
|
||||
box-sizing: border-box;
|
||||
.el-dialog__header {
|
||||
padding-top: 14px;
|
||||
}
|
||||
.el-dialog__body {
|
||||
margin: 0 20px 20px 20px;
|
||||
padding: 0;
|
||||
overflow: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,158 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-drawer v-bind="$attrs" append-to-body v-on="$listeners" @opened="onOpen" @close="onClose">
|
||||
<div class="action-bar" :style="{'text-align': 'left'}">
|
||||
<span class="bar-btn" @click="refresh">
|
||||
<i class="el-icon-refresh" />
|
||||
刷新
|
||||
</span>
|
||||
<span ref="copyBtn" class="bar-btn copy-json-btn">
|
||||
<i class="el-icon-document-copy" />
|
||||
复制JSON
|
||||
</span>
|
||||
<span class="bar-btn" @click="exportJsonFile">
|
||||
<i class="el-icon-download" />
|
||||
导出JSON文件
|
||||
</span>
|
||||
<span class="bar-btn delete-btn" @click="$emit('update:visible', false)">
|
||||
<i class="el-icon-circle-close" />
|
||||
关闭
|
||||
</span>
|
||||
</div>
|
||||
<div id="editorJson" class="json-editor" />
|
||||
</el-drawer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { beautifierConf } from '../utils/index'
|
||||
import ClipboardJS from 'clipboard'
|
||||
import { saveAs } from 'file-saver'
|
||||
import loadMonaco from '../utils/loadMonaco'
|
||||
import loadBeautifier from '../utils/loadBeautifier'
|
||||
// import * as monaco from "monaco-editor";
|
||||
|
||||
let beautifier
|
||||
let monaco
|
||||
|
||||
export default {
|
||||
components: {},
|
||||
props: {
|
||||
jsonStr: {
|
||||
type: String,
|
||||
required: true,
|
||||
beautifier: null,
|
||||
jsonEditor: null
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {}
|
||||
},
|
||||
computed: {},
|
||||
watch: {},
|
||||
created() {},
|
||||
mounted() {
|
||||
window.addEventListener('keydown', this.preventDefaultSave)
|
||||
const clipboard = new ClipboardJS('.copy-json-btn', {
|
||||
text: trigger => {
|
||||
this.$notify({
|
||||
title: '成功',
|
||||
message: '代码已复制到剪切板,可粘贴。',
|
||||
type: 'success'
|
||||
})
|
||||
return this.beautifierJson
|
||||
}
|
||||
})
|
||||
clipboard.on('error', e => {
|
||||
this.$message.error('代码复制失败')
|
||||
})
|
||||
},
|
||||
beforeDestroy() {
|
||||
window.removeEventListener('keydown', this.preventDefaultSave)
|
||||
},
|
||||
methods: {
|
||||
preventDefaultSave(e) {
|
||||
if (e.key === 's' && (e.metaKey || e.ctrlKey)) {
|
||||
e.preventDefault()
|
||||
}
|
||||
},
|
||||
onOpen() {
|
||||
loadBeautifier(btf => {
|
||||
beautifier = btf
|
||||
this.beautifierJson = beautifier.js(this.jsonStr, beautifierConf.js)
|
||||
|
||||
loadMonaco(val => {
|
||||
monaco = val
|
||||
this.setEditorValue('editorJson', this.beautifierJson)
|
||||
})
|
||||
})
|
||||
// monaco.editor.create(document.getElementById("editorJson"), {
|
||||
// value: [
|
||||
// "function x() {",
|
||||
// '\tconsole.integralLog("Hello world!");',
|
||||
// "}"].join(
|
||||
// "\n"
|
||||
// ),
|
||||
// language: "javascript",
|
||||
// theme: 'vs-dark',
|
||||
// automaticLayout: true
|
||||
// });
|
||||
},
|
||||
onClose() {},
|
||||
setEditorValue(id, codeStr) {
|
||||
if (this.jsonEditor) {
|
||||
this.jsonEditor.setValue(codeStr)
|
||||
} else {
|
||||
this.jsonEditor = monaco.editor.create(document.getElementById(id), {
|
||||
value: codeStr,
|
||||
theme: 'vs-dark',
|
||||
language: 'json',
|
||||
automaticLayout: true
|
||||
})
|
||||
// ctrl + s 刷新
|
||||
this.jsonEditor.onKeyDown(e => {
|
||||
if (e.keyCode === 49 && (e.metaKey || e.ctrlKey)) {
|
||||
this.refresh()
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
exportJsonFile() {
|
||||
this.$prompt('文件名:', '导出文件', {
|
||||
inputValue: `${+new Date()}.json`,
|
||||
closeOnClickModal: false,
|
||||
inputPlaceholder: '请输入文件名'
|
||||
}).then(({ value }) => {
|
||||
if (!value) value = `${+new Date()}.json`
|
||||
const codeStr = this.jsonEditor.getValue()
|
||||
const blob = new Blob([codeStr], { type: 'text/plain;charset=utf-8' })
|
||||
saveAs(blob, value)
|
||||
})
|
||||
},
|
||||
refresh() {
|
||||
try {
|
||||
this.$emit('refresh', JSON.parse(this.jsonEditor.getValue()))
|
||||
} catch (error) {
|
||||
this.$notify({
|
||||
title: '错误',
|
||||
message: 'JSON格式错误,请检查',
|
||||
type: 'error'
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '../styles/mixin';
|
||||
|
||||
::v-deep .el-drawer__header {
|
||||
display: none;
|
||||
}
|
||||
@include action-bar;
|
||||
|
||||
.json-editor{
|
||||
height: calc(100vh - 33px);
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,114 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-dialog
|
||||
v-bind="$attrs"
|
||||
title="外部资源引用"
|
||||
width="600px"
|
||||
:close-on-click-modal="false"
|
||||
v-on="$listeners"
|
||||
@open="onOpen"
|
||||
@close="onClose"
|
||||
>
|
||||
<el-input
|
||||
v-for="(item, index) in resources"
|
||||
:key="index"
|
||||
v-model="resources[index]"
|
||||
class="url-item"
|
||||
placeholder="请输入 css 或 js 资源路径"
|
||||
prefix-icon="el-icon-link"
|
||||
clearable
|
||||
>
|
||||
<el-button
|
||||
slot="append"
|
||||
icon="el-icon-delete"
|
||||
@click="deleteOne(index)"
|
||||
/>
|
||||
</el-input>
|
||||
<el-button-group class="add-item">
|
||||
<el-button
|
||||
plain
|
||||
@click="addOne('https://cdn.bootcss.com/jquery/1.8.3/jquery.min.js')"
|
||||
>
|
||||
jQuery1.8.3
|
||||
</el-button>
|
||||
<el-button
|
||||
plain
|
||||
@click="addOne('https://unpkg.com/http-vue-loader')"
|
||||
>
|
||||
http-vue-loader
|
||||
</el-button>
|
||||
<el-button
|
||||
icon="el-icon-circle-plus-outline"
|
||||
plain
|
||||
@click="addOne('')"
|
||||
>
|
||||
添加其他
|
||||
</el-button>
|
||||
</el-button-group>
|
||||
<div slot="footer">
|
||||
<el-button @click="close">
|
||||
取消
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
@click="handelConfirm"
|
||||
>
|
||||
确定
|
||||
</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
components: {},
|
||||
inheritAttrs: false,
|
||||
props: ['originResource'],
|
||||
data() {
|
||||
return {
|
||||
resources: null
|
||||
}
|
||||
},
|
||||
computed: {},
|
||||
watch: {},
|
||||
created() {},
|
||||
mounted() {},
|
||||
methods: {
|
||||
onOpen() {
|
||||
this.resources = this.originResource.length ? JSON.parse(JSON.stringify(this.originResource)) : ['']
|
||||
},
|
||||
onClose() {
|
||||
},
|
||||
close() {
|
||||
this.$emit('update:visible', false)
|
||||
},
|
||||
handelConfirm() {
|
||||
const results = this.resources.filter(item => !!item) || []
|
||||
this.$emit('save', results)
|
||||
this.close()
|
||||
if (results.length) {
|
||||
this.resources = results
|
||||
}
|
||||
},
|
||||
deleteOne(index) {
|
||||
this.resources.splice(index, 1)
|
||||
},
|
||||
addOne(url) {
|
||||
if (this.resources.indexOf(url) > -1) {
|
||||
this.$message('资源已存在')
|
||||
} else {
|
||||
this.resources.push(url)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.add-item{
|
||||
margin-top: 8px;
|
||||
}
|
||||
.url-item{
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
</style>
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,158 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-dialog
|
||||
v-bind="$attrs"
|
||||
:close-on-click-modal="false"
|
||||
:modal-append-to-body="false"
|
||||
v-on="$listeners"
|
||||
@open="onOpen"
|
||||
@close="onClose"
|
||||
>
|
||||
<el-row :gutter="0">
|
||||
<el-form
|
||||
ref="elForm"
|
||||
:model="formData"
|
||||
:rules="rules"
|
||||
size="small"
|
||||
label-width="100px"
|
||||
>
|
||||
<el-col :span="24">
|
||||
<el-form-item
|
||||
label="选项名"
|
||||
prop="label"
|
||||
>
|
||||
<el-input
|
||||
v-model="formData.label"
|
||||
placeholder="请输入选项名"
|
||||
clearable
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<el-form-item
|
||||
label="选项值"
|
||||
prop="value"
|
||||
>
|
||||
<el-input
|
||||
v-model="formData.value"
|
||||
placeholder="请输入选项值"
|
||||
clearable
|
||||
>
|
||||
<el-select
|
||||
slot="append"
|
||||
v-model="dataType"
|
||||
:style="{width: '100px'}"
|
||||
>
|
||||
<el-option
|
||||
v-for="(item, index) in dataTypeOptions"
|
||||
:key="index"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
:disabled="item.disabled"
|
||||
/>
|
||||
</el-select>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-form>
|
||||
</el-row>
|
||||
<div slot="footer">
|
||||
<el-button
|
||||
type="primary"
|
||||
@click="handelConfirm"
|
||||
>
|
||||
确定
|
||||
</el-button>
|
||||
<el-button @click="close">
|
||||
取消
|
||||
</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { isNumberStr } from '../utils/index'
|
||||
import { getTreeNodeId, saveTreeNodeId } from '../utils/db'
|
||||
|
||||
const id = getTreeNodeId()
|
||||
|
||||
export default {
|
||||
components: {},
|
||||
inheritAttrs: false,
|
||||
props: [],
|
||||
data() {
|
||||
return {
|
||||
id,
|
||||
formData: {
|
||||
label: undefined,
|
||||
value: undefined
|
||||
},
|
||||
rules: {
|
||||
label: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入选项名',
|
||||
trigger: 'blur'
|
||||
}
|
||||
],
|
||||
value: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入选项值',
|
||||
trigger: 'blur'
|
||||
}
|
||||
]
|
||||
},
|
||||
dataType: 'string',
|
||||
dataTypeOptions: [
|
||||
{
|
||||
label: '字符串',
|
||||
value: 'string'
|
||||
},
|
||||
{
|
||||
label: '数字',
|
||||
value: 'number'
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
computed: {},
|
||||
watch: {
|
||||
// eslint-disable-next-line func-names
|
||||
'formData.value': function(val) {
|
||||
this.dataType = isNumberStr(val) ? 'number' : 'string'
|
||||
},
|
||||
id(val) {
|
||||
saveTreeNodeId(val)
|
||||
}
|
||||
},
|
||||
created() {},
|
||||
mounted() {},
|
||||
methods: {
|
||||
onOpen() {
|
||||
this.formData = {
|
||||
label: undefined,
|
||||
value: undefined
|
||||
}
|
||||
},
|
||||
onClose() {},
|
||||
close() {
|
||||
this.$emit('update:visible', false)
|
||||
},
|
||||
handelConfirm() {
|
||||
this.$refs.elForm.validate(valid => {
|
||||
if (!valid) return
|
||||
if (this.dataType === 'number') {
|
||||
this.formData.value = parseFloat(this.formData.value)
|
||||
}
|
||||
this.formData.id = this.id++
|
||||
this.$emit('commit', this.formData)
|
||||
this.close()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
</style>
|
||||
@@ -0,0 +1,22 @@
|
||||
<template>
|
||||
<div>
|
||||
<router-view />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
mounted() {
|
||||
// 取消开始的loading动画
|
||||
const preLoader = document.querySelector('#pre-loader')
|
||||
preLoader.style.display = 'none'
|
||||
|
||||
// fix: firefox 下 拖拽 会新打卡一个选项卡
|
||||
// https://github.com/JakHuang/form-generator/issues/15
|
||||
document.body.ondrop = event => {
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,59 @@
|
||||
import Vue from 'vue'
|
||||
import { loadScriptQueue } from '@/utils/loadScript'
|
||||
import Tinymce from '@/components/tinymce'
|
||||
|
||||
Vue.component('tinymce', Tinymce)
|
||||
|
||||
const $previewApp = document.getElementById('previewApp')
|
||||
const childAttrs = {
|
||||
file: '',
|
||||
dialog: ' width="600px" class="dialog-width" v-if="visible" :visible.sync="visible" :modal-append-to-body="false" '
|
||||
}
|
||||
|
||||
window.addEventListener('message', init, false)
|
||||
|
||||
function buildLinks(links) {
|
||||
let strs = ''
|
||||
links.forEach(url => {
|
||||
strs += `<link href="${url}" rel="stylesheet">`
|
||||
})
|
||||
return strs
|
||||
}
|
||||
|
||||
function init(event) {
|
||||
if (event.data.type === 'refreshFrame') {
|
||||
const code = event.data.data
|
||||
const attrs = childAttrs[code.generateConf.type]
|
||||
let links = ''
|
||||
|
||||
if (Array.isArray(code.links) && code.links.length > 0) {
|
||||
links = buildLinks(code.links)
|
||||
}
|
||||
|
||||
$previewApp.innerHTML = `${links}<style>${code.css}</style><div id="app"></div>`
|
||||
|
||||
if (Array.isArray(code.scripts) && code.scripts.length > 0) {
|
||||
loadScriptQueue(code.scripts, () => {
|
||||
newVue(attrs, code.js, code.html)
|
||||
})
|
||||
} else {
|
||||
newVue(attrs, code.js, code.html)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function newVue(attrs, main, html) {
|
||||
main = eval(`(${main})`)
|
||||
main.template = `<div>${html}</div>`
|
||||
new Vue({
|
||||
components: {
|
||||
child: main
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
visible: true
|
||||
}
|
||||
},
|
||||
template: `<div><child ${attrs}/></div>`
|
||||
}).$mount('#app')
|
||||
}
|
||||
@@ -0,0 +1,271 @@
|
||||
$selectedColor: #f6f7ff;
|
||||
$lighterBlue: #409EFF;
|
||||
|
||||
.container-FromGen {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.components-list {
|
||||
padding: 8px;
|
||||
box-sizing: border-box;
|
||||
height: 100%;
|
||||
.components-item {
|
||||
display: inline-block;
|
||||
width: 48%;
|
||||
margin: 1%;
|
||||
transition: transform 0ms !important;
|
||||
}
|
||||
}
|
||||
.components-draggable{
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
.components-title{
|
||||
font-size: 14px;
|
||||
color: #222;
|
||||
margin: 6px 2px;
|
||||
.svg-icon{
|
||||
color: #666;
|
||||
font-size: 18px;
|
||||
}
|
||||
}
|
||||
|
||||
.components-body {
|
||||
padding: 8px 10px;
|
||||
background: $selectedColor;
|
||||
font-size: 12px;
|
||||
cursor: move;
|
||||
border: 1px dashed $selectedColor;
|
||||
border-radius: 3px;
|
||||
.svg-icon{
|
||||
color: #777;
|
||||
font-size: 15px;
|
||||
}
|
||||
&:hover {
|
||||
border: 1px dashed #787be8;
|
||||
color: #787be8;
|
||||
.svg-icon {
|
||||
color: #787be8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.left-board {
|
||||
width: 260px;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
height: 100vh;
|
||||
}
|
||||
.left-scrollbar{
|
||||
height: calc(100vh - 42px);
|
||||
overflow: hidden;
|
||||
}
|
||||
.center-scrollbar {
|
||||
height: calc(100vh - 42px);
|
||||
overflow: hidden;
|
||||
border-left: 1px solid #f1e8e8;
|
||||
border-right: 1px solid #f1e8e8;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.center-board {
|
||||
height: 100vh;
|
||||
width: auto;
|
||||
margin: 0 350px 0 260px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.empty-info{
|
||||
position: absolute;
|
||||
top: 46%;
|
||||
left: 0;
|
||||
right: 0;
|
||||
text-align: center;
|
||||
font-size: 18px;
|
||||
color: #ccb1ea;
|
||||
letter-spacing: 4px;
|
||||
}
|
||||
.action-bar{
|
||||
position: relative;
|
||||
height: 42px;
|
||||
text-align: right;
|
||||
padding: 0 15px;
|
||||
box-sizing: border-box;;
|
||||
border: 1px solid #f1e8e8;
|
||||
border-top: none;
|
||||
border-left: none;
|
||||
.delete-btn{
|
||||
color: #F56C6C;
|
||||
}
|
||||
}
|
||||
.logo-wrapper{
|
||||
position: relative;
|
||||
height: 42px;
|
||||
background: #fff;
|
||||
border-bottom: 1px solid #f1e8e8;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.logo{
|
||||
position: absolute;
|
||||
left: 12px;
|
||||
top: 6px;
|
||||
line-height: 30px;
|
||||
color: #00afff;
|
||||
font-weight: 600;
|
||||
font-size: 17px;
|
||||
white-space: nowrap;
|
||||
> img{
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
vertical-align: top;
|
||||
}
|
||||
.github{
|
||||
display: inline-block;
|
||||
vertical-align: sub;
|
||||
margin-left: 15px;
|
||||
> img{
|
||||
height: 22px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.center-board-row {
|
||||
padding: 12px 12px 15px 12px;
|
||||
box-sizing: border-box;
|
||||
& > .el-form {
|
||||
// 69 = 12+15+42
|
||||
height: calc(100vh - 69px);
|
||||
}
|
||||
}
|
||||
.drawing-board {
|
||||
height: 100%;
|
||||
position: relative;
|
||||
.components-body {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
font-size: 0;
|
||||
}
|
||||
.sortable-ghost {
|
||||
position: relative;
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
&::before {
|
||||
content: " ";
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
height: 3px;
|
||||
background: rgb(89, 89, 223);
|
||||
z-index: 2;
|
||||
}
|
||||
}
|
||||
.components-item.sortable-ghost {
|
||||
width: 100%;
|
||||
height: 60px;
|
||||
background-color: $selectedColor;
|
||||
}
|
||||
.active-from-item {
|
||||
& > .el-form-item{
|
||||
background: $selectedColor;
|
||||
border-radius: 6px;
|
||||
}
|
||||
& > .drawing-item-copy, & > .drawing-item-delete{
|
||||
display: initial;
|
||||
}
|
||||
& > .component-name{
|
||||
color: $lighterBlue;
|
||||
}
|
||||
}
|
||||
.el-form-item{
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
}
|
||||
.drawing-item{
|
||||
position: relative;
|
||||
cursor: move;
|
||||
&.unfocus-bordered:not(.active-from-item) > div:first-child {
|
||||
border: 1px dashed #ccc;
|
||||
}
|
||||
.el-form-item{
|
||||
padding: 12px 10px;
|
||||
}
|
||||
}
|
||||
.drawing-row-item{
|
||||
position: relative;
|
||||
cursor: move;
|
||||
box-sizing: border-box;
|
||||
border: 1px dashed #ccc;
|
||||
border-radius: 3px;
|
||||
padding: 0 2px;
|
||||
margin-bottom: 15px;
|
||||
.drawing-row-item {
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
.el-col{
|
||||
margin-top: 22px;
|
||||
}
|
||||
.el-form-item{
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.drag-wrapper{
|
||||
min-height: 80px;
|
||||
}
|
||||
&.active-from-item{
|
||||
border: 1px dashed $lighterBlue;
|
||||
}
|
||||
.component-name{
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
font-size: 12px;
|
||||
color: #bbb;
|
||||
display: inline-block;
|
||||
padding: 0 6px;
|
||||
}
|
||||
}
|
||||
.drawing-item, .drawing-row-item{
|
||||
&:hover {
|
||||
& > .el-form-item{
|
||||
background: $selectedColor;
|
||||
border-radius: 6px;
|
||||
}
|
||||
& > .drawing-item-copy, & > .drawing-item-delete{
|
||||
display: initial;
|
||||
}
|
||||
}
|
||||
& > .drawing-item-copy, & > .drawing-item-delete{
|
||||
display: none;
|
||||
position: absolute;
|
||||
top: -10px;
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
line-height: 22px;
|
||||
text-align: center;
|
||||
border-radius: 50%;
|
||||
font-size: 12px;
|
||||
border: 1px solid;
|
||||
cursor: pointer;
|
||||
z-index: 1;
|
||||
}
|
||||
& > .drawing-item-copy{
|
||||
right: 56px;
|
||||
border-color: $lighterBlue;
|
||||
color: $lighterBlue;
|
||||
background: #fff;
|
||||
&:hover{
|
||||
background: $lighterBlue;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
& > .drawing-item-delete{
|
||||
right: 24px;
|
||||
border-color: #F56C6C;
|
||||
color: #F56C6C;
|
||||
background: #fff;
|
||||
&:hover{
|
||||
background: #F56C6C;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
$editorTabsborderColor: #121315;
|
||||
body, html{
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background: #fff;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
text-rendering: optimizeLegibility;
|
||||
font-family: -apple-system,BlinkMacSystemFont,Segoe UI,Helvetica,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji;
|
||||
}
|
||||
|
||||
input, textarea{
|
||||
font-family: -apple-system,BlinkMacSystemFont,Segoe UI,Helvetica,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji;
|
||||
}
|
||||
|
||||
.editor-tabs{
|
||||
background: $editorTabsborderColor;
|
||||
.el-tabs__header{
|
||||
margin: 0;
|
||||
border-bottom-color: $editorTabsborderColor;
|
||||
.el-tabs__nav{
|
||||
border-color: $editorTabsborderColor;
|
||||
}
|
||||
}
|
||||
.el-tabs__item{
|
||||
height: 32px;
|
||||
line-height: 32px;
|
||||
color: #888a8e;
|
||||
border-left: 1px solid $editorTabsborderColor!important;
|
||||
background: #363636;
|
||||
margin-right: 5px;
|
||||
user-select: none;
|
||||
}
|
||||
.el-tabs__item.is-active{
|
||||
background: #1e1e1e;
|
||||
border-bottom-color: #1e1e1e!important;
|
||||
color: #fff;
|
||||
}
|
||||
.el-icon-edit{
|
||||
color: #f1fa8c;
|
||||
}
|
||||
.el-icon-document{
|
||||
color: #a95812;
|
||||
}
|
||||
}
|
||||
|
||||
// home
|
||||
.right-scrollbar {
|
||||
.el-scrollbar__view {
|
||||
padding: 12px 18px 15px 15px;
|
||||
}
|
||||
}
|
||||
.el-scrollbar__wrap {
|
||||
box-sizing: border-box;
|
||||
overflow-x: hidden !important;
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
.center-tabs{
|
||||
.el-tabs__header{
|
||||
margin-bottom: 0!important;
|
||||
}
|
||||
.el-tabs__item{
|
||||
width: 50%;
|
||||
text-align: center;
|
||||
}
|
||||
.el-tabs__nav{
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
.reg-item{
|
||||
padding: 12px 6px;
|
||||
background: #f8f8f8;
|
||||
position: relative;
|
||||
border-radius: 4px;
|
||||
.close-btn{
|
||||
position: absolute;
|
||||
right: -6px;
|
||||
top: -6px;
|
||||
display: block;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
line-height: 16px;
|
||||
background: rgba(0, 0, 0, 0.2);
|
||||
border-radius: 50%;
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
z-index: 1;
|
||||
cursor: pointer;
|
||||
font-size: 12px;
|
||||
&:hover{
|
||||
background: rgba(210, 23, 23, 0.5)
|
||||
}
|
||||
}
|
||||
& + .reg-item{
|
||||
margin-top: 18px;
|
||||
}
|
||||
}
|
||||
.action-bar{
|
||||
& .el-button+.el-button {
|
||||
margin-left: 15px;
|
||||
}
|
||||
& i {
|
||||
font-size: 20px;
|
||||
vertical-align: middle;
|
||||
position: relative;
|
||||
top: -1px;
|
||||
}
|
||||
}
|
||||
|
||||
.custom-tree-node{
|
||||
width: 100%;
|
||||
font-size: 14px;
|
||||
.node-operation{
|
||||
float: right;
|
||||
}
|
||||
i[class*="el-icon"] + i[class*="el-icon"]{
|
||||
margin-left: 6px;
|
||||
}
|
||||
.el-icon-plus{
|
||||
color: #409EFF;
|
||||
}
|
||||
.el-icon-delete{
|
||||
color: #157a0c;
|
||||
}
|
||||
}
|
||||
|
||||
.el-scrollbar__view{
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.el-rate{
|
||||
display: inline-block;
|
||||
vertical-align: text-top;
|
||||
}
|
||||
.el-upload__tip{
|
||||
line-height: 1.2;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
@mixin action-bar {
|
||||
.action-bar {
|
||||
height: 33px;
|
||||
background: #f2fafb;
|
||||
padding: 0 15px;
|
||||
box-sizing: border-box;
|
||||
|
||||
.bar-btn {
|
||||
display: inline-block;
|
||||
padding: 0 6px;
|
||||
line-height: 32px;
|
||||
color: #8285f5;
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
user-select: none;
|
||||
& i {
|
||||
font-size: 20px;
|
||||
}
|
||||
&:hover {
|
||||
color: #4348d4;
|
||||
}
|
||||
}
|
||||
.bar-btn + .bar-btn {
|
||||
margin-left: 8px;
|
||||
}
|
||||
.delete-btn {
|
||||
color: #f56c6c;
|
||||
&:hover {
|
||||
color: #ea0b30;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
const DRAWING_ITEMS = 'drawingItems'
|
||||
const DRAWING_ITEMS_VERSION = '1.1'
|
||||
const DRAWING_ITEMS_VERSION_KEY = 'DRAWING_ITEMS_VERSION'
|
||||
const DRAWING_ID = 'idGlobal'
|
||||
const TREE_NODE_ID = 'treeNodeId'
|
||||
const FORM_CONF = 'formConf'
|
||||
|
||||
export function getDrawingList() {
|
||||
// 加入缓存版本的概念,保证缓存数据与程序匹配
|
||||
const version = localStorage.getItem(DRAWING_ITEMS_VERSION_KEY)
|
||||
if (version !== DRAWING_ITEMS_VERSION) {
|
||||
localStorage.setItem(DRAWING_ITEMS_VERSION_KEY, DRAWING_ITEMS_VERSION)
|
||||
saveDrawingList([])
|
||||
return null
|
||||
}
|
||||
|
||||
const str = localStorage.getItem(DRAWING_ITEMS)
|
||||
if (str) return JSON.parse(str)
|
||||
return null
|
||||
}
|
||||
|
||||
export function saveDrawingList(list) {
|
||||
localStorage.setItem(DRAWING_ITEMS, JSON.stringify(list))
|
||||
}
|
||||
|
||||
export function getIdGlobal() {
|
||||
const str = localStorage.getItem(DRAWING_ID)
|
||||
if (str) return parseInt(str, 10)
|
||||
return 100
|
||||
}
|
||||
|
||||
export function saveIdGlobal(id) {
|
||||
localStorage.setItem(DRAWING_ID, `${id}`)
|
||||
}
|
||||
|
||||
export function getTreeNodeId() {
|
||||
const str = localStorage.getItem(TREE_NODE_ID)
|
||||
if (str) return parseInt(str, 10)
|
||||
return 100
|
||||
}
|
||||
|
||||
export function saveTreeNodeId(id) {
|
||||
localStorage.setItem(TREE_NODE_ID, `${id}`)
|
||||
}
|
||||
|
||||
export function getFormConf() {
|
||||
const str = localStorage.getItem(FORM_CONF)
|
||||
if (str) return JSON.parse(str)
|
||||
return null
|
||||
}
|
||||
|
||||
export function saveFormConf(obj) {
|
||||
localStorage.setItem(FORM_CONF, JSON.stringify(obj))
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据自己的需求获取配置的表单信息
|
||||
*/
|
||||
export function getFormConfSelf() {
|
||||
let formConfig = localStorage.getItem(FORM_CONF)
|
||||
formConfig = JSON.parse(formConfig)
|
||||
let formItemConfig = localStorage.getItem(DRAWING_ITEMS)
|
||||
if (!formConfig && !formItemConfig) return 'Error'
|
||||
formItemConfig = JSON.parse(formItemConfig)
|
||||
formConfig.fields = formItemConfig
|
||||
return formConfig
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
["platform-eleme","eleme","delete-solid","delete","s-tools","setting","user-solid","user","phone","phone-outline","more","more-outline","star-on","star-off","s-goods","goods","warning","warning-outline","question","info","remove","circle-plus","success","error","zoom-in","zoom-out","remove-outline","circle-plus-outline","circle-check","circle-close","s-help","help","minus","plus","check","close","picture","picture-outline","picture-outline-round","upload","upload2","download","camera-solid","camera","video-camera-solid","video-camera","message-solid","bell","s-cooperation","s-order","s-platform","s-fold","s-unfold","s-operation","s-promotion","s-home","s-release","s-ticket","s-management","s-open","s-shop","s-marketing","s-flag","s-comment","s-finance","s-claim","s-custom","s-opportunity","s-data","s-check","s-grid","menu","share","d-caret","caret-left","caret-right","caret-bottom","caret-top","bottom-left","bottom-right","back","right","bottom","top","top-left","top-right","arrow-left","arrow-right","arrow-down","arrow-up","d-arrow-left","d-arrow-right","video-pause","video-play","refresh","refresh-right","refresh-left","finished","sort","sort-up","sort-down","rank","loading","view","c-scale-to-original","date","edit","edit-outline","folder","folder-opened","folder-add","folder-remove","folder-delete","folder-checked","tickets","document-remove","document-delete","document-copy","document-checked","document","document-add","printer","paperclip","takeaway-box","search","monitor","attract","mobile","scissors","umbrella","headset","brush","mouse","coordinate","magic-stick","reading","data-line","data-board","pie-chart","data-analysis","collection-tag","film","suitcase","suitcase-1","receiving","collection","files","notebook-1","notebook-2","toilet-paper","office-building","school","table-lamp","house","no-smoking","smoking","shopping-cart-full","shopping-cart-1","shopping-cart-2","shopping-bag-1","shopping-bag-2","sold-out","sell","present","box","bank-card","money","coin","wallet","discount","price-tag","news","guide","male","female","thumb","cpu","link","connection","open","turn-off","set-up","chat-round","chat-line-round","chat-square","chat-dot-round","chat-dot-square","chat-line-square","message","postcard","position","turn-off-microphone","microphone","close-notification","bangzhu","time","odometer","crop","aim","switch-button","full-screen","copy-document","mic","stopwatch","medal-1","medal","trophy","trophy-1","first-aid-kit","discover","place","location","location-outline","location-information","add-location","delete-location","map-location","alarm-clock","timer","watch-1","watch","lock","unlock","key","service","mobile-phone","bicycle","truck","ship","basketball","football","soccer","baseball","wind-power","light-rain","lightning","heavy-rain","sunrise","sunrise-1","sunset","sunny","cloudy","partly-cloudy","cloudy-and-sunny","moon","moon-night","dish","dish-1","food","chicken","fork-spoon","knife-fork","burger","tableware","sugar","dessert","ice-cream","hot-water","water-cup","coffee-cup","cold-drink","goblet","goblet-full","goblet-square","goblet-square-full","refrigerator","grape","watermelon","cherry","apple","pear","orange","coffee","ice-tea","ice-drink","milk-tea","potato-strips","lollipop","ice-cream-square","ice-cream-round"]
|
||||
@@ -0,0 +1,102 @@
|
||||
/**
|
||||
* num 小于0,左缩进num*2个空格; 大于0,右缩进num*2个空格。
|
||||
* @param {string} str 代码
|
||||
* @param {number} num 缩进次数
|
||||
* @param {number} len 【可选】缩进单位,空格数
|
||||
*/
|
||||
export function indent(str, num, len = 2) {
|
||||
if (num === 0) return str
|
||||
const isLeft = num < 0; const result = []; let reg; let
|
||||
spaces = ''
|
||||
if (isLeft) {
|
||||
num *= -1
|
||||
reg = new RegExp(`(^\\s{0,${num * len}})`, 'g')
|
||||
} else {
|
||||
for (let i = 0; i < num * len; i++) spaces += ' '
|
||||
}
|
||||
|
||||
str.split('\n').forEach(line => {
|
||||
line = isLeft ? line.replace(reg, '') : spaces + line
|
||||
result.push(line)
|
||||
})
|
||||
return result.join('\n')
|
||||
}
|
||||
|
||||
// 首字母大小
|
||||
export function titleCase(str) {
|
||||
return str.replace(/( |^)[a-z]/g, L => L.toUpperCase())
|
||||
}
|
||||
|
||||
// 下划转驼峰
|
||||
export function camelCase(str) {
|
||||
return str.replace(/-[a-z]/g, str1 => str1.substr(-1).toUpperCase())
|
||||
}
|
||||
|
||||
export function isNumberStr(str) {
|
||||
return /^[+-]?(0|([1-9]\d*))(\.\d+)?$/g.test(str)
|
||||
}
|
||||
|
||||
export const exportDefault = 'export default '
|
||||
|
||||
export const beautifierConf = {
|
||||
html: {
|
||||
indent_size: '2',
|
||||
indent_char: ' ',
|
||||
max_preserve_newlines: '-1',
|
||||
preserve_newlines: false,
|
||||
keep_array_indentation: false,
|
||||
break_chained_methods: false,
|
||||
indent_scripts: 'separate',
|
||||
brace_style: 'end-expand',
|
||||
space_before_conditional: true,
|
||||
unescape_strings: false,
|
||||
jslint_happy: false,
|
||||
end_with_newline: true,
|
||||
wrap_line_length: '110',
|
||||
indent_inner_html: true,
|
||||
comma_first: false,
|
||||
e4x: true,
|
||||
indent_empty_lines: true
|
||||
},
|
||||
js: {
|
||||
indent_size: '2',
|
||||
indent_char: ' ',
|
||||
max_preserve_newlines: '-1',
|
||||
preserve_newlines: false,
|
||||
keep_array_indentation: false,
|
||||
break_chained_methods: false,
|
||||
indent_scripts: 'normal',
|
||||
brace_style: 'end-expand',
|
||||
space_before_conditional: true,
|
||||
unescape_strings: false,
|
||||
jslint_happy: true,
|
||||
end_with_newline: true,
|
||||
wrap_line_length: '110',
|
||||
indent_inner_html: true,
|
||||
comma_first: false,
|
||||
e4x: true,
|
||||
indent_empty_lines: true
|
||||
}
|
||||
}
|
||||
|
||||
function stringify(obj) {
|
||||
return JSON.stringify(obj, (key, val) => {
|
||||
if (typeof val === 'function') {
|
||||
return `${val}`
|
||||
}
|
||||
return val
|
||||
})
|
||||
}
|
||||
|
||||
function parse(str) {
|
||||
JSON.parse(str, (k, v) => {
|
||||
if (v.indexOf && v.indexOf('function') > -1) {
|
||||
return eval(`(${v})`)
|
||||
}
|
||||
return v
|
||||
})
|
||||
}
|
||||
|
||||
export function jsonClone(obj) {
|
||||
return parse(stringify(obj))
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
import loadScript from './loadScript'
|
||||
import ELEMENT from 'element-ui'
|
||||
|
||||
let beautifierObj
|
||||
|
||||
export default function loadBeautifier(cb) {
|
||||
if (beautifierObj) {
|
||||
cb(beautifierObj)
|
||||
return
|
||||
}
|
||||
|
||||
const loading = ELEMENT.Loading.service({
|
||||
fullscreen: true,
|
||||
lock: true,
|
||||
text: '格式化资源加载中...',
|
||||
spinner: 'el-icon-loading',
|
||||
background: 'rgba(255, 255, 255, 0.5)'
|
||||
})
|
||||
|
||||
loadScript('https://cdn.bootcss.com/js-beautify/1.10.2/beautifier.min.js', () => {
|
||||
loading.close()
|
||||
// eslint-disable-next-line no-undef
|
||||
beautifierObj = beautifier
|
||||
cb(beautifierObj)
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
import { loadScriptQueue } from './loadScript'
|
||||
import ELEMENT from 'element-ui'
|
||||
|
||||
// monaco-editor单例
|
||||
let monacoEidtor
|
||||
|
||||
/**
|
||||
* 动态加载monaco-editor cdn资源
|
||||
* @param {Function} cb 回调,必填
|
||||
*/
|
||||
export default function loadMonaco(cb) {
|
||||
if (monacoEidtor) {
|
||||
cb(monacoEidtor)
|
||||
return
|
||||
}
|
||||
|
||||
const vs = 'https://cdn.bootcss.com/monaco-editor/0.18.0/min/vs'
|
||||
|
||||
// 使用element ui实现加载提示
|
||||
const loading = ELEMENT.Loading.service({
|
||||
fullscreen: true,
|
||||
lock: true,
|
||||
text: '编辑器资源初始化中...',
|
||||
spinner: 'el-icon-loading',
|
||||
background: 'rgba(255, 255, 255, 0.5)'
|
||||
})
|
||||
|
||||
!window.require && (window.require = {})
|
||||
!window.require.paths && (window.require.paths = {})
|
||||
window.require.paths.vs = vs
|
||||
|
||||
loadScriptQueue([
|
||||
`${vs}/loader.js`,
|
||||
`${vs}/editor/editor.main.nls.js`,
|
||||
`${vs}/editor/editor.main.js`
|
||||
], () => {
|
||||
loading.close()
|
||||
// eslint-disable-next-line no-undef
|
||||
monacoEidtor = monaco
|
||||
cb(monacoEidtor)
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
const callbacks = {}
|
||||
|
||||
/**
|
||||
* 加载一个远程脚本
|
||||
* @param {String} src 一个远程脚本
|
||||
* @param {Function} callback 回调
|
||||
*/
|
||||
function loadScript(src, callback) {
|
||||
const existingScript = document.getElementById(src)
|
||||
const cb = callback || (() => {})
|
||||
if (!existingScript) {
|
||||
callbacks[src] = []
|
||||
const $script = document.createElement('script')
|
||||
$script.src = src
|
||||
$script.id = src
|
||||
$script.async = 1
|
||||
document.body.appendChild($script)
|
||||
const onEnd = 'onload' in $script ? stdOnEnd.bind($script) : ieOnEnd.bind($script)
|
||||
onEnd($script)
|
||||
}
|
||||
|
||||
callbacks[src].push(cb)
|
||||
|
||||
function stdOnEnd(script) {
|
||||
script.onload = () => {
|
||||
this.onerror = this.onload = null
|
||||
callbacks[src].forEach(item => {
|
||||
item(null, script)
|
||||
})
|
||||
delete callbacks[src]
|
||||
}
|
||||
script.onerror = () => {
|
||||
this.onerror = this.onload = null
|
||||
cb(new Error(`Failed to load ${src}`), script)
|
||||
}
|
||||
}
|
||||
|
||||
function ieOnEnd(script) {
|
||||
script.onreadystatechange = () => {
|
||||
if (this.readyState !== 'complete' && this.readyState !== 'loaded') return
|
||||
this.onreadystatechange = null
|
||||
callbacks[src].forEach(item => {
|
||||
item(null, script)
|
||||
})
|
||||
delete callbacks[src]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 顺序加载一组远程脚本
|
||||
* @param {Array} list 一组远程脚本
|
||||
* @param {Function} cb 回调
|
||||
*/
|
||||
export function loadScriptQueue(list, cb) {
|
||||
const first = list.shift()
|
||||
list.length ? loadScript(first, () => loadScriptQueue(list, cb)) : loadScript(first, cb)
|
||||
}
|
||||
|
||||
export default loadScript
|
||||
@@ -0,0 +1,26 @@
|
||||
import loadScript from './loadScript'
|
||||
import ELEMENT from 'element-ui'
|
||||
|
||||
let tinymceObj
|
||||
|
||||
export default function loadTinymce(cb) {
|
||||
if (tinymceObj) {
|
||||
cb(tinymceObj)
|
||||
return
|
||||
}
|
||||
|
||||
const loading = ELEMENT.Loading.service({
|
||||
fullscreen: true,
|
||||
lock: true,
|
||||
text: '富文本资源加载中...',
|
||||
spinner: 'el-icon-loading',
|
||||
background: 'rgba(255, 255, 255, 0.5)'
|
||||
})
|
||||
|
||||
loadScript('https://cdn.bootcdn.net/ajax/libs/tinymce/5.2.2/tinymce.min.js', () => {
|
||||
loading.close()
|
||||
// eslint-disable-next-line no-undef
|
||||
tinymceObj = tinymce
|
||||
cb(tinymceObj)
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
<template>
|
||||
<div style="padding: 0 15px;" @click="toggleClick">
|
||||
<svg
|
||||
:class="{'is-active':isActive}"
|
||||
class="hamburger"
|
||||
viewBox="0 0 1024 1024"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="64"
|
||||
height="64"
|
||||
>
|
||||
<path d="M408 442h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm-8 204c0 4.4 3.6 8 8 8h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56zm504-486H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 632H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM142.4 642.1L298.7 519a8.84 8.84 0 0 0 0-13.9L142.4 381.9c-5.8-4.6-14.4-.5-14.4 6.9v246.3a8.9 8.9 0 0 0 14.4 7z" />
|
||||
</svg>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'Hamburger',
|
||||
props: {
|
||||
isActive: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
toggleClick() {
|
||||
this.$emit('toggleClick')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.hamburger {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
.hamburger.is-active {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,185 @@
|
||||
<template>
|
||||
<div :class="{'show':show}" class="header-search">
|
||||
<i class="iconfont iconios-search" style="font-size: 20px;" @click.stop="click"></i>
|
||||
<!--<svg-icon class-name="search-icon" icon-class="search" @click.stop="click" />-->
|
||||
<el-select
|
||||
ref="headerSearchSelect"
|
||||
v-model="search"
|
||||
:remote-method="querySearch"
|
||||
filterable
|
||||
default-first-option
|
||||
remote
|
||||
placeholder="搜索菜单"
|
||||
class="header-search-select"
|
||||
@change="change"
|
||||
>
|
||||
<el-option v-for="item in options" :key="item.url" :value="item" :label="item.name.join(' > ')" />
|
||||
</el-select>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// fuse is a lightweight fuzzy-search module
|
||||
// make search results more in line with expectations
|
||||
import Fuse from 'fuse.js'
|
||||
import path from 'path'
|
||||
import { mapGetters } from 'vuex'
|
||||
export default {
|
||||
name: 'HeaderSearch',
|
||||
data() {
|
||||
return {
|
||||
search: '',
|
||||
options: [],
|
||||
searchPool: [],
|
||||
show: false,
|
||||
fuse: undefined
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters([
|
||||
'permission_routes'
|
||||
]),
|
||||
// routes() {
|
||||
// return this.$store.getters.permission_routes
|
||||
// }
|
||||
},
|
||||
watch: {
|
||||
routes(n) {
|
||||
this.searchPool = this.generateRoutes(this.permission_routes)
|
||||
},
|
||||
searchPool(list) {
|
||||
this.initFuse(list)
|
||||
},
|
||||
show(value) {
|
||||
if (value) {
|
||||
document.body.addEventListener('click', this.close)
|
||||
} else {
|
||||
document.body.removeEventListener('click', this.close)
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.searchPool = this.generateRoutes(this.permission_routes)
|
||||
},
|
||||
methods: {
|
||||
click() {
|
||||
this.show = !this.show
|
||||
if (this.show) {
|
||||
this.$refs.headerSearchSelect && this.$refs.headerSearchSelect.focus()
|
||||
}
|
||||
},
|
||||
close() {
|
||||
this.$refs.headerSearchSelect && this.$refs.headerSearchSelect.blur()
|
||||
this.options = []
|
||||
this.show = false
|
||||
},
|
||||
change(val) {
|
||||
this.$router.push(val.path)
|
||||
this.search = ''
|
||||
this.options = []
|
||||
this.$nextTick(() => {
|
||||
this.show = false
|
||||
})
|
||||
},
|
||||
initFuse(list) {
|
||||
this.fuse = new Fuse(list, {
|
||||
shouldSort: true,
|
||||
threshold: 0.4,
|
||||
location: 0,
|
||||
distance: 100,
|
||||
maxPatternLength: 32,
|
||||
minMatchCharLength: 1,
|
||||
keys: [{
|
||||
name: 'name',
|
||||
weight: 0.7
|
||||
}, {
|
||||
name: 'url',
|
||||
weight: 0.3
|
||||
}]
|
||||
})
|
||||
},
|
||||
// Filter out the routes that can be displayed in the sidebar
|
||||
// And generate the internationalized title
|
||||
generateRoutes(routes, basePath = '/', prefixTitle = []) {
|
||||
let res = []
|
||||
for (const router of routes) {
|
||||
// skip hidden router
|
||||
if (router.hidden) { continue }
|
||||
|
||||
const data = {
|
||||
path: path.resolve(basePath, router.url),
|
||||
name: [...prefixTitle],
|
||||
children: router.child || []
|
||||
}
|
||||
|
||||
if (router.name) {
|
||||
data.name = [...data.name, router.name]
|
||||
|
||||
if (router.redirect !== 'noRedirect') {
|
||||
// only push the routes with title
|
||||
// special case: need to exclude parent router without redirect
|
||||
res.push(data)
|
||||
}
|
||||
}
|
||||
|
||||
// recursive child routes
|
||||
if (router.child) {
|
||||
const tempRoutes = this.generateRoutes(router.child, data.url, data.name)
|
||||
if (tempRoutes.length >= 1) {
|
||||
res = [...res, ...tempRoutes]
|
||||
}
|
||||
}
|
||||
}
|
||||
return res
|
||||
},
|
||||
querySearch(query) {
|
||||
if (query !== '') {
|
||||
this.options = this.fuse.search(query)
|
||||
} else {
|
||||
this.options = []
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.header-search {
|
||||
font-size: 0 !important;
|
||||
display: inline-flex !important;
|
||||
cursor: pointer;
|
||||
.search-icon {
|
||||
cursor: pointer;
|
||||
font-size: 18px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.header-search-select {
|
||||
font-size: 18px;
|
||||
transition: width 0.2s;
|
||||
width: 0;
|
||||
overflow: hidden;
|
||||
background: transparent;
|
||||
border-radius: 0;
|
||||
display: inline-block;
|
||||
/*vertical-align: middle;*/
|
||||
line-height: 50px;
|
||||
::v-deep .el-input__inner {
|
||||
border-radius: 0;
|
||||
border: 0;
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
box-shadow: none !important;
|
||||
/*border-bottom: 1px solid #d9d9d9;*/
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
|
||||
&.show {
|
||||
.header-search-select {
|
||||
width: 210px;
|
||||
margin-left: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,142 @@
|
||||
<template>
|
||||
<div :style="{zIndex:zIndex,height:height,width:width}" class="pan-item">
|
||||
<div class="pan-info">
|
||||
<div class="pan-info-roles-container">
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
<!-- eslint-disable-next-line -->
|
||||
<div :style="{backgroundImage: `url(${image})`}" class="pan-thumb"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'PanThumb',
|
||||
props: {
|
||||
image: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
zIndex: {
|
||||
type: Number,
|
||||
default: 1
|
||||
},
|
||||
width: {
|
||||
type: String,
|
||||
default: '150px'
|
||||
},
|
||||
height: {
|
||||
type: String,
|
||||
default: '150px'
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.pan-item {
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
border-radius: 50%;
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
cursor: default;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.pan-info-roles-container {
|
||||
padding: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.pan-thumb {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-position: center center;
|
||||
background-size: cover;
|
||||
border-radius: 50%;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
transform-origin: 95% 40%;
|
||||
transition: all 0.3s ease-in-out;
|
||||
}
|
||||
|
||||
/* .pan-thumb:after {
|
||||
content: '';
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
position: absolute;
|
||||
border-radius: 50%;
|
||||
top: 40%;
|
||||
left: 95%;
|
||||
margin: -4px 0 0 -4px;
|
||||
background: radial-gradient(ellipse at center, rgba(14, 14, 14, 1) 0%, rgba(125, 126, 125, 1) 100%);
|
||||
box-shadow: 0 0 1px rgba(255, 255, 255, 0.9);
|
||||
} */
|
||||
|
||||
.pan-info {
|
||||
position: absolute;
|
||||
width: inherit;
|
||||
height: inherit;
|
||||
border-radius: 50%;
|
||||
overflow: hidden;
|
||||
box-shadow: inset 0 0 0 5px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.pan-info h3 {
|
||||
color: #fff;
|
||||
text-transform: uppercase;
|
||||
position: relative;
|
||||
letter-spacing: 2px;
|
||||
font-size: 18px;
|
||||
margin: 0 60px;
|
||||
padding: 22px 0 0 0;
|
||||
height: 85px;
|
||||
font-family: 'Open Sans', Arial, sans-serif;
|
||||
text-shadow: 0 0 1px #fff, 0 1px 2px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.pan-info p {
|
||||
color: #fff;
|
||||
padding: 10px 5px;
|
||||
font-style: italic;
|
||||
margin: 0 30px;
|
||||
font-size: 12px;
|
||||
border-top: 1px solid rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
|
||||
.pan-info p a {
|
||||
display: block;
|
||||
color: #333;
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
background: rgba(255, 255, 255, 0.3);
|
||||
border-radius: 50%;
|
||||
color: #fff;
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
font-size: 9px;
|
||||
letter-spacing: 1px;
|
||||
padding-top: 24px;
|
||||
margin: 7px auto 0;
|
||||
font-family: 'Open Sans', Arial, sans-serif;
|
||||
opacity: 0;
|
||||
transition: transform 0.3s ease-in-out 0.2s, opacity 0.3s ease-in-out 0.2s, background 0.2s linear 0s;
|
||||
transform: translateX(60px) rotate(90deg);
|
||||
}
|
||||
|
||||
.pan-info p a:hover {
|
||||
background: rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
|
||||
.pan-item:hover .pan-thumb {
|
||||
transform: rotate(-110deg);
|
||||
}
|
||||
|
||||
.pan-item:hover .pan-info p a {
|
||||
opacity: 1;
|
||||
transform: translateX(0px) rotate(0deg);
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,3 @@
|
||||
<template >
|
||||
<router-view />
|
||||
</template>
|
||||
@@ -0,0 +1,149 @@
|
||||
<template>
|
||||
<div ref="rightPanel" :class="{show:show}" class="rightPanel-container">
|
||||
<div class="rightPanel-background" />
|
||||
<div class="rightPanel">
|
||||
<div class="rightPanel-items">
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { addClass, removeClass } from '@/utils'
|
||||
|
||||
export default {
|
||||
name: 'RightPanel',
|
||||
props: {
|
||||
clickNotClose: {
|
||||
default: false,
|
||||
type: Boolean
|
||||
},
|
||||
buttonTop: {
|
||||
default: 250,
|
||||
type: Number
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
show: {
|
||||
get() {
|
||||
return this.$store.state.settings.showSettings
|
||||
},
|
||||
set(val) {
|
||||
this.$store.dispatch('settings/changeSetting', {
|
||||
key: 'showSettings',
|
||||
value: val
|
||||
})
|
||||
}
|
||||
},
|
||||
theme() {
|
||||
return this.$store.state.settings.theme
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
show(value) {
|
||||
if (value && !this.clickNotClose) {
|
||||
this.addEventClick()
|
||||
}
|
||||
if (value) {
|
||||
addClass(document.body, 'showRightPanel')
|
||||
} else {
|
||||
removeClass(document.body, 'showRightPanel')
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.insertToBody()
|
||||
this.addEventClick()
|
||||
},
|
||||
beforeDestroy() {
|
||||
const elx = this.$refs.rightPanel
|
||||
elx.remove()
|
||||
},
|
||||
methods: {
|
||||
addEventClick() {
|
||||
window.addEventListener('click', this.closeSidebar)
|
||||
},
|
||||
closeSidebar(evt) {
|
||||
const parent = evt.target.closest('.rightPanel')
|
||||
if (!parent) {
|
||||
this.show = false
|
||||
window.removeEventListener('click', this.closeSidebar)
|
||||
}
|
||||
},
|
||||
insertToBody() {
|
||||
const elx = this.$refs.rightPanel
|
||||
const body = document.querySelector('body')
|
||||
body.insertBefore(elx, body.firstChild)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.showRightPanel {
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
width: calc(100% - 15px);
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.rightPanel-background {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
opacity: 0;
|
||||
transition: opacity .3s cubic-bezier(0, 0, .25, 1);
|
||||
background: rgba(0, 0, 0, .2);
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
.rightPanel {
|
||||
width: 100%;
|
||||
max-width: 260px;
|
||||
height: 100vh;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
box-shadow: 0px 0px 15px 0px rgba(0, 0, 0, .05);
|
||||
transition: all .25s cubic-bezier(0, 0, .25, 1);
|
||||
transform: translate(100%);
|
||||
background: #fff;
|
||||
z-index: 40000;
|
||||
}
|
||||
|
||||
.show {
|
||||
transition: all .3s cubic-bezier(0, 0, .25, 1);
|
||||
|
||||
.rightPanel-background {
|
||||
z-index: 20000;
|
||||
opacity: 1;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.rightPanel {
|
||||
transform: translate(0);
|
||||
}
|
||||
}
|
||||
|
||||
.handle-button {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
position: absolute;
|
||||
left: -48px;
|
||||
text-align: center;
|
||||
font-size: 24px;
|
||||
border-radius: 6px 0 0 6px !important;
|
||||
z-index: 0;
|
||||
pointer-events: auto;
|
||||
cursor: pointer;
|
||||
color: #fff;
|
||||
line-height: 48px;
|
||||
i {
|
||||
font-size: 24px;
|
||||
line-height: 48px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,61 @@
|
||||
<template>
|
||||
<div>
|
||||
<i class="iconfont iconios-qr-scanner" style="font-size: 20px;" @click="click"></i>
|
||||
<!--<svg-icon :icon-class="isFullscreen?'exit-fullscreen':'fullscreen'" @click="click" />-->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import screenfull from 'screenfull'
|
||||
|
||||
export default {
|
||||
name: 'Screenfull',
|
||||
data() {
|
||||
return {
|
||||
isFullscreen: false
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.init()
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.destroy()
|
||||
},
|
||||
methods: {
|
||||
click() {
|
||||
if (!screenfull.enabled) {
|
||||
this.$message({
|
||||
message: 'you browser can not work',
|
||||
type: 'warning'
|
||||
})
|
||||
return false
|
||||
}
|
||||
screenfull.toggle()
|
||||
},
|
||||
change() {
|
||||
this.isFullscreen = screenfull.isFullscreen
|
||||
},
|
||||
init() {
|
||||
if (screenfull.enabled) {
|
||||
screenfull.on('change', this.change)
|
||||
}
|
||||
},
|
||||
destroy() {
|
||||
if (screenfull.enabled) {
|
||||
screenfull.off('change', this.change)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.screenfull-svg {
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
fill: #5a5e66;;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
vertical-align: 10px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,103 @@
|
||||
<template>
|
||||
<div :class="{active:isActive}" class="share-dropdown-menu">
|
||||
<div class="share-dropdown-menu-wrapper">
|
||||
<span class="share-dropdown-menu-title" @click.self="clickTitle">{{ title }}</span>
|
||||
<div v-for="(item,index) of items" :key="index" class="share-dropdown-menu-item">
|
||||
<a v-if="item.href" :href="item.href" target="_blank">{{ item.title }}</a>
|
||||
<span v-else>{{ item.title }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
items: {
|
||||
type: Array,
|
||||
default: function() {
|
||||
return []
|
||||
}
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
default: 'vue'
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isActive: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
clickTitle() {
|
||||
this.isActive = !this.isActive
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" >
|
||||
$n: 9; //和items.length 相同
|
||||
$t: .1s;
|
||||
.share-dropdown-menu {
|
||||
width: 250px;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
height: auto!important;
|
||||
&-title {
|
||||
width: 100%;
|
||||
display: block;
|
||||
cursor: pointer;
|
||||
background: black;
|
||||
color: white;
|
||||
height: 60px;
|
||||
line-height: 60px;
|
||||
font-size: 20px;
|
||||
text-align: center;
|
||||
z-index: 2;
|
||||
transform: translate3d(0,0,0);
|
||||
}
|
||||
&-wrapper {
|
||||
position: relative;
|
||||
}
|
||||
&-item {
|
||||
text-align: center;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
background: #e0e0e0;
|
||||
color: #000;
|
||||
line-height: 60px;
|
||||
height: 60px;
|
||||
cursor: pointer;
|
||||
font-size: 18px;
|
||||
overflow: hidden;
|
||||
opacity: 1;
|
||||
transition: transform 0.28s ease;
|
||||
&:hover {
|
||||
background: black;
|
||||
color: white;
|
||||
}
|
||||
@for $i from 1 through $n {
|
||||
&:nth-of-type(#{$i}) {
|
||||
z-index: -1;
|
||||
transition-delay: $i*$t;
|
||||
transform: translate3d(0, -60px, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
&.active {
|
||||
.share-dropdown-menu-wrapper {
|
||||
z-index: 1;
|
||||
}
|
||||
.share-dropdown-menu-item {
|
||||
@for $i from 1 through $n {
|
||||
&:nth-of-type(#{$i}) {
|
||||
transition-delay: ($n - $i)*$t;
|
||||
transform: translate3d(0, ($i - 1)*60px, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,91 @@
|
||||
<template>
|
||||
<div :style="{height:height+'px',zIndex:zIndex}">
|
||||
<div
|
||||
:class="className"
|
||||
:style="{top:(isSticky ? stickyTop +'px' : ''),zIndex:zIndex,position:position,width:width,height:height+'px'}"
|
||||
>
|
||||
<slot>
|
||||
<div>sticky</div>
|
||||
</slot>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'Sticky',
|
||||
props: {
|
||||
stickyTop: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
zIndex: {
|
||||
type: Number,
|
||||
default: 1
|
||||
},
|
||||
className: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
active: false,
|
||||
position: '',
|
||||
width: undefined,
|
||||
height: undefined,
|
||||
isSticky: false
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.height = this.$el.getBoundingClientRect().height
|
||||
window.addEventListener('scroll', this.handleScroll)
|
||||
window.addEventListener('resize', this.handleResize)
|
||||
},
|
||||
activated() {
|
||||
this.handleScroll()
|
||||
},
|
||||
destroyed() {
|
||||
window.removeEventListener('scroll', this.handleScroll)
|
||||
window.removeEventListener('resize', this.handleResize)
|
||||
},
|
||||
methods: {
|
||||
sticky() {
|
||||
if (this.active) {
|
||||
return
|
||||
}
|
||||
this.position = 'fixed'
|
||||
this.active = true
|
||||
this.width = this.width + 'px'
|
||||
this.isSticky = true
|
||||
},
|
||||
handleReset() {
|
||||
if (!this.active) {
|
||||
return
|
||||
}
|
||||
this.reset()
|
||||
},
|
||||
reset() {
|
||||
this.position = ''
|
||||
this.width = 'auto'
|
||||
this.active = false
|
||||
this.isSticky = false
|
||||
},
|
||||
handleScroll() {
|
||||
const width = this.$el.getBoundingClientRect().width
|
||||
this.width = width || 'auto'
|
||||
const offsetTop = this.$el.getBoundingClientRect().top
|
||||
if (offsetTop < this.stickyTop) {
|
||||
this.sticky()
|
||||
return
|
||||
}
|
||||
this.handleReset()
|
||||
},
|
||||
handleResize() {
|
||||
if (this.isSticky) {
|
||||
this.width = this.$el.getBoundingClientRect().width + 'px'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,62 @@
|
||||
<template>
|
||||
<div v-if="isExternal" :style="styleExternalIcon" class="svg-external-icon svg-icon" v-on="$listeners" />
|
||||
<svg v-else :class="svgClass" aria-hidden="true" v-on="$listeners">
|
||||
<use :xlink:href="iconName" />
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// doc: https://panjiachen.github.io/vue-element-admin-site/feature/component/svg-icon.html#usage
|
||||
import { isExternal } from '@/utils/validate'
|
||||
|
||||
export default {
|
||||
name: 'SvgIcon',
|
||||
props: {
|
||||
iconClass: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
className: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
isExternal() {
|
||||
return isExternal(this.iconClass)
|
||||
},
|
||||
iconName() {
|
||||
return `#icon-${this.iconClass}`
|
||||
},
|
||||
svgClass() {
|
||||
if (this.className) {
|
||||
return 'svg-icon ' + this.className
|
||||
} else {
|
||||
return 'svg-icon'
|
||||
}
|
||||
},
|
||||
styleExternalIcon() {
|
||||
return {
|
||||
mask: `url(${this.iconClass}) no-repeat 50% 50%`,
|
||||
'-webkit-mask': `url(${this.iconClass}) no-repeat 50% 50%`
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.svg-icon {
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
vertical-align: -0.15em;
|
||||
fill: currentColor;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.svg-external-icon {
|
||||
background-color: currentColor;
|
||||
mask-size: cover!important;
|
||||
display: inline-block;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,174 @@
|
||||
<template>
|
||||
<el-color-picker
|
||||
v-model="theme"
|
||||
:predefine="['#409EFF', '#1890ff', '#304156','#212121','#11a983', '#13c2c2', '#6959CD', '#f5222d', ]"
|
||||
class="theme-picker"
|
||||
popper-class="theme-picker-dropdown"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
const version = require('element-ui/package.json').version // element-ui version from node_modules
|
||||
const ORIGINAL_THEME = '#409EFF' // default color
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
chalk: '', // content of theme-chalk css
|
||||
theme: ''
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
defaultTheme() {
|
||||
return this.$store.state.settings.theme
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
defaultTheme: {
|
||||
handler: function(val, oldVal) {
|
||||
this.theme = val
|
||||
},
|
||||
immediate: true
|
||||
},
|
||||
async theme(val) {
|
||||
const oldVal = this.chalk ? this.theme : ORIGINAL_THEME
|
||||
if (typeof val !== 'string') return
|
||||
const themeCluster = this.getThemeCluster(val.replace('#', ''))
|
||||
const originalCluster = this.getThemeCluster(oldVal.replace('#', ''))
|
||||
|
||||
const $message = this.$message({
|
||||
message: ' Compiling the theme',
|
||||
customClass: 'theme-message',
|
||||
type: 'success',
|
||||
duration: 0,
|
||||
iconClass: 'el-icon-loading'
|
||||
})
|
||||
|
||||
const getHandler = (variable, id) => {
|
||||
return () => {
|
||||
const originalCluster = this.getThemeCluster(ORIGINAL_THEME.replace('#', ''))
|
||||
const newStyle = this.updateStyle(this[variable], originalCluster, themeCluster)
|
||||
|
||||
let styleTag = document.getElementById(id)
|
||||
if (!styleTag) {
|
||||
styleTag = document.createElement('style')
|
||||
styleTag.setAttribute('id', id)
|
||||
document.head.appendChild(styleTag)
|
||||
}
|
||||
styleTag.innerText = newStyle
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.chalk) {
|
||||
const url = `https://unpkg.com/element-ui@${version}/lib/theme-chalk/index.css`
|
||||
await this.getCSSString(url, 'chalk')
|
||||
}
|
||||
|
||||
const chalkHandler = getHandler('chalk', 'chalk-style')
|
||||
|
||||
chalkHandler()
|
||||
|
||||
const styles = [].slice.call(document.querySelectorAll('style'))
|
||||
.filter(style => {
|
||||
const text = style.innerText
|
||||
return new RegExp(oldVal, 'i').test(text) && !/Chalk Variables/.test(text)
|
||||
})
|
||||
styles.forEach(style => {
|
||||
const { innerText } = style
|
||||
if (typeof innerText !== 'string') return
|
||||
style.innerText = this.updateStyle(innerText, originalCluster, themeCluster)
|
||||
})
|
||||
|
||||
this.$emit('change', val)
|
||||
|
||||
$message.close()
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
updateStyle(style, oldCluster, newCluster) {
|
||||
let newStyle = style
|
||||
oldCluster.forEach((color, index) => {
|
||||
newStyle = newStyle.replace(new RegExp(color, 'ig'), newCluster[index])
|
||||
})
|
||||
return newStyle
|
||||
},
|
||||
|
||||
getCSSString(url, variable) {
|
||||
return new Promise(resolve => {
|
||||
const xhr = new XMLHttpRequest()
|
||||
xhr.onreadystatechange = () => {
|
||||
if (xhr.readyState === 4 && xhr.status === 200) {
|
||||
this[variable] = xhr.responseText.replace(/@font-face{[^}]+}/, '')
|
||||
resolve()
|
||||
}
|
||||
}
|
||||
xhr.open('GET', url)
|
||||
xhr.send()
|
||||
})
|
||||
},
|
||||
|
||||
getThemeCluster(theme) {
|
||||
const tintColor = (color, tint) => {
|
||||
let red = parseInt(color.slice(0, 2), 16)
|
||||
let green = parseInt(color.slice(2, 4), 16)
|
||||
let blue = parseInt(color.slice(4, 6), 16)
|
||||
|
||||
if (tint === 0) { // when primary color is in its rgb space
|
||||
return [red, green, blue].join(',')
|
||||
} else {
|
||||
red += Math.round(tint * (255 - red))
|
||||
green += Math.round(tint * (255 - green))
|
||||
blue += Math.round(tint * (255 - blue))
|
||||
|
||||
red = red.toString(16)
|
||||
green = green.toString(16)
|
||||
blue = blue.toString(16)
|
||||
|
||||
return `#${red}${green}${blue}`
|
||||
}
|
||||
}
|
||||
|
||||
const shadeColor = (color, shade) => {
|
||||
let red = parseInt(color.slice(0, 2), 16)
|
||||
let green = parseInt(color.slice(2, 4), 16)
|
||||
let blue = parseInt(color.slice(4, 6), 16)
|
||||
|
||||
red = Math.round((1 - shade) * red)
|
||||
green = Math.round((1 - shade) * green)
|
||||
blue = Math.round((1 - shade) * blue)
|
||||
|
||||
red = red.toString(16)
|
||||
green = green.toString(16)
|
||||
blue = blue.toString(16)
|
||||
|
||||
return `#${red}${green}${blue}`
|
||||
}
|
||||
|
||||
const clusters = [theme]
|
||||
for (let i = 0; i <= 9; i++) {
|
||||
clusters.push(tintColor(theme, Number((i / 10).toFixed(2))))
|
||||
}
|
||||
clusters.push(shadeColor(theme, 0.1))
|
||||
return clusters
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.theme-message,
|
||||
.theme-picker-dropdown {
|
||||
z-index: 99999 !important;
|
||||
}
|
||||
|
||||
.theme-picker .el-color-picker__trigger {
|
||||
height: 26px !important;
|
||||
width: 26px !important;
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
.theme-picker-dropdown .el-color-dropdown__link-btn {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,57 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-time-select
|
||||
placeholder="起始时间"
|
||||
v-model="startTime"
|
||||
:picker-options="{
|
||||
start: '00:00',
|
||||
step: '01:00',
|
||||
end: '24:00'
|
||||
}">
|
||||
</el-time-select>
|
||||
<el-time-select
|
||||
placeholder="结束时间"
|
||||
v-model="endTime"
|
||||
:picker-options="{
|
||||
start: '00:00',
|
||||
step: '01:00',
|
||||
end: '24:00',
|
||||
minTime: startTime
|
||||
}">
|
||||
</el-time-select>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "index",
|
||||
data() {
|
||||
return {
|
||||
startTime: '',
|
||||
endTime: ''
|
||||
}
|
||||
},
|
||||
props: {
|
||||
value: {}
|
||||
},
|
||||
beforeMount(){
|
||||
// 接收 v-model 数据
|
||||
if(this.value){
|
||||
this.startTime = this.value.split(',')[0]
|
||||
this.endTime = this.value.split(',')[1]
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
startTime: function(val) {
|
||||
this.$emit('input', [val, this.endTime].join(','))
|
||||
},
|
||||
endTime: function(val) {
|
||||
this.$emit('input', [this.startTime, val].join(','))
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,47 @@
|
||||
<template>
|
||||
<div class="upload-container">
|
||||
<el-button :style="{background:color,borderColor:color}" icon="el-icon-upload" size="mini" type="primary" @click="modalPicTap('2')"> upload</el-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'EditorSlideUpload',
|
||||
props: {
|
||||
color: {
|
||||
type: String,
|
||||
default: '#1890ff'
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
dialogVisible: false,
|
||||
listObj: {},
|
||||
fileList: []
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
modalPicTap(tit) {
|
||||
const _this = this
|
||||
this.$modalUpload(function(img) {
|
||||
let arr = [];
|
||||
if(img.length>10) return this.$message.warning("最多选择10张图片!");
|
||||
img.map((item) => {
|
||||
arr.push(item.sattDir)
|
||||
});
|
||||
// console.log(arr);
|
||||
_this.$emit('successCBK', arr)
|
||||
}, tit, 'content')
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.editor-slide-upload {
|
||||
margin-bottom: 20px;
|
||||
::v-deep .el-upload--picture-card {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,59 @@
|
||||
let callbacks = []
|
||||
|
||||
function loadedTinymce() {
|
||||
// to fixed https://github.com/PanJiaChen/vue-element-admin/issues/2144
|
||||
// check is successfully downloaded script
|
||||
return window.tinymce
|
||||
}
|
||||
|
||||
const dynamicLoadScript = (src, callback) => {
|
||||
const existingScript = document.getElementById(src)
|
||||
const cb = callback || function() {}
|
||||
|
||||
if (!existingScript) {
|
||||
const script = document.createElement('script')
|
||||
script.src = src // src url for the third-party library being loaded.
|
||||
script.id = src
|
||||
document.body.appendChild(script)
|
||||
callbacks.push(cb)
|
||||
const onEnd = 'onload' in script ? stdOnEnd : ieOnEnd
|
||||
onEnd(script)
|
||||
}
|
||||
|
||||
if (existingScript && cb) {
|
||||
if (loadedTinymce()) {
|
||||
cb(null, existingScript)
|
||||
} else {
|
||||
callbacks.push(cb)
|
||||
}
|
||||
}
|
||||
|
||||
function stdOnEnd(script) {
|
||||
script.onload = function() {
|
||||
// this.onload = null here is necessary
|
||||
// because even IE9 works not like others
|
||||
this.onerror = this.onload = null
|
||||
for (const cb of callbacks) {
|
||||
cb(null, script)
|
||||
}
|
||||
callbacks = null
|
||||
}
|
||||
script.onerror = function() {
|
||||
this.onerror = this.onload = null
|
||||
cb(new Error('Failed to load ' + src), script)
|
||||
}
|
||||
}
|
||||
|
||||
function ieOnEnd(script) {
|
||||
script.onreadystatechange = function() {
|
||||
if (this.readyState !== 'complete' && this.readyState !== 'loaded') return
|
||||
this.onreadystatechange = null
|
||||
for (const cb of callbacks) {
|
||||
cb(null, script) // there is no way to catch loading errors in IE8
|
||||
}
|
||||
callbacks = null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default dynamicLoadScript
|
||||
@@ -0,0 +1,231 @@
|
||||
<template>
|
||||
<div :class="{fullscreen:fullscreen}" class="tinymce-container editor-container">
|
||||
<textarea :id="tinymceId" class="tinymce-textarea" />
|
||||
<div class="editor-custom-btn-container">
|
||||
<editorImage color="#1890ff" class="editor-upload-btn" @successCBK="imageSuccessCBK" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import editorImage from './components/EditorImage'
|
||||
import plugins from './plugins'
|
||||
import toolbar from './toolbar'
|
||||
|
||||
export default {
|
||||
name: 'Tinymce',
|
||||
components: { editorImage },
|
||||
props: {
|
||||
id: {
|
||||
type: String,
|
||||
default: function() {
|
||||
return 'vue-tinymce-' + +new Date() + ((Math.random() * 1000).toFixed(0) + '')
|
||||
}
|
||||
},
|
||||
value: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
toolbar: {
|
||||
type: Array,
|
||||
required: false,
|
||||
default() {
|
||||
return []
|
||||
}
|
||||
},
|
||||
menubar: {
|
||||
type: String,
|
||||
default: 'file edit insert view format table'
|
||||
},
|
||||
height: {
|
||||
type: Number,
|
||||
required: false,
|
||||
default: 400
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
hasChange: false,
|
||||
hasInit: false,
|
||||
tinymceId: this.id,
|
||||
fullscreen: false,
|
||||
languageTypeList: {
|
||||
'en': 'en',
|
||||
'zh': 'zh_CN'
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
language() {
|
||||
// return this.languageTypeList[this.$store.getters.language]
|
||||
return this.languageTypeList['zh']
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value(val) {
|
||||
if (!this.hasChange && this.hasInit) {
|
||||
this.$nextTick(() =>
|
||||
window.tinymce.get(this.tinymceId).setContent(val || ''))
|
||||
}
|
||||
},
|
||||
language() {
|
||||
this.destroyTinymce()
|
||||
this.$nextTick(() => this.initTinymce())
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.initTinymce()
|
||||
},
|
||||
activated() {
|
||||
this.initTinymce()
|
||||
},
|
||||
deactivated() {
|
||||
this.destroyTinymce()
|
||||
},
|
||||
destroyed() {
|
||||
this.destroyTinymce()
|
||||
},
|
||||
methods: {
|
||||
initTinymce() {
|
||||
const _this = this
|
||||
window.tinymce.init({
|
||||
language: this.language,
|
||||
selector: `#${this.tinymceId}`,
|
||||
height: this.height,
|
||||
body_class: 'panel-body ',
|
||||
object_resizing: false,
|
||||
toolbar: this.toolbar.length > 0 ? this.toolbar : toolbar,
|
||||
menubar: this.menubar,
|
||||
plugins: plugins,
|
||||
end_container_on_empty_block: true,
|
||||
powerpaste_word_import: 'clean',
|
||||
code_dialog_height: 450,
|
||||
code_dialog_width: 1000,
|
||||
advlist_bullet_styles: 'square',
|
||||
advlist_number_styles: 'default',
|
||||
imagetools_cors_hosts: ['www.tinymce.com', 'codepen.io'],
|
||||
default_link_target: '_blank',
|
||||
link_title: false,
|
||||
convert_urls: false, //防止路径被转化为相对路径
|
||||
nonbreaking_force_tab: true, // inserting nonbreaking space need Nonbreaking Space Plugin
|
||||
init_instance_callback: editor => {
|
||||
if (_this.value) {
|
||||
editor.setContent(_this.value)
|
||||
}
|
||||
_this.hasInit = true
|
||||
editor.on('NodeChange Change KeyUp SetContent', () => {
|
||||
this.hasChange = true
|
||||
this.$emit('input', editor.getContent())
|
||||
})
|
||||
},
|
||||
setup(editor) {
|
||||
editor.on('FullscreenStateChanged', (e) => {
|
||||
_this.fullscreen = e.state
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
destroyTinymce() {
|
||||
const tinymce = window.tinymce.get(this.tinymceId)
|
||||
if (this.fullscreen) {
|
||||
tinymce.execCommand('mceFullScreen')
|
||||
}
|
||||
|
||||
if (tinymce) {
|
||||
tinymce.destroy()
|
||||
}
|
||||
},
|
||||
setContent(value) {
|
||||
window.tinymce.get(this.tinymceId).setContent(value)
|
||||
},
|
||||
getContent() {
|
||||
window.tinymce.get(this.tinymceId).getContent()
|
||||
},
|
||||
imageSuccessCBK(arr) {
|
||||
const _this = this;
|
||||
arr.forEach((v) => {
|
||||
if (this.getFileType(v) == "video") {
|
||||
window.tinymce
|
||||
.get(_this.tinymceId)
|
||||
.insertContent(
|
||||
`<video class="wscnph" src="${v}" controls muted></video>`
|
||||
);
|
||||
} else {
|
||||
window.tinymce
|
||||
.get(_this.tinymceId)
|
||||
.insertContent(`<img class="wscnph" src="${v}" />`);
|
||||
}
|
||||
});
|
||||
},
|
||||
getFileType(fileName) {
|
||||
// 后缀获取
|
||||
let suffix = "";
|
||||
// 获取类型结果
|
||||
let result = "";
|
||||
try {
|
||||
const flieArr = fileName.split(".");
|
||||
suffix = flieArr[flieArr.length - 1];
|
||||
} catch (err) {
|
||||
suffix = "";
|
||||
}
|
||||
// fileName无后缀返回 false
|
||||
if (!suffix) {
|
||||
return false;
|
||||
}
|
||||
suffix = suffix.toLocaleLowerCase();
|
||||
// 图片格式
|
||||
const imglist = ["png", "jpg", "jpeg", "bmp", "gif"];
|
||||
// 进行图片匹配
|
||||
result = imglist.find((item) => item === suffix);
|
||||
if (result) {
|
||||
return "image";
|
||||
}
|
||||
// 匹配 视频
|
||||
const videolist = [
|
||||
"mp4",
|
||||
"m2v",
|
||||
"mkv",
|
||||
"rmvb",
|
||||
"wmv",
|
||||
"avi",
|
||||
"flv",
|
||||
"mov",
|
||||
"m4v",
|
||||
];
|
||||
result = videolist.find((item) => item === suffix);
|
||||
if (result) {
|
||||
return "video";
|
||||
}
|
||||
// 其他 文件类型
|
||||
return "other";
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.tinymce-container {
|
||||
position: relative;
|
||||
line-height: normal;
|
||||
}
|
||||
.tinymce-container>>>.mce-fullscreen {
|
||||
z-index: 10000;
|
||||
}
|
||||
.tinymce-textarea {
|
||||
visibility: hidden;
|
||||
z-index: -1;
|
||||
}
|
||||
.editor-custom-btn-container {
|
||||
position: absolute;
|
||||
right: 4px;
|
||||
top: 4px;
|
||||
/*z-index: 2005;*/
|
||||
}
|
||||
.fullscreen .editor-custom-btn-container {
|
||||
z-index: 10000;
|
||||
position: fixed;
|
||||
}
|
||||
.editor-upload-btn {
|
||||
display: inline-block;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,7 @@
|
||||
// Any plugins you want to use has to be imported
|
||||
// Detail plugins list see https://www.tinymce.com/docs/plugins/
|
||||
// Custom builds see https://www.tinymce.com/download/custom-builds/
|
||||
|
||||
const plugins = ['advlist anchor autolink autosave code codesample colorpicker colorpicker contextmenu directionality emoticons fullscreen hr image imagetools insertdatetime link lists media nonbreaking noneditable pagebreak paste preview print save searchreplace spellchecker tabfocus table template textcolor textpattern visualblocks visualchars wordcount']
|
||||
export default plugins
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
// Here is a list of the toolbar
|
||||
// Detail list see https://www.tinymce.com/docs/advanced/editor-control-identifiers/#toolbarcontrols
|
||||
|
||||
const toolbar = ['searchreplace bold italic underline strikethrough alignleft aligncenter alignright outdent indent blockquote undo redo removeformat subscript superscript code codesample fontsizeselect fontselect', 'hr bullist numlist link image charmap preview anchor pagebreak insertdatetime media table emoticons forecolor backcolor ']
|
||||
|
||||
export default toolbar
|
||||
@@ -0,0 +1,5 @@
|
||||
<template>
|
||||
<div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,69 @@
|
||||
<template>
|
||||
<div class="upload-container">
|
||||
<el-upload
|
||||
class="upload-demo mr10 mb15"
|
||||
action
|
||||
:http-request="handleUploadForm"
|
||||
:headers="myHeaders"
|
||||
:show-file-list="false"
|
||||
multiple
|
||||
>
|
||||
<div v-if="url" class="upLoadPicBox">
|
||||
<div class="upLoad">
|
||||
<i class="el-icon-document-checked cameraIconfont" />
|
||||
</div>
|
||||
</div>
|
||||
<el-button v-else size="mini" type="primary" v-hasPermi="['admin:upload:file']">点击上传</el-button>
|
||||
</el-upload>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { fileFileApi } from '@/api/systemSetting'
|
||||
import { getToken } from '@/utils/auth'
|
||||
export default {
|
||||
name: 'UploadFile',
|
||||
props: {
|
||||
value: {}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
myHeaders: { 'X-Token': getToken() },
|
||||
url: ''
|
||||
}
|
||||
},
|
||||
beforeMount() {
|
||||
if (this.value) {
|
||||
this.url = this.value
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 上传
|
||||
handleUploadForm(param) {
|
||||
const formData = new FormData()
|
||||
const data = {
|
||||
model: this.$route.path.split('/')[1],
|
||||
pid: 10
|
||||
}
|
||||
formData.append('multipart', param.file)
|
||||
const loading = this.$loading({
|
||||
lock: true,
|
||||
text: '上传中,请稍候...',
|
||||
spinner: 'el-icon-loading',
|
||||
background: 'rgba(0, 0, 0, 0.7)'
|
||||
})
|
||||
fileFileApi(formData, data).then(res => {
|
||||
loading.close()
|
||||
this.url = res.url
|
||||
this.$emit('input', this.url)
|
||||
this.$message.success('上传成功')
|
||||
}).catch(res => {
|
||||
loading.close()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,138 @@
|
||||
<template>
|
||||
<div>
|
||||
<input ref="excel-upload-input" class="excel-upload-input" type="file" accept=".xlsx, .xls" @change="handleClick">
|
||||
<div class="drop" @drop="handleDrop" @dragover="handleDragover" @dragenter="handleDragover">
|
||||
Drop excel file here or
|
||||
<el-button :loading="loading" style="margin-left:16px;" size="mini" type="primary" @click="handleUpload">
|
||||
Browse
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import XLSX from 'xlsx'
|
||||
|
||||
export default {
|
||||
props: {
|
||||
beforeUpload: Function, // eslint-disable-line
|
||||
onSuccess: Function// eslint-disable-line
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
excelData: {
|
||||
header: null,
|
||||
results: null
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
generateData({ header, results }) {
|
||||
this.excelData.header = header
|
||||
this.excelData.results = results
|
||||
this.onSuccess && this.onSuccess(this.excelData)
|
||||
},
|
||||
handleDrop(e) {
|
||||
e.stopPropagation()
|
||||
e.preventDefault()
|
||||
if (this.loading) return
|
||||
const files = e.dataTransfer.files
|
||||
if (files.length !== 1) {
|
||||
this.$message.error('Only support uploading one file!')
|
||||
return
|
||||
}
|
||||
const rawFile = files[0] // only use files[0]
|
||||
|
||||
if (!this.isExcel(rawFile)) {
|
||||
this.$message.error('Only supports upload .xlsx, .xls, .csv suffix files')
|
||||
return false
|
||||
}
|
||||
this.upload(rawFile)
|
||||
e.stopPropagation()
|
||||
e.preventDefault()
|
||||
},
|
||||
handleDragover(e) {
|
||||
e.stopPropagation()
|
||||
e.preventDefault()
|
||||
e.dataTransfer.dropEffect = 'copy'
|
||||
},
|
||||
handleUpload() {
|
||||
this.$refs['excel-upload-input'].click()
|
||||
},
|
||||
handleClick(e) {
|
||||
const files = e.target.files
|
||||
const rawFile = files[0] // only use files[0]
|
||||
if (!rawFile) return
|
||||
this.upload(rawFile)
|
||||
},
|
||||
upload(rawFile) {
|
||||
this.$refs['excel-upload-input'].value = null // fix can't select the same excel
|
||||
|
||||
if (!this.beforeUpload) {
|
||||
this.readerData(rawFile)
|
||||
return
|
||||
}
|
||||
const before = this.beforeUpload(rawFile)
|
||||
if (before) {
|
||||
this.readerData(rawFile)
|
||||
}
|
||||
},
|
||||
readerData(rawFile) {
|
||||
this.loading = true
|
||||
return new Promise((resolve, reject) => {
|
||||
const reader = new FileReader()
|
||||
reader.onload = e => {
|
||||
const data = e.target.result
|
||||
const workbook = XLSX.read(data, { type: 'array' })
|
||||
const firstSheetName = workbook.SheetNames[0]
|
||||
const worksheet = workbook.Sheets[firstSheetName]
|
||||
const header = this.getHeaderRow(worksheet)
|
||||
const results = XLSX.utils.sheet_to_json(worksheet)
|
||||
this.generateData({ header, results })
|
||||
this.loading = false
|
||||
resolve()
|
||||
}
|
||||
reader.readAsArrayBuffer(rawFile)
|
||||
})
|
||||
},
|
||||
getHeaderRow(sheet) {
|
||||
const headers = []
|
||||
const range = XLSX.utils.decode_range(sheet['!ref'])
|
||||
let C
|
||||
const R = range.s.r
|
||||
/* start in the first row */
|
||||
for (C = range.s.c; C <= range.e.c; ++C) { /* walk every column in the range */
|
||||
const cell = sheet[XLSX.utils.encode_cell({ c: C, r: R })]
|
||||
/* find the cell in the first row */
|
||||
let hdr = 'UNKNOWN ' + C // <-- replace with your desired default
|
||||
if (cell && cell.t) hdr = XLSX.utils.format_cell(cell)
|
||||
headers.push(hdr)
|
||||
}
|
||||
return headers
|
||||
},
|
||||
isExcel(file) {
|
||||
return /\.(xlsx|xls|csv)$/.test(file.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.excel-upload-input{
|
||||
display: none;
|
||||
z-index: -9999;
|
||||
}
|
||||
.drop{
|
||||
border: 2px dashed #bbb;
|
||||
width: 600px;
|
||||
height: 160px;
|
||||
line-height: 160px;
|
||||
margin: 0 auto;
|
||||
font-size: 24px;
|
||||
border-radius: 5px;
|
||||
text-align: center;
|
||||
color: #bbb;
|
||||
position: relative;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,22 @@
|
||||
import Element from 'element-ui'
|
||||
import '@/styles/element-variables.scss'
|
||||
import articleFromComponent from './index.vue'
|
||||
import Vue from 'vue'
|
||||
import Cookies from 'js-cookie'
|
||||
Vue.use(Element, {
|
||||
size: Cookies.get('size') || 'medium' // set element-ui default size
|
||||
})
|
||||
const articleFrom = {}
|
||||
articleFrom.install = function(Vue, options) {
|
||||
const ToastConstructor = Vue.extend(articleFromComponent)
|
||||
// 生成一个该子类的实例
|
||||
const instance = new ToastConstructor()
|
||||
instance.$mount(document.createElement('div'))
|
||||
document.body.appendChild(instance.$el)
|
||||
Vue.prototype.$modalArticle = function(callback, handle) {
|
||||
instance.visible = true
|
||||
instance.callback = callback
|
||||
instance.handle = handle
|
||||
}
|
||||
}
|
||||
export default articleFrom
|
||||
@@ -0,0 +1,48 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-dialog
|
||||
title="提示"
|
||||
:visible.sync="visible"
|
||||
width="896px"
|
||||
:before-close="handleClose"
|
||||
>
|
||||
<article-list v-if="visible" :handle="handle" :userIds="userIds" :couponId="couponId" @getArticle="getArticle" :keyNum="keyNum"></article-list>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import articleList from '../index.vue'
|
||||
export default {
|
||||
name: 'CouponFrom',
|
||||
components:{ articleList },
|
||||
data() {
|
||||
return {
|
||||
visible: false,
|
||||
callback: function() {},
|
||||
handle: '',
|
||||
keyNum: 0,
|
||||
couponId: [],
|
||||
userIds: ''
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
// show() {
|
||||
// this.visible = this.show
|
||||
// }
|
||||
},
|
||||
methods: {
|
||||
handleClose() {
|
||||
this.visible = false
|
||||
},
|
||||
getArticle(couponObj) {
|
||||
this.callback(couponObj)
|
||||
this.visible = false
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,161 @@
|
||||
<template>
|
||||
<div class="divBox">
|
||||
<div class="header clearfix">
|
||||
<div class="container">
|
||||
<el-form inline>
|
||||
<el-form-item>
|
||||
<el-input v-model="listPram.keywords" placeholder="请输入关键词" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-cascader v-model="listPram.cid" :options="categoryTreeData" :props="categoryProps" placeholer="选择分类" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button size="mini" type="primary" @click="handerSearch">搜索</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
</div>
|
||||
<el-table :data="listData.list" style="width: 100%"
|
||||
size="mini"
|
||||
max-height="400"
|
||||
tooltip-effect="dark"
|
||||
highlight-current-row>
|
||||
<el-table-column
|
||||
label=""
|
||||
width="55">
|
||||
<template slot-scope="{ row, index }">
|
||||
<el-radio v-model="templateRadio" :label="row.id" @change.native="getTemplateRow(row)"> </el-radio>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="图片" min-width="80">
|
||||
<template slot-scope="scope">
|
||||
<div class="demo-image__preview">
|
||||
<el-image
|
||||
style="width: 36px; height: 36px"
|
||||
:src="scope.row.imageInput"
|
||||
:preview-src-list="imgList"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="title" label="标题" min-width="150" />
|
||||
<el-table-column prop="visit" label="浏览量" min-width="100">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ scope.row.visit | filterEmpty }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="updateTime" label="更新时间" min-width="150"/>
|
||||
<!--<el-table-column label="操作" min-width="150">-->
|
||||
<!--<template slot-scope="scope">-->
|
||||
<!--<el-button type="warning" disabled>关联产品</el-button>-->
|
||||
<!--</template>-->
|
||||
<!--</el-table-column>-->
|
||||
</el-table>
|
||||
<div class="block mb20">
|
||||
<el-pagination
|
||||
:current-page="listPram.page"
|
||||
:page-sizes="constants.page.limit"
|
||||
:layout="constants.page.layout"
|
||||
:total="listData.total"
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="handleCurrentChange"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import * as articleApi from '@/api/article.js'
|
||||
import * as categoryApi from '@/api/categoryApi.js'
|
||||
import * as selfUtil from '@/utils/ZBKJIutil.js'
|
||||
export default {
|
||||
// name: "list",
|
||||
props: {
|
||||
handle: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
templateRadio:'',
|
||||
imgList: [],
|
||||
constants: this.$constants,
|
||||
listPram: {
|
||||
keywords: null,
|
||||
cid: null,
|
||||
page: 1,
|
||||
limit: this.$constants.page.limit[0]
|
||||
},
|
||||
listData: { list: [], total: 0 },
|
||||
editDialogConfig: {
|
||||
visible: false,
|
||||
data: {},
|
||||
isEdit: 0 // 0=add 1=edit
|
||||
},
|
||||
categoryTreeData: [],
|
||||
categoryProps: {
|
||||
value: 'id',
|
||||
label: 'name',
|
||||
children: 'child',
|
||||
expandTrigger: 'hover',
|
||||
checkStrictly: true,
|
||||
emitPath: false
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.handlerGetListData(this.listPram)
|
||||
this.handlerGetCategoryTreeData()
|
||||
},
|
||||
methods: {
|
||||
getTemplateRow(row){
|
||||
this.$emit('getArticle', row)
|
||||
},
|
||||
handerSearch() {
|
||||
this.listPram.page = 1
|
||||
this.handlerGetListData(this.listPram)
|
||||
},
|
||||
handlerGetListData(pram) {
|
||||
articleApi.ListArticle(pram).then(data => {
|
||||
this.listData = data
|
||||
// this.listData.list.map((item) => {
|
||||
// item.imageInput.map(i => {
|
||||
// this.imgList.push(i)
|
||||
// })
|
||||
// })
|
||||
})
|
||||
},
|
||||
handlerGetCategoryTreeData() {
|
||||
const _pram = { type: this.constants.categoryType[2].value, status: 1 }
|
||||
categoryApi.treeCategroy(_pram).then(data => {
|
||||
this.categoryTreeData = selfUtil.addTreeListLabelForCasCard(data)
|
||||
})
|
||||
},
|
||||
handlerHideDialog() {
|
||||
this.handlerGetListData(this.listPram)
|
||||
this.editDialogConfig.visible = false
|
||||
},
|
||||
handlerDelete(rowData) {
|
||||
this.$confirm('确定删除当前数据', '提示').then(result => {
|
||||
articleApi.DelArticle(rowData).then(data => {
|
||||
this.$message.success('删除数据成功')
|
||||
this.handlerGetListData(this.listPram)
|
||||
})
|
||||
})
|
||||
},
|
||||
handleSizeChange(val) {
|
||||
this.listPram.limit = val
|
||||
this.handlerGetListData(this.listPram)
|
||||
},
|
||||
handleCurrentChange(val) {
|
||||
this.listPram.page = val
|
||||
this.handlerGetListData(this.listPram)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,277 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-form ref="formDynamic" size="small" :model="formDynamic" v-loading="loading" :rules="rules" class="attrFrom mb20" label-width="100px" @submit.native.prevent>
|
||||
<el-row :gutter="24">
|
||||
<el-col :span="8">
|
||||
<el-form-item label="规格名称:" prop="ruleName">
|
||||
<el-input maxlength="20" v-model="formDynamic.ruleName" placeholder="请输入标题名称" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col v-for="(item, index) in formDynamic.ruleValue" :key="index" :span="24" class="noForm">
|
||||
<el-form-item>
|
||||
<div class="acea-row row-middle"><span class="mr5">{{ item.value }}</span><i class="el-icon-circle-close" @click="handleRemove(index)" /></div>
|
||||
<div class="rulesBox">
|
||||
<el-tag
|
||||
v-for="(j, indexn) in item.detail"
|
||||
:key="indexn"
|
||||
closable
|
||||
size="medium"
|
||||
:disable-transitions="false"
|
||||
class="mb5 mr10"
|
||||
@close="handleClose(item.detail,indexn)"
|
||||
>
|
||||
{{ j }}
|
||||
</el-tag>
|
||||
<el-input
|
||||
v-if="item.inputVisible"
|
||||
ref="saveTagInput"
|
||||
v-model="item.detail.attrsVal"
|
||||
class="input-new-tag"
|
||||
size="small"
|
||||
@keyup.enter.native="createAttr(item.detail.attrsVal,index)"
|
||||
@blur="createAttr(item.detail.attrsVal,index)"
|
||||
/>
|
||||
<el-button v-else class="button-new-tag" size="small" @click="showInput(item)">+ 添加</el-button>
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col v-if="isBtn" :span="24" class="mt10" style="padding-left: 0;padding-right: 0;">
|
||||
<el-col :span="8">
|
||||
<el-form-item label="规格:">
|
||||
<el-input v-model="attrsName" placeholder="请输入规格" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="规格值:">
|
||||
<el-input v-model="attrsVal" placeholder="请输入规格值" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-button type="primary" class="mr10" @click="createAttrName">确定</el-button>
|
||||
<el-button @click="offAttrName">取消</el-button>
|
||||
</el-col>
|
||||
</el-col>
|
||||
<Spin v-if="spinShow" size="large" fix />
|
||||
</el-row>
|
||||
<el-button v-if="!isBtn" type="primary" icon="md-add" class="ml40 mt10" @click="addBtn">添加新规格</el-button>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="dialogFormVisible = false">取 消</el-button>
|
||||
<el-button type="primary" @click="dialogFormVisible = false">确 定</el-button>
|
||||
</div>
|
||||
</el-form>
|
||||
<span class="footer acea-row">
|
||||
<el-button @click="resetForm('formDynamic')">取消</el-button>
|
||||
<el-button type="primary" :loading="loadingBtn" @click="handleSubmit('formDynamic')">确 定</el-button>
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { attrCreatApi, attrEditApi } from '@/api/store'
|
||||
export default {
|
||||
name: 'CreatAttr',
|
||||
props: {
|
||||
currentRow: {
|
||||
type: Object,
|
||||
default: null
|
||||
},
|
||||
keyNum: {
|
||||
type: Number,
|
||||
default: 0
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loadingBtn: false,
|
||||
loading: false,
|
||||
dialogVisible: false,
|
||||
inputVisible: false,
|
||||
inputValue: '',
|
||||
spinShow: false,
|
||||
grid: {
|
||||
xl: 3,
|
||||
lg: 3,
|
||||
md: 12,
|
||||
sm: 24,
|
||||
xs: 24
|
||||
},
|
||||
modal: false,
|
||||
index: 1,
|
||||
rules: {
|
||||
ruleName: [
|
||||
{ required: true, message: '请输入规格名称', trigger: 'blur' }
|
||||
]
|
||||
},
|
||||
formDynamic: {
|
||||
ruleName: '',
|
||||
ruleValue: []
|
||||
},
|
||||
attrsName: '',
|
||||
attrsVal: '',
|
||||
isBtn: false,
|
||||
results: [],
|
||||
result: [],
|
||||
ids: 0
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
currentRow: {
|
||||
handler: function(val, oldVal) {
|
||||
this.formDynamic = val
|
||||
},
|
||||
immediate: true
|
||||
},
|
||||
keyNum: {
|
||||
deep: true,
|
||||
handler(val) {
|
||||
if( val>0 ) this.clear()
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.formDynamic.ruleValue.map(item => {
|
||||
this.$set(item, 'inputVisible', false)
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
resetForm(formName) {
|
||||
this.$msgbox.close()
|
||||
this.clear()
|
||||
this.$refs[formName].resetFields()
|
||||
},
|
||||
// 添加按钮
|
||||
addBtn() {
|
||||
this.isBtn = true
|
||||
},
|
||||
handleClose(item, index) {
|
||||
item.splice(index, 1)
|
||||
},
|
||||
// 取消
|
||||
offAttrName() {
|
||||
this.isBtn = false
|
||||
},
|
||||
// 删除
|
||||
handleRemove(index) {
|
||||
this.formDynamic.ruleValue.splice(index, 1)
|
||||
},
|
||||
// 添加规则名称
|
||||
createAttrName() {
|
||||
if (this.attrsName && this.attrsVal) {
|
||||
const data = {
|
||||
value: this.attrsName,
|
||||
detail: [
|
||||
this.attrsVal
|
||||
]
|
||||
}
|
||||
this.formDynamic.ruleValue.push(data)
|
||||
var hash = {}
|
||||
this.formDynamic.ruleValue = this.formDynamic.ruleValue.reduce(function(item, next) {
|
||||
/* eslint-disable */
|
||||
hash[next.value] ? '' : hash[next.value] = true && item.push(next);
|
||||
return item
|
||||
}, [])
|
||||
this.attrsName = '';
|
||||
this.attrsVal = '';
|
||||
this.isBtn = false;
|
||||
} else {
|
||||
this.$message.warning('请添加规格名称');
|
||||
}
|
||||
},
|
||||
// 添加属性
|
||||
createAttr (num, idx) {
|
||||
if (num) {
|
||||
this.formDynamic.ruleValue[idx].detail.push(num);
|
||||
var hash = {};
|
||||
this.formDynamic.ruleValue[idx].detail = this.formDynamic.ruleValue[idx].detail.reduce(function (item, next) {
|
||||
/* eslint-disable */
|
||||
hash[next] ? '' : hash[next] = true && item.push(next);
|
||||
return item
|
||||
}, [])
|
||||
this.formDynamic.ruleValue[idx].inputVisible = false
|
||||
} else {
|
||||
this.$message.warning('请添加属性');
|
||||
}
|
||||
},
|
||||
showInput(item) {
|
||||
this.$set(item, 'inputVisible', true)
|
||||
},
|
||||
// 提交
|
||||
handleSubmit (name) {
|
||||
const data={
|
||||
"id": this.currentRow.id || 0,
|
||||
"ruleName": this.formDynamic.ruleName,
|
||||
"ruleValue": JSON.stringify(this.formDynamic.ruleValue)
|
||||
}
|
||||
this.$refs[name].validate((valid) => {
|
||||
if (valid) {
|
||||
if (this.formDynamic.ruleValue.length === 0) {
|
||||
return this.$message.warning('请至少添加一条属性规格!');
|
||||
}
|
||||
this.loadingBtn = true
|
||||
this.loading = true
|
||||
setTimeout(() => {
|
||||
this.currentRow.id? attrEditApi(data).then(res => {
|
||||
this.$message.success('提交成功');
|
||||
this.$msgbox.close()
|
||||
this.clear();
|
||||
this.$emit('getList');
|
||||
this.loading = false
|
||||
this.loadingBtn = false
|
||||
}).catch(() => {
|
||||
this.loading = false
|
||||
this.loadingBtn = false
|
||||
}):attrCreatApi(data).then(res => {
|
||||
this.$message.success('提交成功');
|
||||
this.$msgbox.close()
|
||||
this.$emit('getList');
|
||||
this.clear();
|
||||
this.loading = false
|
||||
this.loadingBtn = false
|
||||
}).catch(() => {
|
||||
this.loading = false
|
||||
this.loadingBtn = false
|
||||
})
|
||||
}, 1200);
|
||||
} else {
|
||||
this.loading = false
|
||||
this.loadingBtn = false
|
||||
return false
|
||||
}
|
||||
})
|
||||
},
|
||||
clear () {
|
||||
this.$refs['formDynamic'].resetFields();
|
||||
this.formDynamic.ruleValue = [];
|
||||
this.formDynamic.ruleName=''
|
||||
this.isBtn = false;
|
||||
this.attrsName = '';
|
||||
this.attrsVal = '';
|
||||
},
|
||||
handleInputConfirm() {
|
||||
const inputValue = this.inputValue
|
||||
if (inputValue) {
|
||||
this.dynamicTags.push(inputValue)
|
||||
}
|
||||
this.inputVisible = false
|
||||
this.inputValue = ''
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.button-new-tag {
|
||||
height: 28px;
|
||||
line-height: 26px;
|
||||
padding-top: 0;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
.input-new-tag {
|
||||
width: 90px;
|
||||
margin-left: 10px;
|
||||
vertical-align: bottom;
|
||||
}
|
||||
.footer{
|
||||
justify-content: flex-end;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,106 @@
|
||||
<template>
|
||||
<el-row align="middle" :gutter="20" class="ivu-mt">
|
||||
<el-col :xl="6" :lg="6" :md="12" :sm="12" :xs="24" class="ivu-mb mb20" v-for="(item, index) in cardLists"
|
||||
:key="index">
|
||||
<div class="card_box">
|
||||
<div class="card_box_cir" :class="item.class">
|
||||
<span class="iconfont" :class="item.icon" :style="{color:item.color}" v-if="item.icon"></span>
|
||||
<i class="el-icon-edit" style="color: #fff;" v-else></i>
|
||||
</div>
|
||||
<div class="card_box_txt">
|
||||
<span class="sp2" v-text="item.name"></span>
|
||||
<span class="sp1" v-text="item.count || 0"></span>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "index",
|
||||
props: {
|
||||
cardLists: Array
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.one {
|
||||
background: rgba(24, 144, 255, .1);
|
||||
}
|
||||
.two {
|
||||
background: rgba(162, 119, 255, .1);
|
||||
}
|
||||
.three {
|
||||
background: rgba(232, 182, 0, .1);
|
||||
}
|
||||
.four {
|
||||
background: rgba(27, 190, 107, .1);
|
||||
}
|
||||
.five {
|
||||
background: rgba(75, 202, 213, .1);
|
||||
}
|
||||
.six {
|
||||
background: rgba(239, 156, 32, .1);
|
||||
}
|
||||
.one1{
|
||||
background: #1890FF;
|
||||
}
|
||||
.two1{
|
||||
background: #A277FF;
|
||||
}
|
||||
.three1{
|
||||
background: #EF9C20;
|
||||
}
|
||||
.four1{
|
||||
background: #1BBE6B;
|
||||
}
|
||||
.five1{
|
||||
background: #4BCAD5;
|
||||
}
|
||||
.six1{
|
||||
background: #EF9C20;
|
||||
}
|
||||
.card_box {
|
||||
width: 100%;
|
||||
height: 110px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
/*justify-content: center*/
|
||||
padding: 25px;
|
||||
box-sizing: border-box;
|
||||
border-radius: 4px;
|
||||
background: #fff;
|
||||
box-sizing: border-box;
|
||||
.card_box_cir {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
margin-right: 20px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
.card_box_txt {
|
||||
.sp1 {
|
||||
display: block;
|
||||
color: #333333;
|
||||
font-size: 28px;
|
||||
padding-top: 3px;
|
||||
font-family: PingFangSC-Semibold, PingFang SC;
|
||||
font-weight: 600;
|
||||
}
|
||||
.sp2 {
|
||||
display: block;
|
||||
color: #606266;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
}
|
||||
}
|
||||
}
|
||||
.iconfont{
|
||||
font-size: 23px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,26 @@
|
||||
import Element from 'element-ui'
|
||||
import '@/styles/element-variables.scss'
|
||||
import couponFromComponent from './index.vue'
|
||||
import Vue from 'vue'
|
||||
import Cookies from 'js-cookie'
|
||||
Vue.use(Element, {
|
||||
size: Cookies.get('size') || 'medium' // set element-ui default size
|
||||
})
|
||||
const couponFrom = {}
|
||||
couponFrom.install = function(Vue, options) {
|
||||
const ToastConstructor = Vue.extend(couponFromComponent)
|
||||
// 生成一个该子类的实例
|
||||
const instance = new ToastConstructor()
|
||||
instance.$mount(document.createElement('div'))
|
||||
document.body.appendChild(instance.$el)
|
||||
Vue.prototype.$modalCoupon = function(handle, keyNum, coupons=[], callback, userIds='', userType='') {
|
||||
instance.visible = true
|
||||
instance.handle = handle
|
||||
instance.keyNum = keyNum
|
||||
instance.coupons = coupons
|
||||
instance.userIds = userIds
|
||||
instance.callback = callback
|
||||
instance.userType = userType
|
||||
}
|
||||
}
|
||||
export default couponFrom
|
||||
@@ -0,0 +1,50 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-dialog
|
||||
title="优惠劵"
|
||||
:visible.sync="visible"
|
||||
width="896px"
|
||||
:before-close="handleClose"
|
||||
>
|
||||
<coupon-list v-if="visible" :handle="handle" :userIds="userIds" :couponData="coupons" @getCouponId="getCouponId" :keyNum="keyNum" :userType="userType"></coupon-list>
|
||||
<!--<upload-index v-if="visible" :isMore="isMore" @getImage="getImage" />-->
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import couponList from '../index.vue'
|
||||
export default {
|
||||
name: 'CouponFrom',
|
||||
components:{ couponList },
|
||||
data() {
|
||||
return {
|
||||
visible: false,
|
||||
callback: function() {},
|
||||
handle: '',
|
||||
keyNum: 0,
|
||||
coupons: [],
|
||||
userIds: '',
|
||||
userType: ''
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
// show() {
|
||||
// this.visible = this.show
|
||||
// }
|
||||
},
|
||||
methods: {
|
||||
handleClose() {
|
||||
this.visible = false
|
||||
},
|
||||
getCouponId(couponObj) {
|
||||
this.callback(couponObj)
|
||||
this.visible = false
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,284 @@
|
||||
<template>
|
||||
<div class="divBox">
|
||||
<div class="header clearfix">
|
||||
<div class="container">
|
||||
<el-form inline size="small">
|
||||
<el-form-item label="优惠卷名称:">
|
||||
<el-input v-model="tableFrom.keywords" placeholder="请输入优惠券名称" class="selWidth" size="small">
|
||||
<el-button slot="append" icon="el-icon-search" size="small" @click="getList(1)" />
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
</div>
|
||||
<el-table
|
||||
ref="table"
|
||||
v-loading="listLoading"
|
||||
:data="tableData.data"
|
||||
style="width: 100%"
|
||||
size="mini"
|
||||
max-height="400"
|
||||
tooltip-effect="dark"
|
||||
highlight-current-row
|
||||
@selection-change="handleSelectionChange"
|
||||
>
|
||||
<el-table-column
|
||||
v-if="handle==='wu'"
|
||||
type="selection"
|
||||
width="55"
|
||||
/>
|
||||
<el-table-column
|
||||
prop="id"
|
||||
label="ID"
|
||||
min-width="50"
|
||||
/>
|
||||
<el-table-column
|
||||
prop="name"
|
||||
label="优惠券名称"
|
||||
min-width="90"
|
||||
/>
|
||||
<el-table-column
|
||||
prop="money"
|
||||
label="优惠券面值"
|
||||
min-width="90"
|
||||
/>
|
||||
<el-table-column
|
||||
prop="minPrice"
|
||||
label="最低消费额"
|
||||
min-width="90"
|
||||
>
|
||||
<template slot-scope="scope">
|
||||
<span>{{ scope.row.minPrice===0?'不限制':scope.row.minPrice }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
label="有效期限"
|
||||
min-width="190"
|
||||
>
|
||||
<template slot-scope="scope">
|
||||
<span>{{ scope.row.isFixedTime===1?scope.row.useStartTime+' 一 '+scope.row.useEndTime:'不限制' }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
label="剩余数量"
|
||||
min-width="90"
|
||||
>
|
||||
<template slot-scope="scope">
|
||||
<span>{{ !scope.row.isLimited ? '不限量' : scope.row.lastTotal }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column v-if="handle==='send'" label="操作" min-width="120" fixed="right" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-button type="text" size="small" class="mr10" @click="sendGrant(scope.row.id)" v-hasPermi="['admin:coupon:user:receive']">发送</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div class="block mb20">
|
||||
<el-pagination
|
||||
:page-sizes="[10, 20, 30, 40]"
|
||||
:page-size="tableFrom.limit"
|
||||
:current-page="tableFrom.page"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
:total="tableData.total"
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="pageChange"
|
||||
/>
|
||||
</div>
|
||||
<div v-if="handle==='wu'">
|
||||
<el-button size="small" type="primary" class="fr" @click="ok">确定</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { marketingListApi, couponUserApi, marketingSendApi } from '@/api/marketing'
|
||||
export default {
|
||||
name: 'CouponList',
|
||||
props: {
|
||||
handle: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
couponData: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
keyNum: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
userIds: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
userType: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
listLoading: true,
|
||||
tableData: {
|
||||
data: [],
|
||||
total: 0
|
||||
},
|
||||
tableFrom: {
|
||||
page: 1,
|
||||
limit: 10,
|
||||
keywords: '',
|
||||
type: ''
|
||||
},
|
||||
multipleSelection: [],
|
||||
multipleSelectionAll: [],
|
||||
idKey: 'id',
|
||||
nextPageFlag: false,
|
||||
attr: []
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
keyNum: {
|
||||
deep: true,
|
||||
handler(val) {
|
||||
this.getList()
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.tableFrom.page = 1
|
||||
this.getList()
|
||||
if(!this.couponData) return;
|
||||
this.couponData.forEach(row => {
|
||||
this.$refs.table.toggleRowSelection(row);
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
close() {
|
||||
this.multipleSelection = []
|
||||
},
|
||||
handleSelectionChange(val) {
|
||||
this.multipleSelection = val
|
||||
setTimeout(() => {
|
||||
this.changePageCoreRecordData()
|
||||
}, 50)
|
||||
},
|
||||
// 设置选中的方法
|
||||
setSelectRow() {
|
||||
if (!this.multipleSelectionAll || this.multipleSelectionAll.length <= 0) {
|
||||
return
|
||||
}
|
||||
// 标识当前行的唯一键的名称
|
||||
const idKey = this.idKey
|
||||
const selectAllIds = []
|
||||
this.multipleSelectionAll.forEach(row => {
|
||||
selectAllIds.push(row[idKey])
|
||||
})
|
||||
this.$refs.table.clearSelection()
|
||||
for (var i = 0; i < this.tableData.data.length; i++) {
|
||||
if (selectAllIds.indexOf(this.tableData.data[i][idKey]) >= 0) {
|
||||
// 设置选中,记住table组件需要使用ref="table"
|
||||
this.$refs.table.toggleRowSelection(this.tableData.data[i], true)
|
||||
}
|
||||
}
|
||||
},
|
||||
// 记忆选择核心方法
|
||||
changePageCoreRecordData() {
|
||||
// 标识当前行的唯一键的名称
|
||||
const idKey = this.idKey
|
||||
const that = this
|
||||
// 如果总记忆中还没有选择的数据,那么就直接取当前页选中的数据,不需要后面一系列计算
|
||||
if (this.multipleSelectionAll.length <= 0) {
|
||||
this.multipleSelectionAll = this.multipleSelection
|
||||
return
|
||||
}
|
||||
// 总选择里面的key集合
|
||||
const selectAllIds = []
|
||||
this.multipleSelectionAll.forEach(row => {
|
||||
selectAllIds.push(row[idKey])
|
||||
})
|
||||
const selectIds = []
|
||||
// 获取当前页选中的id
|
||||
this.multipleSelection.forEach(row => {
|
||||
selectIds.push(row[idKey])
|
||||
// 如果总选择里面不包含当前页选中的数据,那么就加入到总选择集合里
|
||||
if (selectAllIds.indexOf(row[idKey]) < 0) {
|
||||
that.multipleSelectionAll.push(row)
|
||||
}
|
||||
})
|
||||
const noSelectIds = []
|
||||
// 得到当前页没有选中的id
|
||||
this.tableData.data.forEach(row => {
|
||||
if (selectIds.indexOf(row[idKey]) < 0) {
|
||||
noSelectIds.push(row[idKey])
|
||||
}
|
||||
})
|
||||
noSelectIds.forEach(id => {
|
||||
if (selectAllIds.indexOf(id) >= 0) {
|
||||
for (let i = 0; i < that.multipleSelectionAll.length; i++) {
|
||||
if (that.multipleSelectionAll[i][idKey] == id) {
|
||||
// 如果总选择中有未被选中的,那么就删除这条
|
||||
that.multipleSelectionAll.splice(i, 1)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
ok() {
|
||||
if (this.multipleSelection.length > 0) {
|
||||
this.$emit('getCouponId', this.multipleSelectionAll)
|
||||
this.close()
|
||||
} else {
|
||||
this.$message.warning('请先选择优惠劵')
|
||||
}
|
||||
},
|
||||
// 列表
|
||||
getList(num) {
|
||||
this.listLoading = true
|
||||
this.tableFrom.page = num ? num : this.tableFrom.page
|
||||
this.userType ? this.tableFrom.type = 1 : this.tableFrom.type = 3;
|
||||
marketingSendApi(this.tableFrom).then(res => {
|
||||
this.tableData.data = res.list
|
||||
this.tableData.total = res.total
|
||||
this.listLoading = false
|
||||
this.$nextTick(function() {
|
||||
this.setSelectRow()// 调用跨页选中方法
|
||||
})
|
||||
}).catch(res => {
|
||||
this.listLoading = false
|
||||
})
|
||||
},
|
||||
pageChange(page) {
|
||||
this.changePageCoreRecordData()
|
||||
this.tableFrom.page = page
|
||||
this.getList()
|
||||
},
|
||||
handleSizeChange(val) {
|
||||
this.changePageCoreRecordData()
|
||||
this.tableFrom.limit = val
|
||||
this.getList()
|
||||
},
|
||||
// 发送
|
||||
sendGrant(id){
|
||||
this.$modalSure('发送优惠劵吗').then(() => {
|
||||
couponUserApi({ couponId:id, uid:this.userIds }).then(() => {
|
||||
this.$message.success('发送成功')
|
||||
this.getList()
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.selWidth{
|
||||
width: 219px !important;
|
||||
}
|
||||
.seachTiele{
|
||||
line-height: 35px;
|
||||
}
|
||||
.fr{
|
||||
float: right;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,205 @@
|
||||
<template>
|
||||
<el-dialog v-model="dialogFormVisible" title="请选择管理员" append-to-body :visible.sync="dialogFormVisible" width="1200px" @close="cancel">
|
||||
<el-form ref="form" inline :model="artFrom">
|
||||
<el-form-item label="身份:">
|
||||
<el-select v-model="artFrom.roles" placeholder="请输入身份" clearable class="selWidth">
|
||||
<el-option
|
||||
v-for="item in roleList.list"
|
||||
:key="item.id"
|
||||
:label="item.roleName"
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="姓名:">
|
||||
<el-input v-model="artFrom.realName" size="small" placeholder="请输入姓名或者账号" class="selWidth">
|
||||
<el-button slot="append" icon="el-icon-search" @click="search" class="">搜索</el-button>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<el-table
|
||||
v-loading="loading"
|
||||
:row-style="{height:'50px'}"
|
||||
:data="tableData"
|
||||
max-height="400px"
|
||||
size="mini"
|
||||
style="width: 100%;">
|
||||
<el-table-column
|
||||
label=""
|
||||
width="55">
|
||||
<template slot-scope="{ row, index }">
|
||||
<el-radio v-model="templateRadio" :label="row.uid" @change.native="getTemplateRow(row)"> </el-radio>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="id"
|
||||
label="ID"
|
||||
sortable
|
||||
width="80">
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="realName"
|
||||
label="姓名"
|
||||
min-Width="120">
|
||||
</el-table-column>
|
||||
<!--<el-table-column-->
|
||||
<!--label="客服头像"-->
|
||||
<!--min-Width="100">-->
|
||||
<!--<template slot-scope="{ row, index }" class="picMiddle">-->
|
||||
<!--<div class="demo-image__preview">-->
|
||||
<!--<el-image-->
|
||||
<!--:src="row.avatar"-->
|
||||
<!--:preview-src-list="[row.avatar]"-->
|
||||
<!--/>-->
|
||||
<!--</div>-->
|
||||
<!--</template>-->
|
||||
<!--</el-table-column>-->
|
||||
<el-table-column
|
||||
prop="account"
|
||||
label="账号"
|
||||
min-Width="120"/>
|
||||
<el-table-column label="身份" prop="realName" min-width="230">
|
||||
<template slot-scope="scope">
|
||||
<el-tag size="small" type="info" v-for="(item, index) in scope.row.roleNames.split(',')" :key="index" class="mr5">{{ item }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="最后登录时间" prop="lastTime" min-width="180">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ scope.row.lastTime | filterEmpty }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="最后登录IP" prop="lastIp" min-width="150">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ scope.row.lastIp | filterEmpty }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="状态" prop="status" min-width="100">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ scope.row.status | filterShowOrHide }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="删除标记" prop="status" min-width="100">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ scope.row.isDel | filterYesOrNo }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div class="block">
|
||||
<el-pagination
|
||||
class="mt20"
|
||||
@size-change="sizeChange"
|
||||
@current-change="pageChange"
|
||||
:current-page="artFrom.page"
|
||||
:page-sizes="[20, 40, 60, 100]"
|
||||
:page-size="artFrom.limit"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
:total="total">
|
||||
</el-pagination>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import * as systemAdminApi from '@/api/systemadmin.js'
|
||||
import * as roleApi from '@/api/role.js'
|
||||
export default {
|
||||
name: "index",
|
||||
data(){
|
||||
return {
|
||||
constants: this.$constants,
|
||||
loading:false,
|
||||
templateRadio:'',
|
||||
dialogFormVisible:false,
|
||||
tableData:[],
|
||||
artFrom: {
|
||||
page: 1,
|
||||
limit: 20,
|
||||
status: 1,
|
||||
realName: '',
|
||||
roles: ''
|
||||
},
|
||||
total:0,
|
||||
timeVal:'',
|
||||
roleList: []
|
||||
}
|
||||
},
|
||||
created(){
|
||||
this.handleGetRoleList()
|
||||
},
|
||||
methods:{
|
||||
handleGetRoleList() {
|
||||
const _pram = {
|
||||
page: 1,
|
||||
limit: 9999
|
||||
}
|
||||
roleApi.getRoleList(_pram).then(data => {
|
||||
this.roleList = data
|
||||
})
|
||||
},
|
||||
getTemplateRow(row){
|
||||
this.dialogFormVisible = false;
|
||||
this.$emit("upImgUid", row);
|
||||
},
|
||||
tableList(){
|
||||
this.loading = true;
|
||||
systemAdminApi.adminList( this.artFrom ).then(data => {
|
||||
this.tableData = data.list
|
||||
this.total = data.total
|
||||
this.loading = false;
|
||||
}).catch(() => {
|
||||
this.loading = false;
|
||||
})
|
||||
// let that = this;
|
||||
// that.loading = true;
|
||||
// userListApi(that.artFrom).then(res=>{
|
||||
// that.loading = false;
|
||||
// that.tableData = res.list;
|
||||
// that.total = res.total
|
||||
// })
|
||||
},
|
||||
//切换显示条数
|
||||
sizeChange(index){
|
||||
this.artFrom.limit = index;
|
||||
this.tableList();
|
||||
},
|
||||
//切换页数
|
||||
pageChange(index){
|
||||
this.artFrom.page = index;
|
||||
this.tableList();
|
||||
},
|
||||
onchangeTime(e){
|
||||
this.artFrom.page = 1;
|
||||
if(e!==null){
|
||||
this.artFrom.data = e.join(',');
|
||||
}else {
|
||||
this.artFrom.data = '';
|
||||
}
|
||||
this.tableList();
|
||||
},
|
||||
search(){
|
||||
this.timeVal = '';
|
||||
this.artFrom.page = 1;
|
||||
this.tableList();
|
||||
},
|
||||
cancel(){
|
||||
this.artFrom = {
|
||||
page: 1,
|
||||
limit: 20,
|
||||
data: '',
|
||||
realName: ''
|
||||
};
|
||||
this.timeVal = '';
|
||||
this.templateRadio = ''
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.el-input-group__append, .el-input-group__prepend{
|
||||
background-color:#1890ff!important;
|
||||
color: #fff!important;
|
||||
border-color:#1890ff!important;
|
||||
border-radius: 0 4px 4px 0;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,195 @@
|
||||
<template>
|
||||
<div>
|
||||
<div :id="echarts" :style="styles"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import echarts from 'echarts';
|
||||
|
||||
export default {
|
||||
name: 'index',
|
||||
props: {
|
||||
// styles: {
|
||||
// type: Object,
|
||||
// default: null
|
||||
// },
|
||||
seriesData: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
xAxis: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
echartsTitle: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
yAxisData: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
legendData: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
styles: 'height:300px',
|
||||
infoLists: this.infoList,
|
||||
seriesArray: this.seriesData
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
seriesData: {
|
||||
handler(newVal, oldVal) {
|
||||
this.seriesArray = newVal;
|
||||
this.handleSetVisitChart();
|
||||
},
|
||||
deep: true // 对象内部属性的监听,关键。
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
echarts() {
|
||||
return 'echarts' + Math.ceil(Math.random() * 100)
|
||||
}
|
||||
},
|
||||
mounted: function () {
|
||||
const vm = this
|
||||
vm.$nextTick(() => {
|
||||
vm.handleSetVisitChart();
|
||||
window.addEventListener('resize', this.wsFunc)
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
wsFunc() {
|
||||
this.myChart.resize()
|
||||
},
|
||||
handleSetVisitChart() {
|
||||
this.myChart = echarts.init(document.getElementById(this.echarts))
|
||||
let option = null
|
||||
if (this.echartsTitle === 'circle') {
|
||||
option = {
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
formatter: '{a} <br/>{b} : {c} ({d}%)'
|
||||
},
|
||||
legend: {
|
||||
orient: 'vertical',
|
||||
left: 'right',
|
||||
data: this.legendData || []
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '访问来源',
|
||||
type: 'pie',
|
||||
radius: '70%',
|
||||
center: ['50%', '60%'],
|
||||
data: this.seriesArray || [],
|
||||
emphasis: {
|
||||
itemStyle: {
|
||||
shadowBlur: 10,
|
||||
shadowOffsetX: 0,
|
||||
shadowColor: 'rgba(0, 0, 0, 0.5)'
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
} else {
|
||||
option = {
|
||||
tooltip: {
|
||||
trigger: 'axis'
|
||||
},
|
||||
toolbox: {},
|
||||
legend: {
|
||||
data: this.legendData || []
|
||||
},
|
||||
color: ['#1495EB', '#00CC66', '#F9D249', '#ff9900', '#9860DF'],
|
||||
grid: {
|
||||
left: 16,
|
||||
right: 25,
|
||||
bottom: 10,
|
||||
top: 40,
|
||||
containLabel: true
|
||||
},
|
||||
xAxis: [
|
||||
{
|
||||
type: 'category',
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
color: '#D7DDE4'
|
||||
}
|
||||
},
|
||||
axisTick: {
|
||||
alignWithLabel: true,
|
||||
lineStyle: {
|
||||
color: '#D7DDE4'
|
||||
}
|
||||
},
|
||||
splitLine: {
|
||||
show: false,
|
||||
lineStyle: {
|
||||
color: '#F5F7F9'
|
||||
}
|
||||
},
|
||||
// axisPointer: {
|
||||
// type: 'shadow'
|
||||
// },
|
||||
axisLabel: {
|
||||
interval: 0,
|
||||
rotate: 40,
|
||||
textStyle: {
|
||||
color: '#7F8B9C'
|
||||
}
|
||||
},
|
||||
data: this.xAxis
|
||||
}
|
||||
],
|
||||
yAxis: this.yAxisData.length?this.yAxisData:{
|
||||
axisLine: {
|
||||
show: false
|
||||
},
|
||||
axisTick: {
|
||||
show: false
|
||||
},
|
||||
axisLabel: {
|
||||
textStyle: {
|
||||
color: '#7F8B9C'
|
||||
}
|
||||
},
|
||||
splitLine: {
|
||||
show: true,
|
||||
lineStyle: {
|
||||
color: '#F5F7F9'
|
||||
}
|
||||
},
|
||||
type: 'value'
|
||||
},
|
||||
series: this.seriesArray
|
||||
};
|
||||
}
|
||||
|
||||
// 基于准备好的dom,初始化echarts实例
|
||||
this.myChart.setOption(option, true)
|
||||
},
|
||||
handleResize() {
|
||||
this.myChart.resize()
|
||||
}
|
||||
},
|
||||
beforeDestroy() {
|
||||
window.removeEventListener('resize', this.wsFunc)
|
||||
if (!this.myChart) {
|
||||
return
|
||||
}
|
||||
this.myChart.dispose()
|
||||
this.myChart = null
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,71 @@
|
||||
<template>
|
||||
<div>
|
||||
<div :id="echarts" :style="styles" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import echarts from 'echarts'
|
||||
export default {
|
||||
name: 'Index',
|
||||
props: {
|
||||
styles: {
|
||||
type: Object,
|
||||
default: null
|
||||
},
|
||||
optionData: {
|
||||
type: Object,
|
||||
default: null
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
myChart: null
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
echarts() {
|
||||
return 'echarts' + Math.ceil(Math.random() * 100)
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
optionData: {
|
||||
handler(newVal, oldVal) {
|
||||
this.handleSetVisitChart()
|
||||
},
|
||||
deep: true // 对象内部属性的监听,关键。
|
||||
}
|
||||
},
|
||||
mounted: function() {
|
||||
const vm = this
|
||||
vm.$nextTick(() => {
|
||||
vm.handleSetVisitChart()
|
||||
window.addEventListener('resize', this.wsFunc)
|
||||
})
|
||||
},
|
||||
beforeDestroy() {
|
||||
window.removeEventListener('resize', this.wsFunc)
|
||||
if (!this.myChart) {
|
||||
return
|
||||
}
|
||||
this.myChart.dispose()
|
||||
this.myChart = null
|
||||
},
|
||||
methods: {
|
||||
wsFunc() {
|
||||
this.myChart.resize()
|
||||
},
|
||||
handleSetVisitChart() {
|
||||
this.myChart = echarts.init(document.getElementById(this.echarts))
|
||||
let option = null
|
||||
option = this.optionData
|
||||
// 基于准备好的dom,初始化echarts实例
|
||||
this.myChart.setOption(option, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,23 @@
|
||||
import Element from 'element-ui'
|
||||
import '@/styles/element-variables.scss'
|
||||
import uploadFromComponent from './index.vue'
|
||||
import Vue from 'vue'
|
||||
import Cookies from 'js-cookie'
|
||||
Vue.use(Element, {
|
||||
size: Cookies.get('size') || 'medium' // set element-ui default size
|
||||
})
|
||||
const goodListFrom = {}
|
||||
goodListFrom.install = function(Vue, options) {
|
||||
const ToastConstructor = Vue.extend(uploadFromComponent)
|
||||
// 生成一个该子类的实例
|
||||
const instance = new ToastConstructor()
|
||||
instance.$mount(document.createElement('div'))
|
||||
document.body.appendChild(instance.$el)
|
||||
Vue.prototype.$modalGoodList = function(callback, handleNum, row) {
|
||||
instance.visible = true
|
||||
instance.callback = callback
|
||||
instance.handleNum = handleNum
|
||||
instance.checked = row
|
||||
}
|
||||
}
|
||||
export default goodListFrom
|
||||
@@ -0,0 +1,41 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-dialog
|
||||
title="商品列表"
|
||||
:visible.sync="visible"
|
||||
width="896px"
|
||||
:before-close="handleClose"
|
||||
>
|
||||
<good-list v-if="visible" @getStoreItem="getStoreItem" :handleNum="handleNum" :checked="checked"></good-list>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import goodList from '@/components/goodList/index.vue'
|
||||
export default {
|
||||
name: 'GoodListFrom',
|
||||
components: { goodList },
|
||||
data() {
|
||||
return {
|
||||
handleNum: '',
|
||||
visible: false,
|
||||
callback: function() {},
|
||||
checked: []
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleClose() {
|
||||
this.visible = false
|
||||
},
|
||||
getStoreItem(img) {
|
||||
this.callback(img)
|
||||
this.visible = false
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,271 @@
|
||||
<template>
|
||||
<div class="divBox">
|
||||
<div class="header clearfix">
|
||||
<div class="container">
|
||||
<el-form inline>
|
||||
<el-form-item label="商品分类:">
|
||||
<el-cascader v-model="tableFrom.cateId" :options="merCateList" :props="props" clearable class="selWidth mr20" @change="getList(1)"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="商品搜索:">
|
||||
<el-input v-model="tableFrom.keywords" @input="onInput($event)" placeholder="请输入商品名称,关键字,产品编号" class="selWidth">
|
||||
<el-button slot="append" icon="el-icon-search" @click="getList(1)"/>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
</div>
|
||||
<el-table
|
||||
v-loading="listLoading"
|
||||
:data="tableData.data"
|
||||
style="width: 100%"
|
||||
size="mini"
|
||||
ref="multipleTable"
|
||||
@selection-change="handleSelectionChange"
|
||||
>
|
||||
<el-table-column
|
||||
key="2"
|
||||
v-if="handleNum === 'many'"
|
||||
width="55">
|
||||
<template slot="header" slot-scope="scope">
|
||||
<el-checkbox slot="reference" :value="isChecked && checkedPage.indexOf(tableFrom.page) > -1" @change="changeType" />
|
||||
</template>
|
||||
<template slot-scope="scope">
|
||||
<el-checkbox
|
||||
:value="checkedIds.indexOf(scope.row.id) > -1"
|
||||
@change="(v)=>changeOne(v,scope.row)"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
width="55"
|
||||
key="1"
|
||||
v-if="handleNum !== 'many'"
|
||||
>
|
||||
<template slot-scope="scope">
|
||||
<el-radio :label="scope.row.id" v-model="templateRadio" @change.native="getTemplateRow(scope.row)"> </el-radio>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="id"
|
||||
label="ID"
|
||||
min-width="50"
|
||||
/>
|
||||
<el-table-column label="商品图" width="80">
|
||||
<template slot-scope="scope">
|
||||
<div class="demo-image__preview">
|
||||
<el-image
|
||||
style="width: 36px; height: 36px"
|
||||
:src="scope.row.image"
|
||||
:preview-src-list="imgList"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="storeName"
|
||||
label="商品名称"
|
||||
min-width="180"
|
||||
/>
|
||||
<el-table-column
|
||||
label="商品分类"
|
||||
min-width="150"
|
||||
>
|
||||
<template slot-scope="scope">
|
||||
<span v-for="(item, index) in scope.row.cateValues.split(',')" :key="index" class="mr10">{{ item }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div class="block mb20">
|
||||
<el-pagination
|
||||
:page-sizes="[10, 20, 30, 40]"
|
||||
:page-size="tableFrom.limit"
|
||||
:current-page="tableFrom.page"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
:total="tableData.total"
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="pageChange"
|
||||
/>
|
||||
</div>
|
||||
<div v-if="handleNum === 'many'" class="right-align">
|
||||
<el-button size="small" type="primary" @click="ok">确定</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { productLstApi, productDeleteApi, categoryApi, putOnShellApi, offShellApi, productHeadersApi } from '@/api/store'
|
||||
export default {
|
||||
name: 'GoodList',
|
||||
props: {
|
||||
handleNum: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
checked: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
templateRadio:0,
|
||||
merCateList: [],
|
||||
props: {
|
||||
children: 'child',
|
||||
label: 'name',
|
||||
value: 'id',
|
||||
emitPath: false
|
||||
},
|
||||
listLoading: true,
|
||||
tableData: {
|
||||
data: [],
|
||||
total: 0
|
||||
},
|
||||
tableFrom: {
|
||||
page: 1,
|
||||
limit: 10,
|
||||
cateId: '',
|
||||
keywords: '',
|
||||
type: '1'
|
||||
},
|
||||
imgList: [],
|
||||
multipleSelection: [],
|
||||
checkedPage: [],
|
||||
isChecked: false,
|
||||
isIndex: 0,
|
||||
checkBox: [],
|
||||
checkedIds: [] // 订单当前页选中的数据
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.getList()
|
||||
if(this.checked.length){
|
||||
let [ ...arr2 ] = this.checked
|
||||
this.checkBox = arr2
|
||||
this.checkedIds = arr2.map(item => {
|
||||
return item.id
|
||||
})
|
||||
}
|
||||
this.getCategorySelect()
|
||||
},
|
||||
methods: {
|
||||
onInput(e){
|
||||
this.$forceUpdate();
|
||||
},
|
||||
changeType(v) {
|
||||
this.isChecked = v
|
||||
const index = this.checkedPage.indexOf(this.tableFrom.page)
|
||||
this.isIndex = index
|
||||
this.checkedPage.push(this.tableFrom.page)
|
||||
if (index > -1) {
|
||||
this.checkedPage.splice(index, 1)
|
||||
}
|
||||
this.syncCheckedId(v)
|
||||
},
|
||||
changeOne(v, order) {
|
||||
if (v) {
|
||||
const index = this.checkedIds.indexOf(order.id)
|
||||
if (index === -1){
|
||||
this.checkedIds.push(order.id)
|
||||
this.checkBox.push(order)
|
||||
}
|
||||
} else {
|
||||
const index = this.checkedIds.indexOf(order.id)
|
||||
if (index > -1){
|
||||
this.checkedIds.splice(index, 1)
|
||||
this.checkBox.splice(index, 1)
|
||||
}
|
||||
}
|
||||
},
|
||||
syncCheckedId(o) {
|
||||
const ids = this.tableData.data.map(v => v.id)
|
||||
if(o){
|
||||
this.tableData.data.forEach(item => {
|
||||
const index = this.checkedIds.indexOf(item.id)
|
||||
if (index === -1) {
|
||||
this.checkedIds.push(item.id)
|
||||
this.checkBox.push(item)
|
||||
}
|
||||
})
|
||||
}else{
|
||||
this.tableData.data.forEach(item => {
|
||||
const index = this.checkedIds.indexOf(item.id)
|
||||
if (index > -1) {
|
||||
this.checkedIds.splice(index, 1)
|
||||
this.checkBox.splice(index, 1)
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
handleSelectionChange(val) {
|
||||
const tables = []
|
||||
val.map((item) => {
|
||||
tables.push({ src: item.image,
|
||||
id: item.id })
|
||||
})
|
||||
this.multipleSelection = tables
|
||||
},
|
||||
ok() {
|
||||
this.$emit('getStoreItem', this.checkBox)
|
||||
},
|
||||
getTemplateRow(row){
|
||||
this.$emit('getStoreItem', row)
|
||||
},
|
||||
// 商户分类;
|
||||
getCategorySelect() {
|
||||
categoryApi({ status: -1, type: 1 }).then(res => {
|
||||
this.merCateList = res
|
||||
}).catch(res => {
|
||||
this.$message.error(res.message)
|
||||
})
|
||||
},
|
||||
getList(num) {
|
||||
this.listLoading = true
|
||||
this.tableFrom.page = num ? num : this.tableFrom.page;
|
||||
productLstApi(this.tableFrom).then(res => {
|
||||
this.tableData.data = res.list
|
||||
this.tableData.total = res.total
|
||||
this.tableData.data.map((item) => {
|
||||
this.imgList.push(item.image)
|
||||
})
|
||||
this.tableData.data.forEach(item => {
|
||||
this.checked.forEach(element => {
|
||||
if (Number(item.id) === Number(element.id)) {
|
||||
this.$nextTick(() => {
|
||||
this.$refs.multipleTable.toggleRowSelection(item, true)
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
this.listLoading = false
|
||||
}).catch(res => {
|
||||
this.listLoading = false
|
||||
this.$message.error(res.message)
|
||||
})
|
||||
},
|
||||
pageChange(page) {
|
||||
this.tableFrom.page = page
|
||||
this.getList()
|
||||
},
|
||||
handleSizeChange(val) {
|
||||
this.tableFrom.limit = val
|
||||
this.getList()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.right-align{
|
||||
float: right;
|
||||
}
|
||||
.selWidth{
|
||||
width: 250px !important;
|
||||
}
|
||||
.seachTiele{
|
||||
line-height: 35px;
|
||||
}
|
||||
.fr{
|
||||
float: right;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,3 @@
|
||||
const elementIcons = ['platform-eleme', 'eleme', 'delete-solid', 'delete', 's-tools', 'setting', 'user-solid', 'user', 'phone', 'phone-outline', 'more', 'more-outline', 'star-on', 'star-off', 's-goods', 'goods', 'warning', 'warning-outline', 'question', 'info', 'remove', 'circle-plus', 'success', 'error', 'zoom-in', 'zoom-out', 'remove-outline', 'circle-plus-outline', 'circle-check', 'circle-close', 's-help', 'help', 'minus', 'plus', 'check', 'close', 'picture', 'picture-outline', 'picture-outline-round', 'upload', 'upload2', 'download', 'camera-solid', 'camera', 'video-camera-solid', 'video-camera', 'message-solid', 'bell', 's-cooperation', 's-order', 's-platform', 's-fold', 's-unfold', 's-operation', 's-promotion', 's-home', 's-release', 's-ticket', 's-management', 's-open', 's-shop', 's-marketing', 's-flag', 's-comment', 's-finance', 's-claim', 's-custom', 's-opportunity', 's-data', 's-check', 's-grid', 'menu', 'share', 'd-caret', 'caret-left', 'caret-right', 'caret-bottom', 'caret-top', 'bottom-left', 'bottom-right', 'back', 'right', 'bottom', 'top', 'top-left', 'top-right', 'arrow-left', 'arrow-right', 'arrow-down', 'arrow-up', 'd-arrow-left', 'd-arrow-right', 'video-pause', 'video-play', 'refresh', 'refresh-right', 'refresh-left', 'finished', 'sort', 'sort-up', 'sort-down', 'rank', 'loading', 'view', 'c-scale-to-original', 'date', 'edit', 'edit-outline', 'folder', 'folder-opened', 'folder-add', 'folder-remove', 'folder-delete', 'folder-checked', 'tickets', 'document-remove', 'document-delete', 'document-copy', 'document-checked', 'document', 'document-add', 'printer', 'paperclip', 'takeaway-box', 'search', 'monitor', 'attract', 'mobile', 'scissors', 'umbrella', 'headset', 'brush', 'mouse', 'coordinate', 'magic-stick', 'reading', 'data-line', 'data-board', 'pie-chart', 'data-analysis', 'collection-tag', 'film', 'suitcase', 'suitcase-1', 'receiving', 'collection', 'files', 'notebook-1', 'notebook-2', 'toilet-paper', 'office-building', 'school', 'table-lamp', 'house', 'no-smoking', 'smoking', 'shopping-cart-full', 'shopping-cart-1', 'shopping-cart-2', 'shopping-bag-1', 'shopping-bag-2', 'sold-out', 'sell', 'present', 'box', 'bank-card', 'money', 'coin', 'wallet', 'discount', 'price-tag', 'news', 'guide', 'male', 'female', 'thumb', 'cpu', 'link', 'connection', 'open', 'turn-off', 'set-up', 'chat-round', 'chat-line-round', 'chat-square', 'chat-dot-round', 'chat-dot-square', 'chat-line-square', 'message', 'postcard', 'position', 'turn-off-microphone', 'microphone', 'close-notification', 'bangzhu', 'time', 'odometer', 'crop', 'aim', 'switch-button', 'full-screen', 'copy-document', 'mic', 'stopwatch', 'medal-1', 'medal', 'trophy', 'trophy-1', 'first-aid-kit', 'discover', 'place', 'location', 'location-outline', 'location-information', 'add-location', 'delete-location', 'map-location', 'alarm-clock', 'timer', 'watch-1', 'watch', 'lock', 'unlock', 'key', 'service', 'mobile-phone', 'bicycle', 'truck', 'ship', 'basketball', 'football', 'soccer', 'baseball', 'wind-power', 'light-rain', 'lightning', 'heavy-rain', 'sunrise', 'sunrise-1', 'sunset', 'sunny', 'cloudy', 'partly-cloudy', 'cloudy-and-sunny', 'moon', 'moon-night', 'dish', 'dish-1', 'food', 'chicken', 'fork-spoon', 'knife-fork', 'burger', 'tableware', 'sugar', 'dessert', 'ice-cream', 'hot-water', 'water-cup', 'coffee-cup', 'cold-drink', 'goblet', 'goblet-full', 'goblet-square', 'goblet-square-full', 'refrigerator', 'grape', 'watermelon', 'cherry', 'apple', 'pear', 'orange', 'coffee', 'ice-tea', 'ice-drink', 'milk-tea', 'potato-strips', 'lollipop', 'ice-cream-square', 'ice-cream-round']
|
||||
|
||||
export default elementIcons
|
||||
@@ -0,0 +1,106 @@
|
||||
<template>
|
||||
<div class="iconBox">
|
||||
<el-input
|
||||
ref="search"
|
||||
v-model="iconVal"
|
||||
placeholder="输入关键词搜索,注意全是英文"
|
||||
clearable
|
||||
style="width: 300px"
|
||||
@input="upIcon(iconVal)"
|
||||
/>
|
||||
<div class="icons-container">
|
||||
<div class="grid">
|
||||
<div
|
||||
v-for="item of list"
|
||||
:key="item"
|
||||
@click="handleClipboard(generateElementIconCode(item),$event,item)"
|
||||
>
|
||||
<el-tooltip placement="top">
|
||||
<div slot="content">
|
||||
{{ generateElementIconCode(item) }}
|
||||
</div>
|
||||
<div class="icon-item">
|
||||
<i :class="'el-icon-' + item" />
|
||||
<span>{{ item }}</span>
|
||||
</div>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import elementIcons from './element-icons'
|
||||
|
||||
export default {
|
||||
name: 'Index',
|
||||
data() {
|
||||
return {
|
||||
elementIcons,
|
||||
iconVal: '',
|
||||
modals2: false,
|
||||
list: []
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.list = this.elementIcons
|
||||
},
|
||||
methods: {
|
||||
generateElementIconCode(symbol) {
|
||||
return `<i class="el-icon-${symbol}" />`
|
||||
},
|
||||
handleClipboard(text, event, n) {
|
||||
this.iconChange(n)
|
||||
// clipboard(text, event)
|
||||
},
|
||||
// 搜索
|
||||
upIcon(n) {
|
||||
const arrs = []
|
||||
for (var i = 0; i < this.elementIcons.length; i++) {
|
||||
if (this.elementIcons[i].indexOf(n) !== -1) {
|
||||
arrs.push(this.elementIcons[i])
|
||||
this.list = arrs
|
||||
}
|
||||
}
|
||||
},
|
||||
iconChange(n) {
|
||||
this.$emit('getIcon', n)
|
||||
this.$msgbox.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.icons-container {
|
||||
margin: 10px 20px 0;
|
||||
overflow: hidden;
|
||||
|
||||
.grid {
|
||||
position: relative;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
|
||||
}
|
||||
|
||||
.icon-item {
|
||||
margin: 10px 20px;
|
||||
text-align: center;
|
||||
width: 100px;
|
||||
float: left;
|
||||
font-size: 30px;
|
||||
color: #24292e;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
span {
|
||||
display: block;
|
||||
font-size: 16px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.disabled {
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,149 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="acea-row" v-if="multiple">
|
||||
<div
|
||||
v-for="(item,index) in imageList"
|
||||
:key="index"
|
||||
class="pictrue"
|
||||
draggable="false"
|
||||
@dragstart="handleDragStart($event, item)"
|
||||
@dragover.prevent="handleDragOver($event, item)"
|
||||
@dragenter="handleDragEnter($event, item)"
|
||||
@dragend="handleDragEnd($event, item)"
|
||||
>
|
||||
<img :src="item.sattDir">
|
||||
<i class="el-icon-error btndel" @click="handleRemove(index)" />
|
||||
</div>
|
||||
<div class="upLoadPicBox" @click="modalPicTap('2')" v-show="imageList.length<20">
|
||||
<div class="upLoad">
|
||||
<i class="el-icon-camera cameraIconfont" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="upLoadPicBox" @click="modalPicTap('1')" v-else>
|
||||
<div v-if="image" class="pictrue"><img :src="image"></div>
|
||||
<div v-else class="upLoad">
|
||||
<i class="el-icon-camera cameraIconfont" />
|
||||
</div>
|
||||
</div>
|
||||
<el-dialog
|
||||
title="上传图片"
|
||||
:visible.sync="visible"
|
||||
width="896px"
|
||||
:before-close="handleClose"
|
||||
:modal="false"
|
||||
>
|
||||
<upload-index v-if="visible" :checkedMore="imageList" :isMore="isMore" @getImage="getImage" />
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import UploadIndex from '@/components/uploadPicture/index.vue'
|
||||
export default {
|
||||
name: 'UploadFroms',
|
||||
components: { UploadIndex },
|
||||
props:{
|
||||
value:{},
|
||||
multiple:{
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
image:"",
|
||||
visible: false,
|
||||
callback: function() {},
|
||||
isMore: '',
|
||||
imageList: []
|
||||
}
|
||||
},
|
||||
beforeMount(){
|
||||
if( this.multiple ){
|
||||
// 接收 v-model 数据
|
||||
if(this.value){
|
||||
this.imageList = JSON.parse(this.value)
|
||||
}
|
||||
}else{
|
||||
// 接收 v-model 数据
|
||||
if(this.value){
|
||||
this.image = this.value
|
||||
}
|
||||
}
|
||||
// 处理多选
|
||||
this.isMore = this.multiple ? '2':'1'
|
||||
},
|
||||
methods: {
|
||||
handleClose() {
|
||||
this.visible = false
|
||||
},
|
||||
getImage(img) {
|
||||
if (this.multiple) {
|
||||
let obj = {};
|
||||
this.imageList = img.reduce((cur,next) => {
|
||||
obj[next.attId] ? "" : obj[next.attId] = true && cur.push(next);
|
||||
return cur;
|
||||
},[])
|
||||
this.$emit('input', JSON.stringify(this.imageList))
|
||||
} else {
|
||||
this.image = img[0].sattDir
|
||||
this.$emit('input', this.image)
|
||||
}
|
||||
this.visible = false
|
||||
},
|
||||
// 点击商品图
|
||||
modalPicTap (tit, num, i) {
|
||||
this.visible = true
|
||||
},
|
||||
handleRemove (i) {
|
||||
this.imageList.splice(i, 1)
|
||||
this.$emit('input', JSON.stringify(this.imageList))
|
||||
},
|
||||
// 移动
|
||||
handleDragStart (e, item) {
|
||||
this.dragging = item;
|
||||
},
|
||||
handleDragEnd (e, item) {
|
||||
this.dragging = null
|
||||
},
|
||||
handleDragOver (e) {
|
||||
e.dataTransfer.dropEffect = 'move'
|
||||
},
|
||||
handleDragEnter (e, item) {
|
||||
e.dataTransfer.effectAllowed = 'move'
|
||||
if (item === this.dragging) {
|
||||
return
|
||||
}
|
||||
const newItems = [...this.imageList]
|
||||
const src = newItems.indexOf(this.dragging)
|
||||
const dst = newItems.indexOf(item)
|
||||
newItems.splice(dst, 0, ...newItems.splice(src, 1))
|
||||
this.imageList = newItems;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.btndel{
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
width :20px !important;
|
||||
height: 20px !important;
|
||||
left: 46px;
|
||||
top: -4px;
|
||||
}
|
||||
.pictrue{
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
border: 1px dotted rgba(0,0,0,0.1);
|
||||
margin-right: 10px;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
img{
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,938 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-row :gutter="30">
|
||||
<el-col v-bind="grid">
|
||||
<div class="Nav">
|
||||
<div class="input">
|
||||
<el-input
|
||||
v-model="filterText"
|
||||
placeholder="选择分类"
|
||||
prefix-icon="el-icon-search"
|
||||
style="width: 100%"
|
||||
clearable
|
||||
/>
|
||||
</div>
|
||||
<div class="trees-coadd">
|
||||
<div class="scollhide">
|
||||
<div
|
||||
class="trees"
|
||||
:style="{ maxHeight: !pictureType ? '300px' : '700px' }"
|
||||
>
|
||||
<el-tree
|
||||
ref="tree"
|
||||
:data="treeData2"
|
||||
:filter-node-method="filterNode"
|
||||
:props="defaultProps"
|
||||
highlight-current
|
||||
>
|
||||
<div
|
||||
slot-scope="{ node, data }"
|
||||
class="custom-tree-node"
|
||||
@click.stop="handleNodeClick(data)"
|
||||
>
|
||||
<div>
|
||||
<span
|
||||
class="custom-tree-node-label"
|
||||
:title="node.label"
|
||||
>{{ node.label }}</span
|
||||
>
|
||||
<span
|
||||
v-if="data.space_property_name"
|
||||
style="font-size: 11px; color: #3889b1"
|
||||
>({{ data.name }})</span
|
||||
>
|
||||
</div>
|
||||
<span class="el-ic">
|
||||
<el-dropdown>
|
||||
<span class="el-dropdown-link">
|
||||
<i class="el-icon-more"></i>
|
||||
</span>
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item
|
||||
@click.native="onAdd(data.id)"
|
||||
v-if="checkPermi(['admin:category:save'])"
|
||||
>添加分类</el-dropdown-item
|
||||
>
|
||||
<el-dropdown-item
|
||||
v-if="
|
||||
node.label !== '全部图片' &&
|
||||
checkPermi(['admin:category:update'])
|
||||
"
|
||||
@click.native="onEdit(data.id)"
|
||||
>编辑分类</el-dropdown-item
|
||||
>
|
||||
<el-dropdown-item
|
||||
v-if="
|
||||
node.label !== '全部图片' &&
|
||||
checkPermi(['admin:category:delete'])
|
||||
"
|
||||
@click.native="handleDelete(data.id)"
|
||||
>删除分类</el-dropdown-item
|
||||
>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
</span>
|
||||
</div>
|
||||
</el-tree>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col v-bind="grid2" class="colLeft">
|
||||
<div class="conter mb15 relative">
|
||||
<div class="bnt">
|
||||
<el-button
|
||||
v-if="!pictureType"
|
||||
size="small"
|
||||
type="primary"
|
||||
class="mr15 mb20"
|
||||
@click="checkPics"
|
||||
>使用选中图片</el-button
|
||||
>
|
||||
<div class="mb20">
|
||||
<el-tooltip
|
||||
class="item"
|
||||
effect="dark"
|
||||
content="上传图片"
|
||||
placement="top-start"
|
||||
>
|
||||
<el-upload
|
||||
class="upload-demo"
|
||||
action
|
||||
:http-request="handleUploadForm"
|
||||
:on-change="imgSaveToUrl"
|
||||
:headers="myHeaders"
|
||||
:show-file-list="false"
|
||||
multiple
|
||||
>
|
||||
<el-button
|
||||
icon="el-icon-upload2"
|
||||
size="small"
|
||||
class="mr15"
|
||||
v-if="!pictureType"
|
||||
></el-button>
|
||||
</el-upload>
|
||||
</el-tooltip>
|
||||
<el-tooltip
|
||||
class="item"
|
||||
effect="dark"
|
||||
content="删除图片"
|
||||
placement="top-start"
|
||||
>
|
||||
<el-button
|
||||
icon="el-icon-delete"
|
||||
class="mr15"
|
||||
type="danger"
|
||||
size="small"
|
||||
@click.stop="editPicList('图片')"
|
||||
v-hasPermi="['admin:system:attachment:delete']"
|
||||
v-if="!pictureType"
|
||||
></el-button>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
<el-upload
|
||||
class="upload-demo"
|
||||
action
|
||||
:http-request="handleUploadForm"
|
||||
:on-change="imgSaveToUrl"
|
||||
:headers="myHeaders"
|
||||
:show-file-list="false"
|
||||
multiple
|
||||
>
|
||||
<el-button class="mr10" type="primary" v-if="pictureType"
|
||||
>上传图片</el-button
|
||||
>
|
||||
</el-upload>
|
||||
<div>
|
||||
<el-button
|
||||
class="mr10"
|
||||
type="danger"
|
||||
@click.stop="editPicList('图片')"
|
||||
v-if="pictureType"
|
||||
>删除图片</el-button
|
||||
>
|
||||
</div>
|
||||
<el-select
|
||||
v-model="sleOptions.attachment_category_name"
|
||||
placeholder="图片移动至"
|
||||
class="mb20"
|
||||
:size="pictureType ? '' : 'small'"
|
||||
>
|
||||
<el-option
|
||||
class="demo"
|
||||
:label="sleOptions.attachment_category_name"
|
||||
:value="sleOptions.attachment_category_id"
|
||||
style="
|
||||
max-width: 560px;
|
||||
height: 200px;
|
||||
overflow: auto;
|
||||
background-color: #fff;">
|
||||
<el-tree
|
||||
ref="tree2"
|
||||
:data="treeData2"
|
||||
:filter-node-method="filterNode"
|
||||
:props="defaultProps"
|
||||
highlight-current
|
||||
@node-click="handleSelClick"
|
||||
/>
|
||||
</el-option>
|
||||
</el-select>
|
||||
<div class="abs_video">
|
||||
<el-radio-group v-model="typeDate" @change="radioChange" size="small">
|
||||
<el-radio-button label="pic">图片</el-radio-button>
|
||||
<el-radio-button label="video">视频</el-radio-button>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pictrueList acea-row" v-loading="loadingPic">
|
||||
<div v-show="isShowPic" class="imagesNo">
|
||||
<i
|
||||
class="el-icon-picture"
|
||||
style="font-size: 60px; color: rgb(219, 219, 219)"
|
||||
/>
|
||||
<span class="imagesNo_sp">图片库为空</span>
|
||||
</div>
|
||||
<div
|
||||
class="conters scrollbarAll"
|
||||
:style="{ maxHeight: !pictureType ? '500px' : '700px' }"
|
||||
>
|
||||
<div
|
||||
v-for="(item, index) in pictrueList.list"
|
||||
:key="index"
|
||||
class="gridPic"
|
||||
>
|
||||
<span class="num_badge" v-if="item.num > 0">{{item.num}}</span>
|
||||
<img
|
||||
style="object-fit: contain;"
|
||||
v-lazy="item.sattDir ? item.sattDir : localImg"
|
||||
:class="item.isSelect ? 'on' : ''"
|
||||
@click="changImage(item, index, pictrueList.list)"
|
||||
v-if="item.attType !== 'video/mp4'"
|
||||
/>
|
||||
<video
|
||||
:src="item.sattDir"
|
||||
:class="item.isSelect ? 'on' : ''"
|
||||
@click="changImage(item, index, pictrueList.list)"
|
||||
v-if="item.attType == 'video/mp4'"
|
||||
></video>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="block">
|
||||
<el-pagination
|
||||
:page-sizes="!pictureType ? [10, 20, 30, 40] : [30, 60, 90, 120]"
|
||||
:page-size="tableData.limit"
|
||||
:current-page="tableData.page"
|
||||
:pager-count="5"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
:total="pictrueList.total"
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="pageChange"
|
||||
/>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-dialog
|
||||
:title="bizTitle"
|
||||
:visible.sync="visible"
|
||||
destroy-on-close
|
||||
:close-on-click-modal="false"
|
||||
:modal="modals"
|
||||
@close="closeModel"
|
||||
>
|
||||
<el-form
|
||||
ref="editPram"
|
||||
:model="editPram"
|
||||
label-width="100px"
|
||||
v-loading="loading"
|
||||
>
|
||||
<el-form-item
|
||||
label="上级分类"
|
||||
prop="pid"
|
||||
:rules="[
|
||||
{
|
||||
type: 'number',
|
||||
required: true,
|
||||
message: '请选择上级分类',
|
||||
trigger: ['blur', 'change'],
|
||||
},
|
||||
]"
|
||||
>
|
||||
<el-cascader
|
||||
v-model="editPram.pid"
|
||||
:options="treeData2"
|
||||
:props="categoryProps"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
label="分类名称"
|
||||
prop="name"
|
||||
:rules="[
|
||||
{
|
||||
required: true,
|
||||
message: '请输入分类名称',
|
||||
trigger: ['blur', 'change'],
|
||||
},
|
||||
]"
|
||||
>
|
||||
<el-input v-model="editPram.name" placeholder="分类名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="排序">
|
||||
<el-input-number v-model="editPram.sort" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="handlerSubmit('editPram')"
|
||||
>确定</el-button
|
||||
>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
addCategroy,
|
||||
listCategroy,
|
||||
treeCategroy,
|
||||
infoCategroy,
|
||||
updateCategroy,
|
||||
deleteCategroy,
|
||||
} from "@/api/categoryApi";
|
||||
import {
|
||||
fileImageApi,
|
||||
fileListApi,
|
||||
fileDeleteApi,
|
||||
attachmentMoveApi,
|
||||
} from "@/api/systemSetting";
|
||||
import { getToken } from "@/utils/auth";
|
||||
import { checkPermi } from "@/utils/permission"; // 权限判断函数
|
||||
export default {
|
||||
name: "Upload",
|
||||
props: {
|
||||
pictureType: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
isMore: {
|
||||
type: String,
|
||||
default: "1",
|
||||
},
|
||||
modelName: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
checkedMore: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loadingPic: false,
|
||||
loading: false,
|
||||
modals: false,
|
||||
allTreeList: [],
|
||||
categoryProps: {
|
||||
value: "id",
|
||||
label: "name",
|
||||
children: "child",
|
||||
expandTrigger: "hover",
|
||||
checkStrictly: true,
|
||||
emitPath: false,
|
||||
},
|
||||
editPram: {
|
||||
pid: 1000,
|
||||
name: "",
|
||||
type: 2,
|
||||
sort: 1,
|
||||
status: 0,
|
||||
url: "url",
|
||||
id: 0,
|
||||
},
|
||||
visible: false,
|
||||
bizTitle: "",
|
||||
sleOptions: {
|
||||
attrId: "",
|
||||
pid: "",
|
||||
},
|
||||
list: [],
|
||||
grid: {
|
||||
xl: 7,
|
||||
lg: 7,
|
||||
md: 7,
|
||||
sm: 7,
|
||||
xs: 24,
|
||||
},
|
||||
grid2: {
|
||||
xl: 17,
|
||||
lg: 17,
|
||||
md: 17,
|
||||
sm: 17,
|
||||
xs: 24,
|
||||
},
|
||||
filterText: "",
|
||||
treeData: [],
|
||||
treeData2: [],
|
||||
defaultProps: {
|
||||
children: "child",
|
||||
label: "name",
|
||||
},
|
||||
tableData: {
|
||||
page: 1,
|
||||
limit: 10,
|
||||
pid: 0,
|
||||
attType: "jpg,jpeg,gif,png,bmp,PNG,JPG",
|
||||
},
|
||||
classifyId: 0,
|
||||
myHeaders: { "X-Token": getToken() },
|
||||
treeFrom: {
|
||||
status: -1,
|
||||
type: 2,
|
||||
},
|
||||
pictrueList: {
|
||||
list: [],
|
||||
total: 0,
|
||||
},
|
||||
isShowPic: false,
|
||||
checkPicList: [],
|
||||
ids: [],
|
||||
listPram: {
|
||||
pid: 0,
|
||||
type: 2,
|
||||
status: 0,
|
||||
name: "",
|
||||
page: 1,
|
||||
limit: 9999,
|
||||
},
|
||||
localImg: "",
|
||||
videoStatus: false,
|
||||
typeDate:"pic",
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
filterText(val) {
|
||||
this.$refs.tree.filter(val);
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.pictureType
|
||||
? (this.tableData.limit = 30)
|
||||
: (this.tableData.limit = 10);
|
||||
if (this.$route && this.$route.query.field === "dialog")
|
||||
import("./internal.js");
|
||||
this.getList();
|
||||
this.getFileList();
|
||||
// this.handlerGetList()
|
||||
},
|
||||
methods: {
|
||||
checkPermi,
|
||||
// 选取图片后自动回调,里面可以获取到文件
|
||||
imgSaveToUrl(event) {
|
||||
// 也可以用file
|
||||
this.localFile = event.raw; // 或者 this.localFile=file.raw
|
||||
|
||||
// 转换操作可以不放到这个函数里面,
|
||||
// 因为这个函数会被多次触发,上传时触发,上传成功也触发
|
||||
let reader = new FileReader();
|
||||
reader.readAsDataURL(this.localFile); // 这里也可以直接写参数event.raw
|
||||
|
||||
// 转换成功后的操作,reader.result即为转换后的DataURL ,
|
||||
// 它不需要自己定义,你可以console.integralLog(reader.result)看一下
|
||||
reader.onload = () => {
|
||||
// console.integralLog(reader.result)
|
||||
};
|
||||
|
||||
/* 另外一种本地预览方法 */
|
||||
let URL = window.URL || window.webkitURL;
|
||||
this.localImg = URL.createObjectURL(event.raw);
|
||||
// 转换后的地址为 blob:http://xxx/7bf54338-74bb-47b9-9a7f-7a7093c716b5
|
||||
},
|
||||
closeModel() {
|
||||
this.$refs["editPram"].resetFields();
|
||||
},
|
||||
handlerSubmit(formName) {
|
||||
this.$refs[formName].validate((valid) => {
|
||||
if (valid) {
|
||||
if (this.editPram.pid == 10000) this.editPram.pid = 0;
|
||||
this.bizTitle === "添加分类"
|
||||
? addCategroy(this.editPram).then((data) => {
|
||||
this.$message.success("创建成功");
|
||||
this.visible = false;
|
||||
this.getList();
|
||||
})
|
||||
: updateCategroy(this.editPram).then((data) => {
|
||||
this.$message.success("编辑成功");
|
||||
this.visible = false;
|
||||
this.getList();
|
||||
});
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
},
|
||||
// 表单分类
|
||||
handlerGetList() {
|
||||
let datas = {
|
||||
name: "全部图片",
|
||||
id: "",
|
||||
};
|
||||
treeCategroy(this.treeFrom).then((data) => {
|
||||
this.allTreeList = data;
|
||||
this.allTreeLis.unshift(datas);
|
||||
});
|
||||
},
|
||||
// 搜索分类
|
||||
filterNode(value, data) {
|
||||
if (!value) return true;
|
||||
return data.name.indexOf(value) !== -1;
|
||||
},
|
||||
// 所有分类
|
||||
getList() {
|
||||
const data = {
|
||||
name: "全部图片",
|
||||
id: 10000,
|
||||
};
|
||||
treeCategroy(this.treeFrom).then((res) => {
|
||||
this.treeData = res;
|
||||
this.treeData.unshift(data);
|
||||
this.treeData2 = [...this.treeData];
|
||||
});
|
||||
},
|
||||
// 添加分类
|
||||
onAdd(id) {
|
||||
this.tableData.pid = id;
|
||||
if (this.tableData.pid === 10000) this.tableData.pid = 0;
|
||||
this.bizTitle = "添加分类";
|
||||
this.visible = true;
|
||||
if (id)
|
||||
this.editPram = {
|
||||
pid: id,
|
||||
name: "",
|
||||
type: 2,
|
||||
sort: 1,
|
||||
status: 0,
|
||||
url: "url",
|
||||
id: 0,
|
||||
};
|
||||
},
|
||||
// 编辑
|
||||
onEdit(id) {
|
||||
if (id === 10000) id = 0;
|
||||
this.bizTitle = "编辑分类";
|
||||
this.loading = true;
|
||||
infoCategroy({ id: id }).then((res) => {
|
||||
this.editPram = res;
|
||||
this.loading = false;
|
||||
});
|
||||
this.visible = true;
|
||||
},
|
||||
// 删除
|
||||
handleDelete(id) {
|
||||
if (id === 10000) id = 0;
|
||||
this.$modalSure().then(() => {
|
||||
deleteCategroy({ id: id }).then(() => {
|
||||
this.$message.success("删除成功");
|
||||
this.getList();
|
||||
});
|
||||
});
|
||||
},
|
||||
handleNodeClick(data) {
|
||||
this.checkPicList = [];
|
||||
this.tableData.pid = data.id;
|
||||
this.getFileList();
|
||||
},
|
||||
// 上传
|
||||
handleUploadForm(param) {
|
||||
const formData = new FormData();
|
||||
const data = {
|
||||
model: this.modelName ? this.modelName : this.$route.path.split("/")[1],
|
||||
pid: this.tableData.pid,
|
||||
};
|
||||
formData.append("multipart", param.file);
|
||||
let loading = this.$loading({
|
||||
lock: true,
|
||||
text: "上传中,请稍候...",
|
||||
spinner: "el-icon-loading",
|
||||
background: "rgba(0, 0, 0, 0.7)",
|
||||
});
|
||||
fileImageApi(formData, data)
|
||||
.then((res) => {
|
||||
loading.close();
|
||||
this.$message.success("上传成功");
|
||||
this.tableData.page = 1;
|
||||
this.getFileList();
|
||||
})
|
||||
.catch((res) => {
|
||||
loading.close();
|
||||
});
|
||||
},
|
||||
// 文件列表
|
||||
getFileList() {
|
||||
if (this.tableData.pid === 10000) this.tableData.pid = 0;
|
||||
this.loadingPic = true;
|
||||
fileListApi(this.tableData)
|
||||
.then(async (res) => {
|
||||
this.pictrueList.list = res.list;
|
||||
if (this.tableData.page === 1 && this.pictrueList.list.length > 0)
|
||||
this.pictrueList.list[0].localImg = this.localImg;
|
||||
if (this.pictrueList.list.length) {
|
||||
this.isShowPic = false;
|
||||
} else {
|
||||
this.isShowPic = true;
|
||||
}
|
||||
this.pictrueList.total = res.total;
|
||||
this.loadingPic = false;
|
||||
})
|
||||
.catch(() => {
|
||||
this.loadingPic = false;
|
||||
});
|
||||
},
|
||||
pageChange(page) {
|
||||
this.tableData.page = page;
|
||||
this.checkPicList = [];
|
||||
this.getFileList();
|
||||
},
|
||||
handleSizeChange(val) {
|
||||
this.tableData.limit = val;
|
||||
this.getFileList();
|
||||
},
|
||||
// 选中图片
|
||||
changImage(item, index, row) {
|
||||
let activeIndex = 0;
|
||||
if (!item.isSelect) {
|
||||
this.$set(item, "isSelect", true);
|
||||
this.checkPicList.push(item);
|
||||
} else {
|
||||
this.$set(item, "isSelect", !item.isSelect);
|
||||
this.checkPicList.map((el, index) => {
|
||||
if (el.attId == item.attId) {
|
||||
activeIndex = index;
|
||||
}
|
||||
});
|
||||
this.checkPicList.splice(activeIndex, 1);
|
||||
}
|
||||
this.ids = [];
|
||||
this.checkPicList.map((item, i) => {
|
||||
this.ids.push(item.attId);
|
||||
});
|
||||
|
||||
this.pictrueList.list.map((el, i) => {
|
||||
if (el.isSelect) {
|
||||
this.checkPicList.filter((el2, j) => {
|
||||
if (el.attId == el2.attId) {
|
||||
el.num = j + 1;
|
||||
this.$nextTick(() => {
|
||||
this.pictrueList.list;
|
||||
});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
el.num = 0;
|
||||
}
|
||||
});
|
||||
},
|
||||
// 点击使用选中图片
|
||||
checkPics() {
|
||||
if (!this.checkPicList.length)
|
||||
return this.$message.warning("请先选择图片");
|
||||
if (this.$route && this.$route.query.field === "dialog") {
|
||||
let str = "";
|
||||
for (let i = 0; i < this.checkPicList.length; i++) {
|
||||
str += '<img src="' + this.checkPicList[i].sattDir + '">';
|
||||
}
|
||||
/* eslint-disable */
|
||||
nowEditor.dialog.close(true);
|
||||
nowEditor.editor.setContent(str, true);
|
||||
} else {
|
||||
if (this.isMore === "1" && this.checkPicList.length > 1) {
|
||||
return this.$message.warning("最多只能选一张图片");
|
||||
}
|
||||
|
||||
this.$emit("getImage", [...this.checkedMore, ...this.checkPicList]);
|
||||
}
|
||||
},
|
||||
// 删除图片
|
||||
editPicList(tit) {
|
||||
if (!this.checkPicList.length)
|
||||
return this.$message.warning("请先选择图片");
|
||||
this.$modalSure().then(() => {
|
||||
fileDeleteApi(this.ids.join(",")).then(() => {
|
||||
this.$message.success("刪除成功");
|
||||
this.getFileList();
|
||||
this.checkPicList = [];
|
||||
});
|
||||
});
|
||||
},
|
||||
// 移动分类点击
|
||||
handleSelClick(node) {
|
||||
if (this.ids.length) {
|
||||
this.sleOptions = {
|
||||
attrId: this.ids.join(","),
|
||||
pid: node.id,
|
||||
};
|
||||
this.getMove();
|
||||
} else {
|
||||
this.$message.warning("请先选择图片");
|
||||
}
|
||||
},
|
||||
getMove() {
|
||||
attachmentMoveApi(this.sleOptions)
|
||||
.then(async (res) => {
|
||||
this.$message.success("操作成功");
|
||||
this.clearBoth();
|
||||
this.getFileList();
|
||||
})
|
||||
.catch((res) => {
|
||||
this.clearBoth();
|
||||
});
|
||||
},
|
||||
clearBoth() {
|
||||
this.sleOptions = {
|
||||
attrId: "",
|
||||
pid: "",
|
||||
};
|
||||
this.checkPicList = [];
|
||||
this.ids = [];
|
||||
},
|
||||
videoChange(val) {
|
||||
if (val == false) {
|
||||
this.$set(this.tableData, "attType", "jpg,jpeg,gif,png,bmp,PNG,JPG");
|
||||
this.getFileList();
|
||||
} else {
|
||||
this.$set(this.tableData, "attType", "video/mp4");
|
||||
this.getFileList();
|
||||
}
|
||||
},
|
||||
radioChange(val){
|
||||
if(val === 'video'){
|
||||
this.videoChange(true)
|
||||
}else{
|
||||
this.videoChange(false)
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.demo::-webkit-scrollbar {
|
||||
display: none; /* Chrome Safari */
|
||||
}
|
||||
|
||||
.demo {
|
||||
scrollbar-width: none; /* firefox */
|
||||
-ms-overflow-style: none; /* IE 10+ */
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.temp {
|
||||
height: 0;
|
||||
margin-bottom: 0;
|
||||
border: none;
|
||||
padding: 0;
|
||||
}
|
||||
.selectTreeClass {
|
||||
background: #d5e8fc;
|
||||
}
|
||||
.treeBox {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
.upload-demo {
|
||||
display: inline-block !important;
|
||||
float: left;
|
||||
}
|
||||
.tree_w {
|
||||
padding: 20px 30px;
|
||||
}
|
||||
.custom-tree-node {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
font-size: 12px;
|
||||
padding-right: 8px;
|
||||
color: #4386c6;
|
||||
}
|
||||
.custom-tree-node-label {
|
||||
display: block;
|
||||
width: 125px;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.el-ic {
|
||||
display: none;
|
||||
i,
|
||||
span {
|
||||
/*padding: 0 14px;*/
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
}
|
||||
.svg-icon {
|
||||
color: #4386c6;
|
||||
}
|
||||
}
|
||||
.el-tree-node__content {
|
||||
height: 38px;
|
||||
}
|
||||
.el-tree-node__expand-icon {
|
||||
color: #428bca;
|
||||
/*padding: 10px 10px 0px 10px !important;*/
|
||||
}
|
||||
.el-tree-node__content:hover .el-ic {
|
||||
color: #428bca !important;
|
||||
display: inline-block;
|
||||
}
|
||||
.el-tree-node__content:hover {
|
||||
font-weight: bold;
|
||||
}
|
||||
.el-tree--highlight-current
|
||||
.el-tree-node.is-current
|
||||
> .el-tree-node__content
|
||||
:hover {
|
||||
.el-tree-node__expand-icon.is-leaf {
|
||||
color: transparent;
|
||||
cursor: default;
|
||||
}
|
||||
/*background-color: #3998d9;*/
|
||||
.custom-tree-node {
|
||||
font-weight: bold;
|
||||
}
|
||||
.el-tree-node__expand-icon {
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
.el-dialog__body {
|
||||
.upload-container .image-preview .image-preview-wrapper img {
|
||||
height: 100px;
|
||||
}
|
||||
.el-dialog .el-collapse-item__wrap {
|
||||
padding-top: 0px;
|
||||
}
|
||||
.spatial_img {
|
||||
.el-collapse-item__wrap {
|
||||
margin-bottom: 0;
|
||||
padding-top: 0px;
|
||||
}
|
||||
}
|
||||
.upload-container .image-preview .image-preview-wrapper {
|
||||
width: 120px;
|
||||
}
|
||||
.upload-container .image-preview .image-preview-action {
|
||||
line-height: 100px;
|
||||
height: 100px;
|
||||
}
|
||||
}
|
||||
.trees-coadd {
|
||||
width: 100%;
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
.scollhide {
|
||||
overflow-x: hidden;
|
||||
overflow-y: scroll;
|
||||
padding: 10px 0 10px 0;
|
||||
box-sizing: border-box;
|
||||
.trees {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
.scollhide::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
.conters {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
/*max-height: 296px;*/
|
||||
overflow: auto;
|
||||
}
|
||||
.conters:after {
|
||||
content: "";
|
||||
width: 410px !important;
|
||||
}
|
||||
.gridPic {
|
||||
margin-right: 15px;
|
||||
margin-bottom: 10px;
|
||||
width: 110px;
|
||||
height: 110px;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: block;
|
||||
}
|
||||
video {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: block;
|
||||
}
|
||||
.num_badge {
|
||||
position: absolute;
|
||||
bottom: 6px;
|
||||
right: 6px;
|
||||
display: inline-block;
|
||||
min-width: 20px;
|
||||
height: 20px;
|
||||
border-radius: 10px;
|
||||
background: #1890FF;
|
||||
font-size: 14px;
|
||||
text-align: center;
|
||||
color: #fff;
|
||||
line-height: 20px;
|
||||
}
|
||||
}
|
||||
.conter {
|
||||
width: 99%;
|
||||
height: 100%;
|
||||
.bnt {
|
||||
width: 100%;
|
||||
padding: 0 13px 10px 7px;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
}
|
||||
.pictrueList {
|
||||
/*padding-left: 15px;*/
|
||||
width: 100%;
|
||||
el-image {
|
||||
width: 100%;
|
||||
border: 2px solid #fff;
|
||||
}
|
||||
.on {
|
||||
border: 2px solid #1890FF;
|
||||
}
|
||||
}
|
||||
.el-image {
|
||||
width: 110px;
|
||||
height: 110px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.imagesNo {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
margin: 65px 0;
|
||||
.imagesNo_sp {
|
||||
font-size: 13px;
|
||||
color: #dbdbdb;
|
||||
line-height: 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
.relative{
|
||||
position: relative;
|
||||
}
|
||||
.abs_video{
|
||||
position: absolute;
|
||||
right: 20px;
|
||||
top: 0;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,80 @@
|
||||
(function () {
|
||||
/* eslint-disable */
|
||||
if (window.frameElement.id) {
|
||||
let parent = window.parent,
|
||||
|
||||
dialog = parent.$EDITORUI[window.frameElement.id.replace(/_iframe$/, '')],
|
||||
|
||||
editor = dialog.editor,
|
||||
|
||||
UE = parent.UE,
|
||||
|
||||
domUtils = UE.dom.domUtils,
|
||||
|
||||
utils = UE.utils,
|
||||
|
||||
browser = UE.browser,
|
||||
/* eslint-disable */
|
||||
ajax = UE.ajax,
|
||||
|
||||
$G = function (id) {
|
||||
return document.getElementById(id)
|
||||
},
|
||||
$focus = function (node) {
|
||||
setTimeout(function () {
|
||||
if (browser.ie) {
|
||||
var r = node.createTextRange();
|
||||
r.collapse(false);
|
||||
r.select();
|
||||
} else {
|
||||
node.focus()
|
||||
}
|
||||
}, 0)
|
||||
};
|
||||
window.nowEditor = {editor: editor, dialog: dialog};
|
||||
utils.loadFile(document, {
|
||||
href: editor.options.themePath + editor.options.theme + '/dialogbase.css?cache=' + Math.random(),
|
||||
tag: 'link',
|
||||
type: 'text/css',
|
||||
rel: 'stylesheet'
|
||||
});
|
||||
var lang = editor.getLang(dialog.className.split('-')[2]);
|
||||
if (lang) {
|
||||
domUtils.on(window, 'load', function () {
|
||||
var langImgPath = editor.options.langPath + editor.options.lang + '/images/';
|
||||
// 针对静态资源
|
||||
for (var i in lang['static']) {
|
||||
var dom = $G(i);
|
||||
if (!dom) continue;
|
||||
let tagName = dom.tagName,
|
||||
content = lang['static'][i];
|
||||
if (content.src) {
|
||||
// clone
|
||||
content = utils.extend({}, content, false);
|
||||
content.src = langImgPath + content.src;
|
||||
}
|
||||
if (content.style) {
|
||||
content = utils.extend({}, content, false);
|
||||
content.style = content.style.replace(/url\s*\(/g, 'url(' + langImgPath)
|
||||
}
|
||||
switch (tagName.toLowerCase()) {
|
||||
case 'var':
|
||||
dom.parentNode.replaceChild(document.createTextNode(content), dom);
|
||||
break;
|
||||
case 'select':
|
||||
var ops = dom.options;
|
||||
for (var j = 0, oj; oj = ops[j];) {
|
||||
oj.innerHTML = content.options[j++];
|
||||
}
|
||||
for (var p in content) {
|
||||
p != 'options' && dom.setAttribute(p, content[p]);
|
||||
}
|
||||
break;
|
||||
default :
|
||||
domUtils.setAttributes(dom, content);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
})();
|
||||
@@ -0,0 +1,24 @@
|
||||
import Element from 'element-ui'
|
||||
import '@/styles/element-variables.scss'
|
||||
import uploadFromComponent from './index.vue'
|
||||
import Vue from 'vue'
|
||||
import Cookies from 'js-cookie'
|
||||
Vue.use(Element, {
|
||||
size: Cookies.get('size') || 'medium' // set element-ui default size
|
||||
})
|
||||
const uploadFrom = {}
|
||||
uploadFrom.install = function(Vue, options) {
|
||||
const ToastConstructor = Vue.extend(uploadFromComponent)
|
||||
// 生成一个该子类的实例
|
||||
const instance = new ToastConstructor()
|
||||
instance.$mount(document.createElement('div'))
|
||||
document.body.appendChild(instance.$el)
|
||||
Vue.prototype.$modalUpload = function(callback, isMore, modelName, boolean) {
|
||||
instance.visible = true
|
||||
instance.callback = callback
|
||||
instance.isMore = isMore
|
||||
instance.modelName = modelName
|
||||
instance.booleanVal = boolean
|
||||
}
|
||||
}
|
||||
export default uploadFrom
|
||||
@@ -0,0 +1,50 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-dialog
|
||||
title="上传图片"
|
||||
:visible.sync="visible"
|
||||
width="950px"
|
||||
:modal="booleanVal"
|
||||
append-to-body
|
||||
:before-close="handleClose"
|
||||
>
|
||||
<upload-index v-if="visible" :isMore="isMore" :modelName="modelName" @getImage="getImage" />
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// import UploadIndex from '@/components/uploadPicture/index.vue'
|
||||
export default {
|
||||
name: 'UploadFroms',
|
||||
// components: { UploadIndex },
|
||||
data() {
|
||||
return {
|
||||
visible: false,
|
||||
callback: function() {},
|
||||
isMore: '',
|
||||
modelName: '',
|
||||
ISmodal: false,
|
||||
booleanVal: false
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
// show() {
|
||||
// this.visible = this.show
|
||||
// }
|
||||
},
|
||||
methods: {
|
||||
handleClose() {
|
||||
this.visible = false
|
||||
},
|
||||
getImage(img) {
|
||||
this.callback(img)
|
||||
this.visible = false
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,157 @@
|
||||
<template>
|
||||
<div class="divBox">
|
||||
<el-card class="box-card">
|
||||
<div slot="header" class="clearfix">
|
||||
<el-form inline>
|
||||
<el-form-item>
|
||||
<el-input v-model="tableFrom.keywords" placeholder="请输入用户名称" class="selWidth">
|
||||
<el-button slot="append" icon="el-icon-search" @click="search" />
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
<el-table
|
||||
v-loading="loading"
|
||||
:data="tableData.data"
|
||||
width="800px"
|
||||
size="small"
|
||||
>
|
||||
<el-table-column label="" width="40">
|
||||
<template slot-scope="scope">
|
||||
<el-radio v-model="templateRadio" :label="scope.row.uid" @change.native="getTemplateRow(scope.$index,scope.row)"> </el-radio>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="uid"
|
||||
label="ID"
|
||||
min-width="60"
|
||||
/>
|
||||
<el-table-column
|
||||
prop="nickname"
|
||||
label="微信用户名称"
|
||||
min-width="130"
|
||||
/>
|
||||
<el-table-column label="用户头像" min-width="80">
|
||||
<template slot-scope="scope">
|
||||
<div class="demo-image__preview">
|
||||
<el-image
|
||||
class="tabImage"
|
||||
:src="scope.row.avatar"
|
||||
:preview-src-list="[scope.row.avatar]"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
label="性别"
|
||||
min-width="80"
|
||||
>
|
||||
<template slot-scope="scope">
|
||||
<span>{{ scope.row.sex | saxFilter }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
label="地区"
|
||||
min-width="130"
|
||||
>
|
||||
<template slot-scope="scope">
|
||||
<span>{{ scope.row.addres }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div class="block">
|
||||
<el-pagination
|
||||
:page-sizes="[10, 20, 30, 40]"
|
||||
:page-size="tableFrom.limit"
|
||||
:current-page="tableFrom.page"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
:total="tableData.total"
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="pageChange"
|
||||
/>
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { userListApi } from '@/api/user'
|
||||
export default {
|
||||
name: 'UserList',
|
||||
filters: {
|
||||
saxFilter(status) {
|
||||
const statusMap = {
|
||||
0: '未知',
|
||||
1: '男',
|
||||
2: '女'
|
||||
}
|
||||
return statusMap[status]
|
||||
},
|
||||
statusFilter(status) {
|
||||
const statusMap = {
|
||||
'wechat': '微信用户',
|
||||
'routine': '小程序用户'
|
||||
}
|
||||
return statusMap[status]
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
templateRadio: 0,
|
||||
loading: false,
|
||||
tableData: {
|
||||
data: [],
|
||||
total: 0
|
||||
},
|
||||
tableFrom: {
|
||||
page: 1,
|
||||
limit: 10,
|
||||
keywords: ''
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.getList()
|
||||
},
|
||||
methods: {
|
||||
getTemplateRow(idx, row) {
|
||||
this.$emit('getTemplateRow', row);
|
||||
},
|
||||
// 列表
|
||||
getList() {
|
||||
this.loading = true
|
||||
userListApi(this.tableFrom).then(res => {
|
||||
this.tableData.data = res.list
|
||||
this.tableData.total = res.total
|
||||
this.loading = false
|
||||
}).catch(res => {
|
||||
this.$message.error(res.message)
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
search(){
|
||||
this.loading = true
|
||||
userListApi({keywords:this.tableFrom.keywords}).then(res => {
|
||||
this.tableData.data = res.list
|
||||
this.tableData.total = res.total
|
||||
this.loading = false
|
||||
}).catch(res => {
|
||||
this.$message.error(res.message)
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
pageChange(page) {
|
||||
this.tableFrom.page = page
|
||||
this.getList()
|
||||
},
|
||||
handleSizeChange(val) {
|
||||
this.tableFrom.limit = val
|
||||
this.getList()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
Reference in New Issue
Block a user