diff --git a/.gitignore b/.gitignore index a5060b02..bb2a167e 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,4 @@ test/unit/coverage test/e2e/reports selenium-debug.log .idea +package-lock.json diff --git a/README-en.md b/README-en.md index 39d4c892..25459147 100644 --- a/README-en.md +++ b/README-en.md @@ -32,8 +32,9 @@ - ECharts - 401, 404 error page - Error log -- Exporting to Excel +- Export Excel - Upload Excel +- Export Zip - Table example - Interactive table example - Drag & drop table example @@ -47,6 +48,7 @@ - screenfull - markdown2html - views-tab +- clipboard ## Development diff --git a/README.md b/README.md index 34e78b92..0e0b96d3 100644 --- a/README.md +++ b/README.md @@ -63,6 +63,7 @@ - 401,404错误页面 - 错误日志 - 导出excel +- zip - 前端可视化excel - table example - 动态table example @@ -78,6 +79,7 @@ - screenfull - markdown2html - views-tab +- clipboard ## 开发 diff --git a/build/webpack.prod.conf.js b/build/webpack.prod.conf.js index 05f84874..d69d0e1d 100644 --- a/build/webpack.prod.conf.js +++ b/build/webpack.prod.conf.js @@ -95,6 +95,14 @@ var webpackConfig = merge(baseWebpackConfig, { return context && (context.indexOf('echarts') >= 0 || context.indexOf('zrender') >= 0); } }), + // split xlsx into its own file + new webpack.optimize.CommonsChunkPlugin({ + async: 'xlsx', + minChunks(module) { + var context = module.context; + return context && (context.indexOf('xlsx') >= 0); + } + }), // extract webpack runtime and module manifest to its own file in order to // prevent vendor hash from being updated whenever app bundle is updated new webpack.optimize.CommonsChunkPlugin({ diff --git a/package.json b/package.json index ae01ad50..d1498c15 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "juicy", - "version": "2.1.0", + "version": "2.2.0", "description": "A Vue.js admin", "author": "Pan ", "license": "MIT", @@ -14,9 +14,10 @@ }, "dependencies": { "axios": "0.16.2", + "clipboard": "1.7.1", "codemirror": "5.26.0", "dropzone": "5.1.0", - "echarts": "3.6.2", + "echarts": "3.7.2", "element-ui": "1.4.2", "file-saver": "1.3.3", "js-cookie": "2.1.4", @@ -35,7 +36,8 @@ "vue-splitpane": "^1.0.0", "vuedraggable": "2.14.1", "vuex": "2.3.1", - "xlsx": "^0.10.8" + "xlsx": "^0.10.8", + "jszip": "^3.1.4" }, "devDependencies": { "autoprefixer": "7.1.1", diff --git a/src/components/MDinput/index.vue b/src/components/MDinput/index.vue index 72658598..6d9d5e8e 100644 --- a/src/components/MDinput/index.vue +++ b/src/components/MDinput/index.vue @@ -1,297 +1,272 @@ diff --git a/src/directive/clipboard/clipboard.js b/src/directive/clipboard/clipboard.js new file mode 100644 index 00000000..49c9b391 --- /dev/null +++ b/src/directive/clipboard/clipboard.js @@ -0,0 +1,49 @@ +// Inspired by https://github.com/Inndy/vue-clipboard2 +const Clipboard = require('clipboard') +if (!Clipboard) { + throw new Error('you shold npm install `clipboard` --save at first ') +} + +export default { + bind(el, binding) { + if (binding.arg === 'success') { + el._v_clipboard_success = binding.value + } else if (binding.arg === 'error') { + el._v_clipboard_error = binding.value + } else { + const clipboard = new Clipboard(el, { + text() { return binding.value }, + action() { return binding.arg === 'cut' ? 'cut' : 'copy' } + }) + clipboard.on('success', e => { + const callback = el._v_clipboard_success + callback && callback(e) // eslint-disable-line + }) + clipboard.on('error', e => { + const callback = el._v_clipboard_error + callback && callback(e) // eslint-disable-line + }) + el._v_clipboard = clipboard + } + }, + update(el, binding) { + if (binding.arg === 'success') { + el._v_clipboard_success = binding.value + } else if (binding.arg === 'error') { + el._v_clipboard_error = binding.value + } else { + el._v_clipboard.text = function() { return binding.value } + el._v_clipboard.action = function() { return binding.arg === 'cut' ? 'cut' : 'copy' } + } + }, + unbind(el, binding) { + if (binding.arg === 'success') { + delete el._v_clipboard_success + } else if (binding.arg === 'error') { + delete el._v_clipboard_error + } else { + el._v_clipboard.destroy() + delete el._v_clipboard + } + } +} diff --git a/src/directive/clipboard/index.js b/src/directive/clipboard/index.js new file mode 100644 index 00000000..02c98163 --- /dev/null +++ b/src/directive/clipboard/index.js @@ -0,0 +1,13 @@ +import Clipboard from './clipboard' + +const install = function(Vue) { + Vue.directive('Clipboard', Clipboard) +} + +if (window.Vue) { + window.clipboard = Clipboard + Vue.use(install); // eslint-disable-line +} + +Clipboard.install = install +export default Clipboard diff --git a/src/directive/waves/index.js b/src/directive/waves/index.js new file mode 100644 index 00000000..65f9b308 --- /dev/null +++ b/src/directive/waves/index.js @@ -0,0 +1,13 @@ +import waves from './waves' + +const install = function(Vue) { + Vue.directive('waves', waves) +} + +if (window.Vue) { + window.waves = waves + Vue.use(install); // eslint-disable-line +} + +waves.install = install +export default waves diff --git a/src/directive/waves.css b/src/directive/waves/waves.css similarity index 100% rename from src/directive/waves.css rename to src/directive/waves/waves.css diff --git a/src/directive/waves.js b/src/directive/waves/waves.js similarity index 100% rename from src/directive/waves.js rename to src/directive/waves/waves.js diff --git a/src/icons/svg/tubiao.svg b/src/icons/svg/chart.svg similarity index 100% rename from src/icons/svg/tubiao.svg rename to src/icons/svg/chart.svg diff --git a/src/icons/svg/clipboard.svg b/src/icons/svg/clipboard.svg new file mode 100644 index 00000000..cf1c9b0c --- /dev/null +++ b/src/icons/svg/clipboard.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/icons/svg/component.svg b/src/icons/svg/component.svg new file mode 100644 index 00000000..a8008c84 --- /dev/null +++ b/src/icons/svg/component.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/icons/svg/tuozhuai.svg b/src/icons/svg/drag.svg similarity index 100% rename from src/icons/svg/tuozhuai.svg rename to src/icons/svg/drag.svg diff --git a/src/icons/svg/zonghe.svg b/src/icons/svg/example.svg similarity index 100% rename from src/icons/svg/zonghe.svg rename to src/icons/svg/example.svg diff --git a/src/icons/svg/EXCEL.svg b/src/icons/svg/excel.svg similarity index 100% rename from src/icons/svg/EXCEL.svg rename to src/icons/svg/excel.svg diff --git a/src/icons/svg/yanjing.svg b/src/icons/svg/eye.svg similarity index 100% rename from src/icons/svg/yanjing.svg rename to src/icons/svg/eye.svg diff --git a/src/icons/svg/form.svg b/src/icons/svg/form.svg new file mode 100644 index 00000000..79716f06 --- /dev/null +++ b/src/icons/svg/form.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/icons/svg/from.svg b/src/icons/svg/from.svg deleted file mode 100644 index 7a4bd166..00000000 --- a/src/icons/svg/from.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/icons/svg/icons.svg b/src/icons/svg/icon.svg similarity index 100% rename from src/icons/svg/icons.svg rename to src/icons/svg/icon.svg diff --git a/src/icons/svg/quanxian.svg b/src/icons/svg/lock.svg similarity index 100% rename from src/icons/svg/quanxian.svg rename to src/icons/svg/lock.svg diff --git a/src/icons/svg/mima.svg b/src/icons/svg/password.svg similarity index 100% rename from src/icons/svg/mima.svg rename to src/icons/svg/password.svg diff --git a/src/icons/svg/xinrenzhinan.svg b/src/icons/svg/people.svg similarity index 100% rename from src/icons/svg/xinrenzhinan.svg rename to src/icons/svg/people.svg diff --git a/src/icons/svg/QQ.svg b/src/icons/svg/qq.svg similarity index 100% rename from src/icons/svg/QQ.svg rename to src/icons/svg/qq.svg diff --git a/src/icons/svg/shouce.svg b/src/icons/svg/shouce.svg deleted file mode 100644 index 94c68bb6..00000000 --- a/src/icons/svg/shouce.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/icons/svg/wujiaoxing.svg b/src/icons/svg/star.svg similarity index 100% rename from src/icons/svg/wujiaoxing.svg rename to src/icons/svg/star.svg diff --git a/src/icons/svg/tab.svg b/src/icons/svg/tab.svg index 657057df..17aa088b 100644 --- a/src/icons/svg/tab.svg +++ b/src/icons/svg/tab.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/src/icons/svg/a.svg b/src/icons/svg/trendChart1.svg similarity index 100% rename from src/icons/svg/a.svg rename to src/icons/svg/trendChart1.svg diff --git a/src/icons/svg/b.svg b/src/icons/svg/trendChart2.svg similarity index 100% rename from src/icons/svg/b.svg rename to src/icons/svg/trendChart2.svg diff --git a/src/icons/svg/c.svg b/src/icons/svg/trendChart3.svg similarity index 100% rename from src/icons/svg/c.svg rename to src/icons/svg/trendChart3.svg diff --git a/src/icons/svg/yonghuming.svg b/src/icons/svg/user.svg similarity index 100% rename from src/icons/svg/yonghuming.svg rename to src/icons/svg/user.svg diff --git a/src/icons/svg/weixin.svg b/src/icons/svg/wechat.svg similarity index 100% rename from src/icons/svg/weixin.svg rename to src/icons/svg/wechat.svg diff --git a/src/icons/svg/zip.svg b/src/icons/svg/zip.svg new file mode 100644 index 00000000..e9a9d012 --- /dev/null +++ b/src/icons/svg/zip.svg @@ -0,0 +1 @@ + diff --git a/src/icons/svg/zujian.svg b/src/icons/svg/zujian.svg deleted file mode 100644 index d183e56c..00000000 --- a/src/icons/svg/zujian.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/router/index.js b/src/router/index.js index c235005a..a4cc7b9b 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -32,7 +32,7 @@ export const constantRouterMap = [ path: '/introduction', component: Layout, redirect: '/introduction/index', - icon: 'xinrenzhinan', + icon: 'people', noDropdown: true, children: [{ path: 'index', component: _import('introduction/index'), name: '简述' }] } @@ -50,7 +50,7 @@ export const asyncRouterMap = [ component: Layout, redirect: '/permission/index', name: '权限测试', - icon: 'quanxian', + icon: 'lock', meta: { role: ['admin'] }, noDropdown: true, children: [{ path: 'index', component: _import('permission/index'), name: '权限测试页', meta: { role: ['admin'] }}] @@ -58,7 +58,7 @@ export const asyncRouterMap = [ { path: '/icon', component: Layout, - icon: 'icons', + icon: 'icon', noDropdown: true, children: [{ path: 'index', component: _import('svg-icons/index'), name: 'icons' }] }, @@ -67,7 +67,7 @@ export const asyncRouterMap = [ component: Layout, redirect: '/components/index', name: '组件', - icon: 'zujian', + icon: 'component', children: [ { path: 'index', component: _import('components/index'), name: '介绍 ' }, { path: 'tinymce', component: _import('components/tinymce'), name: '富文本编辑器' }, @@ -88,7 +88,7 @@ export const asyncRouterMap = [ component: Layout, redirect: '/charts/index', name: '图表', - icon: 'tubiao', + icon: 'chart', children: [ { path: 'index', component: _import('charts/index'), name: '介绍' }, { path: 'keyboard', component: _import('charts/keyboard'), name: '键盘图表' }, @@ -102,7 +102,7 @@ export const asyncRouterMap = [ component: Layout, redirect: 'noredirect', name: '综合实例', - icon: 'zonghe', + icon: 'example', children: [ { path: '/example/table', @@ -117,8 +117,8 @@ export const asyncRouterMap = [ { path: 'table', component: _import('example/table/table'), name: '综合table' } ] }, - { path: 'form/edit', icon: 'shouce', component: _import('example/form'), name: '编辑Form', meta: { isEdit: true }}, - { path: 'form/create', icon: 'from', component: _import('example/form'), name: '创建Form' }, + { path: 'form/edit', icon: 'form', component: _import('example/form'), name: '编辑Form', meta: { isEdit: true }}, + { path: 'form/create', icon: 'form', component: _import('example/form'), name: '创建Form' }, { path: 'tab/index', icon: 'tab', component: _import('example/tab/index'), name: 'Tab' } ] }, @@ -147,13 +147,23 @@ export const asyncRouterMap = [ component: Layout, redirect: '/excel/download', name: 'excel', - icon: 'EXCEL', + icon: 'excel', children: [ - { path: 'download', component: _import('excel/index'), name: '导出excel' }, - { path: 'download2', component: _import('excel/selectExcel'), name: '导出已选择项' }, + { path: 'download', component: _import('excel/index'), name: 'export excel' }, + { path: 'download2', component: _import('excel/selectExcel'), name: 'export selected' }, { path: 'upload', component: _import('excel/uploadExcel'), name: 'upload excel' } ] }, + { + path: '/zip', + component: Layout, + redirect: '/zip/download', + name: 'zip', + icon: 'zip', + children: [ + { path: 'download', component: _import('zip/index'), name: 'export zip' } + ] + }, { path: '/theme', component: Layout, @@ -163,6 +173,14 @@ export const asyncRouterMap = [ noDropdown: true, children: [{ path: 'index', component: _import('theme/index'), name: '换肤' }] }, + { + path: '/clipboard', + component: Layout, + redirect: 'noredirect', + icon: 'clipboard', + noDropdown: true, + children: [{ path: 'index', component: _import('clipboard/index'), name: 'clipboard' }] + }, { path: '*', redirect: '/404', hidden: true } ] diff --git a/src/utils/clipboard.js b/src/utils/clipboard.js new file mode 100644 index 00000000..882b1251 --- /dev/null +++ b/src/utils/clipboard.js @@ -0,0 +1,36 @@ +import Clipboard from 'clipboard' +import Vue from 'vue' + +function clipboardSuccess() { + Vue.prototype.$message({ + message: '复制成功', + type: 'success', + duration: 1500 + }) +} + +function clipboardError() { + Vue.prototype.$message({ + message: '复制失败', + type: 'error' + }) +} + +export default function handleClipboard(text, event) { + const clipboard = new Clipboard(event.target, { + text: () => text + }) + clipboard.on('success', () => { + clipboardSuccess() + clipboard.off('error') + clipboard.off('success') + clipboard.destroy() + }) + clipboard.on('error', () => { + clipboardError() + clipboard.off('error') + clipboard.off('success') + clipboard.destroy() + }) + clipboard.onClick(event) +} diff --git a/src/vendor/Export2Excel.js b/src/vendor/Export2Excel.js index a121a33a..8852064e 100644 --- a/src/vendor/Export2Excel.js +++ b/src/vendor/Export2Excel.js @@ -1,7 +1,8 @@ /* eslint-disable */ require('script-loader!file-saver'); require('script-loader!vendor/Blob'); -require('script-loader!xlsx/dist/xlsx.core.min'); +import XLSX from 'xlsx' + function generateArray(table) { var out = []; var rows = table.querySelectorAll('tr'); @@ -93,7 +94,6 @@ function s2ab(s) { export function export_table_to_excel(id) { var theTable = document.getElementById(id); - console.log('a') var oo = generateArray(theTable); var ranges = oo[1]; @@ -117,9 +117,6 @@ export function export_table_to_excel(id) { saveAs(new Blob([s2ab(wbout)], {type: "application/octet-stream"}), "test.xlsx") } -function formatJson(jsonData) { - console.log(jsonData) -} export function export_json_to_excel(th, jsonData, defaultTitle) { /* original data */ diff --git a/src/vendor/Export2Zip.js b/src/vendor/Export2Zip.js new file mode 100644 index 00000000..4fab8aca --- /dev/null +++ b/src/vendor/Export2Zip.js @@ -0,0 +1,22 @@ +/* eslint-disable */ +require('script-loader!file-saver'); +import JSZip from 'jszip' + +export function export_txt_to_zip(th, jsonData, txtName, zipName) { + const zip = new JSZip() + const txt_name = txtName || '文本' + const zip_name = zipName || '压缩包' + const data = jsonData + let txtData = `${th}\r\n` + data.forEach((row) => { + let tempStr = '' + tempStr = row.toString() + txtData += `${tempStr}\r\n` + }) + zip.file(`${txt_name}.txt`, txtData) + zip.generateAsync({type:"blob"}).then(function (blob) { + saveAs(blob, `${zip_name}.zip`) + }, function (err) { + alert('导出失败') + }) +} diff --git a/src/views/clipboard/index.vue b/src/views/clipboard/index.vue new file mode 100644 index 00000000..b0281abc --- /dev/null +++ b/src/views/clipboard/index.vue @@ -0,0 +1,44 @@ + + + + diff --git a/src/views/components/mixin.vue b/src/views/components/mixin.vue index f78ab1e0..5f450c91 100644 --- a/src/views/components/mixin.vue +++ b/src/views/components/mixin.vue @@ -1,9 +1,11 @@ diff --git a/src/views/zip/index.vue b/src/views/zip/index.vue new file mode 100644 index 00000000..fa32a983 --- /dev/null +++ b/src/views/zip/index.vue @@ -0,0 +1,74 @@ + + +