Browse Source

逻辑显示完成70%

old
wangqing 4 years ago
parent
commit
1916f4accb
  1. 40
      src/assets/styles/index.scss
  2. 20
      src/components/parser/Parser.vue
  3. 43
      src/layout/example.vue
  4. 15
      src/utils/convert.js
  5. 50
      src/utils/expression.js
  6. 63
      src/views/form/ProjectForm.vue
  7. 6
      src/views/form/editor.vue
  8. 159
      src/views/form/logic.vue

40
src/assets/styles/index.scss

@ -3,6 +3,7 @@
@import './transition.scss';
@import './element-ui.scss';
@import './btn.scss';
body {
height: 100%;
margin: 0;
@ -11,33 +12,41 @@ body {
text-rendering: optimizeLegibility;
font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB, Microsoft YaHei, Arial, sans-serif;
}
label {
font-weight: 700;
}
html {
height: 100%;
margin: 0;
box-sizing: border-box;
}
#app {
margin: 0;
height: 100%;
}
*,
*::before,
*::after {
box-sizing: inherit;
}
.no-padding {
padding: 0 !important;
}
.padding-content {
padding: 4px 0;
}
a:focus,
a:active {
outline: none;
}
a,
a:focus,
a:hover {
@ -45,33 +54,49 @@ a:hover {
color: inherit;
text-decoration: none;
}
div:focus {
outline: none;
}
.fr {
float: right;
}
.fl {
float: left;
}
.pr-5 {
padding-right: 5px;
}
.pl-5 {
padding-left: 5px;
}
.pl-10 {
padding-left: 10px;
}
.mt-5 {
margin-top: 5px;
}
.mr-10 {
margin-right: 10px;
}
.block {
display: block;
}
.pointer {
cursor: pointer;
}
.inlineBlock {
display: block;
}
.clearfix {
&::after {
visibility: hidden;
@ -82,6 +107,7 @@ div:focus {
height: 0;
}
}
aside {
background: #eef1f6;
padding: 8px 24px;
@ -94,9 +120,11 @@ aside {
color: #2c3e50;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
a {
color: #337ab7;
cursor: pointer;
&:hover {
color: rgb(32, 160, 255);
}
@ -107,16 +135,20 @@ aside {
.app-container {
padding: 20px;
}
.components-container {
margin: 30px 50px;
position: relative;
}
.pagination-container {
margin-top: 30px;
}
.text-center {
text-align: center;
}
.sub-navbar {
height: 50px;
line-height: 50px;
@ -126,27 +158,34 @@ aside {
padding-right: 20px;
transition: 600ms ease position;
background: linear-gradient(90deg, rgba(32, 182, 249, 1) 0%, rgba(32, 182, 249, 1) 0%, rgba(33, 120, 241, 1) 100%, rgba(33, 120, 241, 1) 100%);
.subtitle {
font-size: 20px;
color: #fff;
}
&.draft {
background: #d0d0d0;
}
&.deleted {
background: #d0d0d0;
}
}
.link-type,
.link-type:focus {
color: #337ab7;
cursor: pointer;
&:hover {
color: rgb(32, 160, 255);
}
}
.filter-container {
padding-bottom: 10px;
.filter-item {
display: inline-block;
vertical-align: middle;
@ -158,6 +197,7 @@ aside {
.multiselect {
line-height: 16px;
}
.multiselect--active {
z-index: 1000 !important;
}

20
src/components/parser/Parser.vue

@ -1,5 +1,6 @@
<script>
import {deepClone, jsonClone} from '@/utils/index'
import {evalExpression} from '@/utils/expression'
import render from '@/components/render/render.js'
import _ from 'lodash'
@ -33,8 +34,11 @@ const layouts = {
if (formConfCopy.showNumber) {
label = scheme.serialNumber + ': ' + label
}
this.$set(this[this.formConf.formModel], scheme.__vModel__, [])
let colStyle = scheme.logicShow ? '' : 'display:none'
return (
<el-col span={config.span}>
<el-col span={config.span} style={colStyle}>
<el-form-item label-width={labelWidth} prop={scheme.__vModel__}
label={config.showLabel ? label : ''}>
<render conf={scheme} {...{on: listeners}} />
@ -155,9 +159,15 @@ function deleteUpload(config, scheme, file, fileList) {
}
function setValue(event, config, scheme) {
console.log(event)
console.log(config)
console.log(scheme)
console.log(this[this.formConf.formModel])
this.$set(config, 'defaultValue', event)
this.$set(this[this.formConf.formModel], scheme.__vModel__, event)
setValueLabel.call(this, event, config, scheme)
const {formConfCopy} = this
}
/**
@ -346,10 +356,10 @@ export default {
}
setTimeout(() => {
let isError= document.getElementsByClassName("is-error");
isError[0].querySelector('input').focus();
},100);
return false;
let isError = document.getElementsByClassName('is-error')
isError[0].querySelector('input').focus()
}, 100)
return false
}
// sumit
this.$emit('submit', {

43
src/layout/example.vue

@ -1,43 +0,0 @@
<template>
<div>
<div id="nav">
<RouterLink to="/example/sprite">sprite精灵图</RouterLink>
<RouterLink to="/example/svgicon">svg icon</RouterLink>
<RouterLink to="/example/globalComponent">全局组件</RouterLink>
<RouterLink to="/example/axios">axios</RouterLink>
<RouterLink to="/example/cookie">cookie</RouterLink>
<RouterLink to="/example/meta">meta</RouterLink>
<RouterLink to="/example/vuex">vuex</RouterLink>
<RouterLink to="/example/component">组件</RouterLink>
<RouterLink :to="{name:'exampleParams',params:{test:'123'}}">路由params</RouterLink>
<RouterLink :to="{path:'/example/query',query:{test:'123'}}">路由query</RouterLink>
<RouterLink to="/example/reload">刷新当前页面</RouterLink>
<RouterLink to="/example/permission/router">router鉴权</RouterLink>
<RouterLink to="/example/permission/js">js鉴权</RouterLink>
</div>
<RouterView />
</div>
</template>
<style lang="scss" scoped>
#nav {
margin-bottom: 10px;
a {
text-decoration: none;
font-size: 14px;
&::after {
content: '|';
margin: 0 10px;
font-weight: normal;
font-size: 14px;
}
&:last-child::after {
content: none;
}
&.router-link-active {
font-weight: bold;
font-size: 18px;
}
}
}
</style>

15
src/utils/convert.js

@ -1,6 +1,9 @@
/**
* 处理项目数据
*
*/
import _ from 'lodash'
import {imageComponents, inputComponents, selectComponents} from '@/components/generator/config'
import {jsonClone} from '@/utils/index'
/**
* 表单json转换为后台需要的对象
@ -41,7 +44,7 @@ let typeMap = new Map()
export function dbDataConvertForItemJson(data) {
let {required, placeholder} = data
if (required && !placeholder) {//必填项目验证未填默认提示语
data.placeholder="此题为必填项目"
data.placeholder = '此题为必填项目'
}
if (!typeMap.size > 0) {
//根据类型获取默认数据
@ -58,6 +61,7 @@ export function dbDataConvertForItemJson(data) {
_.set(jsonItem, param[key], value)
})
}
jsonItem.dId = data.id
jsonItem.sort = data.sort
jsonItem.typeId = data.type
jsonItem.__config__.span = data.span
@ -79,16 +83,17 @@ export function dbDataConvertForItemJson(data) {
}
jsonItem.regList = data.regList
jsonItem.placeholder = data.placeholder
jsonItem.formItemId = data.formItemId
jsonItem.__vModel__ = 'field' + data.formItemId
return jsonItem
}
/**
* 不同属性的存在json的位置不用 使用变量记录key 通过lodash表达式获取 1 2 3 4 为typeId
*
* @type {{'1': {maxlength: string, prepend: string, append: string}}}
*
*/
let dataParams = {
const dataParams = {
//单行文本
'INPUT': {
'prepend': '__slot__.prepend',

50
src/utils/expression.js

@ -0,0 +1,50 @@
import _ from 'lodash'
/**
* 自定义表达式
*/
const expressionOperator = {
'eq': function(v1, v2) {
return _.isEqual(v1, v2)
},
'ne': function(v1, v2) {
return !_.isEqual(v1, v2)
}
}
/**
* 逻辑连接符
* @type {{'1': string, '2': string}}
*/
const LogicConnector = {
1: '||',
2: '&&'
}
/**
* 获取表达式
* @conditionList 条件列表
* @connector 连接符 ||或者 &&
*/
export function getExpression(conditionList, connector) {
let exList = conditionList.map(item => {
return `#id${item.formItemId} ${item.expression} ${item.optionValue}`
})
return _.join(exList, LogicConnector[connector])
}
/**
* 执行表达式是否成立
*/
export function evalExpression(context, expression) {
let exArray = expression.split(/[|][&]/)
exArray.forEach(item => {
let itemExpArr = item.split(' ')
let varName = itemExpArr[0].replace('#id')
let sp = itemExpArr[1]
let value = itemExpArr[2]
let flag = expressionOperator[sp](context[varName], value)
})
return flag
}

63
src/views/form/ProjectForm.vue

@ -29,6 +29,7 @@
<script>
import Parser from '@/components/parser/Parser'
import {dbDataConvertForItemJson} from '@/utils/convert'
import {getExpression} from '@/utils/expression'
window.onload = function() {
document.addEventListener('touchstart', function(event) {
@ -54,6 +55,7 @@ export default {
},
data() {
return {
logicShowTriggerRule: {},
startParser: false,
projectTheme: {
headImgUrl: '',
@ -63,6 +65,7 @@ export default {
},
formConf: {
fields: [],
logicShowRule: {},
projectKey: '',
projectKind: 1,
__methods__: {},
@ -106,17 +109,29 @@ export default {
}
this.formConf.size = window.innerWidth < 480 ? 'medium' : 'small'
},
mounted() {
async mounted() {
let url = `/user/project/details/${this.formConf.projectKey}`
if (this.formConf.projectKind == 2) {
url = `/project/template/details/${this.formConf.projectKey}`
}
let logicItemList = []
//
if (this.formConf.projectKind == 1) {
let res = await this.queryLogicItemList()
logicItemList = res.data
}
let logicItemMap = new Map()
logicItemList.forEach(item => {
logicItemMap.set(item.formItemId, item)
this.logicShowTriggerHandle(item)
})
this.$api.get(url).then(res => {
if (res.data) {
let serialNumber = 1
let fields = res.data.projectItems.map(item => {
let projectItem = dbDataConvertForItemJson(item)
projectItem.serialNumber = serialNumber
projectItem.logicShow = logicItemMap.get(projectItem.formItemId) ? false : true
serialNumber++
return projectItem
})
@ -125,18 +140,14 @@ export default {
this.formConf.title = res.data.project.name
this.formConf.description = res.data.project.describe
}
this.formConf.logicShowRule = this.logicShowTriggerRule
//
if (res.data.userProjectTheme) {
this.projectTheme = res.data.userProjectTheme
let {submitBtnText, showNumber, btnsColor} = res.data.userProjectTheme
if (submitBtnText) {
this.formConf.submitBtnText = submitBtnText
}
if (showNumber) {
this.formConf.showNumber = showNumber
}
if (btnsColor) {
this.formConf.submitBtnColor = btnsColor
}
if (submitBtnText) this.formConf.submitBtnText = submitBtnText
if (showNumber) this.formConf.showNumber = showNumber
if (btnsColor) this.formConf.submitBtnColor = btnsColor
}
this.startParser = true
@ -144,6 +155,38 @@ export default {
})
},
methods: {
/**
* 处理逻辑显示数据
*/
logicShowTriggerHandle(logicItem) {
if (!logicItem) {
return
}
//
logicItem.conditionList.forEach(item => {
let rules = this.logicShowTriggerRule[item.formItemId]
if (!rules) {
rules = new Set()
}
rules.add({
//
triggerFormItemId: logicItem.formItemId,
logicExpression: getExpression(logicItem.conditionList, logicItem.expression)
})
this.logicShowTriggerRule[item.formItemId] = rules
})
},
// axios
queryLogicItemList() {
return new Promise((resolve, reject) => {
this.$api.get('/user/project/logic/list', {params: {projectKey: this.formConf.projectKey}})
.then((res) => {
resolve(res)
}).catch((err) => {
reject(err)
})
})
},
submitForm(data) {
this.$emit('submit', data)
}

6
src/views/form/editor.vue

@ -15,14 +15,12 @@
:clone="cloneComponent"
draggable=".components-item"
:sort="false"
@end="onEnd"
>
@end="onEnd">
<div
v-for="(element, index) in item.list"
:key="index"
class="components-item"
@click="addComponent(element)"
>
@click="addComponent(element)">
<div class="components-body">
<svg-icon :name="element.__config__.tagIcon"/>
{{ element.__config__.label }}

159
src/views/form/logic.vue

@ -1,7 +1,7 @@
<template>
<div class="project-logic-container">
<el-scrollbar style="height: 90vh;">
<el-row type="flex" style="width: 230px" justify="center" align="middle">
<el-scrollbar class="scrollbar-container">
<el-row type="flex" class="header-row" justify="center" align="middle">
<el-col :span="12">
<p class="logic_title">显示逻辑</p>
</el-col>
@ -13,25 +13,25 @@
</el-col>
</el-row>
<div class="show-logic-container">
<div v-if="!logicList.length">
<div v-if="!logicList.length" class="not-logic-container">
<el-row>
<el-col :offset="10">
<el-button type="text" @click="addLogicHandle">
<i class="el-icon-circle-plus-outline" style="font-size: 20px"></i>
<span style="font-size: 18px">添加逻辑</span>
<i class="el-icon-circle-plus-outline"></i>
<span class="label">添加逻辑</span>
</el-button>
</el-col>
</el-row>
</div>
<div v-else>
<div v-else class="logic-item-container">
<el-row type="flex" align="middle" justify="center">
<el-col :span="11" :offset="1">
<p style="font-size: 14px;color: #aaa"> {{ logicList.length + 1 }}. 条显示逻辑</p>
<p> {{ logicList.length }}. 条显示逻辑</p>
</el-col>
<el-col :span="6" :offset="6">
<el-button type="primary" size="mini" @click="addLogicHandle">
<i class="el-icon-plus"></i>
<span style="font-size: 15px">添加逻辑</span>
<span class="label">添加逻辑</span>
</el-button>
</el-col>
</el-row>
@ -40,15 +40,15 @@
:key="index">
<el-row type="flex" align="middle" justify="center">
<el-col :offset="1" :span="6">
<span style="margin-right: 8px">{{ index + 1 }}.</span>
<span class="mr-10">{{ index + 1 }}.</span>
<el-select
:disabled="!!logicItem.showFormItemId"
v-model="logicItem.showFormItemId" placeholder="请选择问题">
:disabled="!!logicItem.formItemId"
v-model="logicItem.formItemId" placeholder="请选择问题">
<el-option
v-for="item in getProjectItemList() "
v-for="item in getProjectItemList(logicItem.formItemId) "
:key="item.id"
:label="item.label"
:value="item.id">
:value="item.formItemId">
</el-option>
</el-select>
</el-col>
@ -56,7 +56,7 @@
<span>符合以下</span>
</el-col>
<el-col :span="4">
<el-select v-model="logicItem.showExpression" placeholder="请选择">
<el-select v-model="logicItem.expression" placeholder="请选择">
<el-option
v-for="item in questionOptions"
:key="item.value"
@ -73,14 +73,14 @@
v-for="(cItem,cIndex) in logicItem.conditionList"
:key="cIndex"
:gutter="20"
style="margin-top: 5px" type="flex" align="middle" justify="center">
class="mt-5" type="flex" align="middle" justify="center">
<el-col :offset="1" :span="6">
<el-select v-model="cItem.formItemId" placeholder="请选择题目">
<el-option
v-for="item in getConditionProjectItemList(logicItem)"
:key="item.id"
:label="item.label"
:value="item.id">
:value="item.formItemId">
</el-option>
</el-select>
</el-col>
@ -106,14 +106,12 @@
</el-col>
<el-col :span="9">
<el-button type="text" @click="addConditionHandle(logicItem)">
<i class="el-icon-circle-plus-outline"
style="font-size: 24px"/>
<i class="el-icon-circle-plus-outline"/>
</el-button>
<el-button type="text"
style="color: #ff4949"
class="remove"
@click="removeConditionHandle(logicItem,index,cIndex)">
<i class="el-icon-remove-outline"
style="font-size: 24px"/>
<i class="el-icon-remove-outline"/>
</el-button>
</el-col>
@ -138,13 +136,14 @@ export default {
},
mounted() {
this.queryProjectItems()
this.queryProjectLogics()
},
data() {
return {
//
defaultLogicItem: {
showFormItemId: null,
showExpression: 'all',
formItemId: null,
expression: 1,
conditionList: [
{
formItemId: null,
@ -154,72 +153,113 @@ export default {
]
},
conditionOptions: [{
value: 'checked',
value: 'eq',
label: '选中'
}, {
value: 'unchecked',
value: 'ne',
label: '未选中'
}],
questionOptions: [{
value: 'all',
value: 1,
label: '全部'
}, {
value: 'any',
value: 2,
label: '任意'
}],
allProjectItemList: [],
logicList: []
}
}, methods: {
},
watch: {
getLogicList: {
handler(val, oldVal) {
//
let updateVal = _.differenceWith(val, oldVal, (arrVal, othVal) => {
if (JSON.stringify(arrVal) == JSON.stringify(othVal)) {
return true
}
return false
})
console.log(updateVal)
if (updateVal && updateVal[0] && updateVal[0].conditionList.length) {
let updateData = updateVal[0]
updateData.projectKey = this.projectKey
if (updateData.formItemId) {
this.saveProjectLogic(updateData)
}
}
},
deep: true
}
},
computed: {
// vue watch
getLogicList() {
return JSON.parse(JSON.stringify(this.logicList))
}
},
methods: {
addConditionHandle(logicItem) {
logicItem.conditionList.push({})
},
removeConditionHandle(logicItem, logicIndex, index) {
logicItem.conditionList.splice(index, 1)
if (logicItem.conditionList.length == 0) {
this.$api.post(`/user/project/logic/delete`, logicItem).then(res => {
this.logicList.splice(logicIndex, 1)
})
}
},
addLogicHandle() {
this.logicList.push(jsonSimpleClone(this.defaultLogicItem))
},
getConditionProjectItemList(logicItem) {
let showFormItemId = logicItem.showFormItemId
let showFormItemId = logicItem.formItemId
if (!showFormItemId) {
return
}
//使
let conditionProjectItemList = jsonSimpleClone(this.allProjectItemList)
let index = conditionProjectItemList.findIndex(item => item.id == showFormItemId)
let index = conditionProjectItemList.findIndex(item => item.formItemId == showFormItemId)
conditionProjectItemList = _.slice(conditionProjectItemList, 0, index)
conditionProjectItemList = conditionProjectItemList.filter((item) => {
return ['RADIO', 'CHECKBOX', 'SELECT'].includes(item.type)
})
return conditionProjectItemList
},
getProjectItemList() {
getProjectItemList(formItemId) {
//
let selectedFormItemList = this.logicList.map(item => item.showFormItemId)
let selectedFormItemList = this.logicList.map(item => item.formItemId)
let projectItemList = jsonSimpleClone(this.allProjectItemList)
//
projectItemList.shift()
return projectItemList.filter((item) => {
return !selectedFormItemList.includes(item.id)
return !selectedFormItemList.includes(item.id) || item.formItemId == formItemId
})
},
getFormItemOptions(formItemId) {
let formItem = this.allProjectItemList.find(item => item.id == formItemId)
let formItem = this.allProjectItemList.find(item => item.formItemId == formItemId)
if (formItem) {
return formItem.expand.options
}
return []
},
queryProjectLogics() {
this.$api.get(`/user/project/logic/list`, {params: {projectKey: this.projectKey}}).then(res => {
this.logicList = res.data
})
},
queryProjectItems() {
this.$api.get(`/user/project/item/list`, {params: {key: this.projectKey}}).then(res => {
this.allProjectItemList = res.data
// //
// let projectItemList = res.data
// projectItemList.shift()
// this.projectItemList = projectItemList
})
},
saveProjectLogic(data) {
this.$api.post(`/user/project/logic/save`, data).then(res => {
if (!data.id) {
let index = _.findIndex(this.logicList, {formItemId: data.formItemId})
this.logicList[index].id = res.data.id
}
})
}
}
@ -227,19 +267,24 @@ export default {
}
</script>
<style scoped>
<style lang="scss" scoped>
.project-logic-container {
width: 100%;
padding: 0px;
margin: 0;
background-color: #F7F7F7;
min-height: 84vh;
min-width: 80vw;
display: flex;
justify-content: center;
.scrollbar-container {
height: 90vh;
}
.project-logic-container .logic_title {
.header-row {
width: 230px;
}
.logic_title {
font-size: 18px;
height: 45px;
line-height: 45px;
@ -247,6 +292,32 @@ export default {
text-indent: 20px;
padding-top: 20px;
}
}
.not-logic-container {
.el-icon-circle-plus-outline {
font-size: 20px;
}
.label {
font-size: 18px
}
}
.logic-item-container {
.tips {
font-size: 14px;
color: #aaa
}
.label {
font-size: 15px
}
.remove {
color: #ff4949
}
}
.el-icon-question {
font-size: 23px;
@ -264,6 +335,10 @@ export default {
box-shadow: 0 2px 4px rgba(0, 0, 0, .12), 0 0 6px rgba(0, 0, 0, .04)
}
.el-icon-circle-plus-outline, .el-icon-remove-outline {
font-size: 24px
}
</style>
<style>
.question-popper.el-tooltip__popper[x-placement^="top"] .popper__arrow {

Loading…
Cancel
Save