Skip to content

Commit

Permalink
batch creator
Browse files Browse the repository at this point in the history
  • Loading branch information
szuprefix committed Aug 2, 2019
1 parent 40aa9ee commit b713e09
Show file tree
Hide file tree
Showing 10 changed files with 480 additions and 44 deletions.
6 changes: 4 additions & 2 deletions src/App.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<template>
<div id="app">
<div id="app" v-cloak>
<router-view v-if="layout === 'main'">
</router-view>
<template v-else>
Expand Down Expand Up @@ -65,5 +65,7 @@
</script>

<style>
[v-cloak] {
display: none;
}
</style>
204 changes: 174 additions & 30 deletions src/components/creator/BatchCreator.vue
Original file line number Diff line number Diff line change
@@ -1,19 +1,27 @@
<template>
<div>
<div class="batch-creator">
<template v-for="f in foreignKeys">
<batch-creator :structure="f" :value="getForeignKeyData(f)"
:appModelName="f.name" @optionLoaded="onForeignKeyLoaded"></batch-creator>
:appModelName="f.name" @optionLoaded="onForeignKeyLoaded"
:ref="f.name.split('.')[1]" @exists="onForeignkeyExists"></batch-creator>

</template>

<h4>{{structure.label}}</h4>
<data-table :value="value" :fields="structure.tableItems" v-if="structure.tableItems && value && value.length>0" :topActions="[]" :options="{elTable:{maxHeight:400}}"></data-table>
<template v-if="records.length>0">
<el-budge :value="records.length"><h4>{{structure.label}} {{records.length}}</h4></el-budge>
<data-table :value="records" :fields="structure.tableItems" :cellWidget="TooltipCell"
:topActions="[{name:'create',label:'批量创建',icon:'bolt',do:saveRecords}]"
:options="{elTable:{maxHeight:400}}" ref="table"></data-table>
</template>
</div>
</template>
<script>
import DataTable from '../table/DataTable.vue'
import {pick, last, uniqWith, isEqual} from 'lodash'
import TooltipCell from '../table/widgets/TooltipCell.vue'
import {pick, last, uniqWith, isEqual, uniqueId, filter, forOwn} from 'lodash'
import ModelView from '../../mixins/model_view'
import Qs from 'qs'
import queueLimit from '../../utils/async_queue'
import {Validator, genFieldRules} from '../../utils/validators'
export default{
name: 'BatchCreator',
mixins: [ModelView],
Expand All @@ -24,7 +32,9 @@
data () {
return {
activeList: [],
loaded: false
records: [],
loaded: false,
TooltipCell
}
},
components: {DataTable},
Expand All @@ -38,27 +48,118 @@
this.loaded = true
this.checkStatus()
})
// console.log(this.plainFieldData)
},
methods: {
rowClassName({row, rowIndex}){
return row.id > 0 ? 'table__record-exists' : ''
},
onForeignKeyLoaded(context){
let st = context.structure
Object.assign(this.structure.foreignKeys.find(fk => fk.name === st.name ), st)
Object.assign(this.structure.foreignKeys.find(fk => fk.name === st.name), st)
this.activeList.push(st)
this.checkStatus()
},
onForeignkeyExists({data, structure}){
let rel = structure.rel
let id = data.id
let d = pick(data, structure.fieldNames)
forOwn(d, (v, k) => {
d[`${rel}.${k}`] = d[k]
delete d[k]
})
let fks = filter(this.value, d)
fks.forEach(v => {
v[rel] = id
this.checkExists(v)
})
// console.log([`onForeignKeysExists ${rel}`, data, d, fks])
},
checkStatus() {
if(this.activeList.length=== this.foreignKeys.length && this.loaded){
this.structure = Object.assign({}, this.structure, {
tableItems:this.tableItems,
count: this.activeList.map(a => a.count).reduce((a,b) => a+b, this.plainFields.length)
if (this.activeList.length === this.foreignKeys.length && this.loaded) {
this.structure = Object.assign({insertMode: 'ignore'}, this.structure, {
tableItems: this.tableItems,
fieldNames: this.fieldNames,
count: this.activeList.map(a => a.count).reduce((a, b) => a + b, this.plainFields.length)
})
// console.log(this.structure)
this.$emit('optionLoaded', {structure: this.structure})
}
// console.log([this.activeList.length, this.foreignKeys.length, this.loaded])
},
checkExists(d){
let uns = this.modelOptions.unique_together
uns = uns && uns[0]
let fks = this.foreignKeys
fks = uns ? fks.filter(fk => uns.includes(fk.rel)) : fks
let b = fks.every(fk => d[fk.rel] > 0)
if (!b) {
d.id = null
// console.log([d.teacher, d.clazz, d.course])
return Promise.resolve()
}
let ls = this.structure.lookups
let qs = pick(d, this.fieldNames)
if (ls) {
forOwn(ls, (v, k) => {
qs[`${k}__${v}`] = qs[k]
delete qs[k]
})
}
if (uns) {
qs = pick(qs, uns)
}
return this.$http.get(`${this.modelListUrl}?${Qs.stringify(qs)}`).then(({data}) => {
if (data.count === 1) {
d.id = data.results[0].id
this.$emit('exists', {data: d, structure: this.structure})
} else {
d.id = null
}
})
},
checkExistsRecur(){
if (this.insertMode === 'append') {
return Promise.resolve()
}
return queueLimit(this.activeList, 1, (fk) => {
let rel = fk.name.split('.')[1]
let com = this.$refs[rel][0]
return com.checkExistsRecur()
}).then(() => {
return queueLimit(this.value, 3, (d) => {
return this.checkExists(d)
})
}).then(() => {
this.getRecords()
return Promise.resolve(this.records)
})
},
saveRecords() {
return queueLimit(this.activeList, 1, (fk) => {
let rel = fk.name.split('.')[1]
let com = this.$refs[rel][0]
return com.saveRecords()
}).then(() => {
return queueLimit(this.records, 1, (d => {
if (this.insertMode === 'append' || !d.id) {
return this.$http.post(this.modelListUrl, d).then(({data}) => {
this.$emit('exists', {data: d, structure: this.structure})
})
} else {
return this.$http.put(this.modelGetDetailUrl(d.id), d).then(({data}) => {
})
}
}))
}).then(() => {
this.getRecords()
})
},
getRecords(){
let func = this.insertMode === 'ignore' ? (d => !d.id) : (d => true)
this.records = uniqWith(this.value.filter(func), isEqual)
},
normalizeStructure(){
let r = Object.assign({}, this.structure)
r.insertMode = r.insertMode || 'ignore'
Expand All @@ -76,8 +177,7 @@
return r
},
getForeignKeyData(f){
// console.log(f)
if(!f.tableItems){
if (!f.tableItems) {
return []
}
let fns = []
Expand All @@ -87,45 +187,89 @@
fns.push(ofn)
pairs.push([ofn, ti.name])
})
let data = this.value.map(a =>pick(a, fns))
data.forEach(d => {
fns.push(f.rel)
pairs.push([f.rel, 'id'])
let data = this.value.map(a => {
let d = pick(a, fns)
let $errors={}
let $es=a.$errors || {}
pairs.forEach(p => {
d[p[1]] = d[p[0]]
let $e=$es[p[0]]
if($e){
$errors[p[1]]=$e
}
delete d[p[0]]
})
// console.log([a.$errors,$errors])
if(Object.keys($errors).length>0){
d.$errors=$errors
}
return d
})
console.log([pairs, fns, f])
return uniqWith(data, isEqual)
data = uniqWith(data, isEqual)
return data
}
},
computed: {
insertMode () {
return this.structure.insertMode
},
plainFields(){
return this.structure.plainFields || []
},
foreignKeys() {
return this.structure.foreignKeys || []
},
fieldNames () {
return this.plainFields.concat(this.foreignKeys.map(fk => fk.rel))
},
tableItems(){
let r = []
let fcs = this.modelFieldConfigs
let pfs = this.plainFields && this.plainFields.map(pf => fcs[pf])
let fks = this.foreignKeys && this.foreignKeys.map(fk => fcs[fk])
if(Object.keys(fcs).length === 0){
return r
}
let vs = this.structure.validator || {}
let pfs = this.plainFields && this.plainFields.map(pf => {
let rules = genFieldRules(fcs[pf])
let vld = vs[pf]
let format = undefined
if (vld) {
rules.push(vld)
format = vld.format
}
return {...fcs[pf], rules, format}
})
this.activeList.forEach(fk => {
fk.tableItems.forEach(ti => {
let label = fk.count>1 ?`${fk.label}.${ti.label}`:`${fk.label}`
let synonyms = fk.count>1 ? []: fk.synonyms
let nti = Object.assign({}, ti, {name:`${fk.rel}.${ti.name}`, label, synonyms})
// console.log(nti)
let label = fk.count > 1 ? `${fk.label}.${ti.label}` : `${fk.label}`
let synonyms = fk.count > 1 ? [] : fk.synonyms
let nti = {...ti, name: `${fk.rel}.${ti.name}`, label, synonyms}
r.push(nti)
})
})
r=r.concat(pfs)
r = r.concat(pfs)
// console.log(r)
return r
},
plainFieldData(){
return this.value.map(a => pick(a, this.plainFields))
}
},
watch: {
value(v){
// let vconfig = this.structure.validator
// console.log([this.structure.name, vconfig])
// if (vconfig) {
// let vld = Validator(vconfig)
// v.forEach(d => {
// vld.validate(d)
// })
// }
}
}
}
</script>
<style scoped></style>
<style>
.batch-creator .table__record-exists {
background-color: lightyellow;
}
</style>
50 changes: 50 additions & 0 deletions src/components/creator/Importer.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<template>
<div v-loading="loading" :element-loading-text="loading">
<extractor v-model="sheet" :binding="binding"></extractor>
<batch-creator :structure="_structure" :value="sheet.data" :appModelName="_structure.name"
@optionLoaded="onOptionLoaded" ref="creator"></batch-creator>
</div>
</template>
<script>
import Extractor from '../sheets/Extractor.vue'
import BatchCreator from './BatchCreator.vue'
export default{
props:{
structure: Object,
},
data () {
return {
_structure:{},
loading: false,
binding: [],
sheet: {data: [], fields: [], name: ''}
}
},
created() {
this._structure= Object.assign({}, this.structure)
},
components: {Extractor, BatchCreator},
methods: {
onOptionLoaded(context){
this.binding = context.structure.tableItems
this._structure = Object.assign(this.structure, context.structure)
},
checkExist(){
this.loading = '正在过滤已存在记录...'
this.$refs.creator.checkExistsRecur().then((newRecords) => {
this.loading = false
})
},
saveRecords(){
this.$refs.creator.saveRecords()
}
},
computed: {},
watch: {
sheet(val){
setTimeout( this.checkExist, 100)
}
}
}
</script>
<style scoped></style>
6 changes: 3 additions & 3 deletions src/components/layout/BatchActions.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<template>
<span>
<el-button plain :icon="a.icon" v-for="a in items" @click="onCommand(a.name)"
<el-button plain :icon="a.icon" v-for="a in items" @click="onCommand(a.name)" :title="a.notice"
:disabled="count===0"
:key="a.name">{{a.label}}
</el-button>
Expand All @@ -25,12 +25,12 @@
}
let action = this.items.find((a) => a.name == name)
if (action && action.do) {
this.$confirm(`确定要对勾选中的${rc}条记录执行"${action.label}"操作吗?`, action.notice, {type: 'warning'}).then(() => {
this.$confirm(action.notice, `确定要对勾选中的${rc}条记录执行"${action.label}"操作吗?`, {type: 'warning'}).then(() => {
action.do(name).then(({data}) => {
this.$message(`操作成功 ${data.rows}`)
this.$emit("done", data)
})
}).catch(this.onServerResponseError)
}).catch(this.onServerResponseError).catch(()=>{})
}
this.$emit("command", name)
}
Expand Down
1 change: 1 addition & 0 deletions src/components/rest/RelatedSelect.vue
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
},
created(){
this.modelInit()
Object.assign(this.tableQueries, this.field.tableBaseQueries)
this.tableUrl = this.modelListSubUrl && `${this.modelListUrl}${this.modelListSubUrl}/` || this.modelListUrl
if (['number', 'string'].includes(typeof this.value)) {
this.tableLoad({'id': this.value}).then(() => {
Expand Down

0 comments on commit b713e09

Please sign in to comment.