在线观看不卡亚洲电影_亚洲妓女99综合网_91青青青亚洲娱乐在线观看_日韩无码高清综合久久

鍍金池/ 問(wèn)答/HTML/ React引入組件出現(xiàn)Module not found: Can't resol

React引入組件出現(xiàn)Module not found: Can't resolve 'components'

有如下組件:

clipboard.png

index.js:(src/components/FileTree)

import React, { Component } from 'react'
import PropTypes from 'prop-types'
import './index.less'
import { Icon, Input, Popconfirm } from 'antd'
import classnames from 'classnames'
import { Iconfont } from 'components'

export default class FileTree extends Component {
  constructor (props) {
    super(props)
    this.state = {
      count: 0,
      selectedKey: props.selectedKey || null,
      expandedKeysMap: this.arrayToMap(props.expandedKeys),
      hoverNode: null,
      confirmVisible: false
    }

    this.tempExpandedMap = {}
    this.placeHolder = document.createElement('li')
    this.placeHolder.className = 'tree-placeholder'
  }

  arrayToMap (list) {
    const object = {}
    list && list.map(l => {
      object[l] = true
    })
    return object
  }

  componentWillReceiveProps (nextProps) {
    if (nextProps.selectedKey !== this.props.selectedKey) {
      this.setState({
        selectedKey: nextProps.selectedKey
      })
    }
    if (nextProps.expandedKeys && nextProps.expandedKeys.length !== this.props.expandedKeys.length) {
      this.setState({
        expandedKeysMap: this.arrayToMap(nextProps.expandedKeys)
      })
    }
    if (nextProps.search !== this.props.search) {
      // 保留搜索前的展開(kāi)狀態(tài)
      if (this.props.search === '') {
        this.preMap = { ...this.state.expandedKeysMap }
      }
      // 恢復(fù)搜索前的展開(kāi)狀態(tài)
      if (nextProps.search === '') {
        this.setState({
          expandedKeysMap: { ...this.preMap }
        })
        return
      }
      this.tempExpandedMap = {}
      // 查找需要展開(kāi)的節(jié)點(diǎn)
      this.filterTree(nextProps.data, nextProps.search, false)
      this.setState({
        expandedKeysMap: { ...this.tempExpandedMap }
      })
    }
  }

  onClick (el) {
    const { onClick, onSelectedKeyChange } = this.props
    const { expandedKeysMap } = this.state
    expandedKeysMap[el.id] = !expandedKeysMap[el.id]
    this.setState({
      selectedKey: el.id,
      expandedKeysMap
    })
    onSelectedKeyChange && onSelectedKeyChange(el.id, el)
    onClick && onClick(el)
  }

  onDoubleClick (e, el) {
    this.props.onDoubleClick && this.props.onDoubleClick(e, el, this.props.type)
  }

  onContextMenu (el, e) {
    e.stopPropagation()
    this.props.onContextMenu && this.props.onContextMenu(e, el)
  }

  onDragStart (it, e) {
    e.stopPropagation()
    this.dragged = e.currentTarget.parentNode
    e.dataTransfer.effectAllowed = 'move'
    this.props.onDrag && this.props.onDrag(it)
  }

  onDragEnd (it, e) {
    const { data, onMove } = this.props
    const from = this.dragged.dataset.id
    const to = this.over.dataset.id
    const fromPath = from.split('_')
    const toPath = to.split('_')
    const isAfter = this.nodePlacement === 'after'

    this.dragged.style.display = 'block'
    this.over.parentNode.removeChild(this.placeHolder)

    // 尋找需要更新的兩個(gè)文件夾并進(jìn)行移動(dòng)
    if (fromPath.length === toPath.length) {
      let fromSearch = data
      let toSearch = data
      fromPath.map((f, i) => {
        const fromStep = parseInt(f)
        let toStep = parseInt(toPath[i])
        if (i === fromPath.length - 1) {
          if (isAfter) toStep++
          if (fromSearch === toSearch && fromStep < toStep) toStep--
          // 移動(dòng)源數(shù)據(jù)
          onMove(data, fromSearch, fromStep, toSearch, toStep)
          // toSearch.splice(toStep, 0, fromSearch.splice(fromStep, 1)[0])
        } else {
          fromSearch = fromSearch[fromStep].subTree
          toSearch = toSearch[toStep].subTree
        }
      })
      // 關(guān)閉拖拽
      this.triggerDraggable(it, false)()
    }
  }

  onDrop (it, e) {
    e.stopPropagation()
    e.preventDefault()
    this.props.onDrop && this.props.onDrop(it)
  }

