add dragging to catalog contents
This commit is contained in:
@ -1,41 +1,308 @@
|
|||||||
<template>
|
<template>
|
||||||
<v-container fluid grid-list-lg ma-0 pa-1>
|
<v-container fluid grid-list-lg ma-0 pa-1>
|
||||||
|
|
||||||
<v-layout row fill-height>
|
<v-layout row fill-height>
|
||||||
|
<v-btn @click="step = 1">Add products</v-btn>
|
||||||
|
<v-btn @click="step = 3">Auto arrange</v-btn>
|
||||||
|
</v-layout>
|
||||||
|
|
||||||
<v-flex xs4>
|
<v-layout row fill-height>
|
||||||
<SectionList/>
|
<v-flex xs3>
|
||||||
|
<v-card>
|
||||||
|
<v-toolbar dense>
|
||||||
|
<v-toolbar-title>Sections</v-toolbar-title>
|
||||||
|
</v-toolbar>
|
||||||
|
|
||||||
|
<div id="sections" class="list-group">
|
||||||
|
<v-list-tile
|
||||||
|
v-for="item in sections"
|
||||||
|
:key="item.id"
|
||||||
|
class="v-list__tile list-group-item list-item"
|
||||||
|
v-bind:class="{'selected': selected === item}"
|
||||||
|
>
|
||||||
|
<v-list-tile-content class="section-parent">
|
||||||
|
<v-list-tile-title v-html="item.name"></v-list-tile-title>
|
||||||
|
<v-list-tile-sub-title class="section-drop" :section-id="item.id"></v-list-tile-sub-title>
|
||||||
|
</v-list-tile-content>
|
||||||
|
|
||||||
|
</v-list-tile>
|
||||||
|
</div>
|
||||||
|
</v-card>
|
||||||
</v-flex>
|
</v-flex>
|
||||||
|
|
||||||
<v-flex xs4>
|
<v-flex xs3>
|
||||||
<ProductFamilyList/>
|
<v-card>
|
||||||
|
<v-toolbar dense>
|
||||||
|
<v-toolbar-title>Models</v-toolbar-title>
|
||||||
|
</v-toolbar>
|
||||||
|
|
||||||
|
<div id="models" class="list-group">
|
||||||
|
<div v-for="item in selectedModels" :key="item.model" class="v-list__tile list-item">
|
||||||
|
<v-list-tile-title v-html="item.name"></v-list-tile-title>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</v-card>
|
||||||
</v-flex>
|
</v-flex>
|
||||||
|
|
||||||
<v-flex xs4>
|
<v-flex xs3>
|
||||||
<MaterialList/>
|
<v-card>
|
||||||
|
<v-toolbar dense>
|
||||||
|
<v-toolbar-title>Materials</v-toolbar-title>
|
||||||
|
</v-toolbar>
|
||||||
|
|
||||||
|
<div id="materials" class="list-group">
|
||||||
|
<div
|
||||||
|
v-for="item in selectedMaterials"
|
||||||
|
:key="item.id"
|
||||||
|
class="v-list__tile list-item"
|
||||||
|
>
|
||||||
|
<v-list-tile-title v-html="item.id"></v-list-tile-title>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</v-card>
|
||||||
|
|
||||||
|
</v-flex>
|
||||||
|
|
||||||
|
<v-flex xs3>
|
||||||
|
<RawDisplayer :value="selectedMaterials" title="mats" />
|
||||||
</v-flex>
|
</v-flex>
|
||||||
|
|
||||||
</v-layout>
|
</v-layout>
|
||||||
|
|
||||||
</v-container>
|
</v-container>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import SectionList from './SectionList'
|
import Sortable from 'sortablejs'
|
||||||
import ProductFamilyList from './ProductFamilyList'
|
import { mapState, mapGetters, mapActions } from 'vuex'
|
||||||
// import ModelList from './ModelList'
|
import arrayMove from 'array-move'
|
||||||
import MaterialList from './MaterialList'
|
import RawDisplayer from './RawDisplayer'
|
||||||
|
import { paginateModels } from '@/pagination'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
SectionList,
|
RawDisplayer,
|
||||||
ProductFamilyList,
|
|
||||||
// ModelList,
|
|
||||||
MaterialList,
|
|
||||||
},
|
},
|
||||||
|
|
||||||
data: () => ({
|
data: () => ({
|
||||||
})
|
// NOTE do we need?
|
||||||
|
'placeholderText': 'placeholder',
|
||||||
|
// NOTE do we need?
|
||||||
|
'selected': null,
|
||||||
|
}),
|
||||||
|
computed: {
|
||||||
|
...mapState([
|
||||||
|
'catalog',
|
||||||
|
'materials',
|
||||||
|
]),
|
||||||
|
...mapGetters([
|
||||||
|
'section',
|
||||||
|
'sections',
|
||||||
|
'selectedSection',
|
||||||
|
'selectedModel',
|
||||||
|
'sectionModels',
|
||||||
|
'modelMaterials',
|
||||||
|
'material',
|
||||||
|
]),
|
||||||
|
selectedModels() {
|
||||||
|
let section = this.selectedSection
|
||||||
|
return this.sectionModels(section)
|
||||||
|
},
|
||||||
|
selectedMaterials() {
|
||||||
|
let model = this.selectedModel
|
||||||
|
return this.modelMaterials(model)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
/*
|
||||||
|
watch: {
|
||||||
|
catalog: function(cat) {
|
||||||
|
console.log('catalog changed')
|
||||||
|
addSectionDrops(this)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
*/
|
||||||
|
methods: {
|
||||||
|
reorderModel: function(fromIndex, toIndex) {
|
||||||
|
let models = this.selectedModels
|
||||||
|
arrayMove.mutate(models, fromIndex, toIndex)
|
||||||
|
let pages = paginateModels(models)
|
||||||
|
this.setSectionPages({ section: this.selectedSection, pages: pages })
|
||||||
|
},
|
||||||
|
|
||||||
|
reorderMaterial: function(fromIndex, toIndex) {
|
||||||
|
let mats = this.selectedMaterials
|
||||||
|
arrayMove.mutate(mats, fromIndex, toIndex)
|
||||||
|
this.setModelMaterials({ model: this.selectedModel, materials: mats })
|
||||||
|
},
|
||||||
|
/*
|
||||||
|
groupByModel: function(materials) {
|
||||||
|
let models = []
|
||||||
|
|
||||||
|
for (let mat of materials) {
|
||||||
|
let model = models.length > 0 ? models[models.length - 1] : null
|
||||||
|
|
||||||
|
if (model === null || model.id !== mat.model) {
|
||||||
|
// new model
|
||||||
|
model = { id: mat.model,
|
||||||
|
name: mat.name,
|
||||||
|
materials: [mat.id] }
|
||||||
|
models.push(model)
|
||||||
|
} else {
|
||||||
|
// add to existing
|
||||||
|
model.materials.push(mat.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return models
|
||||||
|
},
|
||||||
|
*/
|
||||||
|
...mapActions([
|
||||||
|
'reorderSection',
|
||||||
|
'setSectionPages',
|
||||||
|
'setModelMaterials',
|
||||||
|
'moveModelToSection',
|
||||||
|
'moveMaterialToSection',
|
||||||
|
]),
|
||||||
|
},
|
||||||
|
mounted: function() {
|
||||||
|
let me = this
|
||||||
|
|
||||||
|
// reordering of sections
|
||||||
|
Sortable.create(document.getElementById('sections'), {
|
||||||
|
group: {
|
||||||
|
name: 'sections',
|
||||||
|
},
|
||||||
|
animation: 150,
|
||||||
|
onEnd: function(evt) {
|
||||||
|
me.reorderSection({ from: evt.oldIndex, to: evt.newIndex })
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
// reordering of models
|
||||||
|
Sortable.create(document.getElementById('models'), {
|
||||||
|
group: {
|
||||||
|
name: 'models',
|
||||||
|
},
|
||||||
|
animation: 150,
|
||||||
|
onStart: function(evt) {
|
||||||
|
addSectionDrops(me)
|
||||||
|
},
|
||||||
|
onEnd: function(evt) {
|
||||||
|
if (evt.from === evt.to) {
|
||||||
|
me.reorderModel(evt.oldIndex, evt.newIndex)
|
||||||
|
} else {
|
||||||
|
// dropped on a section.
|
||||||
|
let models = me.selectedModels
|
||||||
|
let model = models[evt.oldIndex]
|
||||||
|
|
||||||
|
let sectionID = Number(evt.to.getAttribute('section-id'))
|
||||||
|
let newSection = me.section(sectionID)
|
||||||
|
|
||||||
|
if (me.selectedSection.id !== newSection.id) {
|
||||||
|
me.moveModelToSection({ model: model,
|
||||||
|
oldSection: me.selectedSection,
|
||||||
|
newSection: newSection })
|
||||||
|
} else {
|
||||||
|
console.log('not moving to same section')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
// reordering of materials
|
||||||
|
Sortable.create(document.getElementById('materials'), {
|
||||||
|
group: {
|
||||||
|
name: 'materials',
|
||||||
|
},
|
||||||
|
animation: 150,
|
||||||
|
onStart: function(evt) {
|
||||||
|
addSectionDrops(me)
|
||||||
|
},
|
||||||
|
onEnd: function(evt) {
|
||||||
|
if (evt.from === evt.to) {
|
||||||
|
me.reorderMaterial(evt.oldIndex, evt.newIndex)
|
||||||
|
} else {
|
||||||
|
// dropped on a section.
|
||||||
|
let mats = me.selectedMaterials
|
||||||
|
let mat = mats[evt.oldIndex]
|
||||||
|
|
||||||
|
let sectionID = Number(evt.to.getAttribute('section-id'))
|
||||||
|
let newSection = me.section(sectionID)
|
||||||
|
|
||||||
|
if (me.selectedSection.id !== newSection.id) {
|
||||||
|
me.moveMaterialToSection({ material: mat,
|
||||||
|
oldModel: me.selectedModel,
|
||||||
|
newSection: newSection })
|
||||||
|
} else {
|
||||||
|
console.log('not moving to same section')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
function addSectionDrops(myvue) {
|
||||||
|
// make sure our section drop targets are set up
|
||||||
|
// after catalog load or section add
|
||||||
|
|
||||||
|
// drop model or material into a section
|
||||||
|
var drops = document.getElementsByClassName('section-drop')
|
||||||
|
for (var i = 0; i < drops.length; ++i) {
|
||||||
|
if (!drops[i].getAttribute('drop-ready')) {
|
||||||
|
drops[i].setAttribute('drop-ready', true)
|
||||||
|
|
||||||
|
Sortable.create(drops[i], {
|
||||||
|
group: {
|
||||||
|
name: 'section-drops',
|
||||||
|
pull: false,
|
||||||
|
put: function(to, from) {
|
||||||
|
// don't allow drop on section where it already lives
|
||||||
|
let oldSectionID = myvue.selectedSection.id
|
||||||
|
let newSectionID = to.el.getAttribute('section-id')
|
||||||
|
if (Number(oldSectionID) === Number(newSectionID)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// allow drop from anything but another section
|
||||||
|
return from.options.group.name !== 'sections'
|
||||||
|
},
|
||||||
|
},
|
||||||
|
animation: 150,
|
||||||
|
// (actual drop is handled in the originating list)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
.selected {
|
||||||
|
background-color: purple;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sortable-chosen {
|
||||||
|
color: black;
|
||||||
|
background-color: #fddd04;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sortable-ghost {
|
||||||
|
background-color: #bababa;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-parent {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-drop {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-item {
|
||||||
|
border-bottom: 1px solid #ddd;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
import Vue from 'vue'
|
import Vue from 'vue'
|
||||||
import Vuex from 'vuex'
|
import Vuex from 'vuex'
|
||||||
|
import arrayMove from 'array-move'
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
|
import { paginateModels } from '@/pagination'
|
||||||
|
|
||||||
Vue.use(Vuex)
|
Vue.use(Vuex)
|
||||||
|
|
||||||
@ -82,6 +84,77 @@ export const store = new Vuex.Store({
|
|||||||
savingCatalog: false,
|
savingCatalog: false,
|
||||||
},
|
},
|
||||||
getters: {
|
getters: {
|
||||||
|
|
||||||
|
// NOTE don't use if you're not doing anything interesting, just use state.
|
||||||
|
|
||||||
|
material: (state) => (id) => {
|
||||||
|
return state.materials[id]
|
||||||
|
},
|
||||||
|
|
||||||
|
sections: state => {
|
||||||
|
if (state.catalog != null) {
|
||||||
|
return state.catalog.sections
|
||||||
|
} else {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
section: (state) => (id) => {
|
||||||
|
if (state.catalog != null) {
|
||||||
|
return state.catalog.sections.find(s => s.id === id)
|
||||||
|
} else {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
selectedSection: state => {
|
||||||
|
if (state.catalog != null) {
|
||||||
|
return state.catalog.sections[0]
|
||||||
|
} else {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
selectedModel: state => {
|
||||||
|
if (state.catalog != null) {
|
||||||
|
return state.catalog.sections[0].pages[0][0]
|
||||||
|
} else {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
sectionModels: (state, getters) => (section) => {
|
||||||
|
// remove pages, return models (blocks) in order
|
||||||
|
let models = []
|
||||||
|
if (section && section.pages) {
|
||||||
|
for (let page of section.pages) {
|
||||||
|
for (let model of page) {
|
||||||
|
if (model.ids && model.ids.length > 0) {
|
||||||
|
// add extra info from a material
|
||||||
|
let material = getters.material(model.ids[0])
|
||||||
|
if (material) {
|
||||||
|
model['name'] = material.name
|
||||||
|
model['model'] = material.model
|
||||||
|
model['family'] = material.family
|
||||||
|
} else {
|
||||||
|
console.log('no material found for id', model.ids[0])
|
||||||
|
}
|
||||||
|
models.push(model)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return models
|
||||||
|
},
|
||||||
|
|
||||||
|
modelMaterials: (state, getters) => (model) => {
|
||||||
|
if (model && model.ids && model.ids.length > 0) {
|
||||||
|
return model.ids.map(id => getters.material(id))
|
||||||
|
}
|
||||||
|
return []
|
||||||
|
},
|
||||||
|
|
||||||
|
/// OLD STUFF
|
||||||
//
|
//
|
||||||
// catalog info
|
// catalog info
|
||||||
//
|
//
|
||||||
@ -95,6 +168,14 @@ export const store = new Vuex.Store({
|
|||||||
// return state.catalog.sections.find(s => s.id === id)
|
// return state.catalog.sections.find(s => s.id === id)
|
||||||
// },
|
// },
|
||||||
|
|
||||||
|
catalogSections: state => {
|
||||||
|
if (state.catalog != null) {
|
||||||
|
return state.catalog.sections
|
||||||
|
} else {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
catalogProperty: (state) => (key) => {
|
catalogProperty: (state) => (key) => {
|
||||||
if (state.catalog != null) {
|
if (state.catalog != null) {
|
||||||
return state.catalog[key]
|
return state.catalog[key]
|
||||||
@ -137,6 +218,23 @@ export const store = new Vuex.Store({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
mutations: {
|
mutations: {
|
||||||
|
reorderSection(state, { from, to }) {
|
||||||
|
if (from === to) return
|
||||||
|
state.catalog.sections = arrayMove(state.catalog.sections, from, to)
|
||||||
|
},
|
||||||
|
|
||||||
|
setSectionPages(state, { section, pages }) {
|
||||||
|
if (section) {
|
||||||
|
section.pages = pages
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
setModelMaterials(state, { model, materials }) {
|
||||||
|
if (model) {
|
||||||
|
model.ids = materials.map(m => (typeof m === 'object') ? m.id : m)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
// SET_CAT_SECTIONS: (state, payload) => {
|
// SET_CAT_SECTIONS: (state, payload) => {
|
||||||
// state.catalog.sections = payload
|
// state.catalog.sections = payload
|
||||||
// },
|
// },
|
||||||
@ -185,6 +283,71 @@ export const store = new Vuex.Store({
|
|||||||
|
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
|
reorderSection({ commit }, payload) {
|
||||||
|
commit('reorderSection', payload)
|
||||||
|
},
|
||||||
|
|
||||||
|
setSectionPages({ commit }, payload) {
|
||||||
|
commit('setSectionPages', payload)
|
||||||
|
},
|
||||||
|
|
||||||
|
setModelMaterials({ commit }, payload) {
|
||||||
|
commit('setModelMaterials', payload)
|
||||||
|
},
|
||||||
|
|
||||||
|
moveModelToSection({ commit, getters }, { model, oldSection, newSection }) {
|
||||||
|
// remove model from old section
|
||||||
|
let oldModels = getters.sectionModels(oldSection)
|
||||||
|
oldModels = oldModels.filter(m => m.model !== model.model)
|
||||||
|
let oldPages = paginateModels(oldModels)
|
||||||
|
commit('setSectionPages', { section: oldSection, pages: oldPages })
|
||||||
|
|
||||||
|
// add model to new section
|
||||||
|
let newModels = getters.sectionModels(newSection)
|
||||||
|
let existingModel = newModels.find(m => m.model === model.model)
|
||||||
|
if (existingModel) {
|
||||||
|
// merge
|
||||||
|
var ids = existingModel.ids
|
||||||
|
ids.push(...model.ids)
|
||||||
|
// uniquify
|
||||||
|
ids = ids.filter((v, i, a) => a.indexOf(v) === i)
|
||||||
|
existingModel.ids = ids
|
||||||
|
} else {
|
||||||
|
// add at end
|
||||||
|
newModels.push(model)
|
||||||
|
}
|
||||||
|
let newPages = paginateModels(newModels)
|
||||||
|
commit('setSectionPages', { section: newSection, pages: newPages })
|
||||||
|
},
|
||||||
|
|
||||||
|
moveMaterialToSection({ commit, getters }, { material, oldModel, newSection }) {
|
||||||
|
// ensure material, not just id
|
||||||
|
if (typeof material === 'string' || typeof material === 'number') {
|
||||||
|
material = getters.material(material)
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove material from old model
|
||||||
|
let mats = oldModel.ids.filter(id => id !== material.id)
|
||||||
|
commit('setModelMaterials', { model: oldModel, materials: mats })
|
||||||
|
|
||||||
|
// add material to new section
|
||||||
|
let newModels = getters.sectionModels(newSection)
|
||||||
|
let existingModel = newModels.find(m => m.model === material.model)
|
||||||
|
if (existingModel) {
|
||||||
|
// merge
|
||||||
|
var ids = existingModel.ids
|
||||||
|
ids.push(material.id)
|
||||||
|
// uniquify
|
||||||
|
ids = ids.filter((v, i, a) => a.indexOf(v) === i)
|
||||||
|
existingModel.ids = ids
|
||||||
|
} else {
|
||||||
|
// add at end
|
||||||
|
newModels.push({ ids: [material.id] })
|
||||||
|
}
|
||||||
|
let newPages = paginateModels(newModels)
|
||||||
|
commit('setSectionPages', { section: newSection, pages: newPages })
|
||||||
|
},
|
||||||
|
|
||||||
// SET_CAT_SECTIONS: (context, payload) => {
|
// SET_CAT_SECTIONS: (context, payload) => {
|
||||||
// context.commit('SET_CAT_SECTIONS', payload)
|
// context.commit('SET_CAT_SECTIONS', payload)
|
||||||
// },
|
// },
|
||||||
|
|||||||
45
cateditor/src/pagination.js
Normal file
45
cateditor/src/pagination.js
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
|
||||||
|
export function paginateModels(models) {
|
||||||
|
for (let model of models) {
|
||||||
|
// ensure sizes
|
||||||
|
model.size = modelSize(model)
|
||||||
|
}
|
||||||
|
|
||||||
|
let pages = []
|
||||||
|
let currentSize = 0
|
||||||
|
let currentPage = []
|
||||||
|
for (let model of models) {
|
||||||
|
currentSize += model.size
|
||||||
|
if (currentSize > 3) {
|
||||||
|
pages.push(currentPage)
|
||||||
|
currentPage = [model]
|
||||||
|
currentSize = model.size
|
||||||
|
} else {
|
||||||
|
currentPage.push(model)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (currentPage.length > 0) {
|
||||||
|
pages.push(currentPage)
|
||||||
|
}
|
||||||
|
|
||||||
|
return pages
|
||||||
|
}
|
||||||
|
|
||||||
|
export function modelSize(model) {
|
||||||
|
if (!model || !model.ids) return 0
|
||||||
|
if (model.ids.length <= 4) return 1
|
||||||
|
if (model.ids.length <= 8) return 2
|
||||||
|
return 3
|
||||||
|
}
|
||||||
|
|
||||||
|
export function sectionModels(section) {
|
||||||
|
// remove pages, return models (blocks) in order
|
||||||
|
let models = []
|
||||||
|
if (section && section.pages) {
|
||||||
|
for (let page of section.pages) {
|
||||||
|
models.push(...page)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return models
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user