Skip to content

Commit

Permalink
widgets, actions, layout
Browse files Browse the repository at this point in the history
  • Loading branch information
szuprefix committed Nov 21, 2020
1 parent 8524e2a commit 392b9ce
Show file tree
Hide file tree
Showing 15 changed files with 238 additions and 48 deletions.
2 changes: 1 addition & 1 deletion src/components/form/Widget.vue
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
@change="fieldValueChanged">
</el-switch>
<el-input-number v-model="value[field.name]" v-else-if="field.widget === 'number'" :disabled="field.disabled"
:controls="field.type === 'integer'" @change="fieldValueChanged">
controls-position="right" :controls="field.type === 'integer'" @change="fieldValueChanged">
</el-input-number>
<el-date-picker v-model="value[field.name]" :type="field.widget" value-format="yyyy-MM-ddTHH:mm:ss"
:placeholder="field.placeholder || field.label" :readonly="field.read_only"
Expand Down
2 changes: 1 addition & 1 deletion src/components/form/widgets/NumberRange.vue
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@
return !this.range[0] && !this.range[1]
},
options () {
return get(this.field, 'search.options') || [] // || DEFAULT_OPTIONS
return get(this.field, 'options') || [] // || DEFAULT_OPTIONS
}
},
watch: {
Expand Down
25 changes: 20 additions & 5 deletions src/components/form/widgets/Percent.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<template>
<span class="el-input-group" style="width:6rem;">
<el-input-number v-model="number" :controls="false" style="width:6rem;"
@change="onChange"></el-input-number><span class="el-input-group__append">%</span>
@change="onChange"></el-input-number><span class="el-input-group__append percent-input">%</span>
</span>
</template>
<script>
Expand All @@ -12,22 +12,37 @@
},
data () {
return {
number: this.value * 100
number: 0
}
},
components: {},
created () {
this.number = this.value * 100
this.calNumber(this.value)
},
methods: {
calNumber (v) {
this.number = (v * 100).toFixed(this.fixed)
},
onChange(v) {
this.$emit('input', v / 100)
let rv = (v / 100).toFixed(this.fixed+2)
this.calNumber(rv)
this.$emit('input', rv)
}
},
computed: {
fixed () {
return this.field.fixed || 2
}
},
watch: {
value (v) {
this.number = this.value * 100
this.calNumber(v)
}
}
}
</script>
<style>
.el-input-group__append.percent-input{
padding: 0 10px;
}
</style>
2 changes: 1 addition & 1 deletion src/components/layout/Actions.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<template>
<el-button-group v-if="showActions.length>0">
<template v-for="a in showActions">
<el-button :type="a.type" :title="a.title" :size="a.size || size" @click="handleCommand(a.do)"
<el-button v-bind="[a]" :size="a.size || size" @click="handleCommand(a.do)"
v-if="!a.show || a.show()" :key="a.name">
<i :class="getIconClass(a.icon)" v-if="a.icon"></i>{{a.label}}
</el-button>
Expand Down
99 changes: 99 additions & 0 deletions src/components/model/Display.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
<template>
<json-display v-model="value" :field="jsonField" v-if="loaded"></json-display>
</template>
<script>
import Model from './Model'
import ServerResponse from '../../mixins/server_response'
import arrayNormalize from '../../utils/array_normalize'
import JsonDisplay from '../widgets/JsonDisplay.vue'
import {get} from 'lodash'
export default{
mixins: [ServerResponse],
components: {JsonDisplay},
props: {
appModel: String,
items: Array,
value: Object,
field: Object
},
data () {
return {
mid: undefined,
formItems: [],
formValue: {},
loaded : false,
model: Model(this.appModel || this.field.model, this.defaults, this.$store.state.bus)
}
},
created () {
this.init()
this.$on("model-delete", this.onDelete)
},
methods: {
init(){
this.mid = this.model.id = this.getId()
this.model.load().then((data, options) => {
this.mid = this.model.id
this.formValue = Object.assign({}, this.model.data)
this.normalizeItems()
this.loaded = true
this.$emit('loaded', this.model)
}).catch(this.onServerResponseError)
},
getId(){
let id = this.value && this.value.id ||
this.$route.path.startsWith(this.model.getListUrl()) && this.$route.params.id
|| undefined
return id === 'create' ? undefined : id
},
defaultWidget (f) {
if (f.type == 'field' && f.model) {
return RelatedSelect
} else if (['field', 'choice'].includes(f.type) && f.choices) {
return f.choices.length <= 2 ? (f.multiple ? 'checkbox' : 'radio') : 'select'
}
},
getItems () {
if (this.items) {
return Promise.resolve(this.items)
}
return import(`@/views${this.model.getListUrl()}config.js`).then(m => {
let c = m.default
return this.mid && c.update || c.create || c.form || {}
}).catch(() => {
return {}
}).then(config => {
return config.items || Object.values(this.model.options.actions.POST).filter(a => a.read_only !== true)
})
},
normalizeItems() {
this.getItems().then(items => {
this.formItems = arrayNormalize(items, this.model.fieldConfigs)
})
},
clear () {
this.model.clear()
this.mid = this.model.id = undefined
this.formValue = Object.assign({}, this.model.data, this.defaults)
this.$nextTick(this.$refs.form.$refs.form.clearValidate)
},
},
computed: {
jsonField () {
return {...this.field, items: this.formItems}
}
},
watch: {
value(val){
this.formValue = val
},
formValue(val){
this.model.data = val
},
items () {
this.normalizeItems()
}
}
}
</script>
2 changes: 1 addition & 1 deletion src/components/model/Form.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<slot name="actions"></slot>
</el-col>
<el-col :span="6" class="flex-right">
<actions :items="topActionItems" :context="{model: model}"></actions>
<actions :items="topActionItems" :context="{model: model}" style="margin-right: 1rem"></actions>
</el-col>
</el-row>
<x-form :url="url" :items="formItems" v-model="formValue" ref="form" :options="options.form" :disabled="disabled"
Expand Down
80 changes: 57 additions & 23 deletions src/components/model/Search.vue
Original file line number Diff line number Diff line change
@@ -1,40 +1,45 @@
<template>
<div>
<div>
<el-radio-group v-model="form[f.name]" @change="onSearch" v-for="f in radioFilterFields" :key="f.name">
<el-radio-button :label="c.value" v-for="c in f.choices" :key="c.value">{{c.display_name}}</el-radio-button>
</el-radio-group>
</div>
<el-input title="模糊搜索, 多个关键词请用空格隔开"
:placeholder="`搜索${searchFieldNames}`"
v-model="value.search"
v-model="form.search"
suffix-icon="el-icon-search"
@change="onSearch"
clearable
:style="`width:${searchFieldNames.length+5}rem;min-width:10rem;`"
ref="search"
v-if="searchFields.length>0">
</el-input>
<template v-for="f in visiableFilterFields">
<el-select v-model="value[f.name]" clearable :placeholder="`请选择${f.label}`" v-if="f.widget =='boolean'"
<template v-for="f in notRadioFilterFields">
<el-select v-model="form[f.name]" clearable :placeholder="`请选择${f.label}`" v-if="f.widget =='boolean'"
:title="f.label" :style="`width:${f.label.length+5}rem;min-width:8rem;`" @change="onSearch">
<el-option :label="f.label" :value="true"></el-option>
<el-option :label="getBoolFieldFalseLabel(f.label)" :value="false"></el-option>
</el-select>
<model-select :field="f" v-model="value[f.name]" @input="onSearch"
<model-select :field="f" v-model="form[f.name]" @input="onSearch"
:showCreate="false" :appModel="f.model"
:title="f.label" :style="`width:${f.label.length+5}rem;min-width:8rem;`"
v-else-if="f.widget === 'modelselect'" :pageSize="100"></model-select>
<el-select v-model="value[f.name]" clearable :placeholder="`请选择${f.label}`"
<el-select v-model="form[f.name]" clearable :placeholder="`请选择${f.label}`"
v-else-if="f.widget === 'select'"
:title="f.label" @change="onSearch">
<el-option v-for="c in f.choices" :label="c.display_name" :value="c.value" :key="c.value"></el-option>
</el-select>
<date-range :field="f" v-model="value[`${f.name}__range`]" separator=","
<date-range :field="f" v-model="form[`${f.name}__range`]" separator=","
:title="f.label" v-else-if="f.widget === 'daterange'" @input="onSearch"></date-range>