  onDragEnter (it, e) {
    const { expandedKeysMap } = this.state
    const targetLi = e.currentTarget.parentNode
    // 同級(jí)拖動(dòng),折疊文件夾,子級(jí)拖動(dòng)到父級(jí),展開(kāi)文件夾
    expandedKeysMap[it.id] = targetLi.parentNode !== this.dragged.parentNode
    this.setState({
      expandedKeysMap
    })
  }

  onDragOver (e) {
    e.preventDefault()

    const targetLi = e.currentTarget.parentNode
    this.dragged.style.display = 'none'

    // 子文件夾不能添加到和父文件夾同一級(jí),拖動(dòng)到父文件夾上時(shí)默認(rèn)不處理,現(xiàn)在只支持兩級(jí)
    if (targetLi.parentNode === this.dragged.parentNode.parentNode.parentNode) {
      e.dataTransfer.dropEffect = 'none'
      return
    }
    // 父文件夾不能添加到和子文件夾同一級(jí),拖動(dòng)到子文件夾上時(shí)默認(rèn)不處理,現(xiàn)在只支持兩級(jí)
    if (targetLi.parentNode.parentNode.parentNode === this.dragged.parentNode) {
      e.dataTransfer.dropEffect = 'none'
      return
    }

    this.over = targetLi
    const relY = e.clientY - this.over.offsetTop
    const height = this.over.offsetHeight / 2
    const parent = targetLi.parentNode
    if (relY > height) {
      this.nodePlacement = 'after'
      parent.insertBefore(this.placeHolder, targetLi.nextElementSibling)
    } else if (relY < height) {
      this.nodePlacement = 'before'
      parent.insertBefore(this.placeHolder, targetLi)
    }
  }

  onDragLeave () {
  }

  // 拖拽開(kāi)關(guān)
  triggerDraggable = (el, can) => () => {
    el.draggable = can
    const { expandedKeysMap } = this.state
    expandedKeysMap[el.id] = false
    this.setState({
      expandedKeysMap
    })
  }

  // 添加Hover樣式
  addHover = (el) => () => {
    if (this.state.confirmVisible) {
      return
    }
    this.setState({
      hoverNode: el.id
    })
  }

  // 移除當(dāng)前Hover樣式
  removeHover = () => {
    if (this.state.confirmVisible) {
      return
    }
    this.setState({
      hoverNode: null
    })
  }

  onConfirmPopover = (visible) => {
    this.setState({
      confirmVisible: visible
    })
  }

  renderAContent (data, el) {
    const { expandedKeysMap } = this.state
    const { search, readOnly, name } = this.props
    const { content, isEdit, id, subTree, count } = el
    // 高亮關(guān)鍵字
    let showContent = content
    if (search.length) {
      const index = content.toLowerCase().search(search.toLowerCase())
      if (index > -1) {
        const beforeStr = content.substr(0, index)
        const afterStr = content.substr(index + search.length)
        const matchStr = content.substr(index, search.length)
        showContent = (
          <span>{beforeStr}<span style={{ color: '#f50', backgroundColor: '#ffff00' }}>{matchStr}</span>{afterStr}</span>
        )
      }
    }
    return (
      <div className="tree_detail"
        onMouseEnter={this.addHover(el)}>
        <div className="tree_main">
          {!isEdit ? (
            <div className="tree_hd">
              <Icon type={ subTree != null ? (!expandedKeysMap[id] ? 'plus-square-o' : 'minus-square-o') : ''}/>
              <span className="tree_title" title={content}>
                {showContent}({count || 0})
              </span>
            </div>
          ) : (
            <div className="tree_hd">
              <Input
                autoFocus
                defaultValue={content}
                onPressEnter={this.hideEdit(el)}
                onBlur={this.hideEdit(el)}
                className="tree_input"
              />
            </div>
          )}
        </div>
        {isEdit || readOnly ? null : (
          <div className="tree_addon">
            <span>
              <Iconfont type="edit" onClick={this.showEdit(el)}/>
              <Popconfirm
                title={
                  <div style={{ width: 300 }}>
                    <p className="tree-confirm-title">刪除{name}</p>
                    <p className="tree-confirm-content">{name === '標(biāo)簽' && count ?
                      `該${name}將從系統(tǒng)中刪除,您無(wú)法再通過(guò)“${content}”找到對(duì)應(yīng)的${count}項(xiàng)任務(wù),確定刪除嗎?` :
                      `確定刪除“${content}”嗎?`}</p>
                  </div>
                }
                okType="danger"
                okText="刪除"
                cancelText="取消"
                overlayClassName="tree-popover-confirm"
                onConfirm={this.deleteItem(data, el)}
                onVisibleChange={this.onConfirmPopover}
                onClick={e => e.stopPropagation()}
              >
                <Icon type="close-circle-o" />
              </Popconfirm>
              {search.length || data.length < 2 ? null : <Iconfont type="sider-collaspe"
                onMouseDown={this.triggerDraggable(el, true)}
                onMouseUp={this.triggerDraggable(el, false)}/>}
            </span>
          </div>
        )}
      </div>
    )
  }

