This repository has been archived on 2023-08-30. You can view files and clone it, but cannot push or open issues/pull-requests.
bytedo
/
wcui
Archived
1
0
Fork 0

优化组件机制;完成树形菜单组件的改造

old
宇天 2017-12-31 22:12:16 +08:00
parent 320096f029
commit 759e4304a0
6 changed files with 212 additions and 235 deletions

View File

@ -23,11 +23,9 @@ const compileJs = (entry, output) => {
if (/touch\.patch/.test(entry)) { if (/touch\.patch/.test(entry)) {
return return
} }
let t1 = Date.now()
if (/anot/.test(entry)) {
setTimeout(() => { setTimeout(() => {
if (/anot/.test(entry)) {
fs.cp(entry, output) fs.cp(entry, output)
}, 100)
} else { } else {
try { try {
const { code } = babel.transformFileSync(entry, jsOpt) const { code } = babel.transformFileSync(entry, jsOpt)
@ -36,38 +34,29 @@ const compileJs = (entry, output) => {
return log(err) return log(err)
} }
} }
log( }, 100)
'编译JS: %s, 耗时 %s ms', log('编译JS: %s', chalk.green(entry))
chalk.green(entry),
chalk.yellow(Date.now() - t1)
)
} }
const compileCss = (entry, output) => { const compileCss = (entry, output) => {
setTimeout(() => {
try { try {
let t1 = Date.now()
const { css } = scss.renderSync({ ...cssOpt, file: entry }) const { css } = scss.renderSync({ ...cssOpt, file: entry })
log(
'编译scss: %s, 耗时 %s ms',
chalk.green(entry),
chalk.yellow(Date.now() - t1)
)
fs.echo(css, output) fs.echo(css, output)
} catch (err) { } catch (err) {
log(err) log(err)
} }
}, 100)
log('编译scss: %s', chalk.green(entry))
} }
const compileHtm = (entry, output) => { const compileHtm = (entry, output) => {
let t1 = Date.now() setTimeout(() => {
let htm = fs.cat(entry).toString('utf8') let htm = fs.cat(entry).toString('utf8')
htm = htm.replace(/[\r\n\t]+/g, ' ').replace(/\s{2,}/g, ' ') htm = htm.replace(/[\r\n\t]+/g, ' ').replace(/\s{2,}/g, ' ')
log(
'压缩HTML: %s, 耗时 %s ms',
chalk.green(entry),
chalk.yellow(Date.now() - t1)
)
fs.echo(htm, output) fs.echo(htm, output)
}, 100)
log('压缩HTML: %s', chalk.green(entry))
} }
/*=======================================================*/ /*=======================================================*/

View File

