├── style ├── table.css ├── table-column.css ├── editable.css └── editable-column.css ├── postcss.config.js ├── docs ├── favicon.ico ├── static │ └── fonts │ │ ├── element-icons.535877f5.woff │ │ └── element-icons.732389de.ttf └── index.html ├── public ├── favicon.ico └── index.html ├── examples ├── mock │ ├── api │ │ ├── conf │ │ │ ├── languages.json │ │ │ ├── sex.json │ │ │ └── columns.json │ │ ├── role │ │ │ └── list.json │ │ ├── user │ │ │ └── list.json │ │ ├── i18n │ │ │ └── list.json │ │ ├── file │ │ │ └── list.json │ │ ├── column │ │ │ └── list.json │ │ └── index.js │ ├── index.js │ ├── setup.js │ └── error.js ├── plugins │ ├── element.js │ └── element-extends.js ├── main.js └── views │ ├── table │ ├── Base1.vue │ ├── Base9.vue │ ├── Scroll2.vue │ ├── Scroll1.vue │ ├── Base4.vue │ ├── Base3.vue │ ├── Base6.vue │ ├── Base7.vue │ ├── Base5.vue │ ├── Base8.vue │ ├── Base2.vue │ ├── Custom1.vue │ ├── Custom3.vue │ └── Custom2.vue │ └── editable │ ├── Excel1.vue │ ├── Click1.vue │ ├── Dblclick1.vue │ ├── BigScroll3.vue │ ├── BigScroll2.vue │ ├── BigScroll4.vue │ ├── Excel2.vue │ ├── Dblclick8.vue │ ├── Click11.vue │ ├── Click8.vue │ ├── BigScroll1.vue │ ├── Manual1.vue │ └── Excel4.vue ├── types └── index.d.ts ├── packages ├── table │ ├── index.js │ └── src │ │ └── table-column.vue ├── editable │ ├── index.js │ └── src │ │ ├── event.js │ │ ├── props.js │ │ ├── tool.js │ │ └── scroll.js ├── table-column │ └── index.js └── editable-column │ └── index.js ├── lib ├── demo.html └── index.css ├── babel.config.js ├── .gitignore ├── .github └── ISSUE_TEMPLATE │ ├── help.md │ ├── feature_request.md │ └── bug_report.md ├── .eslintrc.js ├── scripts └── postinstall.js ├── src └── index.js ├── LICENSE ├── vue.config.js └── package.json /style/table.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /style/table-column.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | autoprefixer: {} 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /docs/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuliangzhan/vue-element-extends/HEAD/docs/favicon.ico -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuliangzhan/vue-element-extends/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /docs/static/fonts/element-icons.535877f5.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuliangzhan/vue-element-extends/HEAD/docs/static/fonts/element-icons.535877f5.woff -------------------------------------------------------------------------------- /docs/static/fonts/element-icons.732389de.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuliangzhan/vue-element-extends/HEAD/docs/static/fonts/element-icons.732389de.ttf -------------------------------------------------------------------------------- /examples/mock/api/conf/languages.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "label": "中文", 4 | "value": "zh_CN" 5 | }, 6 | { 7 | "label": "English", 8 | "value": "en_US" 9 | } 10 | ] -------------------------------------------------------------------------------- /examples/plugins/element.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Element from 'element-ui' 3 | import 'element-ui/lib/theme-chalk/index.css' 4 | 5 | // Vue.use(Element, { size: 'small', zIndex: 3000 }) 6 | Vue.use(Element) 7 | -------------------------------------------------------------------------------- /examples/mock/index.js: -------------------------------------------------------------------------------- 1 | import XEAjax from 'xe-ajax' 2 | import XEAjaxMock from 'xe-ajax-mock' 3 | 4 | // import mock api 5 | import './setup' 6 | import './api' 7 | import './error' 8 | 9 | XEAjax.use(XEAjaxMock) 10 | -------------------------------------------------------------------------------- /types/index.d.ts: -------------------------------------------------------------------------------- 1 | export interface ElxMethods { 2 | 3 | } 4 | 5 | /** 6 | * Extension component based on ElementUI 2.x. 7 | */ 8 | declare var VueElementExtends: ElxMethods; 9 | 10 | export default VueElementExtends; -------------------------------------------------------------------------------- /packages/table/index.js: -------------------------------------------------------------------------------- 1 | import ElxTable from '../table/src/table' 2 | 3 | ElxTable.install = function (Vue) { 4 | Vue.component(ElxTable.name, ElxTable) 5 | } 6 | 7 | export const Table = ElxTable 8 | export default ElxTable 9 | -------------------------------------------------------------------------------- /examples/plugins/element-extends.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import { Table, TableColumn, Editable, EditableColumn } from '../../src' 3 | 4 | Vue.use(Table) 5 | Vue.use(TableColumn) 6 | Vue.use(Editable) 7 | Vue.use(EditableColumn) 8 | -------------------------------------------------------------------------------- /lib/demo.html: -------------------------------------------------------------------------------- 1 | 2 | index demo 3 | 4 | 5 | 6 | 7 | 8 | 11 | -------------------------------------------------------------------------------- /examples/mock/setup.js: -------------------------------------------------------------------------------- 1 | import XEAjaxMock from 'xe-ajax-mock' 2 | 3 | // mock defaults 4 | XEAjaxMock.setup({ 5 | template: true, 6 | pathVariable: 'auto', 7 | timeout: '40-300', 8 | error: true, 9 | log: true 10 | }) 11 | -------------------------------------------------------------------------------- /packages/editable/index.js: -------------------------------------------------------------------------------- 1 | import ElxEditable from './src/editable' 2 | 3 | ElxEditable.install = function (Vue) { 4 | Vue.component(ElxEditable.name, ElxEditable) 5 | } 6 | 7 | export const Editable = ElxEditable 8 | export default ElxEditable 9 | -------------------------------------------------------------------------------- /examples/mock/error.js: -------------------------------------------------------------------------------- 1 | // import { GET, POST } from 'xe-ajax-mock' 2 | 3 | // // error api 4 | // GET('api/**', { status: 404, body: { message: 'Services does not exist.' } }) 5 | // POST('api/**', { status: 404, body: { message: 'Services does not exist.' } }) 6 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | 'presets': [ 3 | ['@vue/app', { 4 | polyfills: [ 5 | 'es6.promise', 6 | 'es6.symbol', 7 | 'es6.array.iterator', 8 | 'es6.object.assign' 9 | ] 10 | }] 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /packages/table-column/index.js: -------------------------------------------------------------------------------- 1 | import ElxTableColumn from '../table/src/table-column' 2 | 3 | ElxTableColumn.install = function (Vue) { 4 | Vue.component(ElxTableColumn.name, ElxTableColumn) 5 | } 6 | 7 | export const TableColumn = ElxTableColumn 8 | export default ElxTableColumn 9 | -------------------------------------------------------------------------------- /examples/mock/api/conf/sex.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "label": "男", 4 | "spell": "nan", 5 | "value": "1", 6 | "value2": 1, 7 | "val": "x" 8 | }, 9 | { 10 | "label": "女", 11 | "spell": "nv", 12 | "value": "0", 13 | "value2": 0, 14 | "val": "o" 15 | } 16 | ] -------------------------------------------------------------------------------- /packages/editable-column/index.js: -------------------------------------------------------------------------------- 1 | import ElxEditableColumn from '../editable/src/editable-column' 2 | 3 | ElxEditableColumn.install = function (Vue) { 4 | Vue.component(ElxEditableColumn.name, ElxEditableColumn) 5 | } 6 | 7 | export const EditableColumn = ElxEditableColumn 8 | export default ElxEditableColumn 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | package-lock.json 4 | 5 | # local env files 6 | .env.local 7 | .env.*.local 8 | 9 | # Log files 10 | npm-debug.log* 11 | yarn-debug.log* 12 | yarn-error.log* 13 | 14 | # Editor directories and files 15 | .idea 16 | .vscode 17 | *.suo 18 | *.ntvs* 19 | *.njsproj 20 | *.sln 21 | *.sw* 22 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/help.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Help 3 | about: 遇到问题 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **描述 Describe** 11 | 12 | 13 | **请提供能重现问题的链接(jsfiddle、jsrun) Expected behavior** 14 | 15 | 16 | 17 | **请填写以下版本信息 please complete the following information** 18 | vue: ? 19 | element-ui: ? 20 | vue-element-extends: ? 21 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { 4 | node: true 5 | }, 6 | 'extends': [ 7 | 'plugin:vue/essential', 8 | 'standard' 9 | ], 10 | rules: { 11 | 'no-console': 'off', 12 | 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off' 13 | }, 14 | parserOptions: { 15 | parser: 'babel-eslint' 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: 功能需求 4 | title: '' 5 | labels: 需求 6 | assignees: '' 7 | 8 | --- 9 | 10 | **这个新特性和一个问题相关吗?简单描述下,比如我什么场景下,需要解决什么问题,发现表格不支持这个功能 Is your feature request related to a problem? Please describe.** 11 | 12 | 13 | **描述下,你希望得到的什么解决方案 Describe the solution you'd like** 14 | 15 | 16 | **是否有不错的替代方案 Describe alternatives you've considered** 17 | -------------------------------------------------------------------------------- /scripts/postinstall.js: -------------------------------------------------------------------------------- 1 | const env = process.env 2 | const ADBLOCK = is(env.ADBLOCK) 3 | const CI = is(env.CI) 4 | const DISABLE_OPENCOLLECTIVE = is(env.DISABLE_OPENCOLLECTIVE) 5 | 6 | function is (it) { 7 | return !!it && it !== '0' && it !== 'false' 8 | } 9 | 10 | if (!ADBLOCK && !CI && !DISABLE_OPENCOLLECTIVE) { 11 | console.log('\n\x1B[91m>\x1B[91m 注: 该组件 vue-element-extends 已废弃不再维护,不再建议使用!') 12 | console.log('\x1B[93m>\x1B[92m 推荐使用: https://xuliangzhan_admin.gitee.io/vxe-table/\n') 13 | } 14 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: 提 Bug 4 | title: '' 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | **问题描述 Describe the bug** 11 | 12 | 13 | **重现问题的步骤 To Reproduce** 14 | 15 | 16 | **请提供能重现问题的链接(jsfiddle、jsrun) Expected behavior** 17 | 18 | 19 | **报错信息或截图 Error message or screenshots** 20 | 21 | 22 | **期望的结果 Expected behavior** 23 | 24 | 25 | **请填写以下版本信息 please complete the following information** 26 | - vue: ? 27 | - element-ui: ? 28 | - vue-element-extends: ? 29 | -------------------------------------------------------------------------------- /examples/mock/api/role/list.json: -------------------------------------------------------------------------------- 1 | { 2 | "!return|array(5-50)": { 3 | "id|number": "{{ $index+1 }}", 4 | "name": "{{ random.repeat(['前端', '后端', '测试', '项目经理', '设计'],1) }}_{{ $index }}", 5 | "describe": "{{ random.repeat('角色描述',4,50) }}", 6 | "seq|number": "{{ $index }}", 7 | "createTime|number": "{{ random.time('2019-01-01 00:00:00','2019-02-20 23:59:59', 'yyyy-MM-dd HH:mm:ss') }}", 8 | "updateTime|number": "{{ random.time('2019-01-01 00:00:00','2019-02-20 23:59:59', 'yyyy-MM-dd HH:mm:ss') }}" 9 | } 10 | } -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | <%= htmlWebpackPlugin.options.title %> 9 | 10 | 11 | 14 |
15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import Table from '../packages/table' 2 | import TableColumn from '../packages/table-column' 3 | import Editable from '../packages/editable' 4 | import EditableColumn from '../packages/editable-column' 5 | 6 | const components = [ 7 | Table, 8 | TableColumn, 9 | Editable, 10 | EditableColumn 11 | ] 12 | 13 | const install = function (Vue) { 14 | if (!install.installed) { 15 | components.map(component => Vue.component(component.name, component)) 16 | } 17 | } 18 | 19 | if (typeof window !== 'undefined' && window.Vue) { 20 | install(window.Vue) 21 | } 22 | 23 | export * from '../packages/table' 24 | export * from '../packages/table-column' 25 | export * from '../packages/editable' 26 | export * from '../packages/editable-column' 27 | export default { 28 | install, 29 | Table, 30 | TableColumn, 31 | Editable, 32 | EditableColumn 33 | } 34 | -------------------------------------------------------------------------------- /packages/editable/src/event.js: -------------------------------------------------------------------------------- 1 | import XEUtils from 'xe-utils' 2 | 3 | // 监听全局事件 4 | const wheelName = /Firefox/i.test(navigator.userAgent) ? 'DOMMouseScroll' : 'mousewheel' 5 | const eventStore = [] 6 | const GlobalEvent = { 7 | on (comp, type, cb) { 8 | eventStore.push({ comp, type, cb }) 9 | }, 10 | off (comp, type) { 11 | XEUtils.remove(eventStore, item => item.comp === comp && item.type === type) 12 | }, 13 | trigger (evnt) { 14 | eventStore.forEach(({ comp, type, cb }) => { 15 | if (type === evnt.type || (type === 'mousewheel' && evnt.type === wheelName)) { 16 | cb.call(comp, evnt) 17 | } 18 | }) 19 | } 20 | } 21 | 22 | document.addEventListener('keydown', GlobalEvent.trigger, false) 23 | document.addEventListener('contextmenu', GlobalEvent.trigger, false) 24 | window.addEventListener('click', GlobalEvent.trigger, false) 25 | window.addEventListener(wheelName, GlobalEvent.trigger, false) 26 | 27 | export default GlobalEvent 28 | -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | ElementUI 2.X components extends.
-------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Xu Liangzhan 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /examples/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import App from './App.vue' 3 | import router from './router' 4 | 5 | import './plugins/element.js' 6 | import './plugins/element-extends.js' 7 | import './mock' 8 | 9 | Vue.config.productionTip = false 10 | 11 | // 后台异步生成70万数据,为了避免大量运算卡主页面,生成大约需要15秒左右 12 | var list = window.CACHE_DATA_LIST = [] 13 | var currTime = Date.now() 14 | var fullIndex = 0 15 | var size = 700000 16 | function mockData () { 17 | for (var index = 0; index < 1500; index++) { 18 | currTime += 5000 19 | fullIndex++ 20 | list.push({ 21 | id: fullIndex, 22 | name: 'name_' + fullIndex, 23 | date: currTime, 24 | sex: index % 3 ? '0' : '1', 25 | age: index % 4 === 0 ? 30 : index % 3 === 0 ? 28 : index % 2 === 0 ? 26 : 24, 26 | region: index % 4 === 0 ? [19, 199, 1773] : index % 3 === 0 ? [9, 73, 719] : [1, 1, 5], 27 | rate: index % 4 === 0 ? 4 : index % 3 === 0 ? 3 : index % 2 === 0 ? 2 : 1, 28 | updateTime: currTime, 29 | createTime: currTime 30 | }) 31 | } 32 | if (fullIndex < size) { 33 | setTimeout(mockData, 30) 34 | } 35 | } 36 | 37 | mockData() 38 | 39 | new Vue({ 40 | router, 41 | render: h => h(App) 42 | }).$mount('#app') 43 | -------------------------------------------------------------------------------- /examples/mock/api/conf/columns.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "prop": "name", 4 | "label": "名称", 5 | "visible": true 6 | }, 7 | { 8 | "prop": "nickname", 9 | "label": "昵称", 10 | "visible": true 11 | }, 12 | { 13 | "prop": "sex", 14 | "label": "性别", 15 | "visible": true 16 | }, 17 | { 18 | "prop": "age", 19 | "label": "年龄", 20 | "visible": true 21 | }, 22 | { 23 | "prop": "phone", 24 | "label": "手机", 25 | "visible": true 26 | }, 27 | { 28 | "prop": "region", 29 | "label": "地区", 30 | "visible": true 31 | }, 32 | { 33 | "prop": "date", 34 | "label": "日期", 35 | "visible": true 36 | }, 37 | { 38 | "prop": "rate", 39 | "label": "评分", 40 | "visible": true 41 | }, 42 | { 43 | "prop": "attr1", 44 | "label": "属性1", 45 | "visible": false 46 | }, 47 | { 48 | "prop": "attr2", 49 | "label": "属性2", 50 | "visible": false 51 | }, 52 | { 53 | "prop": "attr3", 54 | "label": "属性3", 55 | "visible": false 56 | }, 57 | { 58 | "prop": "attr4", 59 | "label": "属性4", 60 | "visible": false 61 | }, 62 | { 63 | "prop": "attr5", 64 | "label": "属性5", 65 | "visible": false 66 | } 67 | ] -------------------------------------------------------------------------------- /vue.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | 3 | function resolve (dir) { 4 | return path.join(__dirname, '.', dir) 5 | } 6 | 7 | module.exports = { 8 | publicPath: process.env.NODE_ENV === 'production' ? '/vue-element-extends/' : '/', 9 | outputDir: 'docs', 10 | assetsDir: 'static', 11 | productionSourceMap: false, 12 | configureWebpack: { 13 | performance: { 14 | hints: false 15 | } 16 | }, 17 | pages: { 18 | index: { 19 | entry: 'examples/main.js', 20 | template: 'public/index.html', 21 | filename: 'index.html', 22 | title: 'ElementUI 2.X components extends.' 23 | } 24 | }, 25 | chainWebpack (config) { 26 | config.resolve.alias 27 | .set('@', resolve('examples')) 28 | config.output 29 | .set('libraryExport', 'default') 30 | .set('library', 'VueElementExtends') 31 | if (process.env.npm_lifecycle_event.indexOf('lib') === 0) { 32 | let XEUtils = { 33 | root: 'XEUtils', 34 | commonjs: 'xe-utils', 35 | commonjs2: 'xe-utils', 36 | amd: 'xe-utils' 37 | } 38 | if (config.has('externals')) { 39 | config.externals 40 | .set('xe-utils', XEUtils) 41 | } else { 42 | config 43 | .set('externals', { 44 | 'xe-utils': XEUtils 45 | }) 46 | } 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /examples/mock/api/user/list.json: -------------------------------------------------------------------------------- 1 | { 2 | "!return|array(50-500)": { 3 | "id|number": "{{ $index+1 }}", 4 | "name": "{{ random.repeat(['name', 'test'],1) }}_{{ $index }}", 5 | "nickname": "{{ random.repeat(['昵称', '名字'],1) }}_{{ $index }}", 6 | "password": "{{ random.repeat('abcdefgABCDEFG',6,12) }}", 7 | "sex|random(1)": [null, "0", "1"], 8 | "sexs|random(1)": [[], ["0"], ["1"], ["0", "1"]], 9 | "role|random(1)": ["前端", "后端", "测试", "设计", "项目经理"], 10 | "region|random(1)": [[1, 1, 5], [19, 199, 1773], [9, 73, 719]], 11 | "email": "{{ random.repeat('abcdefg',5,20) }}@{{ random.repeat(['qq','163'],1) }}.{{ random.repeat(['com','net'],1) }}", 12 | "age|number": "{{ random.num(18,30) }}", 13 | "rate|number": "{{ random.num(0,5) }}", 14 | "flag|boolean": "{{ random.num(0,1) }}", 15 | "phone": "136{{ random.num(10000000,99999999) }}", 16 | "describe": "{{ random.repeat('用户信息',2,6) }}", 17 | "describe2": "{{ random.repeat('用户信息',4,10) }}", 18 | "describe3": "{{ random.repeat('用户信息',6,20) }}", 19 | "seq|number": "{{ $index }}", 20 | "date|number": "{{ random.time('2019-01-01 00:00:00','2019-02-20 23:59:59', 'yyyy-MM-dd HH:mm:ss') }}", 21 | "createTime|number": "{{ random.time('2019-01-01 00:00:00','2019-02-20 23:59:59', 'yyyy-MM-dd HH:mm:ss') }}", 22 | "updateTime|number": "{{ random.time('2019-01-01 00:00:00','2019-02-20 23:59:59', 'yyyy-MM-dd HH:mm:ss') }}", 23 | "attr1": null, 24 | "attr2": null, 25 | "attr3": null, 26 | "attr4": null, 27 | "attr5": null, 28 | "attr6": null 29 | } 30 | } -------------------------------------------------------------------------------- /examples/mock/api/i18n/list.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": 100, 4 | "key": "home.label.name", 5 | "name": "名字", 6 | "language": "zh_CN", 7 | "createTime": 1550923830095, 8 | "updateTime": 1552923830095 9 | }, 10 | { 11 | "id": 101, 12 | "key": "home.label.name", 13 | "name": "Name", 14 | "language": "en_US", 15 | "createTime": 1550923830095, 16 | "updateTime": 1552923830095 17 | }, 18 | { 19 | "id": 102, 20 | "key": "home.label.age", 21 | "name": "年龄", 22 | "language": "zh_CN", 23 | "createTime": 1550923830095, 24 | "updateTime": 1552923830095 25 | }, 26 | { 27 | "id": 103, 28 | "key": "home.label.age", 29 | "name": "Age", 30 | "language": "en_US", 31 | "createTime": 1550923830095, 32 | "updateTime": 1552923830095 33 | }, 34 | { 35 | "id": 104, 36 | "key": "home.label.role", 37 | "name": "Role", 38 | "language": "en_US", 39 | "createTime": 1550923830095, 40 | "updateTime": 1552923830095 41 | }, 42 | { 43 | "id": 105, 44 | "key": "comm.label.cancel", 45 | "name": "取消", 46 | "language": "zh_CN", 47 | "createTime": 1550923830095, 48 | "updateTime": 1552923830095 49 | }, 50 | { 51 | "id": 106, 52 | "key": "comm.label.cancel", 53 | "name": "Cancel", 54 | "language": "en_US", 55 | "createTime": 1550923830095, 56 | "updateTime": 1552923830095 57 | }, 58 | { 59 | "id": 107, 60 | "key": "home.label.phone", 61 | "name": "手机", 62 | "language": "zh_CN", 63 | "createTime": 1550923830095, 64 | "updateTime": 1552923830095 65 | }, 66 | { 67 | "id": 108, 68 | "key": "comm.label.confirm", 69 | "name": "确认", 70 | "language": "zh_CN", 71 | "createTime": 1550923830095, 72 | "updateTime": 1552923830095 73 | } 74 | ] -------------------------------------------------------------------------------- /examples/mock/api/file/list.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": 100, 4 | "parentId": null, 5 | "name": "一级目录", 6 | "size": null, 7 | "type": "0", 8 | "createTime": 1550923830095, 9 | "updateTime": 1551923830095 10 | }, 11 | { 12 | "id": 101, 13 | "parentId": 100, 14 | "name": "二级目录", 15 | "size": null, 16 | "type": "0", 17 | "createTime": 1550923830095, 18 | "updateTime": 1552923830095 19 | }, 20 | { 21 | "id": 10100, 22 | "parentId": 101, 23 | "name": "文件10100.pdf", 24 | "size": 218, 25 | "type": "1", 26 | "createTime": 1550923830095, 27 | "updateTime": 1553924830095 28 | }, 29 | { 30 | "id": 10101, 31 | "parentId": 101, 32 | "name": "文件10101.excel", 33 | "size": 512, 34 | "type": "1", 35 | "createTime": 1550923830095, 36 | "updateTime": 1550925830095 37 | }, 38 | { 39 | "id": 102, 40 | "parentId": 100, 41 | "name": "文件102.word", 42 | "size": 101024, 43 | "type": "1", 44 | "createTime": 1550923830095, 45 | "updateTime": 1550963830095 46 | }, 47 | { 48 | "id": 200, 49 | "parentId": null, 50 | "name": "我的文件", 51 | "size": null, 52 | "type": "0", 53 | "createTime": 1550923830095, 54 | "updateTime": 1550983830095 55 | }, 56 | { 57 | "id": 201, 58 | "parentId": 200, 59 | "name": "文件201.zip", 60 | "size": 1024, 61 | "type": "1", 62 | "createTime": 1550923830095, 63 | "updateTime": 1550927830095 64 | }, 65 | { 66 | "id": 202, 67 | "parentId": 200, 68 | "name": "文件202.text", 69 | "size": 4096, 70 | "type": "1", 71 | "createTime": 1550923830095, 72 | "updateTime": 1550926830095 73 | }, 74 | { 75 | "id": 301, 76 | "parentId": null, 77 | "name": "文件301.zip", 78 | "size": 4024, 79 | "type": "1", 80 | "createTime": 1550923830095, 81 | "updateTime": 1550823830095 82 | } 83 | ] -------------------------------------------------------------------------------- /examples/mock/api/column/list.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": 1, 4 | "key": "name", 5 | "name": "名字", 6 | "readonly": false, 7 | "required": true, 8 | "validator": "", 9 | "validMsg": "", 10 | "visible": true, 11 | "width": null, 12 | "align": "", 13 | "type": "ElInput", 14 | "seq": 1, 15 | "describe": "", 16 | "createTime": 1550923830095, 17 | "updateTime": 1550105830095 18 | }, 19 | { 20 | "id": 2, 21 | "key": "age", 22 | "name": "年龄", 23 | "readonly": false, 24 | "required": false, 25 | "validator": "", 26 | "validMsg": "", 27 | "visible": true, 28 | "width": "140", 29 | "align": "center", 30 | "type": "ElInputNumber", 31 | "seq": 2, 32 | "describe": "", 33 | "createTime": 1550923830095, 34 | "updateTime": 1550205830095 35 | }, 36 | { 37 | "id": 3, 38 | "key": "date", 39 | "name": "日期", 40 | "readonly": false, 41 | "required": false, 42 | "validator": "", 43 | "validMsg": "", 44 | "visible": true, 45 | "width": "200", 46 | "align": "", 47 | "type": "ElDatePicker", 48 | "seq": 3, 49 | "describe": "", 50 | "createTime": 1550923830095, 51 | "updateTime": 1550305830095 52 | }, 53 | { 54 | "id": 4, 55 | "key": "phone", 56 | "name": "手机", 57 | "readonly": false, 58 | "required": false, 59 | "validator": "^1[34578][0-9]{9}$", 60 | "validMsg": "手机格式输入错误", 61 | "visible": true, 62 | "width": null, 63 | "align": "", 64 | "type": "ElInput", 65 | "seq": 4, 66 | "describe": "", 67 | "createTime": 1550923830095, 68 | "updateTime": 1550405830095 69 | }, 70 | { 71 | "id": 5, 72 | "key": "role", 73 | "name": "角色", 74 | "readonly": false, 75 | "required": false, 76 | "validator": "", 77 | "validMsg": "", 78 | "visible": false, 79 | "width": null, 80 | "align": "", 81 | "type": "ElInput", 82 | "seq": 5, 83 | "describe": "", 84 | "createTime": 1550923830095, 85 | "updateTime": 1550505830095 86 | } 87 | ] -------------------------------------------------------------------------------- /packages/editable/src/props.js: -------------------------------------------------------------------------------- 1 | const PropsStatic = { 2 | table: { 3 | data: Array, 4 | height: [String, Number], 5 | maxHeight: [String, Number], 6 | stripe: Boolean, 7 | border: Boolean, 8 | size: String, 9 | fit: { type: Boolean, default: true }, 10 | showHeader: { type: Boolean, default: true }, 11 | highlightCurrentRow: Boolean, 12 | currentRowKey: [String, Number], 13 | lazy: Boolean, 14 | indent: Number, 15 | rowClassName: [Function, String], 16 | rowStyle: [Function, Object], 17 | cellClassName: [Function, String], 18 | cellStyle: [Function, Object], 19 | headerRowClassName: [Function, String], 20 | headerRowStyle: [Function, Object], 21 | headerCellClassName: [Function, String], 22 | headerCellStyle: [Function, Object], 23 | rowKey: [Function, String], 24 | emptyText: String, 25 | defaultExpandAll: Boolean, 26 | expandRowKeys: Array, 27 | defaultSort: Object, 28 | tooltipEffect: { type: String, default: 'dark' }, 29 | showSummary: Boolean, 30 | sumText: String, 31 | summaryMethod: Function, 32 | selectOnIndeterminate: { type: Boolean, default: true }, 33 | spanMethod: Function, 34 | load: Function 35 | }, 36 | tableColumn: { 37 | index: [Number, Function], 38 | type: String, 39 | label: String, 40 | columnKey: String, 41 | prop: String, 42 | width: String, 43 | minWidth: String, 44 | fixed: [Boolean, String], 45 | sortable: [Boolean, String], 46 | sortMethod: Function, 47 | sortBy: [String, Array, Function], 48 | sortOrders: Array, 49 | resizable: { type: Boolean, default: true }, 50 | formatter: Function, 51 | showOverflowTooltip: Boolean, 52 | align: { type: String, default: 'left' }, 53 | headerAlign: String, 54 | className: { type: String, default: '' }, 55 | labelClassName: String, 56 | selectable: Function, 57 | reserveSelection: Boolean, 58 | filters: Array, 59 | filterPlacement: String, 60 | filterMultiple: { type: Boolean, default: true }, 61 | filterMethod: Function, 62 | filteredValue: Array 63 | } 64 | } 65 | 66 | export default PropsStatic 67 | -------------------------------------------------------------------------------- /examples/views/table/Base1.vue: -------------------------------------------------------------------------------- 1 | 30 | 31 | 64 | 65 | 70 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-element-extends", 3 | "version": "1.2.29", 4 | "description": "Extension component based on ElementUI 2.x", 5 | "scripts": { 6 | "serve": "vue-cli-service serve", 7 | "build": "vue-cli-service build", 8 | "lint": "vue-cli-service lint", 9 | "lib": "vue-cli-service build --target lib --name index --dest lib src/index.js", 10 | "postinstall": "node scripts/postinstall || echo \"ignore\"" 11 | }, 12 | "files": [ 13 | "lib", 14 | "packages", 15 | "style", 16 | "types", 17 | "scripts", 18 | "src" 19 | ], 20 | "main": "lib/index.common.js", 21 | "style": "lib/index.css", 22 | "unpkg": "lib/index.umd.js", 23 | "jsdelivr": "lib/index.umd.js", 24 | "typings": "types/index.d.ts", 25 | "devDependencies": { 26 | "@vue/cli-plugin-babel": "^3.6.0", 27 | "@vue/cli-plugin-eslint": "^3.6.0", 28 | "@vue/cli-service": "^3.6.0", 29 | "@vue/eslint-config-standard": "^4.0.0", 30 | "babel-eslint": "^10.0.1", 31 | "babel-plugin-component": "^1.1.1", 32 | "core-js": "^2.6.5", 33 | "element-ui": "^2.11.1", 34 | "eslint": "^5.16.0", 35 | "eslint-plugin-vue": "^5.0.0", 36 | "font-awesome": "^4.7.0", 37 | "node-sass": "^4.9.0", 38 | "sass-loader": "^7.1.0", 39 | "sortablejs": "^1.10.0-rc3", 40 | "vue": "^2.6.10", 41 | "vue-router": "^3.0.3", 42 | "vue-template-compiler": "^2.6.10", 43 | "xe-ajax": "^3.6.0", 44 | "xe-ajax-mock": "^1.7.10", 45 | "xe-clipboard": "^1.6.0", 46 | "xe-utils": "^1.9.7", 47 | "xlsx": "^0.14.3" 48 | }, 49 | "peerDependencies": { 50 | "vue": "^2.6.0", 51 | "element-ui": "^2.1.0" 52 | }, 53 | "postcss": { 54 | "plugins": { 55 | "autoprefixer": {} 56 | } 57 | }, 58 | "repository": { 59 | "type": "git", 60 | "url": "git+https://github.com/xuliangzhan/vue-element-extends.git" 61 | }, 62 | "keywords": [ 63 | "vue-element-extends", 64 | "elx-editable", 65 | "vue editable", 66 | "el-editable" 67 | ], 68 | "author": { 69 | "name": "Xu Liangzhan", 70 | "email": "xu_liangzhan@163.com" 71 | }, 72 | "license": "MIT", 73 | "bugs": { 74 | "url": "https://github.com/xuliangzhan/vue-element-extends/issues" 75 | }, 76 | "homepage": "https://github.com/xuliangzhan/vue-element-extends#readme", 77 | "browserslist": [ 78 | "> 1%", 79 | "last 2 versions", 80 | "not ie <= 8" 81 | ] 82 | } 83 | -------------------------------------------------------------------------------- /examples/views/table/Base9.vue: -------------------------------------------------------------------------------- 1 | 23 | 24 | 74 | 75 | 80 | -------------------------------------------------------------------------------- /examples/views/table/Scroll2.vue: -------------------------------------------------------------------------------- 1 | 34 | 35 | 84 | 85 | 94 | -------------------------------------------------------------------------------- /style/editable.css: -------------------------------------------------------------------------------- 1 | /* el tooltip */ 2 | .el-tooltip__popper.elx-valid_tooltip { 3 | background: #f56c6c; 4 | } 5 | .el-tooltip__popper.elx-valid_tooltip[x-placement^=top] .popper__arrow, 6 | .el-tooltip__popper.elx-valid_tooltip[x-placement^=top] .popper__arrow::after { 7 | border-top-color: #f56c6c; 8 | } 9 | .el-tooltip__popper.elx-valid_tooltip[x-placement^=bottom] .popper__arrow, 10 | .el-tooltip__popper.elx-valid_tooltip[x-placement^=bottom] .popper__arrow::after { 11 | border-bottom-color: #f56c6c; 12 | } 13 | .el-tooltip__popper.elx-valid_tooltip[x-placement^=left] .popper__arrow, 14 | .el-tooltip__popper.elx-valid_tooltip[x-placement^=left] .popper__arrow::after { 15 | border-left-color: #f56c6c; 16 | } 17 | .el-tooltip__popper.elx-valid_tooltip[x-placement^=right] .popper__arrow, 18 | .el-tooltip__popper.elx-valid_tooltip[x-placement^=right] .popper__arrow::after { 19 | border-right-color: #f56c6c; 20 | } 21 | .elx-contextmenu, 22 | .elx-contextmenu .ctx-menu_child-wrapper { 23 | position: absolute; 24 | top: 0; 25 | left: 0; 26 | z-index: 88; 27 | font-size: 12px; 28 | color: #000; 29 | background: #FFF; 30 | border: 1px solid #C6C6C6; 31 | box-shadow: 2px 2px 4px -2px #666; 32 | padding: 0 1px; 33 | font-family: "Source Sans Pro", "Helvetica Neue", Arial, sans-serif; 34 | -webkit-font-smoothing: antialiased; 35 | -moz-osx-font-smoothing: grayscale; 36 | -webkit-user-select: none; 37 | -moz-user-select: none; 38 | -ms-user-select: none; 39 | user-select: none; 40 | } 41 | .elx-contextmenu > .ctx-menu_wrapper, 42 | .elx-contextmenu > .ctx-menu_wrapper .ctx-menu_child-wrapper { 43 | margin: 0; 44 | padding: 0; 45 | list-style-type: none; 46 | border-bottom: 1px solid #E8EAED; 47 | } 48 | .elx-contextmenu > .ctx-menu_wrapper li { 49 | position: relative; 50 | margin: 1px 0; 51 | border: 1px solid transparent; 52 | } 53 | .elx-contextmenu > .ctx-menu_wrapper li:last-child { 54 | border: 0; 55 | } 56 | .elx-contextmenu > .ctx-menu_wrapper li.active { 57 | color: #2B2B2B; 58 | background-color: #C5C5C5; 59 | border-color:#C5C5C5; 60 | } 61 | .elx-contextmenu > .ctx-menu_wrapper li.disabled { 62 | color: #B1B1B1; 63 | } 64 | .elx-contextmenu > .ctx-menu_wrapper li.disabled.active { 65 | border-color:#C0C1C2; 66 | background-color: #EEEEEE; 67 | } 68 | .elx-contextmenu > .ctx-menu_wrapper li.disabled.active:hover { 69 | background-color: inherit; 70 | } 71 | .elx-contextmenu > .ctx-menu_wrapper .ctx-menu_link { 72 | display: block; 73 | padding: 0 30px; 74 | width: 100px; 75 | line-height: 26px; 76 | } 77 | .elx-contextmenu > .ctx-menu_wrapper .ctx-prefix-icon, 78 | .elx-contextmenu > .ctx-menu_wrapper .ctx-suffix-icon { 79 | position: absolute; 80 | top: 5px; 81 | margin-right: 5px; 82 | font-size: 16px; 83 | } 84 | .elx-contextmenu > .ctx-menu_wrapper .ctx-prefix-icon { 85 | left: 5px; 86 | } 87 | .elx-contextmenu > .ctx-menu_wrapper .ctx-suffix-icon { 88 | right: 5px; 89 | } 90 | .elx-contextmenu > .ctx-menu_wrapper .content { 91 | display: block; 92 | overflow: hidden; 93 | text-overflow: ellipsis; 94 | white-space: nowrap; 95 | } 96 | .elx-contextmenu .ctx-menu_child-wrapper { 97 | display: none; 98 | z-index: 99; 99 | top: 0; 100 | left: 100%; 101 | } 102 | .elx-contextmenu .ctx-menu_child-wrapper.show { 103 | display: block; 104 | } 105 | -------------------------------------------------------------------------------- /examples/views/table/Scroll1.vue: -------------------------------------------------------------------------------- 1 | 39 | 40 | 96 | 97 | 106 | -------------------------------------------------------------------------------- /packages/editable/src/tool.js: -------------------------------------------------------------------------------- 1 | import XEUtils from 'xe-utils' 2 | 3 | const browse = XEUtils.browse() 4 | const UtilHandle = { 5 | browse, 6 | addClass (cell, clss) { 7 | let classList = cell.className.split(' ') 8 | clss.forEach(name => { 9 | if (classList.indexOf(name) === -1) { 10 | classList.push(name) 11 | } 12 | }) 13 | cell.className = classList.join(' ') 14 | }, 15 | hasClass (cell, cls) { 16 | return cell && cell.className && cell.className.split && cell.className.split(' ').indexOf(cls) > -1 17 | }, 18 | removeClass (cell, clss) { 19 | let classList = [] 20 | cell.className.split(' ').forEach(name => { 21 | if (clss.indexOf(name) === -1) { 22 | classList.push(name) 23 | } 24 | }) 25 | cell.className = classList.join(' ') 26 | }, 27 | getCsvUrl (opts, content) { 28 | if (window.Blob && window.URL && window.URL.createObjectURL && !browse.safari) { 29 | return URL.createObjectURL(new Blob([content], { type: 'text/csv' })) 30 | } 31 | return `data:attachment/csv;charset=utf-8,${encodeURIComponent(content)}` 32 | }, 33 | getCsvLabelData (columns, oData, tableElem) { 34 | let trElemList = tableElem.querySelectorAll('.el-table__body-wrapper .el-table__row') 35 | return Array.from(trElemList).map((trElem, rowIndex) => { 36 | let item = {} 37 | let row = oData[rowIndex] 38 | columns.forEach(column => { 39 | let cell = trElem.querySelector(`.${column.id}`) 40 | item[column.id] = cell ? cell.innerText.trim() : (row ? XEUtils.get(row, column.property) : '') 41 | }) 42 | return item 43 | }) 44 | }, 45 | getCsvData (opts, oData, oColumns, tableElem) { 46 | let isOriginal = opts.original 47 | let columns = opts.columns ? opts.columns : oColumns 48 | if (opts.columnFilterMethod) { 49 | columns = columns.filter(opts.columnFilterMethod) 50 | } 51 | let datas = opts.data ? opts.data : (isOriginal ? oData : UtilHandle.getCsvLabelData(columns, oData, tableElem)) 52 | if (opts.dataFilterMethod) { 53 | datas = datas.filter(opts.dataFilterMethod) 54 | } 55 | return { columns, datas } 56 | }, 57 | getCsvContent (opts, oData, oColumns, tableElem) { 58 | let isOriginal = opts.original 59 | let { columns, datas } = UtilHandle.getCsvData(opts, oData, oColumns, tableElem) 60 | let content = '\ufeff' 61 | if (opts.isHeader) { 62 | content += columns.map(column => column.label).join(',') + '\n' 63 | } 64 | datas.forEach((record, rowIndex) => { 65 | if (isOriginal) { 66 | content += columns.map(column => { 67 | if (column.type === 'index') { 68 | return `"${column.index ? column.index(rowIndex) : rowIndex + 1}"` 69 | } 70 | return `"${XEUtils.get(record, column.property) || ''}"` 71 | }).join(',') + '\n' 72 | } else { 73 | content += columns.map(column => `"${record[column.id]}"`).join(',') + '\n' 74 | } 75 | }) 76 | return content 77 | }, 78 | downloadCsc (opts, content) { 79 | if (!opts.download) { 80 | return Promise.resolve(content) 81 | } 82 | if (navigator.msSaveBlob && window.Blob) { 83 | navigator.msSaveBlob(new Blob([content], { type: 'text/csv' }), opts.filename) 84 | } else if (browse['-ms']) { 85 | var win = window.top.open('about:blank', '_blank') 86 | win.document.charset = 'utf-8' 87 | win.document.write(content) 88 | win.document.close() 89 | win.document.execCommand('SaveAs', opts.filename) 90 | win.close() 91 | } else { 92 | var linkElem = document.createElement('a') 93 | linkElem.target = '_blank' 94 | linkElem.download = opts.filename 95 | linkElem.href = UtilHandle.getCsvUrl(opts, content) 96 | document.body.appendChild(linkElem) 97 | linkElem.click() 98 | document.body.removeChild(linkElem) 99 | } 100 | } 101 | } 102 | 103 | export default UtilHandle 104 | -------------------------------------------------------------------------------- /examples/views/table/Base4.vue: -------------------------------------------------------------------------------- 1 | 53 | 54 | 111 | 112 | 121 | -------------------------------------------------------------------------------- /packages/table/src/table-column.vue: -------------------------------------------------------------------------------- 1 | 30 | 31 | 117 | 118 | 119 | -------------------------------------------------------------------------------- /examples/views/editable/Excel1.vue: -------------------------------------------------------------------------------- 1 | 27 | 28 | 84 | 85 | 127 | -------------------------------------------------------------------------------- /examples/views/table/Base3.vue: -------------------------------------------------------------------------------- 1 | 58 | 59 | 116 | 117 | 126 | -------------------------------------------------------------------------------- /examples/views/table/Base6.vue: -------------------------------------------------------------------------------- 1 | 54 | 55 | 117 | 118 | 127 | -------------------------------------------------------------------------------- /style/editable-column.css: -------------------------------------------------------------------------------- 1 | .elx-editable .editable-required-icon:before { 2 | content: "*"; 3 | color: #f56c6c; 4 | } 5 | .elx-editable.el-table--mini .elx-editable-column { 6 | height: 36px; 7 | } 8 | .elx-editable.el-table--small .elx-editable-column { 9 | height: 40px; 10 | } 11 | .elx-editable.el-table--medium .elx-editable-column { 12 | height: 44px; 13 | } 14 | .elx-editable .elx-editable-column, 15 | .elx-editable.el-table--large .elx-editable-column { 16 | height: 48px; 17 | padding: 0; 18 | } 19 | .elx-editable .elx-editable-column.elx_dirty { 20 | position: relative; 21 | } 22 | .elx-editable .elx-editable-column.elx_dirty:before { 23 | content: ''; 24 | top: -5px; 25 | left: -5px; 26 | position: absolute; 27 | border: 5px solid; 28 | border-color: transparent #f56c6c transparent transparent; 29 | -webkit-transform: rotate(45deg); 30 | -moz-transform: rotate(45deg); 31 | -ms-transform: rotate(45deg); 32 | transform: rotate(45deg); 33 | } 34 | .elx-editable .elx-editable-column .cell > .edit-input, 35 | .elx-editable .elx-editable-column .cell > .el-cascader, 36 | .elx-editable .elx-editable-column .cell > .el-autocomplete, 37 | .elx-editable .elx-editable-column .cell > .el-input-number, 38 | .elx-editable .elx-editable-column .cell > .el-date-editor, 39 | .elx-editable .elx-editable-column .cell > .el-select { 40 | width: 100%; 41 | } 42 | .elx-editable .elx-editable-row.elx_disabled, 43 | .elx-editable .elx-editable-column.elx_edit.elx_disabled { 44 | cursor: not-allowed; 45 | } 46 | .elx-editable .elx-editable-column.elx_edit.elx_active .cell { 47 | line-height: inherit; 48 | overflow: inherit; 49 | position: relative; 50 | } 51 | .elx-editable.editable_click .elx-editable-column.elx_edit, 52 | .elx-editable.editable_dblclick .elx-editable-column.elx_edit { 53 | -webkit-user-select: none; 54 | -moz-user-select: none; 55 | -ms-user-select: none; 56 | user-select: none; 57 | } 58 | .elx-editable .elx-editable-column.valid-error .el-input__inner, 59 | .elx-editable .elx-editable-column.valid-error .el-input__inner:focus, 60 | .elx-editable .elx-editable-column.valid-error .el-textarea__inner, 61 | .elx-editable .elx-editable-column.valid-error .el-textarea__inner:focus { 62 | border-color: #f56c6c; 63 | } 64 | /* default tip */ 65 | .elx-editable .elx-editable-column .editable-valid_error { 66 | display: none; 67 | text-align: left; 68 | } 69 | .elx-editable .elx-editable-column.valid-error .el-input__inner, 70 | .elx-editable .elx-editable-column.valid-error .el-input__inner:focus, 71 | .elx-editable .elx-editable-column.valid-error .el-textarea__inner, 72 | .elx-editable .elx-editable-column.valid-error .el-textarea__inner:focus { 73 | border-color: #f56c6c; 74 | } 75 | .elx-editable .editable-row:last-child .elx-editable-column.valid-error .editable-valid_error { 76 | top: auto; 77 | bottom: -moz-calc(100% + 10px); 78 | bottom: -webkit-calc(100% + 10px); 79 | bottom: calc(100% + 10px); 80 | } 81 | .elx-editable .editable-row:last-child .elx-editable-column.valid-error .editable-valid_error:before { 82 | top: auto; 83 | bottom: -12px; 84 | border-color: #f56c6c transparent transparent transparent; 85 | } 86 | .elx-editable .elx-editable-column.valid-error .editable-valid_error, 87 | .elx-editable .editable-row:first-child .elx-editable-column.valid-error .editable-valid_error { 88 | display: block; 89 | position: absolute; 90 | top: -moz-calc(100% + 10px); 91 | top: -webkit-calc(100% + 10px); 92 | top: calc(100% + 10px); 93 | left: 10px; 94 | bottom: auto; 95 | z-index: 2; 96 | min-width: 300px; 97 | } 98 | .elx-editable .elx-editable-column.valid-error .editable-valid_error>.valid-message, 99 | .elx-editable .editable-row:first-child .elx-editable-column.valid-error .editable-valid_error>.valid-message { 100 | display: inline-block; 101 | padding: 8px 10px; 102 | line-height: 1.5; 103 | color: #fff; 104 | background-color: #f56c6c; 105 | border-radius: 4px; 106 | font-size: 12px; 107 | word-wrap: break-word; 108 | } 109 | .elx-editable .elx-editable-column .editable-valid_error:before, 110 | .elx-editable .editable-row:first-child .elx-editable-column.valid-error .editable-valid_error:before { 111 | content: ""; 112 | position: absolute; 113 | top: -12px; 114 | left: 20px; 115 | bottom: auto; 116 | border: 6px solid; 117 | border-color: transparent transparent #f56c6c transparent; 118 | } 119 | .elx-editable .elx-editable-column .editable-valid_wrapper { 120 | display: block; 121 | width: 100%; 122 | height: 100%; 123 | top: 0; 124 | left: 0; 125 | position: absolute; 126 | z-index: -1; 127 | } 128 | -------------------------------------------------------------------------------- /examples/views/table/Base7.vue: -------------------------------------------------------------------------------- 1 | 58 | 59 | 119 | 120 | 129 | -------------------------------------------------------------------------------- /examples/views/table/Base5.vue: -------------------------------------------------------------------------------- 1 | 54 | 55 | 127 | 128 | 137 | -------------------------------------------------------------------------------- /examples/views/table/Base8.vue: -------------------------------------------------------------------------------- 1 | 68 | 69 | 126 | 127 | 132 | -------------------------------------------------------------------------------- /examples/mock/api/index.js: -------------------------------------------------------------------------------- 1 | import { DELETE, POST, GET } from 'xe-ajax-mock' 2 | import Helper from './helper' 3 | 4 | GET('/api/conf/region/list', require('./conf/region.json')) 5 | GET('/api/conf/sex/list', require('./conf/sex.json')) 6 | GET('/api/conf/columns/list', require('./conf/columns.json')) 7 | GET('/api/conf/languages/list', require('./conf/languages.json')) 8 | 9 | class RoleVO { 10 | constructor (data) { 11 | this.id = data.id 12 | this.name = data.name 13 | this.describe = data.describe 14 | this.createTime = data.createTime 15 | this.updateTime = data.updateTime 16 | } 17 | } 18 | const roleHelper = new Helper(require('./role/list.json'), RoleVO) 19 | DELETE('/api/role/delete/{id}', roleHelper.deleteByPathVariable()) 20 | POST('/api/role/add', roleHelper.insertByBody()) 21 | POST('/api/role/update', roleHelper.updateByBody()) 22 | POST('/api/role/save', roleHelper.saveListByBody()) 23 | GET('/api/role/list', roleHelper.findList({ max: 10 })) 24 | GET('/api/role/page/list/{pageSize}/{currentPage}', roleHelper.findPageList()) 25 | 26 | class UserVO { 27 | constructor (data) { 28 | this.id = data.id 29 | this.name = data.name 30 | this.password = data.password 31 | this.sex = data.sex 32 | this.role = data.role 33 | this.region = data.region 34 | this.email = data.email 35 | this.age = data.age 36 | this.rate = data.rate 37 | this.flag = data.flag 38 | this.phone = data.phone 39 | this.describe = data.describe 40 | this.describe2 = data.describe2 41 | this.describe3 = data.describe3 42 | this.attr1 = data.attr1 43 | this.attr2 = data.attr2 44 | this.attr3 = data.attr3 45 | this.attr4 = data.attr4 46 | this.attr5 = data.attr5 47 | this.attr6 = data.attr6 48 | this.createTime = data.createTime 49 | this.updateTime = data.updateTime 50 | } 51 | } 52 | const userHelper = new Helper(require('./user/list.json'), UserVO) 53 | DELETE('/api/user/delete/{id}', userHelper.deleteByPathVariable()) 54 | POST('/api/user/add', userHelper.insertByBody()) 55 | POST('/api/user/update', userHelper.updateByBody()) 56 | POST('/api/user/save', userHelper.saveListByBody()) 57 | GET('/api/user/list', userHelper.findList({ max: 10 })) 58 | GET('/api/user/page/list/{pageSize}/{currentPage}', userHelper.findPageList()) 59 | 60 | class FileVO { 61 | constructor (data) { 62 | this.id = data.id 63 | this.parentId = data.parentId 64 | this.name = data.name 65 | this.size = data.size 66 | this.type = data.type 67 | this.createTime = data.createTime 68 | this.updateTime = data.updateTime 69 | } 70 | } 71 | const fileHelper = new Helper(require('./file/list.json'), FileVO) 72 | DELETE('/api/file/delete/{id}', fileHelper.deleteByPathVariable()) 73 | POST('/api/file/add', fileHelper.insertByBody()) 74 | POST('/api/file/update', fileHelper.updateByBody()) 75 | POST('/api/file/save', fileHelper.saveTreeListByBody()) 76 | GET('/api/file/list', fileHelper.findList()) 77 | GET('/api/file/node/list', fileHelper.findTreeNodeList()) 78 | GET('/api/file/page/list/{pageSize}/{currentPage}', fileHelper.findPageList()) 79 | 80 | class ColumnVO { 81 | constructor (data) { 82 | this.id = data.id 83 | this.key = data.key 84 | this.name = data.name 85 | this.readonly = data.readonly 86 | this.required = data.required 87 | this.validator = data.validator 88 | this.validMsg = data.validMsg 89 | this.visible = data.visible 90 | this.width = data.width 91 | this.describe = data.describe 92 | this.createTime = data.createTime 93 | this.updateTime = data.updateTime 94 | } 95 | } 96 | const columnHelper = new Helper(require('./column/list.json'), ColumnVO) 97 | DELETE('/api/column/delete/{id}', columnHelper.deleteByPathVariable()) 98 | POST('/api/column/add', columnHelper.insertByBody()) 99 | POST('/api/column/update', columnHelper.updateByBody()) 100 | POST('/api/column/save', columnHelper.saveListByBody()) 101 | GET('/api/column/list', columnHelper.findList()) 102 | GET('/api/column/page/list/{pageSize}/{currentPage}', columnHelper.findPageList()) 103 | 104 | class i18nVO { 105 | constructor (data) { 106 | this.id = data.id 107 | this.key = data.key 108 | this.name = data.name 109 | this.language = data.language 110 | this.createTime = data.createTime 111 | this.updateTime = data.updateTime 112 | } 113 | } 114 | const i18nHelper = new Helper(require('./i18n/list.json'), i18nVO) 115 | DELETE('/api/i18n/delete/{id}', i18nHelper.deleteByPathVariable()) 116 | POST('/api/i18n/add', i18nHelper.insertByBody()) 117 | POST('/api/i18n/update', i18nHelper.updateByBody()) 118 | POST('/api/i18n/save', i18nHelper.saveListByBody()) 119 | GET('/api/i18n/list', i18nHelper.findList()) 120 | GET('/api/i18n/page/list/{pageSize}/{currentPage}', i18nHelper.findPageList({ sort: ['key'], order: 'asc' })) 121 | -------------------------------------------------------------------------------- /examples/views/editable/Click1.vue: -------------------------------------------------------------------------------- 1 | 33 | 34 | 129 | 130 | 139 | -------------------------------------------------------------------------------- /examples/views/editable/Dblclick1.vue: -------------------------------------------------------------------------------- 1 | 34 | 35 | 130 | 131 | 140 | -------------------------------------------------------------------------------- /examples/views/table/Base2.vue: -------------------------------------------------------------------------------- 1 | 70 | 71 | 128 | 129 | 138 | -------------------------------------------------------------------------------- /examples/views/table/Custom1.vue: -------------------------------------------------------------------------------- 1 | 70 | 71 | 146 | 147 | 159 | -------------------------------------------------------------------------------- /lib/index.css: -------------------------------------------------------------------------------- 1 | .el-tooltip__popper.elx-valid_tooltip{background:#f56c6c}.el-tooltip__popper.elx-valid_tooltip[x-placement^=top] .popper__arrow,.el-tooltip__popper.elx-valid_tooltip[x-placement^=top] .popper__arrow:after{border-top-color:#f56c6c}.el-tooltip__popper.elx-valid_tooltip[x-placement^=bottom] .popper__arrow,.el-tooltip__popper.elx-valid_tooltip[x-placement^=bottom] .popper__arrow:after{border-bottom-color:#f56c6c}.el-tooltip__popper.elx-valid_tooltip[x-placement^=left] .popper__arrow,.el-tooltip__popper.elx-valid_tooltip[x-placement^=left] .popper__arrow:after{border-left-color:#f56c6c}.el-tooltip__popper.elx-valid_tooltip[x-placement^=right] .popper__arrow,.el-tooltip__popper.elx-valid_tooltip[x-placement^=right] .popper__arrow:after{border-right-color:#f56c6c}.elx-contextmenu,.elx-contextmenu .ctx-menu_child-wrapper{position:absolute;top:0;left:0;z-index:88;font-size:12px;color:#000;background:#fff;border:1px solid #c6c6c6;-webkit-box-shadow:2px 2px 4px -2px #666;box-shadow:2px 2px 4px -2px #666;padding:0 1px;font-family:Source Sans Pro,Helvetica Neue,Arial,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.elx-contextmenu>.ctx-menu_wrapper,.elx-contextmenu>.ctx-menu_wrapper .ctx-menu_child-wrapper{margin:0;padding:0;list-style-type:none;border-bottom:1px solid #e8eaed}.elx-contextmenu>.ctx-menu_wrapper li{position:relative;margin:1px 0;border:1px solid transparent}.elx-contextmenu>.ctx-menu_wrapper li:last-child{border:0}.elx-contextmenu>.ctx-menu_wrapper li.active{color:#2b2b2b;background-color:#c5c5c5;border-color:#c5c5c5}.elx-contextmenu>.ctx-menu_wrapper li.disabled{color:#b1b1b1}.elx-contextmenu>.ctx-menu_wrapper li.disabled.active{border-color:#c0c1c2;background-color:#eee}.elx-contextmenu>.ctx-menu_wrapper li.disabled.active:hover{background-color:inherit}.elx-contextmenu>.ctx-menu_wrapper .ctx-menu_link{display:block;padding:0 30px;width:100px;line-height:26px}.elx-contextmenu>.ctx-menu_wrapper .ctx-prefix-icon,.elx-contextmenu>.ctx-menu_wrapper .ctx-suffix-icon{position:absolute;top:5px;margin-right:5px;font-size:16px}.elx-contextmenu>.ctx-menu_wrapper .ctx-prefix-icon{left:5px}.elx-contextmenu>.ctx-menu_wrapper .ctx-suffix-icon{right:5px}.elx-contextmenu>.ctx-menu_wrapper .content{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.elx-contextmenu .ctx-menu_child-wrapper{display:none;z-index:99;top:0;left:100%}.elx-contextmenu .ctx-menu_child-wrapper.show{display:block}.elx-editable .editable-required-icon:before{content:"*";color:#f56c6c}.elx-editable.el-table--mini .elx-editable-column{height:36px}.elx-editable.el-table--small .elx-editable-column{height:40px}.elx-editable.el-table--medium .elx-editable-column{height:44px}.elx-editable.el-table--large .elx-editable-column,.elx-editable .elx-editable-column{height:48px;padding:0}.elx-editable .elx-editable-column.elx_dirty{position:relative}.elx-editable .elx-editable-column.elx_dirty:before{content:"";top:-5px;left:-5px;position:absolute;border:5px solid;border-color:transparent #f56c6c transparent transparent;-webkit-transform:rotate(45deg);transform:rotate(45deg)}.elx-editable .elx-editable-column .cell>.edit-input,.elx-editable .elx-editable-column .cell>.el-autocomplete,.elx-editable .elx-editable-column .cell>.el-cascader,.elx-editable .elx-editable-column .cell>.el-date-editor,.elx-editable .elx-editable-column .cell>.el-input-number,.elx-editable .elx-editable-column .cell>.el-select{width:100%}.elx-editable .elx-editable-column.elx_edit.elx_disabled,.elx-editable .elx-editable-row.elx_disabled{cursor:not-allowed}.elx-editable .elx-editable-column.elx_edit.elx_active .cell{line-height:inherit;overflow:inherit;position:relative}.elx-editable.editable_click .elx-editable-column.elx_edit,.elx-editable.editable_dblclick .elx-editable-column.elx_edit{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.elx-editable .elx-editable-column .editable-valid_error{display:none;text-align:left}.elx-editable .elx-editable-column.valid-error .el-input__inner,.elx-editable .elx-editable-column.valid-error .el-input__inner:focus,.elx-editable .elx-editable-column.valid-error .el-textarea__inner,.elx-editable .elx-editable-column.valid-error .el-textarea__inner:focus{border-color:#f56c6c}.elx-editable .editable-row:last-child .elx-editable-column.valid-error .editable-valid_error{top:auto;bottom:calc(100% + 10px)}.elx-editable .editable-row:last-child .elx-editable-column.valid-error .editable-valid_error:before{top:auto;bottom:-12px;border-color:#f56c6c transparent transparent transparent}.elx-editable .editable-row:first-child .elx-editable-column.valid-error .editable-valid_error,.elx-editable .elx-editable-column.valid-error .editable-valid_error{display:block;position:absolute;top:calc(100% + 10px);left:10px;bottom:auto;z-index:2;min-width:300px}.elx-editable .editable-row:first-child .elx-editable-column.valid-error .editable-valid_error>.valid-message,.elx-editable .elx-editable-column.valid-error .editable-valid_error>.valid-message{display:inline-block;padding:8px 10px;line-height:1.5;color:#fff;background-color:#f56c6c;border-radius:4px;font-size:12px;word-wrap:break-word}.elx-editable .editable-row:first-child .elx-editable-column.valid-error .editable-valid_error:before,.elx-editable .elx-editable-column .editable-valid_error:before{content:"";position:absolute;top:-12px;left:20px;bottom:auto;border:6px solid;border-color:transparent transparent #f56c6c transparent}.elx-editable .elx-editable-column .editable-valid_wrapper{display:block;width:100%;height:100%;top:0;left:0;position:absolute;z-index:-1} -------------------------------------------------------------------------------- /examples/views/editable/BigScroll3.vue: -------------------------------------------------------------------------------- 1 | 45 | 46 | 137 | 138 | 147 | -------------------------------------------------------------------------------- /examples/views/editable/BigScroll2.vue: -------------------------------------------------------------------------------- 1 | 45 | 46 | 140 | 141 | 150 | -------------------------------------------------------------------------------- /examples/views/table/Custom3.vue: -------------------------------------------------------------------------------- 1 | 83 | 84 | 176 | 177 | 186 | -------------------------------------------------------------------------------- /examples/views/table/Custom2.vue: -------------------------------------------------------------------------------- 1 | 83 | 84 | 181 | 182 | 191 | -------------------------------------------------------------------------------- /examples/views/editable/BigScroll4.vue: -------------------------------------------------------------------------------- 1 | 51 | 52 | 196 | 197 | 206 | -------------------------------------------------------------------------------- /examples/views/editable/Excel2.vue: -------------------------------------------------------------------------------- 1 | 31 | 32 | 145 | 146 | 191 | -------------------------------------------------------------------------------- /examples/views/editable/Dblclick8.vue: -------------------------------------------------------------------------------- 1 | 36 | 37 | 204 | 205 | 214 | -------------------------------------------------------------------------------- /packages/editable/src/scroll.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 滚动渲染,以优化的方式渲染表格 3 | * 计算规则: 4 | * top --> Space 5 | * --> visibleStart 6 | * --> renderSize 7 | * --> offsetSize 8 | * table --> visibleIndex 9 | * --> offsetSize 10 | * bottom --> Space 11 | */ 12 | const ScrollHandle = { 13 | reload () { 14 | return function (isReload) { 15 | return this.$nextTick().then(() => { 16 | // 如果重新加载表格,索引重新初始化 17 | if (!isReload) { 18 | this.visibleIndex = 0 19 | this.visibleStart = 0 20 | this._computeScroll(isReload) 21 | } 22 | this.datas = this._fullData.slice(this.visibleStart, this.visibleStart + this.renderSize) 23 | return this.$nextTick().then(() => { 24 | this._computeScroll(isReload) 25 | // 如果重新加载表格,滚动位置重新初始化 26 | if (!isReload) { 27 | updateScroll(this.elemStore) 28 | } 29 | }) 30 | }) 31 | } 32 | }, 33 | bind () { 34 | return function () { 35 | // 绑定事件,生成对应元素 36 | return this.$nextTick().then(() => { 37 | let elTableElem = this.$refs.refElTable.$el 38 | let bodyWrapper = elTableElem.querySelector('.el-table__body-wrapper') 39 | let fixedLeftWrapper = elTableElem.querySelector('.el-table__fixed .el-table__fixed-body-wrapper') 40 | let fixedRightWrapper = elTableElem.querySelector('.el-table__fixed-right .el-table__fixed-body-wrapper') 41 | let elemStore = createScrollElem(elTableElem, bodyWrapper, '.el-table__header-wrapper', '.el-table__body') 42 | if (fixedLeftWrapper) { 43 | elemStore.fixedLeft = createScrollElem(elTableElem, fixedLeftWrapper, 0, 'table') 44 | } 45 | if (fixedRightWrapper) { 46 | elemStore.fixedRight = createScrollElem(elTableElem, fixedRightWrapper, 0, 'table') 47 | } 48 | bodyWrapper.addEventListener('scroll', this._scrollEvent, false) 49 | this.elemStore = elemStore 50 | }) 51 | } 52 | }, 53 | unbind () { 54 | return function () { 55 | // 事件解绑 56 | unbindSpace(this.elemStore) 57 | this.elemStore.bodyWrapper.removeEventListener('scroll', this._scrollEvent) 58 | } 59 | }, 60 | scroll () { 61 | return function (evnt) { 62 | let isRender 63 | let { bodyWrapper } = this.elemStore 64 | let fullData = this._fullData 65 | let len = fullData.length 66 | let renderSize = this.renderSize 67 | let scrollTop = bodyWrapper.scrollTop 68 | let isTop = scrollTop < this.scrollTop 69 | let visibleStart = this.visibleStart 70 | let visibleSize = this.visibleSize 71 | let offsetSize = this.offsetSize 72 | let rowHeight = this.rowHeight 73 | let toVisibleIndex = Math.ceil(scrollTop / rowHeight) 74 | if (isTop) { 75 | // 如果向上滚动 76 | if (visibleStart > 0 && (toVisibleIndex - offsetSize <= visibleStart)) { 77 | isRender = true 78 | } 79 | } else { 80 | // 如果向下滚动 81 | if (visibleStart < len - renderSize && (toVisibleIndex + visibleSize + offsetSize >= visibleStart + renderSize)) { 82 | isRender = true 83 | } 84 | } 85 | if (isRender) { 86 | // 超过阈值重新渲染 87 | let toVisibleStart = toVisibleIndex - Math.floor(isTop ? renderSize / 2 : visibleSize) 88 | if (toVisibleStart < 0) { 89 | toVisibleStart = 0 90 | } else if (toVisibleStart + renderSize >= len) { 91 | toVisibleStart = len - renderSize 92 | } 93 | if (toVisibleStart !== visibleStart) { 94 | this.visibleStart = toVisibleStart 95 | // 渲染指定位置的数据 96 | this.datas = fullData.slice(toVisibleStart, toVisibleStart + renderSize) 97 | // 重新计算顶部空间和底部空间,支撑滚动条 98 | this._setScrollSpace(toVisibleStart * rowHeight, (len - renderSize - toVisibleStart) * rowHeight) 99 | // 渲染完成复原最后位置,保持滚动位置不变 100 | this.$nextTick(() => { 101 | bodyWrapper.scrollTop = scrollTop 102 | }) 103 | } 104 | } 105 | this.scrollTop = scrollTop 106 | this.visibleIndex = toVisibleIndex 107 | } 108 | }, 109 | space () { 110 | return function (topHeight, bottomHeight) { 111 | let { topSpace, bottomSpace, fixedLeft, fixedRight } = this.elemStore 112 | topSpace.style.height = `${topHeight}px` 113 | bottomSpace.style.height = `${bottomHeight}px` 114 | if (fixedLeft) { 115 | fixedLeft.topSpace.style.height = `${topHeight}px` 116 | fixedLeft.bottomSpace.style.height = `${bottomHeight}px` 117 | } 118 | if (fixedRight) { 119 | fixedRight.topSpace.style.height = `${topHeight}px` 120 | fixedRight.bottomSpace.style.height = `${bottomHeight}px` 121 | } 122 | } 123 | }, 124 | compute (size) { 125 | return function (isReload) { 126 | if (this.scrollLoad) { 127 | let { headerWrapper, bodyWrapper, table } = this.elemStore 128 | let fullData = this._fullData 129 | let firstTrElem = table.querySelector('tbody>tr') 130 | if (!firstTrElem) { 131 | firstTrElem = headerWrapper.querySelector('thead>tr') 132 | } 133 | if (firstTrElem) { 134 | this.rowHeight = firstTrElem.clientHeight 135 | } 136 | let visibleSize = Math.ceil(bodyWrapper.clientHeight / this.rowHeight) 137 | let renderSize = getRenderSize(this.configs, this.elemStore, visibleSize, size) 138 | this.offsetSize = getOffsetSize(this.configs, renderSize, visibleSize) 139 | this.visibleSize = visibleSize 140 | this.renderSize = renderSize 141 | if (!isReload) { 142 | this._setScrollSpace(0, fullData.length > renderSize ? (fullData.length - renderSize) * this.rowHeight : 0) 143 | } 144 | } 145 | } 146 | } 147 | } 148 | 149 | function updateScroll ({ bodyWrapper, fixedLeft, fixedRight }, scrollTop, scrollLeft) { 150 | bodyWrapper.scrollTop = scrollTop 151 | bodyWrapper.scrollLeft = scrollLeft 152 | if (fixedLeft) { 153 | updateScroll(fixedLeft, scrollTop, scrollLeft) 154 | } 155 | if (fixedRight) { 156 | updateScroll(fixedRight, scrollTop, scrollLeft) 157 | } 158 | } 159 | 160 | function unbindSpace ({ bodyWrapper, topSpace, bottomSpace, fixedLeft, fixedRight }) { 161 | bodyWrapper.removeChild(topSpace) 162 | bodyWrapper.removeChild(bottomSpace) 163 | if (fixedLeft) { 164 | unbindSpace(fixedLeft) 165 | } 166 | if (fixedRight) { 167 | unbindSpace(fixedRight) 168 | } 169 | } 170 | 171 | function createScrollElem (elTableElem, bodyWrapper, queryHeaderWrapper, queryTable) { 172 | let table = bodyWrapper.querySelector(queryTable) 173 | let topSpace = document.createElement('div') 174 | let bottomSpace = document.createElement('div') 175 | topSpace.className = 'elx-scroll_top-space' 176 | bottomSpace.className = 'elx-scroll_bottom-space' 177 | bodyWrapper.insertBefore(topSpace, table) 178 | bodyWrapper.insertBefore(bottomSpace, table.nextSibling) 179 | return { 180 | headerWrapper: queryHeaderWrapper && elTableElem.querySelector(queryHeaderWrapper), 181 | bodyWrapper, 182 | table, 183 | topSpace, 184 | bottomSpace 185 | } 186 | } 187 | 188 | /** 189 | * renderSize 不应该低于 visibleSize 的3倍 190 | */ 191 | function getRenderSize (configs, elemStore, visibleSize, defSize) { 192 | if (configs.renderSize) { 193 | return configs.renderSize 194 | } 195 | if (elemStore.fixedLeft || elemStore.fixedRight) { 196 | return visibleSize * Math.max(Math.floor(defSize / 2), 3) 197 | } 198 | return visibleSize * defSize 199 | } 200 | 201 | /** 202 | * offsetSize 不应该低于 visibleSize 的一半 203 | */ 204 | function getOffsetSize (configs, renderSize, visibleSize) { 205 | let offsetSize = 0 206 | if (configs.offsetSize) { 207 | return configs.offsetSize 208 | } 209 | if (renderSize > visibleSize * 4) { 210 | offsetSize = visibleSize * 2 211 | } else if (renderSize > visibleSize * 3) { 212 | offsetSize = visibleSize 213 | } 214 | return Math.max(offsetSize, Math.ceil(visibleSize / 2)) 215 | } 216 | 217 | export default ScrollHandle 218 | -------------------------------------------------------------------------------- /examples/views/editable/Click11.vue: -------------------------------------------------------------------------------- 1 | 75 | 76 | 185 | 186 | 211 | -------------------------------------------------------------------------------- /examples/views/editable/Click8.vue: -------------------------------------------------------------------------------- 1 | 62 | 63 | 227 | 228 | 245 | -------------------------------------------------------------------------------- /examples/views/editable/BigScroll1.vue: -------------------------------------------------------------------------------- 1 | 49 | 50 | 215 | 216 | 225 | -------------------------------------------------------------------------------- /examples/views/editable/Manual1.vue: -------------------------------------------------------------------------------- 1 | 40 | 41 | 236 | 237 | 246 | -------------------------------------------------------------------------------- /examples/views/editable/Excel4.vue: -------------------------------------------------------------------------------- 1 | 50 | 51 | 190 | 191 | 240 | --------------------------------------------------------------------------------