  renderDropDir (data, liClass, el, subTree, positionId) {
    const { readOnly } = this.props
    return (
      <li className={classnames('tree_item', liClass, { 'actived': this.state.selectedKey === el.id }, { 'hover': this.state.hoverNode === el.id })}
        key={el.id}
        data-id={positionId}
        onContextMenu={this.onContextMenu.bind(this, el)}>
        <div className="tree_wp"
          onDrop={readOnly ? null : this.onDrop.bind(this, el)}
          onDragOver={readOnly ? null : this.onDragOver.bind(this)}
          onDragEnter={readOnly ? null : this.onDragEnter.bind(this, el)}
          onDragLeave={readOnly ? null : this.onDragLeave.bind(this)}
          onClick={this.onClick.bind(this, el)}
          onDoubleClick={this.onDoubleClick.bind(this, el)}>
          {this.renderAContent(data, el)}
        </div>
        {subTree}
      </li>
    )
  }

  renderDragItem (data, liClass, el, subTree, positionId) {
    const { readOnly } = this.props
    return <li className={classnames('tree_item', liClass, { 'actived': this.state.selectedKey === el.id }, { 'hover': this.state.hoverNode === el.id })}
      key={el.id}
      data-id={positionId}
      onContextMenu={this.onContextMenu.bind(this, el)}>
      <div className="tree_wp"
        draggable={!readOnly && el.draggable}
        onDragStart={readOnly ? null : this.onDragStart.bind(this, el)}
        onDragEnd={readOnly ? null : this.onDragEnd.bind(this, el)}
        onClick={this.onClick.bind(this, el)}
        onDoubleClick={this.onDoubleClick.bind(this, el)}>
        {this.renderAContent(data, el)}
      </div>

      {subTree}
    </li>
  }

  renderLi (data, liClass, el, subTree, positionId) {
    return <li className={classnames('tree_item', liClass, { 'actived': this.state.selectedKey === el.id }, { 'hover': this.state.hoverNode === el.id })}
      key={el.id}
      data-id={positionId}
      onContextMenu={this.onContextMenu.bind(this, el)}>
      <div className="tree_wp"
        onClick={this.onClick.bind(this, el)}
        onDoubleClick={this.onDoubleClick.bind(this, el)}>
        {this.renderAContent(data, el)}
      </div>

      {subTree}
    </li>
  }

  renderDragAndDropItem (data, liClass, el, subTree, positionId) {
    const { readOnly } = this.props
    return (
      <li className={classnames('tree_item', liClass, { 'actived': this.state.selectedKey === el.id }, { 'hover': this.state.hoverNode === el.id })}
        key={el.id}
        data-id={positionId}
        onContextMenu={this.onContextMenu.bind(this, el)}>
        <div className="tree_wp"
          draggable={!readOnly && el.draggable}
          onDragStart={readOnly ? null : this.onDragStart.bind(this, el)}
          onDragEnd={readOnly ? null : this.onDragEnd.bind(this, el)}
          onDrop={readOnly ? null : this.onDrop.bind(this, el)}
          onDragOver={readOnly ? null : this.onDragOver.bind(this)}
          onDragEnter={readOnly ? null : this.onDragEnter.bind(this, el)}
          onDragLeave={readOnly ? null : this.onDragLeave.bind(this)}
          onClick={this.onClick.bind(this, el)}
          onDoubleClick={this.onDoubleClick.bind(this, el)}>
          {this.renderAContent(data, el)}
        </div>

        {subTree}
      </li>
    )
  }

  renderTree (parentId, displayData, originalData, isRoot = false, parentPosition = '') {
    const { readOnly } = this.props
    const { expandedKeysMap } = this.state
    return (
      <ul className={classnames('tree', { 'tree-root': isRoot })}
        onMouseLeave={isRoot ? this.removeHover : null}>
        {displayData && displayData.map((it, index) => {
          const { id, subTree, filteredSubTree, liClass, isDrop, isDrag } = it
          const positionId = parentPosition ? parentPosition + '_' + index : `${index}`
          const subTreeRender = filteredSubTree ? this.renderTree(id, filteredSubTree, subTree, false, positionId) : ''
          const isOpened = !!expandedKeysMap[id]
          const liClassName = (isOpened ? 'tree-open' : '') + ' ' + (liClass || '')
          if (isDrop && !isDrag) {
            return this.renderDropDir(originalData, liClassName, it, subTreeRender, positionId)
          } else if (isDrag && !isDrop) {
            return this.renderDragItem(originalData, liClassName, it, subTreeRender, positionId)
          } else if (isDrop && isDrag) {
            return this.renderDragAndDropItem(originalData, liClassName, it, subTreeRender, positionId)
          } else {
            return this.renderLi(originalData, liClassName, it, subTreeRender, positionId)
          }
        })}
        {isRoot || readOnly ? null : (
          <li className="tree_item-add">
            <a href="javascript:void(0)" onClick={this.addItem(originalData, parentId)}><Icon type="plus"/>新建</a>
          </li>
        )}
      </ul>
    )
  }