@ -3082,17 +3082,19 @@
} }
var rnoCollect = /^(:\S+|data-\S+|on[a-z]+|id|style|class)$/ var rnoCollect = /^(:\S+|data-\S+|on[a-z]+|id|style|class)$/
var ronattr = /^on\-[\w-]+$/ var ronattr = '__fn__'
function getOptionsFromTag(elem, vmodels) { function getOptionsFromTag(elem, vmodels) {
var attributes = elem.attributes var attributes = elem.attributes
var ret = {} var ret = {}
for (var i = 0, attr; (attr = attributes[i++]); ) { for (var i = 0, attr; (attr = attributes[i++]); ) {
var name = attr.name var name = attr.name
if (attr.specified && !rnoCollect.test(name)) { if (attr.specified && !rnoCollect.test(name)) {
var camelizeName = camelize(attr.name) if (name.indexOf(ronattr) === 0) {
if (/^on\-[\w-]+$/.test(name)) { name = attr.value.slice(6)
ret[camelizeName] = getBindingCallback(elem, name, vmodels) ret[name] = elem[attr.value]
delete elem[attr.value]
} else { } else {
var camelizeName = camelize(name)
ret[camelizeName] = parseData(attr.value) ret[camelizeName] = parseData(attr.value)
} }
} }
@ -3471,34 +3473,19 @@
return return
} }
var props = getOptionsFromTag(elem, host.vmodels) var props = getOptionsFromTag(elem, host.vmodels)
var vmOpts = getOptionsFromVM(host.vmodels, props.config)
var $id = props.uuid || generateID(widget) var $id = props.uuid || generateID(widget)
var componentDefinition = {}
props = Object.assign({}, vmOpts, props)
vmOpts = void 0
// log(props)
for (var i in props) {
if (props[i] === '__fn__') {
props[i] = elem[i]
delete elem[i]
}
}
delete props.config
delete props.uuid delete props.uuid
delete props.name delete props.name
hooks.props = hooks.props || {} hooks.props = hooks.props || {}
hooks.state = hooks.state || {} hooks.state = hooks.state || {}
hooks.construct.call(elem, props, {}, function next(props, state) {
Object.assign(hooks.props, props) Object.assign(hooks.props, props)
Object.assign(hooks.state, state)
Object.assign(componentDefinition, hooks)
})
componentDefinition.$id = $id hooks.construct.call(elem, hooks.props, hooks.state)
hooks.$id = $id
//==========构建VM========= //==========构建VM=========
var { var {
@ -3507,15 +3494,15 @@
childComponentDidMount, childComponentDidMount,
componentWillUnmount, componentWillUnmount,
render render
} = componentDefinition } = hooks
delete componentDefinition.construct delete hooks.construct
delete componentDefinition.componentWillMount delete hooks.componentWillMount
delete componentDefinition.componentDidMount delete hooks.componentDidMount
delete componentDefinition.childComponentDidMount delete hooks.childComponentDidMount
delete componentDefinition.componentWillUnmount delete hooks.componentWillUnmount
var vmodel = Anot(componentDefinition) var vmodel = Anot(hooks)
vmodel.$refs = {} vmodel.$refs = {}
elem.msResolved = 1 //防止二进扫描此元素 elem.msResolved = 1 //防止二进扫描此元素
@ -3525,18 +3512,30 @@
if (!elem.content.firstElementChild) { if (!elem.content.firstElementChild) {
Anot.clearHTML(elem) Anot.clearHTML(elem)
elem.innerHTML = render() var html = render.call(vmodel) || ''
html = html.replace(/<\w+[^>]*>/g, function(m, s) {
return m.replace(/[\n\t\s]{1,}/g, ' ')
})
elem.innerHTML = html
} }
// 组件所使用的标签是temlate,所以必须要要用子元素替换掉 // 组件所使用的标签是temlate,所以必须要要用子元素替换掉
var child = elem.content.firstElementChild var child = elem.content.firstElementChild
elem.parentNode.replaceChild(child, elem) var nullComponent = DOC.createComment('empty component')
elem.parentNode.replaceChild(child || nullComponent, elem)
// 空组件直接跳出
if (!child) {
return
}
child.msResolved = 1 child.msResolved = 1
var cssText = elem.style.cssText var cssText = elem.style.cssText
var className = elem.className var className = elem.className
elem = host.element = child elem = host.element = child
elem.style.cssText += ';' + cssText elem.style && (elem.style.cssText += ';' + cssText)
if (className) { if (className) {
Anot(elem).addClass(className) Anot(elem).addClass(className)
@ -3554,9 +3553,10 @@
dependencies += ev.childReady dependencies += ev.childReady
if (vmodel !== ev.vm) { if (vmodel !== ev.vm) {
vmodel.$refs[ev.vm.$id] = ev.vm vmodel.$refs[ev.vm.$id] = ev.vm
ev.vm.$up = vmodel
if (ev.childReady === -1) { if (ev.childReady === -1) {
children++ children++
childComponentDidMount.call(vmodel, elem, ev) childComponentDidMount.call(vmodel, ev.vm)
} }
ev.stopPropagation() ev.stopPropagation()
} }
@ -3601,24 +3601,11 @@
}) })
}, 17) }, 17)
} }
})(obj, Anot.components[name], obj.element, obj.name) // jshint ignore:line })(obj, toJson(Anot.components[name]), obj.element, obj.name) // jshint ignore:line
} }
} }
} }
function getOptionsFromVM(vmodels, pre) {
if (pre) {
for (var i = 0, v; (v = vmodels[i++]); ) {
if (v.hasOwnProperty(pre) && typeof v[pre] === 'object') {
var vmOptions = v[pre]
return vmOptions.$model || vmOptions
break
}
}
}
return {}
}
function isWidget(el) { function isWidget(el) {
//如果是组件,则返回组件的名字 //如果是组件,则返回组件的名字
var name = el.nodeName.toLowerCase() var name = el.nodeName.toLowerCase()
@ -3726,7 +3713,7 @@
//chrome v37- 下embed标签动态设置的src无法发起请求 //chrome v37- 下embed标签动态设置的src无法发起请求
if (window.chrome && elem.tagName === 'EMBED') { if (window.chrome && elem.tagName === 'EMBED') {
var parent = elem.parentNode var parent = elem.parentNode
var com = document.createComment(':src') var com = DOC.createComment(':src')
parent.replaceChild(com, elem) parent.replaceChild(com, elem)
parent.replaceChild(elem, com) parent.replaceChild(elem, com)
} }
@ -3757,9 +3744,9 @@
if (typeof obj[i] === 'object') { if (typeof obj[i] === 'object') {
obj[i] = JSON.stringify(obj[i]) obj[i] = JSON.stringify(obj[i])
} else if (typeof obj[i] === 'function') { } else if (typeof obj[i] === 'function') {
var ck = Anot.filters.camelize(k) k = '__fn__' + camelize(k)
elem[ck] = obj[i] elem[k] = obj[i]
obj[i] = '__fn__' obj[i] = k
} }
elem.setAttribute(k, obj[i]) elem.setAttribute(k, obj[i])
} }