<number-range :field="f" v-model="value[`${f.name}__range`]" separator="-"
<number-range :field="f" v-model="form[`${f.name}__range`]" separator="-" :options="f.options"
:title="f.label" v-else-if="f.widget === 'numberrange'" @input="onSearch"></number-range>

<array-input v-if="f.widget === 'array'"
v-model="value[`${f.name}__in`]" :placeholder="`批量查询${f.label}`" style="width: 10rem;"
:title="f.label" :autosize="{minRows:1,maxRows:4}" @change="onSearch"></array-input>
<el-input v-model="value[f.name]" :placeholder="`请输入${f.label}`"
<el-input v-model="form[f.name]" :placeholder="`请输入${f.label}`"
v-else-if="f.widget === 'input'"
:title="f.label" style="width: 10rem;" clearable @change="onSearch"></el-input>
</template>
Expand Down Expand Up @@ -63,42 +68,62 @@
orderingFields: [],
filterFields: [],
searchFields: [],
baseQueries: {}
baseQueries: {},
form: {}
}
},
components: {ModelSelect, ArrayInput, DateRange, NumberRange},
created () {
this.init()
},
mounted () {
this.selectRadioDefault()
},
methods: {
onSearch () {
this.$emit('change', this.value)
this.$emit('change', {...this.form})
},
init () {
this.form = {...this.value}
let search = this.model.options.actions.SEARCH
this.searchFields = search.search_fields
let items = this.items || search.filter_fields
let ss = this.model.viewsConfig.search || {}
let ffields = arrayNormalize(items, this.model.fieldConfigs, (a) => {
let label = this.map[a.name] && this.map[a.name].label || a.label
return {multiple: false, ...a, label, widget: this.defaultWidget(a)}
let d = {widget: this.defaultWidget(a),...ss[a.name], ...a, multiple: false, label}
return d
})
this.addSelectOptions(ffields)
// console.log(ffields)
// this.addSelectOptions(ffields)
this.filterFields = this.reorder(ffields)
this.filters = Object.assign({}, this.getFilters())
},
addSelectOptions (fields) {
let ss = this.model.viewsConfig.search
if (!ss) {
return
}
console.log(ss)
fields.forEach(f => {
let a = ss[f.name]
if (a) {
f.search = a
selectRadioDefault() {
let b = false
this.radioFilterFields.forEach(f => {
if(!this.form[f.name]) {
this.form[f.name] = f.choices[0].value
b =true
}
})
if(b) {
this.onSearch()
}
},
// addSelectOptions (fields) {
// let ss = this.model.viewsConfig.search
// if (!ss) {
// return
// }
// console.log(ss)
// fields.forEach(f => {
// let a = ss[f.name]
// if (a) {
// f.search = a
// }
// })
// },
defaultWidget (item) {
let f = item
if (f.type == 'boolean') {
Expand All @@ -118,7 +143,7 @@
}
},
reorder (items) {
let os = ['boolean', 'select', 'modelselect', 'input', 'array', 'numberrange', 'daterange']
let os = ['boolean', 'radio', 'select', 'modelselect', 'input', 'array', 'numberrange', 'daterange']
let rs = []
os.forEach(w => {
rs = rs.concat(items.filter(a => a.widget === w))
Expand Down Expand Up @@ -183,9 +208,18 @@
},
visiableFilterFields () {
return this.filterFields.filter(f => !(f.name in this.exclude))
},
radioFilterFields () {
return this.visiableFilterFields.filter(f => f.widget === 'radio')
},
notRadioFilterFields () {
return this.visiableFilterFields.filter(f => f.widget !== 'radio')
}
},
watch: {
value(v) {
this.form = {...v}
},
map () {
this.init()
}
Expand Down
17 changes: 14 additions & 3 deletions src/components/model/Select.vue
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@
<el-alert type="info" v-if="moreThanOnePage" show-icon title="记录太多未展示完全,请输入关键字进行搜索" :closable="false">
</el-alert>

<el-alert v-if="showCreate" @click.native="toCreateModel" type="warning" center style="cursor: pointer"
<el-alert v-if="showCreate && canAdd" @click.native="toCreateModel" type="warning" center style="cursor: pointer"
:closable="false">
<i class="fa fa-plus" style="margin-right: 1rem"></i>新增{{field.label}}
</el-alert>
<template #empty>
<el-alert v-if="showCreate" @click.native="toCreateModel" type="warning" center style="cursor: pointer"
<el-alert v-if="showCreate && canAdd" @click.native="toCreateModel" type="warning" center style="cursor: pointer"
:closable="false">
<i class="fa fa-plus" style="margin-right: 1rem"></i>新增{{field.label}}
</el-alert>
Expand Down Expand Up @@ -80,6 +80,9 @@
load (qs) {
return this.loadData(Object.assign({page_size: DEFAULT_PAGE_SIZE}, this.field.baseQueries, qs)).then(({data}) => {
this.data = data.results
if(data.count === 1 && !this.selectedValue) {
this.$emit('input', this.data[0].id)
}
this.moreThanOnePage = data.next
})
},
Expand All @@ -94,7 +97,12 @@
this.$refs.select.blur()
let url = `${this.url}create/?${this.model.config.title_field || 'name'}=${this.search}`
this.$router.push(url)
}
},
checkPermission(p, m){
m = m || this
let ps = this.$store.state.user.model_permissions[m.appModel]
return ps && ps.includes(p)
},
},
computed: {
_placeholder(){
Expand All @@ -110,6 +118,9 @@
},
url () {
return this.model.getListUrl()
},
canAdd () {
return this.checkPermission('create')
}
},
Expand Down

0 comments on commit 392b9ce

Please sign in to comment.