  showEdit = (el) => (e) => {
    e.stopPropagation()
    el.isEdit = true
    this.forceUpdate()
  }

  hideEdit = (el) => (e) => {
    const { onRename, onCreate, data } = this.props
    el.isEdit = false
    if (e.target.value || el.isNew) {
      const newName = e.target.value.trim()
      if (el.isNew) {
        if (newName) {
          onCreate(data, el, newName)
        } else {
          el.parent.pop()
        }
      } else {
        if (newName !== el.content) {
          onRename(data, el, newName)
        }
      }
      el.content = newName
    }
    this.forceUpdate()
  }

  addItem = (parent, parentId) => () => {
    const { count } = this.state
    const newItem = {
      'content': '',
      'id': `new${count}`,
      'isDir': false,
      'isDrag': true,
      'isDrop': true,
      'isEdit': true,
      'isNew': true,
      'parentId': parentId,
      'parent': parent
    }
    parent.push(newItem)
    this.setState({
      count: count + 1
    })
  }

  deleteItem = (parent, el) => (e) => {
    const { onDelete, data } = this.props
    e.stopPropagation()
    let index = -1
    parent.find((p, i) => {
      if (p.id === el.id) {
        index = i
        return true
      }
    })
    if (index > -1) {
      onDelete(data, el, parent, index)
      // parent.splice(index, 1)
      this.forceUpdate()
    }
  }

  filterTree (data, search, stop) {
    return data.filter(d => {
      const { subTree, content } = d
      let exist = content.toLowerCase().search(search.toLowerCase()) > -1 || stop
      if (subTree) {
        // 如果父級(jí)匹配到了,那么子級(jí)就不用再過(guò)濾了
        const filteredSubTree = this.filterTree(subTree, search, exist)
        d.filteredSubTree = filteredSubTree
        if (filteredSubTree.length) {
          exist = true
        }
        // 記錄需要展開(kāi)子樹(shù)的節(jié)點(diǎn)
        if (search && exist) {
          this.tempExpandedMap[d.id] = true
        }
      }
      return exist
    })
  }

  render () {
    const { data, noDataTips, search } = this.props
    const filteredTree = this.filterTree(data, search, false)
    return filteredTree.length ? this.renderTree(0, filteredTree, data, true) : <span>{noDataTips}</span>
  }
}

FileTree.defaultProps = {
  name: '文件夾',
  search: '',
  noDataTips: '沒(méi)有找到相關(guān)內(nèi)容',
  readOnly: false,
  expandedKeys: []
}

FileTree.propTypes = {
  type: PropTypes.string,
  name: PropTypes.string,
  readOnly: PropTypes.bool,
  data: PropTypes.array,
  onChange: PropTypes.func,
  onClick: PropTypes.func,
  onDoubleClick: PropTypes.func,
  onContextMenu: PropTypes.func,
  onDrag: PropTypes.func,
  onDrop: PropTypes.func,
  search: PropTypes.string,
  noDataTips: PropTypes.string,
  selectedKey: PropTypes.any,
  expandedKeys: PropTypes.array,
  onSelectedKeyChange: PropTypes.func,
  onRename: PropTypes.func,
  onDelete: PropTypes.func,
  onCreate: PropTypes.func,
  onMove: PropTypes.func,
}

在(src/Pages/Favorite/Favorite.js)使用如下語(yǔ)句引入:

import FileTree from '@/components/FileTree'

出現(xiàn)下圖錯(cuò)誤:

clipboard.png

檢查到已經(jīng)export了這個(gè)組件,請(qǐng)各位大神看看還有哪里出錯(cuò)了呢?

回答
編輯回答
愿如初

寫(xiě)成這樣試試
import FileTree from '@/components/FileTree/index'

2018年4月27日 04:13
編輯回答
兔囡囡
import 'components'

會(huì)被認(rèn)為是引用全局module,如果要引用當(dāng)前目錄下的某個(gè)組件,應(yīng)該這么寫(xiě):

import './components'

https://segmentfault.com/a/11...
前段時(shí)間的一篇文章這里有介紹到module的路徑問(wèn)題,雖說(shuō)是原生ES-Module,但是在處理非全局module下,規(guī)則基本通用(除了后綴)

2018年5月12日 20:10