View File

@ -1,5 +1,5 @@
'use strict' 'use strict'
import tpl from './main.htm'
import './main.scss' import './main.scss'
Anot.ui.pages = '1.0.0' Anot.ui.pages = '1.0.0'
@ -50,15 +50,46 @@ function update(currPage, vm) {
} }
export default Anot.component('pages', { export default Anot.component('pages', {
construct: function(props, state, next) { construct: function(props, state) {
props.className = props.className =
'skin-' + (props.theme || 1) + ' ' + (props.color || 'plain') 'skin-' + (props.theme || 1) + ' ' + (props.color || 'plain')
delete props.theme delete props.theme
delete props.color delete props.color
next(props, state)
}, },
render: function() { render: function() {
return tpl return `
<div class="do-pages do-fn-noselect" :class="{{props.className}}">
<a class="normal"
:if="currPage > 1 && !props.simpleMode"
:attr="{href: parseUrl(1)}"
:text="props.btns.home"
:click="setPage(1, $event)"></a>
<a class="normal"
:if="currPage > 1"
:attr="{href: parseUrl(currPage - 1)}"
:text="props.btns.prev"
:click="setPage(currPage - 1, $event)"></a>
<a :if-loop="!props.simpleMode || currPage === el"
:repeat="pageList"
:attr="{href: parseUrl(el)}"
:class="{normal: currPage !== el, disabled: '...' === el, curr: currPage === el}"
:text="el"
:click="setPage(el, $event)"></a>
<a class="normal"
:if="currPage < totalPages"
:attr="{href: parseUrl(currPage + 1)}"
:click="setPage(currPage + 1, $event)">{{props.btns.next}}</a>
<a class="normal"
:if="currPage < totalPages && !props.simpleMode"
:attr="{href: parseUrl(totalPages)}"
:click="setPage(totalPages, $event)">{{props.btns.end}}</a>
<div class="input-box" :if="!props.simpleMode">
<span> {{totalPages}} {{totalItems}} 前往</span>
<input type="text" :duplex="inputPage" :keyup="setPage(null, $event)">
<span></span>
</div>
</div>`
}, },
componentWillMount: function() { componentWillMount: function() {
const { currPage, totalPages, props } = this const { currPage, totalPages, props } = this
@ -68,7 +99,9 @@ export default Anot.component('pages', {
) )
}, },
componentDidMount: function() { componentDidMount: function() {
this.props.onSuccess.call(null, this) if (typeof this.props.onCreated === 'function') {
this.props.onCreated.call(null, this)
}
}, },
state: { state: {
currPage: 1, currPage: 1,
@ -95,7 +128,7 @@ export default Anot.component('pages', {
className: '', className: '',
simpleMode: !1, simpleMode: !1,
onPageChange: Anot.noop, onPageChange: Anot.noop,
onSuccess: Anot.noop onCreated: Anot.noop
}, },
methods: { methods: {
// 格式化页码的URL // 格式化页码的URL

View File

@ -1,32 +0,0 @@
<div class="do-pages do-fn-noselect" :class="{{props.className}}">
<a class="normal"
:if="currPage > 1 && !props.simpleMode"
:attr="{href: parseUrl(1)}"
:text="props.btns.home"
:click="setPage(1, $event)"></a>
<a class="normal"
:if="currPage > 1"
:attr="{href: parseUrl(currPage - 1)}"
:text="props.btns.prev"
:click="setPage(currPage - 1, $event)"></a>
<a :if-loop="!props.simpleMode || currPage === el"
:repeat="pageList"
:attr="{href: parseUrl(el)}"
:class="{normal: currPage !== el, disabled: '...' === el, curr: currPage === el}"
:text="el"
:click="setPage(el, $event)"></a>
<a class="normal"
:if="currPage < totalPages"
:attr="{href: parseUrl(currPage + 1)}"
:click="setPage(currPage + 1, $event)">{{props.btns.next}}</a>
<a class="normal"
:if="currPage < totalPages && !props.simpleMode"
:attr="{href: parseUrl(totalPages)}"
:click="setPage(totalPages, $event)">{{props.btns.end}}</a>
<div class="input-box" :if="!props.simpleMode">
<span>共 {{totalPages}} 页 {{totalItems}} 条,前往</span>
<input type="text" :duplex="inputPage" :keyup="setPage(null, $event)">
<span></span>
</div>
</div>

View File

@ -12,141 +12,135 @@ import './main.scss'
//储存版本信息 //储存版本信息
Anot.ui.tree = '1.0.0' Anot.ui.tree = '1.0.0'
var box = '<ul>{li}</ul>', function format(arr, { id, parent, label, children }) {
ul = '<ul :class="{open: {it}.open}">{li}</ul>', let tmp = {}
li = let farr = []
'<li :class="{open: {it}.open, dir: {it}.children}">' +
'<em :click="toggle({it})"></em>' +
'<span :click="$select({it})" :class="{active: {it}.id === currItem}" :text="{it}.name"></span>' +
'{child}</li>'
var keyPath = {}
function repeat(arr, name) {
var html = ''
arr.forEach(function(it, i) {
var from = name + '[' + i + ']',
child = ''
html += li.replace(/\{it\}/g, from)
if (it.children) {
child += repeat(it.children, from + '.children')
child = ul.replace('{li}', child).replace('{it}', from)
}
html = html.replace(/\{child\}/, child)
})
return html
}
function format(arr) {
var tmp = {},
farr = []
arr.sort(function(a, b) { arr.sort(function(a, b) {
return a.pid === b.pid ? a.sort - b.sort : a.pid - b.pid return a.pid === b.pid ? a.sort - b.sort : a.pid - b.pid
}) })
arr.forEach(function(it) { arr.forEach(function(it) {
tmp[it.id] = it it.checked = !!it.checked
keyPath[it.id] = '' it.open = !!it.open
var parentItem = tmp[it.pid] tmp[it[id]] = it
var parentItem = tmp[it[parent]]
if (!parentItem) { if (!parentItem) {
return farr.push(tmp[it.id]) return farr.push(tmp[it[id]])
} }
keyPath[it.id] += keyPath[parentItem.id] + parentItem.id + ',' parentItem[children] = parentItem[children] || []
parentItem.open = !!parentItem.open parentItem[children].push(it)
parentItem.children = parentItem.children || []
parentItem.children.push(it)
}) })
return farr return farr
} }
export default Anot.component('tree', { export default Anot.component('tree', {
render: function() { render: function() {
// return '<div class="do-tree" :class="{{props.className}}" :html="treeHTML"></div>' if (!this.list.size()) {
return null
}
return ` return `
<ul class="do-tree" :class="{{props.className}}" :if="list.size()"> <ul class="do-tree" :class="{{props.className}}" :if="list.size()">
<li :repeat="list" :class="{open: el.open, dir: el.children}"> <li :repeat="list" :class="{open: el.open, dir: el[props.children]}">
<em :click="toggle(el)"></em> <em class="ui-font" :click="toggle(el)"></em>
<span :click="select(el)" :class="{active: el.id === currItem}" :text="el.name"></span> <span
<template name="tree" :attr="{list: el.children}"></template> class="checkbox ui-font"
:class="{checked: el.checked}"
:click="onChecked(el)"></span>
<span
:click="onSelected(el)"
:class="{active: el[props.id] === currItem}"
:text="el[props.label]"></span>
<template
name="tree"
:attr="{
list: el[props.children],
onSelected: props.onSelected,
onChecked: onChecked,
id: 'id',
label: 'label',
parent: 'parent',
children: 'children'
}"></template>
</li> </li>
</ul> </ul>
` `
}, },
construct: function(props, state, next) { construct: function(props, state) {
props.className = 'skin-' + (props.theme || 'def') props.className = 'skin-' + (props.theme || 'def')
state.list = format(props.list || []) props.id = props.id || 'id'
props.label = props.label || 'label'
props.parent = props.parent || 'parent'
props.children = props.children || 'children'
state.list = format(props.list || [], props)
delete props.list delete props.list
delete props.theme delete props.theme
next(props, state)
},
componentWillMount: function() {
// this.$reset(this.props.arr)
}, },
componentDidMount: function() { componentDidMount: function() {
if (typeof this.props.created === 'function') { if (typeof this.props.onCreated === 'function') {
this.props.created.call(null, this) this.props.onCreated.call(null, this)
} }
}, },
state: { state: {
treeHTML: '',
list: [], list: [],
currItem: -1 currItem: -1,
checked: {}
}, },
skip: ['checked'],
props: { props: {
className: '', className: '',
created: Anot.PropsTypes.isFunction(), id: 'id',
componentWillMount: Anot.PropsTypes.isFunction() label: 'label',
parent: 'parent',
children: 'children',
onCreated: Anot.PropsTypes.isFunction(),
onSelected: Anot.PropsTypes.isFunction(),
onChecked: Anot.PropsTypes.isFunction()
}, },
methods: { methods: {
toggle: function(obj) { toggle: function(obj) {
obj.open = !obj.open obj.open = !obj.open
}, },
select: function(obj) { onChecked: function(el, childChecked) {
this.currItem = obj.id let item = null
console.log(obj, this.props.componentWillMount) let arr = []
if (typeof this.props.componentWillMount === 'function') { let { id, onChecked } = this.props
this.props.componentWillMount(obj)
}
},
$update: function(id, obj) {
var path = keyPath[id],
tmpid = null,
tmpobj = null
path += id if (!childChecked) {
path = path.split(',') el.checked = !el.checked
item = el.$model
while ((tmpid = +path.shift())) { this.checked[item[id]] = el.checked ? item : null
if (!tmpobj) {
tmpobj = this.treeArr
} else { } else {
tmpobj = tmpobj.children item = el
Object.assign(this.checked, childChecked)
} }
for (var i = 0, it; (it = tmpobj[i++]); ) { if (!this.$up) {
if (it.id === tmpid) { for (let i in this.checked) {
tmpobj = it if (!this.checked[i]) {
break delete this.checked[i]
} else {
arr.push(this.checked[i])
} }
} }
} else {
arr = this.checked
} }
for (var j in obj) {
tmpobj[j] = obj[j] if (typeof onChecked === 'function') {
onChecked.call(this.$up, item, arr)
} }
}, },
$reset: function(arr) { onSelected: function(el) {
this.treeArr.clear() let { id, onSelected } = this.props
this.treeHTML = '' this.currItem = el[id]
if (typeof onSelected === 'function') {
this.treeArr.pushArray(format(arr)) onSelected(el.$model)
this.currItem = -1 }
console.log(this.treeArr) },
/* var tpl = repeat(this.treeArr.$model, 'treeArr') reset: function(arr) {
Anot.nextTick(() => { this.checked = {}
this.treeHTML = box.replace('{li}', tpl) this.list.clear()
})*/ this.list.pushArray(format(arr || []))
} }
} }
}) })

View File

@ -9,8 +9,9 @@
@import '../../css/var.scss'; @import '../../css/var.scss';
.do-tree {overflow:hidden;overflow-y:auto;position:relative;width:100%;height:100%;line-height:28px;font-size:13px; .do-tree {overflow:hidden;overflow-y:auto;position:relative;width:100%;height:100%;line-height:28px;font-size:13px;color:nth($cgr, 1);
.ui-font {font-family:"ui font" !important;font-style:normal;-webkit-font-smoothing: antialiased;-webkit-text-stroke-width: 0.2px;-moz-osx-font-smoothing: grayscale;}
ul {width:100%;height:auto;} ul {width:100%;height:auto;}
li {overflow:hidden; white-space:nowrap; text-overflow:ellipsis} li {overflow:hidden; white-space:nowrap; text-overflow:ellipsis}
@ -18,10 +19,15 @@
li.open ul {display:block;} li.open ul {display:block;}
li em, li em,
li span {display:block;cursor:pointer;} li span {display:inline-block;cursor:pointer;color:nth($cgr, 2);}
li em {float:left;padding:0 5px;color:#000;font-family:"ui font" !important;font-style:normal;-webkit-font-smoothing: antialiased;-webkit-text-stroke-width: 0.2px;-moz-osx-font-smoothing: grayscale;} li em {float:left;padding:0 5px;}
li span:hover {color:nth($cgr, 2);} li span:hover {color:nth($cgr, 2);}
li span.active {color:#000;font-weight:bold;} li span.active {color:nth($cgr, 3);font-weight:bold;}
li span.checkbox {float:left;position:relative;width:14px;height:14px;margin:7px 5px 7px 0;border:1px solid nth($cgr, 1);border-radius:3px;
&::after {display:block;width:12px;height:12px;line-height:12px;}
&.checked::after {content:"\e60f";}
}
&.skin-def { &.skin-def {
li>em::before {content:"\e612";} li>em::before {content:"\e612";}