phpcms模型管理如何支持联动菜单多选?
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了phpcms模型管理如何支持联动菜单多选?相关的知识,希望对你有一定的参考价值。
模型管理如何支持联动菜多选吗?我创建一个模型,关联联动菜单 但是不能多选,请教下大家如何解决呢,谢谢~~
参考技术A 今天也碰到了这个问题 转下小波群主的回复0793jay(小波)()23:15:17新建字段,复选框--,随意设置选项---/phpcms/templates/defualt/member/content_published.html 修改 0793jay(小波)()23:15:40loop .....0793jay(小波)()23:16:48增加 if $k=='新建复选框字段名'}<tr></th></th><td></td>{else0793jay(小波)()23:17:32接下来,新增函数, 获取指定联动菜单名,0793jay(小波)()23:18:37函数中根据新增函数的参数进行 读取指定文件,将联动菜单数组中 ,Parentid=0 项目 遍历出。0793jay(小波)()23:19:24新增 第二个函数,参数设置 :联动菜单,父级ID0793jay(小波)()23:19:53将第二个函数,放置于 content.php 中。便于ajax 提交方式0793jay(小波)()23:19:59新增JS 函数。0793jay(小波)()23:21:00通过 change ,触发JS函数, 进行 AJAX 异步更新 下级, 0793jay(小波)()23:22:07AJAX 返回成品内容, JQUERY 直接.html 到指定ID中。 通过ctrl 键与 select 属性 ,确定返回的 成品内容是个多选框。0793jay(小波)()23:22:51进行赋值给返回 来的最后级 <select name= “”0793jay(小波)()23:23:08name 等于新增的字段名·· 。提交·保存即可黄页30号内测,估计黄页里就有这个功能了,不急的话就等等吧多选联动标签
<tag-select
allowLoadChildren
:options="organOptions"
:defaultOptions="organDefaultOptions"
v-model="form.organId"
placeholder="选择组织机构"
@change="ispChange"
@spread="spread"
@focus="focus"
ref="tagSelect"
size="default"
:loadChildrenMethod="loadChildren"
labelKey="organName"
valueKey="organId"
childrenKey="sub"
:panelWidth="\'auto\'"
></tag-select>
<template>
<div class="cascader-wrapper">
<crec-popover
placement="bottomLeft"
trigger="click"
:popper-class="popOverClass"
v-model="showPopover"
>
<!-- <div slot="reference"> -->
<!-- multiple
mode="tags" -->
<crec-select
mode="multiple"
v-model="selectedLabels"
:default-value="defaultLabels"
:placeholder="placeholder"
:disabled="disabled"
:size="size"
:collapse-tags="collapseTags"
:showArrow="true"
style="width: 100%;"
popper-class="hide-popper"
:open="false"
@focus="handleFocus"
@deselect="removeTag"
@dropdown-visible-change="visibleChange"
></crec-select>
<!-- </div> -->
<template slot="content">
<div
class="cascader-menu-wrapper"
v-clickoutside="hidePopover"
>
<template v-if="options.length > 0">
<ul
class="crec-cascader-menu cascader-menu"
:style="{\'width\': panelWidth === \'auto\' ? \'auto\' : panelWidth + \'px\'}"
v-for="(cas, index) in casTree"
:key="index"
>
<!-- -->
<!-- \'can-load-children\': !item.isLeaf && !item[childrenKey] && allowLoadChildren && showLoadingIndicator, -->
<!-- \'loading-children\': !item.isLeaf && item.loading && allowLoadChildren && showLoadingIndicator, -->
<!-- \'crec-cascader-menu-item-expand\': item[childrenKey] && item[childrenKey].length > 0, -->
<li
:class="{
\'crec-cascader-menu-item\': true,
\'crec-cascader-menu-item-expand\': true,
\'has-checked-child\': item.indeterminate || item.hasCheckedChild,
\'crec-cascader-menu-item-active\': item.checked,
}"
@click="spreadNext(item[childrenKey], index, item)"
v-for="(item, itemIdx) in cas"
:key="itemIdx"
:title="item[labelKey]"
>
<crec-checkbox
class="cascader-checkbox"
@click.native.stop
:disabled="item.disabled"
v-model="item.checked"
:indeterminate="item.indeterminate"
@change="({target}) => { checkedChange(item, target.checked) }"
></crec-checkbox>
<span>{{ item[labelKey] }}</span>
<span
v-if="item[childrenKey] && item[childrenKey].length > 0"
class="crec-cascader-menu-item-expand-icon"
>
<crec-icon type="right"></crec-icon>
</span>
</li>
</ul>
</template>
<template v-else>
<ul class="crec-cascader-menu cascader-menu">
<li class="crec-cascader-menu-item dropdown__empty">
{{ noDataText }}
</li>
</ul>
</template>
</div>
</template>
</crec-popover>
</div>
</template>
<script>
import Clickoutside from \'./clickoutside\'
import { props, hasArrayChild, deepClone, getId, fireEvent, isPromise } from \'./utils\'
export default {
name: \'TagSelect\',
props,
watch: {
selectedLabels: {
deep: true,
handler () {
if (this.selectedLabels && this.selectedLabels.length === 0) {
this.selectedLabels = this.defaultLabels
}
}
},
options: {
deep: true,
handler () {
this.initOpts()
this.initDatas()
}
},
defaultOptions: {
deep: true,
handler () {
console.log(this.defaultOptions)
this.defaultLabels = this.defaultOptions.map(e => e.organName)
}
},
value: {
deep: true,
handler () {
console.log(this.selectedValues, this.value)
if (this.selectedValues !== this.value) {
this.initOpts()
this.initDatas()
}
}
},
disabled (disabled) {
if (disabled) {
this.hidePopover()
}
},
showPopover (flag) {
if (flag) {
console.log(this.value)
this.$emit(\'focus\')
}
}
},
directives: { Clickoutside },
created () {
this.classRef = `popper-class-${getId()}`
this.popOverClass = `cascader-popper ${this.classRef} ${this.popperClass}`
this.initOpts()
this.initDatas()
},
mounted () {
// 设置弹出层宽度
this.elWidth = this.$el.offsetWidth
},
destroyed () {
this.clonedOpts = null
this.casTree = null
this.selectedItems = null
this.selectedLabels = null
this.selectedvalues = null
},
data () {
return {
elWidth: \'\',
popperWidth: \'\',
popOverClass: \'\',
classRef: \'\',
showPopover: false,
clonedOpts: [],
casTree: [],
selectedItems: [],
selectedLabels: [],
selectedValues: [],
loadChildrenPromise: null,
defaultLabels: []
}
},
methods: {
initOpts () {
console.log(this.defaultOptions)
this.defaultLabels = this.defaultOptions.map(e => e.organName)
console.log(this.defaultLabels)
this.clonedOpts = deepClone(this.options)
this.recursiveOpt(this.clonedOpts, null)
this.casTree = [this.clonedOpts]
console.log(this.casTree)
},
/**
* 初始化数据
* 空值初始化,两个绑定不一致的情况
*/
initDatas () {
this.pickCheckedItem(this.clonedOpts)
},
/**
* 递归option数据
* 标记数据树形层级 parent
* 打上初始状态 checked indeterminate
*/
recursiveOpt (nodeArr, parent) {
const vm = this
nodeArr.forEach(node => {
if (parent) {
node.parent = parent
}
node.indeterminate = false
node.checked = false
if (this.value.some(val => val === this.getLevel(node, vm.valueKey, this.outputLevelValue))) {
node.checked = true
}
this.markChildrenChecked(node)
this.markParentChecked(node)
this.markParentHasCheckChild(node)
if (hasArrayChild(node, vm.childrenKey)) {
vm.recursiveOpt(node[vm.childrenKey], node)
}
})
},
/**
* 根据当前节点 checked
* 更改所有子孙节点 checked
* 依赖 this.selectChildren
*/
markChildrenChecked (node) {
const vm = this
function loop (children, status) {
if (children) {
children.map(child => {
if (!child.disabled) {
child.checked = status
if (child.checked) {
child.indeterminate = false
}
}
if (hasArrayChild(child, vm.childrenKey)) {
loop(child[vm.childrenKey], status)
}
})
}
}
if (node && hasArrayChild(node, vm.childrenKey) && this.selectChildren) {
loop(node[vm.childrenKey], node.checked)
}
},
/**
* 标记父节点 checked、indeterminate 状态
* 依赖 this.selectChildren
*/
markParentChecked (node) {
const vm = this
node.indeterminate = false
function loop (node) {
let checkCount = 0
if (hasArrayChild(node, vm.childrenKey)) {
const childIndeterminate = node[vm.childrenKey].some(child => child.indeterminate)
node[vm.childrenKey].map(child => {
if (child.checked) {
checkCount++
}
})
// 子节点全部被选中
if (checkCount === node[vm.childrenKey].length) {
node.checked = true
node.indeterminate = false
} else {
node.checked = false
if (checkCount > 0 || childIndeterminate) {
node.indeterminate = true
} else {
node.indeterminate = false
}
}
}
if (node.parent) {
loop(node.parent)
}
}
if (node && node.parent && this.selectChildren) {
loop(node.parent)
}
},
/**
* 标记是否有被选子项
* 依赖 this.selectChildren
*/
markParentHasCheckChild (node) {
const vm = this
node.hasCheckedChild = false
function loop (node) {
let checkCount = 0
if (hasArrayChild(node, vm.childrenKey)) {
const childHasCheckedChild = node[vm.childrenKey].some(child => child.hasCheckedChild)
node[vm.childrenKey].map(child => {
if (child.checked) {
checkCount++
}
})
// 子节点有被选中
node.hasCheckedChild = (checkCount > 0) || childHasCheckedChild
}
if (node.parent) {
loop(node.parent)
}
}
if (node && node.parent && !this.selectChildren) {
loop(node.parent)
}
},
// 展示标签所有层级
getLevel (node, key, leveled) {
const levels = []
function loop (data) {
levels.push(data[key])
if (data.parent) {
loop(data.parent)
}
}
if (leveled) {
loop(node)
return levels.reverse().join(this.separator)
} else {
return node[key]
}
},
/**
* 处理已选中
* 重新遍历tree,pick除已选中项目
*/
pickCheckedItem (tree) {
const vm = this
/**
* 移除parent引用
*/
function removeParent (node) {
const obj = {}
Object.keys(node).forEach(key => {
if (key !== \'parent\') {
obj[key] = node[key]
}
})
if (hasArrayChild(obj, vm.childrenKey)) {
obj[vm.childrenKey] = obj[vm.childrenKey].map(child => {
return removeParent(child)
})
}
return obj
}
vm.selectedItems = []
vm.selectedLabels = []
vm.selectedValues = []
function loop (data) {
if (Array.isArray(data)) {
data.map(item => {
if (item.checked) {
const newItem = removeParent(item)
vm.selectedItems.push(newItem)
vm.selectedLabels.push(vm.getLevel(item, vm.labelKey, vm.showAllLevels))
vm.selectedValues.push(vm.getLevel(item, vm.valueKey, vm.outputLevelValue))
}
if (hasArrayChild(item, vm.childrenKey)) {
loop(item[vm.childrenKey])
}
})
}
}
loop(tree)
},
removeTag (label) {
/**
* 遍历 tree
* 根据传入label 寻找 item
*/
const vm = this
function findNodeByLabel (label) {
let result = null
function loop (tree) {
if (tree) {
tree.find(node => {
if (vm.getLevel(node, vm.labelKey, vm.showAllLevels) === label) {
result = node
return true
}
if (hasArrayChild(node, vm.childrenKey)) {
loop(node[vm.childrenKey])
}
})
}
}
if (label) {
loop(vm.clonedOpts)
return result
}
}
const deletedItem = findNodeByLabel(label)
if (deletedItem) {
vm.checkedChange(deletedItem, false)
}
this.$emit(\'remove-tag\', label, deletedItem)
},
clearTag () {
const vm = this
function loop (nodeArr) {
nodeArr.forEach(node => {
node.checked = false
node.indeterminate = false
if (hasArrayChild(node, vm.childrenKey)) {
loop(node[vm.childrenKey])
}
})
}
// 关闭全部状态
loop(this.clonedOpts)
this.selectedLabels = []
this.selectedValues = []
this.selectedItems = []
this.$emit(\'clear\')
this.syncData()
},
// 菜单选中变化
checkedChange (item, checked) {
// 这是是做单选的处理,如果多选去掉此行
this.defaultLabels = []
this.clearTag()
console.log(item, checked)
item.checked = checked
this.$emit(\'clickItem\', item)
this.markChildrenChecked(item)
this.markParentChecked(item)
this.markParentHasCheckChild(item)
this.pickCheckedItem(this.clonedOpts)
this.refresPopover()
this.syncData()
},
// 同步数据到上层
syncData () {
console.log(this.selectedValues)
this.$emit(\'input\', this.selectedValues)
this.$emit(\'change\', this.selectedValues, this.selectedItems)
},
// 展开下一级
async spreadNext (children, index, item) {
const vm = this
if (
vm.allowLoadChildren &&
!children && !item[vm.childrenKey] &&
vm.loadChildrenMethod &&
vm.loadChildrenMethod.constructor === Function &&
!vm.loadChildrenPromise && // promise 不存在
!item.isLeaf
) {
const isPromiseMethod = this.loadChildrenMethod(item)
if (isPromise(isPromiseMethod)) {
vm.loadChildrenPromise = isPromiseMethod
this.$set(item, \'loading\', true)
const result = await vm.loadChildrenPromise.catch(e => {
this.$set(item, \'loading\', false)
})
this.$set(item, \'loading\', false)
vm.loadChildrenPromise = null
if (result && result.constructor === Array) {
this.recursiveOpt(result, item)
this.$set(item, vm.childrenKey, result)
children = result
this.initDatas()
} else {
console.warn(\'The resolved value by loadChildrenMethod must be an Option Array !\')
}
} else {
console.warn(\'You must return a Promise instance in loadChildrenMethod !\')
}
}
if (index || index === 0) {
if (vm.casTree.indexOf(children) === -1) {
if (children && children.length > 0) {
vm.casTree.splice(index + 1, vm.casTree.length - 1, children)
} else {
vm.casTree.splice(index + 1, vm.casTree.length - 1)
}
vm.$emit(\'spread\', item)
}
}
},
visibleChange (visible) {
if (visible) {
this.showPopover = true
}
},
handleFocus (evt) {
if (this.disabled) return
this.$emit(\'focus\', evt)
},
hidePopover (evt) {
this.showPopover = false
this.$emit(\'blur\', evt)
},
refresPopover () {
setTimeout(() => {
fireEvent(window, \'resize\')
}, 66)
}
}
}
</script>
<style lang="less">
.hide-popper {
display: none;
}
.cascader-popper {
padding: 0px;
}
.crec-popover-inner-content {
padding: 0;
}
.cascader-menu-wrapper {
white-space: nowrap;
.crec-cascader-menu {
padding: 8px 0;
height: 240px;
}
}
.cascader-menu-wrapper .cascader-checkbox {
margin-right: 10px;
font-weight: 500;
font-size: 14px;
cursor: pointer;
user-select: none;
}
.crec-cascader-menu-item.has-checked-child {
background-color: #f5f7fa !important;
}
.dropdown__empty {
height: 100%;
padding-top: 50%;
margin: 0;
text-align: center;
color: #999;
font-size: 14px;
}
.can-load-children {
position: relative;
}
.can-load-children::after {
content: \'\';
display: inline-block;
position: absolute;
width: 5px;
height: 5px;
background: #a5d279;
right: 20px;
top: 50%;
border-radius: 50%;
transform: translateY(-50%);
-webkit-transform: translateY(-50%);
}
.can-load-children.loading-children::after {
animation: loading 0.22s infinite alternate;
-moz-animation: loading 0.22s infinite alternate; /* Firefox */
-webkit-animation: loading 0.22s infinite alternate; /* Safari 和 Chrome */
-o-animation: loading 0.22s infinite alternate; /* Opera */
}
@keyframes loading {
from {
background: #a5d279;
}
to {
background: #334d19;
}
}
</style>
export function deepClone (source) {
if (!source && typeof source !== \'object\') {
throw new Error(\'error arguments\', \'shallowClone\')
}
const targetObj = source.constructor === Array ? [] : {}
Object.keys(source).forEach(keys => {
if (source[keys] && typeof source[keys] === \'object\') {
targetObj[keys] = source[keys].constructor === Array ? [] : {}
targetObj[keys] = deepClone(source[keys])
} else {
targetObj[keys] = source[keys]
}
})
return targetObj
}
export function hasArrayChild (obj, childrenKey) {
return obj[childrenKey] && Array.isArray(obj[childrenKey])
}
let id = 0
export function getId () {
return ++id
}
export function fireEvent (element, event) {
if (document.createEventObject) {
// IE浏览器支持fireEvent方法
const evt = document.createEventObject()
return element.fireEvent(\'on\' + event, evt)
} else {
// 其他标准浏览器使用dispatchEvent方法
const evt = document.createEvent(\'HTMLEvents\')
evt.initEvent(event, true, true)
return !element.dispatchEvent(evt)
}
}
export function isPromise (obj) {
return !!obj && (typeof obj === \'object\' || typeof obj === \'function\') && typeof obj.then === \'function\'
}
// 所有选项
export const props = {
value: {
type: [Array, String],
default () {
return \'\'
}
},
placeholder: {
type: String,
default: \'请选择\'
},
disabled: {
type: Boolean,
default: false
},
options: {
type: Array,
default () {
return []
}
},
defaultOptions: {
type: Array,
default () {
return []
}
},
size: {
type: String,
default: \'\'
},
selectChildren: {
type: Boolean,
default: false
},
noDataText: {
type: String,
default: \'无数据\'
},
collapseTags: {
type: Boolean,
default: false
},
separator: {
type: String,
default: \'/\'
},
showAllLevels: {
type: Boolean,
default: false
},
outputLevelValue: {
type: Boolean,
default: false
},
// 显示加载指示器
showLoadingIndicator: {
type: Boolean,
default: true
},
// 允许加载子项
allowLoadChildren: {
type: Boolean,
default: false
},
// 加载方法
loadChildrenMethod: {
type: Function,
default: null,
return: Promise
},
// key
labelKey: {
type: String,
default: \'label\'
},
valueKey: {
type: String,
default: \'value\'
},
childrenKey: {
type: String,
default: \'children\'
},
popperClass: {
type: String,
default: \'\'
},
clearable: {
type: Boolean,
default: false
},
panelWidth: {
type: [Number, String],
default: 160
}
}
import Vue from \'vue\'
const isServer = Vue.prototype.$isServer
/* istanbul ignore next */
export const on = (function () {
if (!isServer && document.addEventListener) {
return function (element, event, handler) {
if (element && event && handler) {
element.addEventListener(event, handler, false)
}
}
} else {
return function (element, event, handler) {
if (element && event && handler) {
element.attachEvent(\'on\' + event, handler)
}
}
}
})()
const nodeList = []
const ctx = \'@@clickoutsideContext\'
let startClick
let seed = 0
!Vue.prototype.$isServer && on(document, \'mousedown\', e => (startClick = e))
!Vue.prototype.$isServer && on(document, \'mouseup\', e => {
nodeList.forEach(node => node[ctx].documentHandler(e, startClick))
})
function createDocumentHandler (el, binding, vnode) {
return function (mouseup = {}, mousedown = {}) {
if (!vnode ||
!vnode.context ||
!mouseup.target ||
!mousedown.target ||
el.contains(mouseup.target) ||
el.contains(mousedown.target) ||
el === mouseup.target ||
(vnode.context.popperElm &&
(vnode.context.popperElm.contains(mouseup.target) ||
vnode.context.popperElm.contains(mousedown.target)))) return
if (binding.expression &&
el[ctx].methodName &&
vnode.context[el[ctx].methodName]) {
vnode.context[el[ctx].methodName]()
} else {
el[ctx].bindingFn && el[ctx].bindingFn()
}
}
}
/**
* v-clickoutside
* @desc 点击元素外面才会触发的事件
* @example
* ```vue
* <div v-element-clickoutside="handleClose">
* ```
*/
export default {
bind (el, binding, vnode) {
nodeList.push(el)
const id = seed++
el[ctx] = {
id,
documentHandler: createDocumentHandler(el, binding, vnode),
methodName: binding.expression,
bindingFn: binding.value
}
},
update (el, binding, vnode) {
el[ctx].documentHandler = createDocumentHandler(el, binding, vnode)
el[ctx].methodName = binding.expression
el[ctx].bindingFn = binding.value
},
unbind (el) {
const len = nodeList.length
for (let i = 0; i < len; i++) {
if (nodeList[i][ctx].id === el[ctx].id) {
nodeList.splice(i, 1)
break
}
}
delete el[ctx]
}
}
以上是关于phpcms模型管理如何支持联动菜单多选?的主要内容,如果未能解决你的问题,请参考以下文章
求phpcms分类信息的设置及联动菜单的使用?每个独立的区域怎么可作为搜索条件用?