Merge branch '2.0' into 2.0

This commit is contained in:
SuGod 2018-06-19 13:02:55 +08:00 committed by GitHub
commit 0b4d60744e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
74 changed files with 2604 additions and 134 deletions

1
.eslintignore Normal file
View File

@ -0,0 +1 @@
routers.js

View File

@ -9,6 +9,7 @@ module.exports = {
'generator-star-spacing': 'off',
// allow debugger during development
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
'vue/no-parsing-error': [2, { 'x-invalid-end-tag': false }]
'vue/no-parsing-error': [2, { 'x-invalid-end-tag': false }],
'no-undef': 'off'
}
}

2
.gitignore vendored
View File

@ -22,3 +22,5 @@ yarn-error.log*
*.njsproj
*.sln
*.sw*
build/env.js

70
package-lock.json generated
View File

@ -3402,6 +3402,19 @@
"integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=",
"dev": true
},
"codemirror": {
"version": "5.38.0",
"resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.38.0.tgz",
"integrity": "sha512-PEPnDg8U3DTGFB/Dn2T/INiRNC9CB5k2vLAQJidYCsHvAgtXbklqnuidEwx7yGrMrdGhl0L0P3iNKW9I07J6tQ=="
},
"codemirror-spell-checker": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/codemirror-spell-checker/-/codemirror-spell-checker-1.1.2.tgz",
"integrity": "sha1-HGYPkIlIPMtRE7m6nKGcP0mTNx4=",
"requires": {
"typo-js": "1.0.3"
}
},
"collection-visit": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz",
@ -8177,24 +8190,29 @@
"dev": true
},
"iview": {
"version": "2.14.0-rc.2",
"resolved": "https://registry.npmjs.org/iview/-/iview-2.14.0-rc.2.tgz",
"integrity": "sha512-uednXFP3yH+ow8UDJTMbq6S+5fIxkjXh5Jap2W9zp2JvmunwMmntQmF7R44ZYjq9puB5yZJVYJudNlOTHQT1lw==",
"version": "2.14.1",
"resolved": "https://registry.npmjs.org/iview/-/iview-2.14.1.tgz",
"integrity": "sha512-0ykE0nN+uFSpIBqJAwUPotfamajxkYIk5U/Z/SzzQdzl1++byDF/+HDL0w/ShEvHV4h1zJJP6D9jyyK+Qu08Xg==",
"requires": {
"async-validator": "1.8.2",
"deepmerge": "2.1.0",
"deepmerge": "2.1.1",
"element-resize-detector": "1.1.14",
"js-calendar": "1.2.3",
"lodash.throttle": "4.1.1",
"popper.js": "1.14.3",
"tinycolor2": "1.4.1",
"v-click-outside-x": "2.4.0"
"v-click-outside-x": "3.0.0"
},
"dependencies": {
"deepmerge": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-2.1.0.tgz",
"integrity": "sha512-Q89Z26KAfA3lpPGhbF6XMfYAm3jIV3avViy6KOJ2JLzFbeWHOvPQUu5aSJIWXap3gDZC2y1eF5HXEPI2wGqgvw=="
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-2.1.1.tgz",
"integrity": "sha512-urQxA1smbLZ2cBbXbaYObM1dJ82aJ2H57A1C/Kklfh/ZN1bgH4G/n5KWhdNfOK11W98gqZfyYj7W4frJJRwA2w=="
},
"v-click-outside-x": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/v-click-outside-x/-/v-click-outside-x-3.0.0.tgz",
"integrity": "sha512-VKm35tQ1tlZFXZc527v05sRXbyoQ8KKT1aeefZrcRCW+mPU4KuTiPy4pe1AH8Pibjzx80iU3pNJ4gNUFSXekUQ=="
}
}
},
@ -9381,6 +9399,11 @@
"object-visit": "1.0.1"
}
},
"marked": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/marked/-/marked-0.4.0.tgz",
"integrity": "sha512-tMsdNBgOsrUophCAFQl0XPe6Zqk/uy9gnue+jIIKhykO51hxyu6uNx7zBPy0+y/WKYVZZMspV9YeXLNdKk+iYw=="
},
"math-expression-evaluator": {
"version": "1.2.17",
"resolved": "https://registry.npmjs.org/math-expression-evaluator/-/math-expression-evaluator-1.2.17.tgz",
@ -13476,6 +13499,16 @@
"integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=",
"dev": true
},
"simplemde": {
"version": "1.11.2",
"resolved": "https://registry.npmjs.org/simplemde/-/simplemde-1.11.2.tgz",
"integrity": "sha1-ojo12XjSxA7wfewAjJLwcNjggOM=",
"requires": {
"codemirror": "5.38.0",
"codemirror-spell-checker": "1.1.2",
"marked": "0.4.0"
}
},
"slash": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz",
@ -14428,6 +14461,11 @@
"integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=",
"dev": true
},
"typo-js": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/typo-js/-/typo-js-1.0.3.tgz",
"integrity": "sha1-VNjrx5SfGngQkItgAsaEFSbJnVo="
},
"uglify-js": {
"version": "3.3.25",
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.3.25.tgz",
@ -14800,11 +14838,6 @@
"integrity": "sha512-jZnMwlb9Iku/O3smGWvZhauCf6cvvpKi4BKRiliS3cxnI+Gz9j5MEpTz2UFuXiKPJocb7gnsLHwiS05ige5BEA==",
"dev": true
},
"v-click-outside-x": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/v-click-outside-x/-/v-click-outside-x-2.4.0.tgz",
"integrity": "sha512-xAouyFRaMDD074px+J3PoxhU5nGQsIj8yxXRYyFd0/PRhY1ob3F55L9mGsd35KzXkQteajEhap6SClaMB0MENg=="
},
"validate-npm-package-license": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.3.tgz",
@ -14881,9 +14914,9 @@
"dev": true
},
"vue-i18n": {
"version": "7.6.0",
"resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-7.6.0.tgz",
"integrity": "sha512-IqyGj4nOFrGopCCpRucfMPJSgp5WauuI8HTaAQc7XIpHT7iOH4flndy1g09FiUzidi0lRVKJME+S7sPJny/t/A=="
"version": "7.8.0",
"resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-7.8.0.tgz",
"integrity": "sha512-06lw8q6+hWhcCq1O5hw7pZ0CYKcY8CFV4iUjKctz5VRXTVvYC/soEfEQyWrQ3qjYHNCtePv/YieArm3pF70JVg=="
},
"vue-loader": {
"version": "15.0.11",
@ -14943,6 +14976,11 @@
"browser-process-hrtime": "0.1.2"
}
},
"wangeditor": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/wangeditor/-/wangeditor-3.1.1.tgz",
"integrity": "sha1-+9PB1JdpI8nt67hbKdMLNVEq0Dk="
},
"watchpack": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.6.0.tgz",

View File

@ -13,19 +13,22 @@
"dependencies": {
"axios": "^0.18.0",
"clipboard": "^2.0.0",
"codemirror": "^5.38.0",
"countup": "^1.8.2",
"cropperjs": "^1.3.5",
"echarts": "^4.0.4",
"html2canvas": "^1.0.0-alpha.12",
"iview": "^2.14.0-rc.1",
"iview": "^2.14.1",
"iview-area": "^1.5.17",
"js-cookie": "^2.2.0",
"simplemde": "^1.11.2",
"sortablejs": "^1.7.0",
"tinymce": "^4.7.11",
"vue": "^2.5.10",
"vue-i18n": "^7.6.0",
"vue-i18n": "^7.8.0",
"vue-router": "^3.0.1",
"vuex": "^3.0.1"
"vuex": "^3.0.1",
"wangeditor": "^3.1.1"
},
"devDependencies": {
"@vue/cli-plugin-babel": "^3.0.0-beta.10",

8
src/api/data.js Normal file
View File

@ -0,0 +1,8 @@
import axios from '@/libs/api.request'
export const getTableData = () => {
return axios.request({
url: 'get_table_data',
method: 'get'
})
}

View File

@ -0,0 +1,31 @@
@font-face {font-family: "iconfont";
src: url('iconfont.eot?t=1528361751996'); /* IE9*/
src: url('iconfont.eot?t=1528361751996#iefix') format('embedded-opentype'), /* IE6-IE8 */
url('data:application/x-font-woff;charset=utf-8;base64,d09GRgABAAAAAAgQAAsAAAAADXAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABHU1VCAAABCAAAADMAAABCsP6z7U9TLzIAAAE8AAAARAAAAFZW7knUY21hcAAAAYAAAACXAAACCuRxddRnbHlmAAACGAAAA7cAAAaA6QCNLmhlYWQAAAXQAAAAMQAAADYRnGwraGhlYQAABgQAAAAgAAAAJAfdA4pobXR4AAAGJAAAABkAAAAkI+n//2xvY2EAAAZAAAAAFAAAABQGrAiKbWF4cAAABlQAAAAfAAAAIAEaAG5uYW1lAAAGdAAAAUUAAAJtPlT+fXBvc3QAAAe8AAAAUwAAAGh5QjWjeJxjYGRgYOBikGPQYWB0cfMJYeBgYGGAAJAMY05meiJQDMoDyrGAaQ4gZoOIAgCKIwNPAHicY2Bk/ss4gYGVgYOpk+kMAwNDP4RmfM1gxMjBwMDEwMrMgBUEpLmmMDgwVDx/ytzwv4EhhrmBoQEozAiSAwA2Dg1XeJzFkTsKwzAQRJ/8iwkpQs6Rg6VM6SoYfAVXuY5vYrD3GM5I60YkdTziCTRiV2IWqIFS3EUF4U0gapQbkl9yTn7FQ+cbVzkF3Yq9rLfBJptt2Tbd/fJyBdXnK3qFetc06nuildF81f1N4binc13S/txPSoVuR19ccZScEnfi9Kx34lRtcJQrNjlx0jY7yhpbHNoPwhozgQB4nLVUz2/cRBSeZ3s8zjbrxb+T3fVudjdrp93G2dreH2pCkoamSRGgVEkktkqQIlR1VQlOSO0BVeaAGgECJC6IA1SAhFT+hFalF5Qb4lAJKRwqVP4AjhyyDs+7CSRFcAjCGr/3PPP8/H3vGw+hhOz/wj/gR4hGJsg5cpGsEAJiDcoyZ0PJDT2uBkaJGpYu827FLbFK2eOfB6ss6qbfDB1LZGIGZChAUPKbrse50AhnuWnwTRtgNJddVat5lf8YUiNu4d34Re5LMIqVfGZ2Mr58dk73xzTp5rCqjqrqB5JIqcRxQkaGNyxziA6lxPhrmskaD4qnuSIMj7rZlzrpsZy6tR2+aVetIYAoAi03Jn8zp2QVHG9nTU0dZc+lpZFsujKuw81fT41ow7bzlOBF9/f3PxcI/zqpkiZZIq+SLeSqM9PQZa7scY2w5STYA9+0ZqFvAr/AgT/LYRuwIfq/PVDf1MWyEzYF4nkr799/fP+9K56Xn5ofry1b3rXuNW9kqVadm8rv/ZBSZMZkRVPTkpRWYbqxeWli4tLmjYGD6yk1zVha1RRZkmQl/mmi3V5pt7lvl4JOd+ve7eXl2/e2up1gyVkI8tWiomlKsZoPFuJpo1pv1ccNYxxd1Ygf5yZnFmcmcwcOzh5fH9NtTbN1wuEeiLAvETlFjER9wkxiNUnLIXzA3ECptKwKR3bjJ5RCaXcXSpTGTz6bj6L55OYjfNo9stpbRFmiKEp6zrD2I/4RP4/xEEnhLiuSNnkZv1IpQLJ/yiL9M+JlYAXAtrc80HBflV0PXCds4UTTt3DF1JlYxSTdCpqtvkQmRg0PYKUOuYkcDvjoMHrnYY/S3sO+vRwuLH66+ML1vG3nu3+F8fcLHSrVGQiNVxoCsLpEO/Cznryd0wcOrhzWQNubDezjFQbhd9z6BQYgOr7viADswvo/c7/4H7i7JZEZplVqthrOCSl/Ff+ISBkgaTgJ1S+wS0mBOvsftHUrIpOh7LgORsYgPTwp0eOyLqDSJ5L2qKpconP/f/md3+YlPEfOJWz7ogTMg8rgKMBjoRE6biuZRalCB5ArUkFCMvA39igAvbvjdOrnu6u12mr3LXQFewdom4ISl67e4rhbV/uWl/qTO3bhIA3d+XrH2bmb1NhLK72Zw1S0qEOC7Tf+E14jp0kDsblOpn+LRfg7Psucg+YUCnAM34dnRHFjTaQ1QVjffhbknTVRPCMIaxtCLz6Ck3uK2ZvrgoDvrt15Fu32wcKG2HvtONw/AMgISb8AeJxjYGRgYADiXSzz7sXz23xl4GZhAIHr9iLiMPr///8NLAzMDUAuBwMTSBQAJlsKewAAAHicY2BkYGBu+N/AEMPC8P8/AwMLAwNQBAVwAgB14ARweJxjYWBgYH7JwMDC8P8/CwOIxsQAUdgDCwAAAAAAAAAAdgECATABrgIeApoC6ANAeJxjYGRgYOBkSGJgZwABJiDmAkIGhv9gPgMAE3ABiQB4nGWPTU7DMBCFX/oHpBKqqGCH5AViASj9EatuWFRq911036ZOmyqJI8et1ANwHo7ACTgC3IA78EgnmzaWx9+8eWNPANzgBx6O3y33kT1cMjtyDRe4F65TfxBukF+Em2jjVbhF/U3YxzOmwm10YXmD17hi9oR3YQ8dfAjXcI1P4Tr1L+EG+Vu4iTv8CrfQ8erCPuZeV7iNRy/2x1YvnF6p5UHFockikzm/gple75KFrdLqnGtbxCZTg6BfSVOdaVvdU+zXQ+ciFVmTqgmrOkmMyq3Z6tAFG+fyUa8XiR6EJuVYY/62xgKOcQWFJQ6MMUIYZIjK6Og7VWb0r7FDwl57Vj3N53RbFNT/c4UBAvTPXFO6stJ5Ok+BPV8bUnV0K27LnpQ0kV7NSRKyQl7WtlRC6gE2ZVeOEXpc0Yk/KGdI/wAJWm7IAAAAeJxtxE0KgCAQBtD5UivddJIOZTGR4A+MkdHpC9r2Fo86+jj6Z9FBQcOgx4ARFo5w6YW9TMI13DyfLEdYfTSblJZV4t3UFCKr5LNp5Z3oAd4+EYcA') format('woff'),
url('iconfont.ttf?t=1528361751996') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+*/
url('iconfont.svg?t=1528361751996#iconfont') format('svg'); /* iOS 4.1- */
}
.iconfont {
font-family:"iconfont" !important;
font-size:16px;
font-style:normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.icon-bear:before { content: "\e600"; }
.icon-resize-vertical:before { content: "\e7c3"; }
.icon-frown:before { content: "\e77e"; }
.icon-meh:before { content: "\e780"; }
.icon-smile:before { content: "\e783"; }
.icon-man:before { content: "\e7e2"; }
.icon-woman:before { content: "\e7e5"; }

Binary file not shown.

View File

@ -0,0 +1,54 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
<!--
2013-9-30: Created.
-->
<svg>
<metadata>
Created by iconfont
</metadata>
<defs>
<font id="iconfont" horiz-adv-x="1024" >
<font-face
font-family="iconfont"
font-weight="500"
font-stretch="normal"
units-per-em="1024"
ascent="896"
descent="-128"
/>
<missing-glyph />
<glyph glyph-name="x" unicode="x" horiz-adv-x="1001"
d="M281 543q-27 -1 -53 -1h-83q-18 0 -36.5 -6t-32.5 -18.5t-23 -32t-9 -45.5v-76h912v41q0 16 -0.5 30t-0.5 18q0 13 -5 29t-17 29.5t-31.5 22.5t-49.5 9h-133v-97h-438v97zM955 310v-52q0 -23 0.5 -52t0.5 -58t-10.5 -47.5t-26 -30t-33 -16t-31.5 -4.5q-14 -1 -29.5 -0.5
t-29.5 0.5h-32l-45 128h-439l-44 -128h-29h-34q-20 0 -45 1q-25 0 -41 9.5t-25.5 23t-13.5 29.5t-4 30v167h911zM163 247q-12 0 -21 -8.5t-9 -21.5t9 -21.5t21 -8.5q13 0 22 8.5t9 21.5t-9 21.5t-22 8.5zM316 123q-8 -26 -14 -48q-5 -19 -10.5 -37t-7.5 -25t-3 -15t1 -14.5
t9.5 -10.5t21.5 -4h37h67h81h80h64h36q23 0 34 12t2 38q-5 13 -9.5 30.5t-9.5 34.5q-5 19 -11 39h-368zM336 498v228q0 11 2.5 23t10 21.5t20.5 15.5t34 6h188q31 0 51.5 -14.5t20.5 -52.5v-227h-327z" />
<glyph glyph-name="bear" unicode="&#58880;" d="M1024 683.008q0-70.656-46.08-121.856 46.08-89.088 46.08-193.536 0-96.256-39.936-181.248t-109.568-147.968-162.816-99.328-199.68-36.352-199.68 36.352-162.304 99.328-109.568 147.968-40.448 181.248q0 104.448 46.08 193.536-46.08 51.2-46.08 121.856 0 37.888 13.824 71.168t37.376 58.368 55.808 39.424 68.096 14.336q43.008 0 78.848-18.432t59.392-50.176q46.08 17.408 96.256 26.624t102.4 9.216 102.4-9.216 96.256-26.624q24.576 31.744 59.904 50.176t78.336 18.432q36.864 0 68.608-14.336t55.296-39.424 37.376-58.368 13.824-71.168zM205.824 268.288q10.24 0 18.944 10.24t15.36 28.672 10.24 42.496 3.584 51.712-3.584 51.712-10.24 41.984-15.36 28.16-18.944 10.24q-9.216 0-17.92-10.24t-15.36-28.16-10.752-41.984-4.096-51.712 4.096-51.712 10.752-42.496 15.36-28.672 17.92-10.24zM512-31.744000000000028q53.248 0 99.84 13.312t81.408 35.84 54.784 52.736 19.968 65.024q0 33.792-19.968 64t-54.784 52.736-81.408 35.84-99.84 13.312-99.84-13.312-81.408-35.84-54.784-52.736-19.968-64q0-34.816 19.968-65.024t54.784-52.736 81.408-35.84 99.84-13.312zM818.176 268.288q10.24 0 18.944 10.24t15.36 28.672 10.24 42.496 3.584 51.712-3.584 51.712-10.24 41.984-15.36 28.16-18.944 10.24q-9.216 0-17.92-10.24t-15.36-28.16-10.752-41.984-4.096-51.712 4.096-51.712 10.752-42.496 15.36-28.672 17.92-10.24zM512 235.51999999999998q39.936 0 68.096-9.728t28.16-24.064-28.16-24.064-68.096-9.728-68.096 9.728-28.16 24.064 28.16 24.064 68.096 9.728z" horiz-adv-x="1024" />
<glyph glyph-name="resize-vertical" unicode="&#59331;" d="M512 896C229.248 896 0 666.752 0 384s229.248-512 512-512 512 229.248 512 512S794.752 896 512 896zM576 192l64 0-128-128-128 128 64 0L448 576l-64 0 128 128 128-128-64 0L576 192z" horiz-adv-x="1024" />
<glyph glyph-name="frown" unicode="&#59262;" d="M336 475m-48 0a48 48 0 1 1 96 0 48 48 0 1 1-96 0ZM688 475m-48 0a48 48 0 1 1 96 0 48 48 0 1 1-96 0ZM512 832C264.6 832 64 631.4 64 384s200.6-448 448-448 448 200.6 448 448S759.4 832 512 832z m263-711c-34.2-34.2-74-61-118.3-79.8C611 21.8 562.3 12 512 12c-50.3 0-99 9.8-144.8 29.2-44.3 18.7-84.1 45.6-118.3 79.8-34.2 34.2-61 74-79.8 118.3C149.8 285 140 333.7 140 384s9.8 99 29.2 144.8c18.7 44.3 45.6 84.1 79.8 118.3 34.2 34.2 74 61 118.3 79.8C413 746.2 461.7 756 512 756c50.3 0 99-9.8 144.8-29.2 44.3-18.7 84.1-45.6 118.3-79.8 34.2-34.2 61-74 79.8-118.3C874.2 483 884 434.3 884 384s-9.8-99-29.2-144.8c-18.7-44.3-45.6-84.1-79.8-118.2zM512 363c-85.5 0-155.6-67.3-160-151.6-0.2-4.6 3.4-8.4 8-8.4h48.1c4.2 0 7.8 3.2 8.1 7.4C420 259.9 461.5 299 512 299s92.1-39.1 95.8-88.6c0.3-4.2 3.9-7.4 8.1-7.4H664c4.6 0 8.2 3.8 8 8.4-4.4 84.3-74.5 151.6-160 151.6z" horiz-adv-x="1024" />
<glyph glyph-name="meh" unicode="&#59264;" d="M336 475m-48 0a48 48 0 1 1 96 0 48 48 0 1 1-96 0ZM688 475m-48 0a48 48 0 1 1 96 0 48 48 0 1 1-96 0ZM512 832C264.6 832 64 631.4 64 384s200.6-448 448-448 448 200.6 448 448S759.4 832 512 832z m263-711c-34.2-34.2-74-61-118.3-79.8C611 21.8 562.3 12 512 12c-50.3 0-99 9.8-144.8 29.2-44.3 18.7-84.1 45.6-118.3 79.8-34.2 34.2-61 74-79.8 118.3C149.8 285 140 333.7 140 384s9.8 99 29.2 144.8c18.7 44.3 45.6 84.1 79.8 118.3 34.2 34.2 74 61 118.3 79.8C413 746.2 461.7 756 512 756c50.3 0 99-9.8 144.8-29.2 44.3-18.7 84.1-45.6 118.3-79.8 34.2-34.2 61-74 79.8-118.3C874.2 483 884 434.3 884 384s-9.8-99-29.2-144.8c-18.7-44.3-45.6-84.1-79.8-118.2zM664 331H360c-4.4 0-8-3.6-8-8v-48c0-4.4 3.6-8 8-8h304c4.4 0 8 3.6 8 8v48c0 4.4-3.6 8-8 8z" horiz-adv-x="1024" />
<glyph glyph-name="smile" unicode="&#59267;" d="M336 475m-48 0a48 48 0 1 1 96 0 48 48 0 1 1-96 0ZM688 475m-48 0a48 48 0 1 1 96 0 48 48 0 1 1-96 0ZM512 832C264.6 832 64 631.4 64 384s200.6-448 448-448 448 200.6 448 448S759.4 832 512 832z m263-711c-34.2-34.2-74-61-118.3-79.8C611 21.8 562.3 12 512 12c-50.3 0-99 9.8-144.8 29.2-44.3 18.7-84.1 45.6-118.3 79.8-34.2 34.2-61 74-79.8 118.3C149.8 285 140 333.7 140 384s9.8 99 29.2 144.8c18.7 44.3 45.6 84.1 79.8 118.3 34.2 34.2 74 61 118.3 79.8C413 746.2 461.7 756 512 756c50.3 0 99-9.8 144.8-29.2 44.3-18.7 84.1-45.6 118.3-79.8 34.2-34.2 61-74 79.8-118.3C874.2 483 884 434.3 884 384s-9.8-99-29.2-144.8c-18.7-44.3-45.6-84.1-79.8-118.2zM664 363h-48.1c-4.2 0-7.8-3.2-8.1-7.4C604 306.1 562.5 267 512 267s-92.1 39.1-95.8 88.6c-0.3 4.2-3.9 7.4-8.1 7.4H360c-4.6 0-8.2-3.8-8-8.4 4.4-84.3 74.5-151.6 160-151.6s155.6 67.3 160 151.6c0.2 4.6-3.4 8.4-8 8.4z" horiz-adv-x="1024" />
<glyph glyph-name="man" unicode="&#59362;" d="M874 776H622c-3.3 0-6-2.7-6-6v-56c0-3.3 2.7-6 6-6h160.4L583.1 508.7c-50 38.5-111 59.3-175.1 59.3-76.9 0-149.3-30-203.6-84.4S120 356.9 120 280s30-149.3 84.4-203.6C258.7 22 331.1-8 408-8s149.3 30 203.6 84.4C666 130.7 696 203.1 696 280c0 64.1-20.8 124.9-59.2 174.9L836 654.1V494c0-3.3 2.7-6 6-6h56c3.3 0 6 2.7 6 6V746c0 16.5-13.5 30-30 30zM408 68c-116.9 0-212 95.1-212 212s95.1 212 212 212 212-95.1 212-212-95.1-212-212-212z" horiz-adv-x="1024" />
<glyph glyph-name="woman" unicode="&#59365;" d="M909.7 739.4l-42.2 42.2c-3.1 3.1-8.2 3.1-11.3 0L764 689.4l-84.2 84.2c-3.1 3.1-8.2 3.1-11.3 0l-42.1-42.1c-3.1-3.1-3.1-8.1 0-11.3l84.2-84.2-135.5-135.3c-50 38.5-111 59.3-175.1 59.3-76.9 0-149.3-30-203.6-84.4S112 348.9 112 272s30-149.3 84.4-203.6C250.7 14 323.1-16 400-16s149.3 30 203.6 84.4C658 122.7 688 195.1 688 272c0 64.2-20.9 125.1-59.3 175.1l135.4 135.4 84.2-84.2c3.1-3.1 8.2-3.1 11.3 0l42.1 42.1c3.1 3.1 3.1 8.1 0 11.3l-84.2 84.2 92.2 92.2c3.1 3.1 3.1 8.2 0 11.3zM400 60c-116.9 0-212 95.1-212 212s95.1 212 212 212 212-95.1 212-212-95.1-212-212-212z" horiz-adv-x="1024" />
</font>
</defs></svg>

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,42 @@
<template>
<component :is="iconType" :type="iconName" :color="iconColor" :size="iconSize"/>
</template>
<script>
import Icons from '_c/icons'
export default {
name: 'CommonIcon',
components: {Icons},
props: {
type: {
type: String,
required: true
},
color: String,
size: Number
},
computed: {
iconType () {
return this.type.indexOf('_') === 0 ? 'Icons' : 'Icon'
},
iconName () {
return this.iconType === 'Icons' ? this.getCustomIconName(this.type) : this.type
},
iconSize () {
return this.size || (this.iconType === 'Icons' ? 12 : undefined)
},
iconColor () {
return this.color || ''
}
},
methods: {
getCustomIconName (iconName) {
return iconName.slice(1)
}
}
}
</script>
<style>
</style>

View File

@ -0,0 +1,2 @@
import CommonIcon from './common-icon.vue'
export default CommonIcon

View File

@ -0,0 +1,174 @@
<template>
<div class="count-to-wrapper">
<slot name="left"/>
<p class="content-outer"><span :class="['count-to-count-text', countClass]" :id="counterId">{{ init }}</span><i :class="['count-to-unit-text', unitClass]">{{ unitText }}</i></p>
<slot name="right"/>
</div>
</template>
<script>
import CountUp from 'countup'
import './index.less'
export default {
name: 'CountTo',
props: {
init: {
type: Number,
default: 0
},
/**
* @description 起始值即动画开始前显示的数值
*/
startVal: {
type: Number,
default: 0
},
/**
* @description 结束值即动画结束后显示的数值
*/
end: {
type: Number,
required: true
},
/**
* @description 保留几位小数
*/
decimals: {
type: Number,
default: 0
},
/**
* @description 分隔整数和小数的符号默认是小数点
*/
decimal: {
type: String,
default: '.'
},
/**
* @description 动画持续的时间单位是秒
*/
duration: {
type: Number,
default: 2
},
/**
* @description 动画持续的时间单位是秒
*/
delay: {
type: Number,
default: 0
},
/**
* @description 是否禁用easing动画效果
*/
uneasing: {
type: Boolean,
default: false
},
/**
* @description 是否使用分组分组后每三位会用一个符号分隔
*/
usegroup: {
type: Boolean,
default: false
},
/**
* @description 用于分组(usegroup)的符号
*/
separator: {
type: String,
default: ','
},
/**
* @description 是否简化显示设为true后会使用unit单位来做相关省略
*/
simplify: {
type: Boolean,
default: false
},
/**
* @description 自定义单位[3, 'K+'], [6, 'M+']即大于3位数小于6位数的用k+来做省略
* 1000即显示为1K+
*/
unit: {
type: Array,
default () {
return [[3, 'K+'], [6, 'M+'], [9, 'B+']]
}
},
countClass: {
type: String,
default: ''
},
unitClass: {
type: String,
default: ''
}
},
data () {
return {
counter: null,
unitText: ''
}
},
computed: {
counterId () {
return `count_to_${this._uid}`
}
},
methods: {
getHandleVal (val, len) {
return {
endVal: parseInt(val / Math.pow(10, this.unit[len - 1][0])),
unitText: this.unit[len - 1][1]
}
},
transformValue (val) {
let len = this.unit.length
let res = {
endVal: 0,
unitText: ''
}
if (val < Math.pow(10, this.unit[0][0])) res.endVal = val
else {
for (let i = 1; i < len; i++) {
if (val >= Math.pow(10, this.unit[i - 1][0]) && val < Math.pow(10, this.unit[i][0])) res = this.getHandleVal(val, i)
}
}
if (val > Math.pow(10, this.unit[len - 1][0])) res = this.getHandleVal(val, len)
return res
},
getValue (val) {
let res = 0
if (this.simplify) {
let { endVal, unitText } = this.transformValue(val)
this.unitText = unitText
res = endVal
} else {
res = val
}
return res
}
},
mounted () {
this.$nextTick(() => {
setTimeout(() => {
let endVal = this.getValue(this.end)
this.counter = new CountUp(this.counterId, this.startVal, endVal, this.decimals, this.duration, {
useEasing: !this.uneasing,
useGrouping: this.useGroup,
separator: this.separator,
decimal: this.decimal
})
if (!this.counter.error) this.counter.start()
}, this.delay)
})
},
watch: {
end (newVal) {
let endVal = this.getValue(newVal)
this.counter.update(endVal)
}
}
}
</script>

View File

@ -0,0 +1,2 @@
import countTo from './count-to.vue'
export default countTo

View File

@ -0,0 +1,10 @@
@prefix: ~"count-to";
.@{prefix}-wrapper{
.content-outer{
display: inline-block;
.@{prefix}-unit-text{
font-style: normal;
}
}
}

View File

@ -0,0 +1,68 @@
<template>
<div class="editor-wrapper">
<div :id="editorId"></div>
</div>
</template>
<script>
import Editor from 'wangeditor'
import 'wangeditor/release/wangEditor.min.css'
import { oneOf } from '@/libs/tools'
export default {
name: 'Editor',
props: {
value: {
type: String,
default: ''
},
/**
* 绑定的值的类型, enum: ['html', 'text']
*/
valueType: {
type: String,
default: 'html',
validator: (val) => {
return oneOf(val, ['html', 'text'])
}
},
/**
* @description 设置change事件触发时间间隔
*/
changeInterval: {
type: Number,
default: 200
},
/**
* @description 是否开启本地存储
*/
cache: {
type: Boolean,
default: true
}
},
computed: {
editorId () {
return `editor${this._uid}`
}
},
mounted () {
this.editor = new Editor(`#${this.editorId}`)
this.editor.customConfig.onchange = (html) => {
let text = this.editor.txt.text()
if (this.cache) localStorage.editorCache = html
this.$emit('input', this.valueType === 'html' ? html : text)
this.$emit('on-change', html, text)
}
this.editor.customConfig.onchangeTimeout = this.changeInterval
// create
this.editor.create()
//
let html = localStorage.editorCache
if (html) this.editor.txt.html(html)
}
}
</script>
<style>
</style>

View File

@ -0,0 +1,2 @@
import Editor from './editor.vue'
export default Editor

View File

@ -0,0 +1,35 @@
<template>
<i :class="`iconfont icon-${type}`" :style="styles"></i>
</template>
<script>
export default {
name: 'Icons',
props: {
type: {
type: String,
required: true
},
color: {
type: String,
default: '#5c6b77'
},
size: {
type: Number,
default: 16
}
},
computed: {
styles () {
return {
fontSize: `${this.size}px`,
color: this.color
}
}
}
}
</script>
<style>
</style>

View File

@ -0,0 +1,2 @@
import Icons from './icons.vue'
export default Icons

View File

@ -1,2 +1,2 @@
import loginForm from './login-form.vue'
export default loginForm
import LoginForm from './login-form.vue'
export default LoginForm

View File

@ -21,7 +21,7 @@
</template>
<script>
export default {
name: 'loginForm',
name: 'LoginForm',
props: {
userNameRules: {
type: Array,

View File

@ -0,0 +1,2 @@
import MarkdownEditor from './markdown.vue'
export default MarkdownEditor

View File

@ -0,0 +1,73 @@
<template>
<div class="markdown-wrapper">
<textarea ref="editor"></textarea>
</div>
</template>
<script>
import Simplemde from 'simplemde'
import 'simplemde/dist/simplemde.min.css'
export default {
naem: 'MarkdownEditor',
props: {
value: {
type: String,
default: ''
},
options: {
type: Object,
default: () => {
return {}
}
},
localCache: {
type: Boolean,
default: true
}
},
data () {
return {
editor: null
}
},
methods: {
addEvents () {
this.editor.codemirror.on('change', () => {
let value = this.editor.value()
if (this.localCache) localStorage.markdownContent = value
this.$emit('input', value)
this.$emit('on-change', value)
})
this.editor.codemirror.on('focus', () => {
this.$emit('on-focus', this.editor.value())
})
this.editor.codemirror.on('blur', () => {
this.$emit('on-blur', this.editor.value())
})
}
},
mounted () {
this.editor = new Simplemde(Object.assign(this.options, {
element: this.$refs.editor
}))
/**
* 事件列表为Codemirror编辑器的事件更多事件类型请参考
* https://codemirror.net/doc/manual.html#events
*/
this.addEvents()
let content = localStorage.markdownContent
if (content) this.editor.value(content)
}
}
</script>
<style lang="less">
.markdown-wrapper{
.editor-toolbar.fullscreen{
z-index: 9999;
}
.CodeMirror-fullscreen{
z-index: 9999;
}
}
</style>

View File

@ -1,2 +1,2 @@
import parentView from './parent-view.vue'
export default parentView
import ParentView from './parent-view.vue'
export default ParentView

View File

@ -3,6 +3,6 @@
</template>
<script>
export default {
name: 'parentView'
name: 'ParentView'
}
</script>

View File

@ -0,0 +1,2 @@
import PasteEditor from './paste-editor.vue'
export default PasteEditor

View File

@ -0,0 +1,25 @@
.paste-editor-wrapper{
width: 100%;
height: 100%;
border: 1px dashed gainsboro;
textarea.textarea-el{
width: 100%;
height: 100%;
}
.CodeMirror{
height: 100%;
.CodeMirror-code div .CodeMirror-line > span > span.cm-tab{
&::after{
content: '→';
color: #BFBFBF;
}
}
}
.first-row{
font-weight: 700;
font-size: 14px;
}
.incorrect-row{
background: #F5CBD1;
}
}

View File

@ -0,0 +1,111 @@
<template>
<div class="paste-editor-wrapper">
<textarea ref="codemirror" class="textarea-el"></textarea>
</div>
</template>
<script>
import CodeMirror from 'codemirror'
import 'codemirror/lib/codemirror.css'
import { forEach } from '@/libs/tools'
import createPlaceholder from './plugins/placeholder'
export default {
name: 'PasteEditor',
props: {
value: Array,
pasteData: {
type: String,
default: ''
}
},
data () {
return {
pasteDataArr: [],
rowArrLength: 0,
editor: null
}
},
watch: {
pasteData (val) {
if (val === '') {
this.editor.setValue('')
}
}
},
computed: {
rowNum () {
return this.pasteDataArr.length
},
colNum () {
return this.pasteDataArr[0] ? this.pasteDataArr[0].length : 0
}
},
methods: {
handleKeyup (e) {
this.handleAreaData()
},
/**
* @description 处理粘贴操作
*/
handleContentChanged (content) {
let pasteData = content.trim()
this.$emit('on-content-change', pasteData)
let rows = pasteData.split((/[\n\u0085\u2028\u2029]|\r\n?/g)).map(row => {
return row.split('\t')
})
if (content === '') rows = []
this.pasteDataArr = rows
this.clearLineClass()
this.checkColNumInEveryRow()
this.$emit('input', this.pasteDataArr)
},
/**
* @description 检查除第一行的每一行列数是否与第一行相同
*/
checkColNumInEveryRow () {
let i = 0
const len = this.rowNum
if (len === 0) return
while (++i < len) {
let item = this.pasteDataArr[i]
if (item.length !== this.colNum && (!(i === len - 1 && item.length === 1 && item[0] === '') || i !== len - 1)) {
this.markIncorrectRow(i)
this.$emit('on-error', i)
return false
}
}
this.$emit('on-success', this.pasteDataArr)
return true
},
/**
* @description 标记不符合格式的一行
*/
markIncorrectRow (index) {
this.editor.addLineClass(index, 'text', 'incorrect-row')
},
/**
* @description 标记不符合格式的一行
*/
clearLineClass () {
forEach(this.pasteDataArr, (item, index) => {
this.editor.removeLineClass(index, 'text', 'incorrect-row')
})
}
},
mounted () {
createPlaceholder(CodeMirror)
this.editor = CodeMirror.fromTextArea(this.$refs.codemirror, {
lineNumbers: true,
tabSize: 1,
lineWrapping: true,
placeholder: '从网页或其他应用软件复制表格数据,粘贴到这里 。默认第一行是表头使用回车键添加新行使用Tab键区分列。'
})
this.editor.on('change', (editor) => {
this.handleContentChanged(editor.getValue())
})
this.editor.addLineClass(0, 'text', 'first-row')
}
}
</script>
<style lang="less">
@import './paste-editor.less';
</style>

View File

@ -0,0 +1,58 @@
export default (codemirror) => {
(function (mod) {
mod(codemirror)
})(function (CodeMirror) {
CodeMirror.defineOption('placeholder', '', function (cm, val, old) {
var prev = old && old !== CodeMirror.Init
if (val && !prev) {
cm.on('blur', onBlur)
cm.on('change', onChange)
cm.on('swapDoc', onChange)
onChange(cm)
} else if (!val && prev) {
cm.off('blur', onBlur)
cm.off('change', onChange)
cm.off('swapDoc', onChange)
clearPlaceholder(cm)
var wrapper = cm.getWrapperElement()
wrapper.className = wrapper.className.replace(' CodeMirror-empty', '')
}
if (val && !cm.hasFocus()) onBlur(cm)
})
function clearPlaceholder (cm) {
if (cm.state.placeholder) {
cm.state.placeholder.parentNode.removeChild(cm.state.placeholder)
cm.state.placeholder = null
}
}
function setPlaceholder (cm) {
clearPlaceholder(cm)
var elt = cm.state.placeholder = document.createElement('pre')
elt.style.cssText = 'height: 0; overflow: visible; color: #80848f;'
elt.style.direction = cm.getOption('direction')
elt.className = 'CodeMirror-placeholder'
var placeHolder = cm.getOption('placeholder')
if (typeof placeHolder === 'string') placeHolder = document.createTextNode(placeHolder)
elt.appendChild(placeHolder)
cm.display.lineSpace.insertBefore(elt, cm.display.lineSpace.firstChild)
}
function onBlur (cm) {
if (isEmpty(cm)) setPlaceholder(cm)
}
function onChange (cm) {
let wrapper = cm.getWrapperElement()
let empty = isEmpty(cm)
wrapper.className = wrapper.className.replace(' CodeMirror-empty', '') + (empty ? ' CodeMirror-empty' : '')
if (empty) setPlaceholder(cm)
else clearPlaceholder(cm)
}
function isEmpty (cm) {
return (cm.lineCount() === 1) && (cm.getLine(0) === '')
}
})
}

View File

@ -0,0 +1,2 @@
import Split from './split.vue'
export default Split

View File

@ -0,0 +1,114 @@
@split-prefix-cls: ~"ivu-split";
@box-shadow: 0 0 4px 0 rgba(28, 36, 56, 0.4);
@trigger-bar-background: rgba(23, 35, 61, 0.25);
@trigger-background: #F8F8F9;
@trigger-width: 6px;
@trigger-bar-width: 4px;
@trigger-bar-offset: (@trigger-width - @trigger-bar-width) / 2;
@trigger-bar-interval: 3px;
@trigger-bar-weight: 1px;
@trigger-bar-con-height: (@trigger-bar-weight + @trigger-bar-interval) * 8;
.@{split-prefix-cls}{
&-wrapper{
position: relative;
width: 100%;
height: 100%;
}
&-pane{
position: absolute;
&.left-pane, &.right-pane{
top: 0px;
bottom: 0px;
}
&.left-pane{
left: 0px;
}
&.right-pane{
right: 0px;
}
&.top-pane, &.bottom-pane{
left: 0px;
right: 0px;
}
&.top-pane{
top: 0px;
}
&.bottom-pane{
bottom: 0px;
}
}
&-trigger{
&-con{
position: absolute;
transform: translate(-50%, -50%);
z-index: 10;
}
&-bar-con{
position: absolute;
overflow: hidden;
&.vertical{
left: @trigger-bar-offset;
top: 50%;
height: @trigger-bar-con-height;
transform: translate(0, -50%);
}
&.horizontal{
left: 50%;
top: @trigger-bar-offset;
width: @trigger-bar-con-height;
transform: translate(-50%, 0);
}
}
&-vertical{
width: @trigger-width;
height: 100%;
background: @trigger-background;
box-shadow: @box-shadow;
cursor: col-resize;
.@{split-prefix-cls}-trigger-bar{
width: @trigger-bar-width;
height: 1px;
background: @trigger-bar-background;
float: left;
margin-top: @trigger-bar-interval;
}
}
&-horizontal{
height: @trigger-width;
width: 100%;
background: @trigger-background;
box-shadow: @box-shadow;
cursor: row-resize;
.@{split-prefix-cls}-trigger-bar{
height: @trigger-bar-width;
width: 1px;
background: @trigger-bar-background;
float: left;
margin-right: @trigger-bar-interval;
}
}
}
&-horizontal{
.@{split-prefix-cls}-trigger-con{
top: 50%;
height: 100%;
width: 0;
}
}
&-vertical{
.@{split-prefix-cls}-trigger-con{
left: 50%;
height: 0;
width: 100%;
}
}
.no-select{
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
}

View File

@ -0,0 +1,158 @@
<template>
<div ref="outerWrapper" :class="wrapperClasses">
<div v-if="isHorizontal" :class="`${prefix}-horizontal`">
<div :style="{right: `${anotherOffset}%`}" :class="[`${prefix}-pane`, 'left-pane']"><slot name="left"/></div>
<div :class="`${prefix}-trigger-con`" :style="{left: `${offset}%`}" @mousedown="handleMousedown">
<slot name="trigger">
<trigger mode="vertical"/>
</slot>
</div>
<div :style="{left: `${offset}%`}" :class="[`${prefix}-pane`, 'right-pane']"><slot name="right"/></div>
</div>
<div v-else :class="`${prefix}-vertical`">
<div :style="{bottom: `${anotherOffset}%`}" :class="[`${prefix}-pane`, 'top-pane']"><slot name="top"/></div>
<div :class="`${prefix}-trigger-con`" :style="{top: `${offset}%`}" @mousedown="handleMousedown">
<slot name="trigger">
<trigger mode="horizontal"/>
</slot>
</div>
<div :style="{top: `${offset}%`}" :class="[`${prefix}-pane`, 'bottom-pane']"><slot name="bottom"/></div>
</div>
</div>
</template>
<script>
import { oneOf, on, off } from '@/libs/tools'
import Trigger from './trigger.vue'
export default {
name: 'SplitPane',
components: {
Trigger
},
props: {
value: {
type: [Number, String],
default: 0.5
},
mode: {
validator (value) {
return oneOf(value, ['horizontal', 'vertical'])
},
default: 'horizontal'
},
min: {
type: [Number, String],
default: '40px'
},
max: {
type: [Number, String],
default: '40px'
}
},
/**
* Events
* @on-move-start
* @on-moving 返回值事件对象但是在事件对象中加入了两个参数atMin(当前是否在最小值处), atMax(当前是否在最大值处)
* @on-move-end
*/
data () {
return {
prefix: 'ivu-split',
offset: 0,
oldOffset: 0,
isMoving: false
}
},
computed: {
wrapperClasses () {
return [
`${this.prefix}-wrapper`,
this.isMoving ? 'no-select' : ''
]
},
isHorizontal () {
return this.mode === 'horizontal'
},
anotherOffset () {
return 100 - this.offset
},
valueIsPx () {
return typeof this.value === 'string'
},
offsetSize () {
return this.isHorizontal ? 'offsetWidth' : 'offsetHeight'
},
computedMin () {
return this.getComputedThresholdValue('min')
},
computedMax () {
return this.getComputedThresholdValue('max')
}
},
methods: {
px2percent (numerator, denominator) {
return parseFloat(numerator) / parseFloat(denominator)
},
getComputedThresholdValue (type) {
let size = this.$refs.outerWrapper[this.offsetSize]
if (this.valueIsPx) return typeof this[type] === 'string' ? this[type] : size * this[type]
else return typeof this[type] === 'string' ? this.px2percent(this[type], size) : this[type]
},
getMin (value1, value2) {
if (this.valueIsPx) return `${Math.min(parseFloat(value1), parseFloat(value2))}px`
else return Math.min(value1, value2)
},
getMax (value1, value2) {
if (this.valueIsPx) return `${Math.max(parseFloat(value1), parseFloat(value2))}px`
else return Math.max(value1, value2)
},
getAnotherOffset (value) {
let res = 0
if (this.valueIsPx) res = `${this.$refs.outerWrapper[this.offsetSize] - parseFloat(value)}px`
else res = 1 - value
return res
},
handleMove (e) {
let pageOffset = this.isHorizontal ? e.pageX : e.pageY
let offset = pageOffset - this.initOffset
let outerWidth = this.$refs.outerWrapper[this.offsetSize]
let value = this.valueIsPx ? `${parseFloat(this.oldOffset) + offset}px` : (this.px2percent(outerWidth * this.oldOffset + offset, outerWidth))
let anotherValue = this.getAnotherOffset(value)
if (parseFloat(value) <= parseFloat(this.computedMin)) value = this.getMax(value, this.computedMin)
if (parseFloat(anotherValue) <= parseFloat(this.computedMax)) value = this.getAnotherOffset(this.getMax(anotherValue, this.computedMax))
e.atMin = this.value === this.computedMin
e.atMax = this.valueIsPx ? this.getAnotherOffset(this.value) === this.computedMax : this.getAnotherOffset(this.value).toFixed(5) === this.computedMax.toFixed(5)
this.$emit('input', value)
this.$emit('on-moving', e)
},
handleUp () {
this.isMoving = false
off(document, 'mousemove', this.handleMove)
off(document, 'mouseup', this.handleUp)
this.$emit('on-move-end')
},
handleMousedown (e) {
this.initOffset = this.isHorizontal ? e.pageX : e.pageY
this.oldOffset = this.value
this.isMoving = true
on(document, 'mousemove', this.handleMove)
on(document, 'mouseup', this.handleUp)
this.$emit('on-move-start')
}
},
watch: {
value () {
this.offset = (this.valueIsPx ? this.px2percent(this.value, this.$refs.outerWrapper[this.offsetSize]) : this.value) * 10000 / 100
}
},
mounted () {
this.$nextTick(() => {
this.offset = (this.valueIsPx ? this.px2percent(this.value, this.$refs.outerWrapper[this.offsetSize]) : this.value) * 10000 / 100
})
}
}
</script>
<style lang="less">
@import './index.less';
</style>

View File

@ -0,0 +1,43 @@
<template>
<div :class="classes">
<div :class="barConClasses">
<i :class="`${prefix}-bar`" v-once v-for="i in 8" :key="`trigger-${i}`"></i>
</div>
</div>
</template>
<script>
export default {
name: 'Trigger',
props: {
mode: String
},
data () {
return {
prefix: 'ivu-split-trigger',
initOffset: 0
}
},
computed: {
isVertical () {
return this.mode === 'vertical'
},
classes () {
return [
this.prefix,
this.isVertical ? `${this.prefix}-vertical` : `${this.prefix}-horizontal`
]
},
barConClasses () {
return [
`${this.prefix}-bar-con`,
this.isVertical ? 'vertical' : 'horizontal'
]
}
}
}
</script>
<style lang="less">
@import './index.less';
</style>

View File

@ -0,0 +1,73 @@
<template>
<div class="tables-edit-outer">
<div v-if="!isEditting" class="tables-edit-con">
<span class="value-con">{{ value }}</span>
<Button v-if="editable" @click="startEdit" class="tables-edit-btn" style="padding: 2px 4px;" type="text"><Icon type="edit"></Icon></Button>
</div>
<div v-else class="tables-editting-con">
<Input :value="value" @input="handleInput" class="tables-edit-input"/>
<Button @click="saveEdit" style="padding: 6px 4px;" type="text"><Icon type="checkmark-round"></Icon></Button>
<Button @click="canceltEdit" style="padding: 6px 4px;" type="text"><Icon type="close-round"></Icon></Button>
</div>
</div>
</template>
<script>
export default {
name: 'TablesEdit',
props: {
value: [String, Number],
edittingCellId: String,
params: Object,
editable: Boolean
},
computed: {
isEditting () {
return this.edittingCellId === `editting-${this.params.index}-${this.params.column.key}`
}
},
methods: {
handleInput (val) {
this.$emit('input', val)
},
startEdit () {
this.$emit('on-start-edit', this.params)
},
saveEdit () {
this.$emit('on-save-edit', this.params)
},
canceltEdit () {
this.$emit('on-cancel-edit', this.params)
}
}
}
</script>
<style lang="less">
.tables-edit-outer{
height: 100%;
.tables-edit-con{
position: relative;
height: 100%;
.value-con{
vertical-align: middle;
}
.tables-edit-btn{
position: absolute;
right: 10px;
top: 0;
display: none;
}
&:hover{
.tables-edit-btn{
display: inline-block;
}
}
}
.tables-editting-con{
.tables-edit-input{
width: ~"calc(100% - 50px)";
}
}
}
</style>

View File

@ -0,0 +1,31 @@
const btns = {
delete: (h, params, vm) => {
return h('Poptip', {
props: {
confirm: true,
title: '你确定要删除吗?'
},
on: {
'on-ok': () => {
vm.$emit('on-delete', params)
vm.$emit('input', params.tableData.filter((item, index) => index !== params.row.initRowIndex))
}
}
}, [
h('Button', {
props: {
type: 'text'
}
}, [
h('Icon', {
props: {
type: 'trash-b',
size: 18
}
})
])
])
}
}
export default btns

View File

@ -0,0 +1,2 @@
import Tables from './tables.vue'
export default Tables

View File

@ -0,0 +1,17 @@
.search-con{
padding: 10px 0;
.search{
&-col{
display: inline-block;
width: 200px;
}
&-input{
display: inline-block;
width: 200px;
margin-left: 2px;
}
&-btn{
margin-left: 2px;
}
}
}

View File

@ -0,0 +1,277 @@
<template>
<div>
<div v-if="searchable && searchPlace === 'top'" class="search-con search-con-top">
<Select v-model="searchKey" class="search-col">
<Option v-for="item in columns" v-if="item.key !== 'handle'" :value="item.key" :key="`search-col-${item.key}`">{{ item.title }}</Option>
</Select>
<Input @on-change="handleClear" clearable placeholder="输入关键字搜索" class="search-input" v-model="searchValue"/>
<Button @click="handleSearch" class="search-btn" type="primary"><Icon type="search"/>&nbsp;&nbsp;搜索</Button>
</div>
<Table
ref="tablesMain"
:data="insideTableData"
:columns="insideColumns"
:stripe="stripe"
:border="border"
:show-header="showHeader"
:width="width"
:height="height"
:loading="loading"
:disabled-hover="disabledHover"
:highlight-row="highlightRow"
:row-class-name="rowClassName"
:size="size"
:no-data-text="noDataText"
:no-filtered-data-text="noFilteredDataText"
@on-current-change="onCurrentChange"
@on-select="onSelect"
@on-select-cancel="onSelectCancel"
@on-select-all="onSelectAll"
@on-selection-change="onSelectionChange"
@on-sort-change="onSortChange"
@on-filter-change="onFilterChange"
@on-row-click="onRowClick"
@on-row-dblclick="onRowDblclick"
@on-expand="onExpand"
>
<slot name="header" slot="header"></slot>
<slot name="footer" slot="footer"></slot>
<slot name="loading" slot="loading"></slot>
</Table>
<div v-if="searchable && searchPlace === 'bottom'" class="search-con search-con-top">
<Select v-model="searchKey" class="search-col">
<Option v-for="item in columns" v-if="item.key !== 'handle'" :value="item.key" :key="`search-col-${item.key}`">{{ item.title }}</Option>
</Select>
<Input placeholder="输入关键字搜索" class="search-input" v-model="searchValue"/>
<Button class="search-btn" type="primary"><Icon type="search"/>&nbsp;&nbsp;搜索</Button>
</div>
<a id="hrefToExportTable" style="display: none;width: 0px;height: 0px;"></a>
</div>
</template>
<script>
import TablesEdit from './edit.vue'
import handleBtns from './handle-btns'
import './index.less'
export default {
name: 'Tables',
props: {
value: {
type: Array,
default () {
return []
}
},
columns: {
type: Array,
default () {
return []
}
},
size: String,
width: {
type: [Number, String]
},
height: {
type: [Number, String]
},
stripe: {
type: Boolean,
default: false
},
border: {
type: Boolean,
default: false
},
showHeader: {
type: Boolean,
default: true
},
highlightRow: {
type: Boolean,
default: false
},
rowClassName: {
type: Function,
default () {
return ''
}
},
context: {
type: Object
},
noDataText: {
type: String
},
noFilteredDataText: {
type: String
},
disabledHover: {
type: Boolean
},
loading: {
type: Boolean,
default: false
},
/**
* @description 全局设置是否可编辑
*/
editable: {
type: Boolean,
default: false
},
/**
* @description 是否可搜索
*/
searchable: {
type: Boolean,
default: false
},
/**
* @description 搜索控件所在位置'top' / 'bottom'
*/
searchPlace: {
type: String,
default: 'top'
}
},
/**
* Events
* @on-start-edit 返回值 {Object} 同iview中render函数中的params对象 { row, index, column }
* @on-cancel-edit 返回值 {Object} 同上
* @on-save-edit 返回值 {Object} 除上面三个参数外还有一个value: 修改后的数据
*/
data () {
return {
insideColumns: [],
insideTableData: [],
edittingCellId: '',
edittingText: '',
searchValue: '',
searchKey: ''
}
},
methods: {
suportEdit (item, index) {
item.render = (h, params) => {
return h(TablesEdit, {
props: {
params: params,
value: this.insideTableData[params.index][params.column.key],
edittingCellId: this.edittingCellId,
editable: this.editable
},
on: {
'input': val => {
this.edittingText = val
},
'on-start-edit': (params) => {
this.edittingCellId = `editting-${params.index}-${params.column.key}`
this.$emit('on-start-edit', params)
},
'on-cancel-edit': (params) => {
this.edittingCellId = ''
this.$emit('on-cancel-edit', params)
},
'on-save-edit': (params) => {
this.value[params.index][params.column.key] = this.edittingText
this.$emit('input', this.value)
this.$emit('on-save-edit', Object.assign(params, {value: this.edittingText}))
this.edittingCellId = ''
}
}
})
}
return item
},
surportHandle (item) {
let options = item.options || []
let insideBtns = []
options.forEach(item => {
if (handleBtns[item]) insideBtns.push(handleBtns[item])
})
let btns = item.button ? [].concat(insideBtns, item.button) : insideBtns
item.render = (h, params) => {
params.tableData = this.value
return h('div', btns.map(item => item(h, params, this)))
}
return item
},
handleColumns (columns) {
this.insideColumns = columns.map((item, index) => {
let res = item
if (res.editable) res = this.suportEdit(res, index)
if (res.key === 'handle') res = this.surportHandle(res)
return res
})
},
setDefaultSearchKey () {
this.searchKey = this.columns[0].key !== 'handle' ? this.columns[0].key : (this.columns.length > 1 ? this.columns[1].key : '')
},
handleClear (e) {
if (e.target.value === '') this.insideTableData = this.value
},
handleSearch () {
this.insideTableData = this.value.filter(item => item[this.searchKey].indexOf(this.searchValue) > -1)
},
handleTableData () {
this.insideTableData = this.value.map((item, index) => {
let res = item
res.initRowIndex = index
return res
})
},
exportCsv (params) {
this.$refs.tablesMain.exportCsv(params)
},
clearCurrentRow () {
this.$refs.talbesMain.clearCurrentRow()
},
onCurrentChange (currentRow, oldCurrentRow) {
this.$emit('on-current-change', currentRow, oldCurrentRow)
},
onSelect (selection, row) {
this.$emit('on-select', selection, row)
},
onSelectCancel (selection, row) {
this.$emit('on-select-cancel', selection, row)
},
onSelectAll (selection) {
this.$emit('on-select-all', selection)
},
onSelectionChange (selection) {
this.$emit('on-selection-change', selection)
},
onSortChange (column, key, order) {
this.$emit('on-sort-change', column, key, order)
},
onFilterChange (row) {
this.$emit('on-filter-change', row)
},
onRowClick (row, index) {
this.$emit('on-row-click', row, index)
},
onRowDblclick (row, index) {
this.$emit('on-row-dblclick', row, index)
},
onExpand (row, status) {
this.$emit('on-expand', row, status)
}
},
watch: {
columns (columns) {
this.handleColumns(columns)
this.setDefaultSearchKey()
},
value (val) {
this.handleTableData()
this.handleSearch()
}
},
mounted () {
this.handleColumns(this.columns)
this.setDefaultSearchKey()
this.handleTableData()
}
}
</script>

View File

@ -0,0 +1,45 @@
import { on } from '@/libs/tools'
const directives = {
draggable: {
inserted: (el, binding, vnode) => {
let triggerDom = document.querySelector(binding.value.trigger)
triggerDom.style.cursor = 'move'
let bodyDom = document.querySelector(binding.value.body)
let pageX = 0
let pageY = 0
let transformX = 0
let transformY = 0
let canMove = false
const handleMousedown = e => {
let transform = /\(.*\)/.exec(bodyDom.style.transform)
if (transform) {
transform = transform[0].slice(1, transform[0].length - 1)
let splitxy = transform.split('px, ')
transformX = parseFloat(splitxy[0])
transformY = parseFloat(splitxy[1].split('px')[0])
}
pageX = e.pageX
pageY = e.pageY
canMove = true
}
const handleMousemove = e => {
let xOffset = e.pageX - pageX + transformX
let yOffset = e.pageY - pageY + transformY
if (canMove) bodyDom.style.transform = `translate(${xOffset}px, ${yOffset}px)`
}
const handleMouseup = e => {
canMove = false
}
on(triggerDom, 'mousedown', handleMousedown)
on(document, 'mousemove', handleMousemove)
on(document, 'mouseup', handleMouseup)
},
update: (el, binding, vnode) => {
if (!binding.value.recover) return
let bodyDom = document.querySelector(binding.value.body)
bodyDom.style.transform = ''
}
}
}
export default directives

15
src/directive/index.js Normal file
View File

@ -0,0 +1,15 @@
import directive from './directives'
const importDirective = Vue => {
/**
* 拖拽指令 v-draggable="options"
* options = {
* trigger: /这里传入作为拖拽触发器的CSS选择器/,
* body: /这里传入需要移动容器的CSS选择器/,
* recover: /拖动结束之后是否恢复到原来的位置/
* }
*/
Vue.directive('draggable', directive.draggable)
}
export default importDirective

View File

@ -42,6 +42,19 @@ export const hasOneOf = (target, arr) => {
return target.some(_ => arr.indexOf(_) > -1)
}
/**
* @param {String|Number} value 要验证的字符串或数值
* @param {*} validList 用来验证的列表
*/
export function oneOf (value, validList) {
for (let i = 0; i < validList.length; i++) {
if (value === validList[i]) {
return true
}
}
return false
}
/**
* @param {Number} timeStamp 判断时间戳格式是否是毫秒
* @returns {Boolean}
@ -121,3 +134,56 @@ export const getRelativeTime = timeStamp => {
else resStr = getDate(timeStamp, 'year')
return resStr
}
/**
* @returns {String} 当前浏览器名称
*/
export const getExplorer = () => {
const ua = window.navigator.userAgent
const isExplorer = (exp) => {
return ua.indexOf(exp) > -1
}
if (isExplorer('MSIE')) return 'IE'
else if (isExplorer('Firefox')) return 'Firefox'
else if (isExplorer('Chrome')) return 'Chrome'
else if (isExplorer('Opera')) return 'Opera'
else if (isExplorer('Safari')) return 'Safari'
}
/**
* @description 绑定事件 on(element, event, handler)
*/
export const on = (function () {
if (document.addEventListener) {
return function (element, event, handler) {
if (element && event && handler) {
element.addEventListener(event, handler, false)
}
}
} else {
return function (element, event, handler) {
if (element && event && handler) {
element.attachEvent('on' + event, handler)
}
}
}
})()
/**
* @description 解绑事件 off(element, event, handler)
*/
export const off = (function () {
if (document.removeEventListener) {
return function (element, event, handler) {
if (element && event) {
element.removeEventListener(event, handler, false)
}
}
} else {
return function (element, event, handler) {
if (element && event) {
element.detachEvent('on' + event, handler)
}
}
}
})()

View File

@ -52,7 +52,9 @@ export const getMenuByRouter = (list, access) => {
* @returns {Array}
*/
export const getBreadCrumbList = (routeMetched) => {
let res = routeMetched.map(item => {
let res = routeMetched.filter(item => {
return item.meta === undefined || !item.meta.hide
}).map(item => {
let obj = {
icon: (item.meta && item.meta.icon) || '',
name: item.name,
@ -164,6 +166,10 @@ export const canTurnTo = (name, access, routes) => {
return canTurnToNames.indexOf(name) > -1
}
/**
* @param {String} url
* @description 从URL中解析参数
*/
export const getParams = url => {
const keyValueArr = url.split('?')[1].split('&')
let paramObj = {}
@ -173,3 +179,85 @@ export const getParams = url => {
})
return paramObj
}
/**
* @param {Array} list 标签列表
* @param {String} name 当前关闭的标签的name
*/
export const getNextName = (list, name) => {
let res = ''
if (list.length === 2) {
res = 'home'
} else {
if (list.findIndex(item => item.name === name) === list.length - 1) res = list[list.length - 2].name
else res = list[list.findIndex(item => item.name === name) + 1].name
}
return res
}
/**
* @param {Number} times 回调函数需要执行的次数
* @param {Function} callback 回调函数
*/
export const doCustomTimes = (times, callback) => {
let i = -1
while (++i < times) {
callback()
}
}
/**
* @param {Object} file 从上传组件得到的文件对象
* @returns {Promise} resolve参数是解析后的二维数组
* @description 从Csv文件中解析出表格解析成二维数组
*/
export const getArrayFromFile = (file) => {
let nameSplit = file.name.split('.')
let format = nameSplit[nameSplit.length - 1]
return new Promise((resolve, reject) => {
let reader = new FileReader()
reader.readAsText(file) // 以文本格式读取
let arr = []
reader.onload = function (evt) {
let data = evt.target.result // 读到的数据
let pasteData = data.trim()
arr = pasteData.split((/[\n\u0085\u2028\u2029]|\r\n?/g)).map(row => {
return row.split('\t')
}).map(item => {
return item[0].split(',')
})
if (format === 'csv') resolve(arr)
else reject(new Error('[Format Error]:你上传的不是Csv文件'))
}
})
}
/**
* @param {Array} array 表格数据二维数组
* @returns {Object} { columns, tableData }
* @description 从二维数组中获取表头和表格数据将第一行作为表头用于在iView的表格中展示数据
*/
export const getTableDataFromArray = (array) => {
let columns = []
let tableData = []
if (array.length > 1) {
let titles = array.shift()
columns = titles.map(item => {
return {
title: item,
key: item
}
})
tableData = array.map(item => {
let res = {}
item.forEach((col, i) => {
res[titles[i]] = col
})
return res
})
}
return {
columns,
tableData
}
}

View File

@ -16,17 +16,22 @@ Vue.use(VueI18n)
let lang = 'zh-CN'
Vue.config.lang = lang
Vue.locale = () => {}
// vue-i18n 6.x+写法
Vue.locale = () => {}
const messages = {
'zh-CN': Object.assign(zhCnLocale, customZhCn),
'zh-TW': Object.assign(zhTwLocale, customZhTw),
'en-US': Object.assign(enUsLocale, customEnUs)
}
const i18n = new VueI18n({
locale: lang,
messages
})
export default i18n
// vue-i18n 5.x写法
// Vue.locale('zh-CN', Object.assign(zhCnLocale, customZhCn))
// Vue.locale('en-US', Object.assign(zhTwLocale, customZhTw))
// Vue.locale('zh-TW', Object.assign(enUsLocale, customEnUs))

View File

@ -7,22 +7,29 @@ import store from './store'
import iView from 'iview'
import i18n from '@/locale'
import config from '@/config'
import importDirective from '@/directive'
import 'iview/dist/styles/iview.css'
import '@/assets/icons/iconfont.css'
import env from '../config/env'
if (env === 'development') require('@/mock')
Vue.use(iView)
Vue.use(iView, {
i18n: (key, value) => i18n.t(key, value)
})
Vue.config.productionTip = false
/**
* @description 全局注册应用配置
*/
Vue.prototype.$config = config
/**
* 注册指令
*/
importDirective(Vue)
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
store,
i18n,
render: h => h(App)
})

18
src/mock/data.js Normal file
View File

@ -0,0 +1,18 @@
import Mock from 'mockjs'
import { doCustomTimes } from '@/libs/util'
export const getTableData = req => {
let tableData = []
doCustomTimes(5, () => {
tableData.push(Mock.mock({
name: '@name',
email: '@email',
createTime: '@date'
}))
})
return {
code: 200,
data: tableData,
msg: ''
}
}

View File

@ -1,9 +1,11 @@
import Mock from 'mockjs'
import { login, logout, getUserInfo } from './login'
import { getTableData } from './data'
// 登录相关和获取用户信息
Mock.mock(/\/login/, login)
Mock.mock(/\/get_info/, getUserInfo)
Mock.mock(/\/logout/, logout)
Mock.mock(/\/get_table_data/, getTableData)
export default Mock

View File

@ -1,50 +0,0 @@
import Main from '@/view/main'
import parentView from '@/components/main/parent-view'
export default [
{
path: '/login',
name: 'login',
meta: {
title: 'Login - 登录'
},
component: () => import('@/view/login/login.vue')
},
{
path: '/',
name: 'index',
// redirect: '/home',
component: Main,
children: [
{
path: 'home',
name: 'home',
component: () => import('@/view/single-page/home')
},
{
path: 'multilevel',
name: 'multilevel',
component: parentView,
children: [
{
path: 'level_1',
name: 'level_1',
component: () => import('@/view/multilevel/level-1.vue')
},
{
path: 'level_2',
name: 'level_2',
component: parentView,
children: [
{
path: 'level_2_1',
name: 'level_2_1',
component: () => import('@/view/multilevel/level-2/level-2-1.vue')
}
]
}
]
}
]
}
]

View File

@ -13,7 +13,7 @@ export default [
},
{
path: '/',
name: 'index',
name: 'home',
redirect: '/home',
component: Main,
meta: {
@ -42,13 +42,106 @@ export default [
component: Main,
children: [
{
path: 'count_to',
name: 'count_to',
path: 'count_to_page',
name: 'count_to_page',
meta: {
icon: 'arrow-graph-up-right',
title: '数字渐变'
},
component: () => import('@/view/components/count-to/count-to.vue')
},
{
path: 'tables_page',
name: 'tables_page',
meta: {
icon: 'ios-grid-view',
title: '多功能表格'
},
component: () => import('@/view/components/tables/tables.vue')
},
{
path: 'split_pane_page',
name: 'split_pane_page',
meta: {
icon: 'pause',
title: '分割窗口'
},
component: () => import('@/view/components/split-pane/split-pane.vue')
},
{
path: 'markdown_page',
name: 'markdown_page',
meta: {
icon: 'social-markdown',
title: 'Markdown编辑器'
},
component: () => import('@/view/components/markdown/markdown.vue')
},
{
path: 'editor_page',
name: 'editor_page',
meta: {
icon: 'compose',
title: '富文本编辑器'
},
component: () => import('@/view/components/editor/editor.vue')
},
{
path: 'icons_page',
name: 'icons_page',
meta: {
icon: '_bear',
title: '自定义图标'
},
component: () => import('@/view/components/icons/icons.vue')
}
]
},
{
path: '/update',
name: 'update',
meta: {
icon: 'upload',
title: '数据上传'
},
component: Main,
children: [
{
path: 'update_table_page',
name: 'update_table_page',
meta: {
icon: 'document-text',
title: '上传Csv'
},
component: () => import('@/view/update/update-table.vue')
},
{
path: 'update_paste_page',
name: 'update_paste_page',
meta: {
icon: 'clipboard',
title: '粘贴表格数据'
},
component: () => import('@/view/update/update-paste.vue')
}
]
},
{
path: '/directive',
name: 'directive',
meta: {
hide: true
},
component: Main,
children: [
{
path: '/directive_page',
name: 'directive_page',
meta: {
icon: 'ios-navigate',
title: '指令'
},
component: () => import('@/view/directive/directive.vue')
}
]
},

View File

@ -1,13 +1,193 @@
<template>
<div>count to</div>
<div>
<Row :gutter="14">
<i-col span="3">
<Card>
<p slot="title">
<Icon type="waterdrop"></Icon>
count-to组件基础用法
</p>
<Row type="flex" justify="center" align="middle" class="countto-page-row">
<div class="count-to-con">
<count-to :end="2534"/>
</div>
</Row>
</Card>
</i-col>
<i-col span="5" class="padding-left-10">
<Card>
<p slot="title">
<Icon type="code"></Icon>
可添加左右文字
</p>
<Row type="flex" justify="center" align="middle" class="countto-page-row">
<div class="count-to-con">
<count-to :end="2534">
<span slot="left">Total:&nbsp;</span>
<span slot="right">&nbsp;times</span>
</count-to>
</div>
</Row>
</Card>
</i-col>
<i-col span="8" class="padding-left-10">
<Card>
<p slot="title">
<Icon type="paintbucket"></Icon>
自定义样式
</p>
<Row type="flex" justify="center" align="middle" class="countto-page-row">
<div class="count-to-con">
<count-to :end="2534" count-class="count-text" unit-class="unit-class">
<span class="slot-text" slot="left">Total:&nbsp;</span>
<span class="slot-text" slot="right">&nbsp;times</span>
</count-to>
</div>
</Row>
</Card>
</i-col>
<i-col span="8" class="padding-left-10">
<Card>
<p slot="title">
<Icon type="settings"></Icon>
设置数据格式
</p>
<Row type="flex" justify="center" align="middle" class="countto-page-row">
<div class="count-to-con">
<count-to :end="2534" count-class="count-text" unit-class="unit-class" :decimals="2">
<span class="slot-text" slot="left">Total:&nbsp;</span>
<span class="slot-text" slot="right">&nbsp;times</span>
</count-to>
</div>
</Row>
</Card>
</i-col>
</Row>
<Row :gutter="14" style="margin-top: 14px;">
<i-col span="8">
<Card>
<p slot="title">
<Icon type="ios-color-wand"></Icon>
转换单位简化数据
</p>
<Row type="flex" justify="center" align="middle" class="countto-page-row">
<div class="count-to-con">
<count-to :simplify="true" :end="2534" count-class="count-text" unit-class="unit-class">
<span class="slot-text" slot="left">Total:&nbsp;</span>
<span class="slot-text" slot="right">&nbsp;times</span>
</count-to>
</div>
</Row>
</Card>
</i-col>
<i-col span="8" class="padding-left-10">
<Card>
<p slot="title">
<Icon type="ios-shuffle-strong"></Icon>
自定义单位
</p>
<Row type="flex" justify="center" align="middle" class="countto-page-row">
<div class="count-to-con">
<count-to :simplify="true" :unit="unit" :end="253" count-class="count-text" unit-class="unit-class">
<span class="slot-text" slot="left">原始数据253&nbsp;=>&nbsp;</span>
</count-to>
<count-to :simplify="true" :unit="unit" :end="2534" count-class="count-text" unit-class="unit-class">
<span class="slot-text" slot="left">原始数据2534&nbsp;=>&nbsp;</span>
</count-to>
<count-to :simplify="true" :unit="unit" :end="257678" count-class="count-text" unit-class="unit-class">
<span class="slot-text" slot="left">原始数据257678&nbsp;=>&nbsp;</span>
</count-to>
</div>
</Row>
</Card>
</i-col>
<i-col span="8" class="padding-left-10">
<Card>
<p slot="title">
<Icon type="android-stopwatch"></Icon>
可异步更新数据
</p>
<Row type="flex" justify="center" align="middle" class="countto-page-row">
<div class="count-to-con">
<count-to :end="asynEndVal" count-class="count-text" unit-class="unit-class">
<span class="slot-text" slot="left">Total:&nbsp;</span>
<span class="slot-text" slot="right">&nbsp;times</span>
</count-to>
</div>
</Row>
</Card>
</i-col>
</Row>
<Row :gutter="14" style="margin-top: 14px;">
<i-col>
<Card>
<p slot="title">
<Icon type="ios-analytics"></Icon>
综合实例
</p>
<Row type="flex" justify="center" align="middle" class="countto-page-row">
<div class="count-to-con">
<count-to :delay="500" :simplify="true" :unit="unit2" :end="integratedEndVal" count-class="count-text" unit-class="unit-class">
<span class="slot-text" slot="left">原始数据:&nbsp;{{ integratedEndVal }}&nbsp;=>&nbsp;</span>
<span class="slot-text" slot="right">&nbsp;times</span>
</count-to>
</div>
</Row>
</Card>
</i-col>
</Row>
</div>
</template>
<script>
import CountTo from '_c/count-to'
export default {
name: 'countTo'
name: 'count_to_page',
components: {
CountTo
},
data () {
return {
end: 0,
unit: [[3, '千多'], [4, '万多'], [5, '十万多']],
unit2: [[1, '十多'], [2, '百多'], [3, '千多'], [4, '万多'], [5, '十万多'], [6, '百万多'], [7, '千万多'], [8, '亿多']],
asynEndVal: 487,
integratedEndVal: 3
}
},
methods: {
init () {
setInterval(() => {
this.asynEndVal += parseInt(Math.random() * 20)
this.integratedEndVal += parseInt(Math.random() * 30)
}, 2000)
}
},
mounted () {
this.init()
}
}
</script>
<style>
<style lang="less">
@baseColor: ~"#dc9387";
.countto-page-row{
height: 200px;
}
.count-to-con{
display: block;
width: 100%;
text-align: center;
}
.count-text{
font-size: 50px;
color: @baseColor;
}
.slot-text{
font-size: 22px;
}
.unit-class{
font-size: 30px;
color: @baseColor;
}
</style>

View File

@ -0,0 +1,29 @@
<template>
<div>
<editor v-model="content" @on-change="handleChange"/>
</div>
</template>
<script>
import Editor from '_c/editor'
export default {
name: 'editor_page',
components: {
Editor
},
data () {
return {
content: ''
}
},
methods: {
handleChange (html, text) {
console.log(html, text)
}
}
}
</script>
<style>
</style>

View File

@ -0,0 +1,63 @@
<template>
<Row>
<i-col span="16">
<Row v-for="i in (customIconList.length / 3)" :key="`custom-icon-row-${i}`">
<i-col span="8" v-for="item in customIconList.slice((i - 1) * 3, i * 3)" :key="`custom-icon-${item}`">
<Card style="margin: 0 5px 5px; text-align: center;">
<icons :size="30" :type="item"/>
<p class="icon-code">&lt;Icons :size="30" type="{{ item }}"&gt;</p>
<p>&lt;CommonIcon :size="30" type="_{{ item }}"&gt;</p>
</Card>
</i-col>
</Row>
<Row>
<i-col>
<Card style="margin: 0 5px 5px; text-align: center;">
<common-icon :size="30" type="ionic"/>
<p class="icon-code">iView内置图标</p>
<p>&lt;CommonIcon :size="30" type="ionic"&gt;</p>
</Card>
</i-col>
</Row>
</i-col>
<i-col span="8">
<Card>
<p class="intro-p"><Icon style="margin-right: 10px;" :size="10" type="heart"/>Icons组件支持自定义图标的显示具体自定义图标字体文件的制作请参考文档</p>
<p class="intro-p"><Icon style="margin-right: 10px;" :size="10" type="heart"/>CommonIcon组件同时支持iView内置图标类型和自定义图标类型为了区别这两种类型需要在自定义图标名称前加下划线"_"</p>
</Card>
</i-col>
</Row>
</template>
<script>
import Icons from '_c/icons'
import CommonIcon from '_c/common-icon'
export default {
name: 'icons_pages',
components: {
Icons,
CommonIcon
},
data () {
return {
customIconList: [
'woman',
'man',
'smile',
'meh',
'frown',
'bear'
]
}
}
}
</script>
<style lang="less">
.icon-code{
margin: 20px 0 10px;
}
.intro-p{
margin-bottom: 10px;
}
</style>

View File

@ -0,0 +1,24 @@
<template>
<div>
<markdown-editor v-model="content"/>
</div>
</template>
<script>
import MarkdownEditor from '_c/markdown'
export default {
name: 'markdown_page',
components: {
MarkdownEditor
},
data () {
return {
content: ''
}
}
}
</script>
<style>
</style>

View File

@ -0,0 +1,79 @@
<template>
<div class="split-pane-page-wrapper">
<split-pane v-model="offset" @on-moving="handleMoving">
<div slot="left" class="pane left-pane">
<split-pane v-model="offsetVertical" mode="vertical" @on-moving="handleMoving">
<div slot="top" class="pane top-pane"></div>
<div slot="bottom" class="pane bottom-pane"></div>
<div slot="trigger" class="custom-trigger">
<icons class="trigger-icon" :size="22" type="resize-vertical" color="#fff"/>
</div>
</split-pane>
</div>
<div slot="right" class="pane right-pane"></div>
</split-pane>
</div>
</template>
<script>
import SplitPane from '_c/split-pane'
import Icons from '_c/icons'
export default {
name: 'split_pane_page',
components: {
SplitPane,
Icons
},
data () {
return {
offset: 0.6,
offsetVertical: '250px'
}
},
methods: {
handleMoving (e) {
console.log(e.atMin, e.atMax)
}
}
}
</script>
<style lang="less">
.center-middle{
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
}
.split-pane-page-wrapper{
height: 600px;
.pane{
width: 100%;
height: 100%;
&.left-pane{
background: sandybrown;
}
&.right-pane{
background: palevioletred;
}
&.top-pane{
background: sandybrown;
}
&.bottom-pane{
background: palevioletred;
}
}
.custom-trigger{
width: 20px;
height: 20px;
border-radius: 50%;
background: #000000;
position: absolute;
.center-middle;
box-shadow: 0 0 6px 0 rgba(28, 36, 56, 0.4);
i.trigger-icon{
.center-middle;
}
}
}
</style>

View File

@ -0,0 +1,71 @@
<template>
<div>
<Card>
<tables ref="tables" editable searchable search-place="top" v-model="tableData" :columns="columns" @on-delete="handleDelete"/>
<Button style="margin: 10px 0;" type="primary" @click="exportExcel">导出为Csv文件</Button>
</Card>
</div>
</template>
<script>
import Tables from '_c/tables'
import { getTableData } from '@/api/data'
export default {
name: 'tables_page',
components: {
Tables
},
data () {
return {
columns: [
{title: 'Name', key: 'name', sortable: true},
{title: 'Email', key: 'email', editable: true},
{title: 'Create-Time', key: 'createTime'},
{
title: 'Handle',
key: 'handle',
options: ['delete'],
button: [
(h, params, vm) => {
return h('Poptip', {
props: {
confirm: true,
title: '你确定要删除吗?'
},
on: {
'on-ok': () => {
vm.$emit('on-delete', params)
vm.$emit('input', params.tableData.filter((item, index) => index !== params.row.initRowIndex))
}
}
}, [
h('Button', '自定义删除')
])
}
]
}
],
tableData: []
}
},
methods: {
handleDelete (params) {
console.log(params)
},
exportExcel () {
this.$refs.tables.exportCsv({
filename: `table-${(new Date()).valueOf()}.csv`
})
}
},
mounted () {
getTableData().then(res => {
this.tableData = res.data
})
}
}
</script>
<style>
</style>

View File

@ -0,0 +1,69 @@
<template>
<div>
<Row>
<i-col>
<Card>
<Row>
<i-col span="4">
<Button type="primary" @click="showModal">显示可拖动弹窗</Button>
<Button v-draggable="buttonOptions" class="draggable-btn">这个按钮也是可以拖动的</Button>
</i-col>
<i-col span="20">
<div class="intro-con">
&lt;Modal v-draggable="options" v-model="visible"&gt;标题&lt;/Modal&gt;
<pre class="code-con">
options = {
trigger: '.ivu-modal-body',
body: '.ivu-modal'
}
</pre>
</div>
</i-col>
</Row>
</Card>
</i-col>
</Row>
<Modal v-draggable="options" v-model="modalVisible">
拖动这里即可拖动整个弹窗
</Modal>
</div>
</template>
<script>
export default {
name: 'directive_page',
data () {
return {
modalVisible: false,
options: {
trigger: '.ivu-modal-body',
body: '.ivu-modal',
recover: true
},
buttonOptions: {
trigger: '.draggable-btn',
body: '.draggable-btn'
}
}
},
methods: {
showModal () {
this.modalVisible = true
}
}
}
</script>
<style>
.intro-con{
height: 140px;
}
.draggable-btn{
margin-top: 20px;
}
.code-con{
width: 400px;
background: #F9F9F9;
padding-top: 10px;
}
</style>

View File

@ -16,11 +16,11 @@
</template>
<script>
import loginForm from '_c/login-form'
import LoginForm from '_c/login-form'
import { mapActions } from 'vuex'
export default {
components: {
loginForm
LoginForm
},
methods: {
...mapActions([

View File

@ -1,15 +1,22 @@
<template>
<div class="custom-bread-crumb">
<Breadcrumb :style="{fontSize: `${fontSize}px`}">
<BreadcrumbItem v-for="item in list" :to="item.to" :key="`bread-crumb-${item.name}`"><Icon v-if="showIcon" :type="item.icon" style="margin-right: 4px;" />{{ showTitle(item) }}</BreadcrumbItem>
<BreadcrumbItem v-for="item in list" :to="item.to" :key="`bread-crumb-${item.name}`">
<common-icon style="margin-right: 4px;" :type="item.icon || ''"/>
{{ showTitle(item) }}
</BreadcrumbItem>
</Breadcrumb>
</div>
</template>
<script>
import { showTitle } from '_c/common/util'
import CommonIcon from '_c/common-icon'
import './custom-bread-crumb.less'
export default {
name: 'customBreadCrumb',
components: {
CommonIcon
},
props: {
list: {
type: Array,
@ -27,6 +34,13 @@ export default {
methods: {
showTitle (item) {
return showTitle(item, this)
},
isCustomIcon (iconName) {
return iconName.indexOf('_') === 0
},
getCustomIconName (iconName) {
console.log(iconName)
return iconName.slice(1)
}
}
}

View File

@ -12,7 +12,7 @@ import siderTrigger from './sider-trigger'
import customBreadCrumb from './custom-bread-crumb'
import './header-bar.less'
export default {
name: 'headerBar',
name: 'HeaderBar',
components: {
siderTrigger,
customBreadCrumb

View File

@ -1,2 +1,2 @@
import headerBar from './header-bar'
export default headerBar
import HeaderBar from './header-bar'
export default HeaderBar

View File

@ -13,7 +13,7 @@
import mixin from './mixin'
import itemMixin from './item-mixin'
export default {
name: 'collapsedMenu',
name: 'CollapsedMenu',
mixins: [ mixin, itemMixin ],
props: {
hideTitle: {

View File

@ -1,2 +1,2 @@
import sideMenu from './side-menu.vue'
export default sideMenu
import SideMenu from './side-menu.vue'
export default SideMenu

View File

@ -1,10 +1,14 @@
import CommonIcon from '_c/common-icon'
export default {
components: {
CommonIcon
},
methods: {
showTitle (item) {
return this.$config.useI18n ? this.$t(item.name) : ((item.meta && item.meta.title) || item.name)
},
showChildren (item) {
return item.children && item.children.length !== 0
return item.children && item.children.length > 1
}
}
}

View File

@ -5,8 +5,14 @@
<span>{{ showTitle(parentItem) }}</span>
</template>
<template v-for="item in children">
<side-menu-item v-if="item.children && item.children.length !== 0" :key="`menu-${item.name}`" :parent-item="item"></side-menu-item>
<menu-item v-else :name="`${item.name}`" :key="`menu-${item.name}`"><Icon :type="item.icon"/><span>{{ showTitle(item) }}</span></menu-item>
<template v-if="item.children && item.children.length === 1">
<side-menu-item v-if="showChildren(item)" :key="`menu-${item.name}`" :parent-item="item"></side-menu-item>
<menu-item v-else :name="`${item.children[0].name}`" :key="`menu-${item.children[0].name}`"><common-icon :type="item.children[0].icon || ''"/><span>{{ showTitle(item.children[0]) }}</span></menu-item>
</template>
<template v-else>
<side-menu-item v-if="showChildren(item)" :key="`menu-${item.name}`" :parent-item="item"></side-menu-item>
<menu-item v-else :name="`${item.name}`" :key="`menu-${item.name}`"><common-icon :type="item.icon || ''"/><span>{{ showTitle(item) }}</span></menu-item>
</template>
</template>
</Submenu>
</template>
@ -14,7 +20,7 @@
import mixin from './mixin'
import itemMixin from './item-mixin'
export default {
name: 'sideMenuItem',
name: 'SideMenuItem',
mixins: [ mixin, itemMixin ]
}
</script>

View File

@ -7,19 +7,28 @@
user-select: none;
.menu-collapsed{
padding-top: 10px;
a.drop-menu-a{
display: inline-block;
padding: 6px 15px;
width: 100%;
text-align: center;
color: #495060;
}
.ivu-dropdown{
width: 100%;
.ivu-dropdown-rel button{
.ivu-dropdown-rel a{
width: 100%;
}
}
.ivu-tooltip{
width: 100%;
.ivu-tooltip-rel{
width: 100%;
}
.ivu-tooltip-popper .ivu-tooltip-content{
.ivu-tooltip-arrow{
border-right-color: #fff;
}
.ivu-tooltip-inner{
background: #fff;
color: #495060;
}
}
}
.menu-title{
margin-left: 6px;
}
@ -28,4 +37,11 @@
margin-left: 4px;
}
}
a.drop-menu-a{
display: inline-block;
padding: 6px 15px;
width: 100%;
text-align: center;
color: #495060;
}
}

View File

@ -3,12 +3,23 @@
<slot></slot>
<Menu ref="menu" v-show="!collapsed" :active-name="activeName" :open-names="openedNames" :accordion="accordion" :theme="theme" width="auto" @on-select="handleSelect">
<template v-for="item in menuList">
<side-menu-item v-if="showChildren(item)" :key="`menu-${item.name}`" :parent-item="item"></side-menu-item>
<menu-item v-else :name="`${item.name}`" :key="`menu-${item.name}`"><Icon :type="item.icon"/><span>{{ showTitle(item) }}</span></menu-item>
<template v-if="item.children && item.children.length === 1">
<side-menu-item v-if="showChildren(item)" :key="`menu-${item.name}`" :parent-item="item"></side-menu-item>
<menu-item v-else :name="`${item.children[0].name}`" :key="`menu-${item.children[0].name}`"><common-icon :type="item.children[0].icon || ''"/><span>{{ showTitle(item.children[0]) }}</span></menu-item>
</template>
<template v-else>
<side-menu-item v-if="showChildren(item)" :key="`menu-${item.name}`" :parent-item="item"></side-menu-item>
<menu-item v-else :name="`${item.name}`" :key="`menu-${item.name}`"><common-icon :type="item.icon || ''"/><span>{{ showTitle(item) }}</span></menu-item>
</template>
</template>
</Menu>
<div class="menu-collapsed" v-show="collapsed" :list="menuList">
<collapsed-menu @on-click="handleSelect" hide-title :root-icon-size="rootIconSize" :icon-size="iconSize" :theme="theme" v-for="item in menuList" :parent-item="item" :key="`drop-menu-${item.name}`"></collapsed-menu>
<template v-for="item in menuList">
<collapsed-menu v-if="item.children && item.children.length > 1" @on-click="handleSelect" hide-title :root-icon-size="rootIconSize" :icon-size="iconSize" :theme="theme" :parent-item="item" :key="`drop-menu-${item.name}`"></collapsed-menu>
<Tooltip v-else :content="item.children[0].meta.title" placement="right" :key="`drop-menu-${item.children[0].name}`">
<a @click="handleSelect(item.children[0].name)" class="drop-menu-a" :style="{textAlign: 'center'}"><Icon :size="rootIconSize" :color="textColor" :type="item.children[0].icon"/></a>
</Tooltip>
</template>
</div>
</div>
</template>
@ -17,12 +28,13 @@ import sideMenuItem from './side-menu-item.vue'
import collapsedMenu from './collapsed-menu.vue'
import { getUnion } from '@/libs/tools'
import mixin from './mixin'
export default {
name: 'sideMenu',
name: 'SideMenu',
mixins: [ mixin ],
components: {
sideMenuItem,
collapsedMenu
SideMenuItem,
CollapsedMenu
},
props: {
menuList: {
@ -69,6 +81,11 @@ export default {
return this.$route.matched.map(item => item.name).filter(item => item !== name)
}
},
computed: {
textColor () {
return this.theme === 'dark' ? '#fff' : '#495060'
}
},
watch: {
activeName (name) {
if (this.accordion) this.openedNames = this.getOpenedNamesByActiveName(name)

View File

@ -1,2 +1,2 @@
import tagsNav from './tags-nav.vue'
export default tagsNav
import TagsNav from './tags-nav.vue'
export default TagsNav

View File

@ -28,7 +28,7 @@
:name="item.name"
@on-close="handleClose"
@click.native="handleClick(item)"
:closable="item.name==='home_index'?false:true"
:closable="item.name !== 'home'"
:color="item.name === value.name ? 'blue' : 'default'"
>{{ showTitleInside(item) }}</Tag>
</transition-group>
@ -40,7 +40,7 @@
<script>
import { showTitle } from '@/libs/util'
export default {
name: 'tagsNav',
name: 'TagsNav',
props: {
value: Object,
list: {

View File

@ -1,2 +1,2 @@
import user from './user.vue'
export default user
import User from './user.vue'
export default User

View File

@ -14,7 +14,7 @@
import './user.less'
import { mapActions } from 'vuex'
export default {
name: 'user',
name: 'User',
props: {
userAvator: {
type: String,

View File

@ -19,4 +19,13 @@
background:#F0F0F0;
overflow: hidden;
}
.content-wrapper{
padding: 24px;
}
}
.ivu-menu-item > i{
margin-right: 12px !important;
}
.ivu-menu-submenu > .ivu-menu > .ivu-menu-item > i {
margin-right: 8px !important;
}

View File

@ -20,7 +20,7 @@
<div class="tag-nav-wrapper">
<tags-nav :value="$route" @input="handleClick" :list="tagNavList" @on-close="handleCloseTag"/>
</div>
<Content>
<Content class="content-wrapper">
<keep-alive :include="cacheList">
<router-view/>
</keep-alive>
@ -31,22 +31,22 @@
</Layout>
</template>
<script>
import sideMenu from './components/side-menu'
import headerBar from './components/header-bar'
import tagsNav from './components/tags-nav'
import user from './components/user'
import SideMenu from './components/side-menu'
import HeaderBar from './components/header-bar'
import TagsNav from './components/tags-nav'
import User from './components/user'
import { mapMutations, mapActions } from 'vuex'
import { getNewTagList } from '@/libs/util'
import { getNewTagList, getNextName } from '@/libs/util'
import minLogo from '@/assets/images/logo-min.jpg'
import maxLogo from '@/assets/images/logo.jpg'
import './main.less'
export default {
name: 'Main',
components: {
sideMenu,
headerBar,
tagsNav,
user
SideMenu,
HeaderBar,
TagsNav,
User
},
data () {
return {
@ -59,11 +59,14 @@ export default {
tagNavList () {
return this.$store.state.app.tagNavList
},
tagRouter () {
return this.$store.state.app.tagRouter
},
userAvator () {
return this.$store.state.user.avatorImgPath
},
cacheList () {
return this.tagNavList.length ? this.tagNavList.filter(item => !(item.meta && item.meta.notCache)) : []
return this.tagNavList.length ? this.tagNavList.filter(item => !(item.meta && item.meta.notCache)).map(item => item.name) : []
},
menuList () {
return this.$store.getters.menuList
@ -86,9 +89,11 @@ export default {
handleCollapsedChange (state) {
this.collapsed = state
},
handleCloseTag (res, type) {
handleCloseTag (res, type, name) {
const nextName = getNextName(this.tagNavList, name)
this.setTagNavList(res)
if (type === 'all') this.turnToPage('home')
else if (this.$route.name === name) this.$router.push({ name: nextName })
},
handleClick (item) {
this.turnToPage(item.name)

View File

@ -0,0 +1,77 @@
<template>
<Row :gutter="10">
<i-col span="12">
<Card>
<div class="update-paste-con">
<paste-editor v-model="pasteDataArr" @on-success="handleSuccess" @on-error="handleError"/>
</div>
<div class="update-paste-btn-con">
<span class="paste-tip">使用Tab键换列使用回车键换行</span>
<Button type="primary" style="float: right;" @click="handleShow">显示表格数据</Button>
</div>
</Card>
</i-col>
<i-col span="12">
<Card>
<Table :height="400" :columns="columns" :data="tableData"/>
</Card>
</i-col>
</Row>
</template>
<script>
import PasteEditor from '_c/paste-editor'
import { getTableDataFromArray } from '@/libs/util'
export default {
name: 'update_paste_page',
components: {
PasteEditor
},
data () {
return {
pasteDataArr: [],
columns: [],
tableData: [],
validated: true,
errorIndex: 0
}
},
methods: {
handleSuccess () {
this.validated = true
},
handleError (index) {
this.validated = false
this.errorIndex = index
},
handleShow () {
if (!this.validated) {
this.$Notice.error({
title: '您的内容不规范',
desc: `您的第${this.errorIndex + 1}行数据不规范,请修改`
})
} else {
let { columns, tableData } = getTableDataFromArray(this.pasteDataArr)
this.columns = columns
this.tableData = tableData
}
}
}
}
</script>
<style lang="less">
.update-paste{
&-con{
height: 350px;
}
&-btn-con{
box-sizing: content-box;
height: 30px;
padding: 15px 0 5px;
}
}
.paste-tip{
color: #19be6b;
}
</style>

View File

@ -0,0 +1,55 @@
<template>
<Row :gutter="10">
<i-col span="6">
<Card>
<Upload action="" :before-upload="beforeUpload">
<Button type="ghost" icon="ios-cloud-upload-outline">上传Csv文件</Button>
&nbsp;&nbsp;&nbsp;&nbsp;点击上传Csv文件
</Upload>
<p>util.js提供两个方法用来实现这个功能</p>
<p class="update-table-intro"><Icon style="margin-right: 10px;" :size="10" type="heart"/><span class="code-high-line">getArrayFromFile</span>将Csv文件解析为二维数组</p>
<p class="update-table-intro"><Icon style="margin-right: 10px;" :size="10" type="heart"/><span class="code-high-line">getTableDataFromArray</span>将二维数组转为表格数据具体请看文档</p>
</Card>
</i-col>
<i-col span="18">
<Table :height="500" :columns="columns" :data="tableData"/>
</i-col>
</Row>
</template>
<script>
import { getArrayFromFile, getTableDataFromArray } from '@/libs/util'
export default {
name: 'update_table_page',
data () {
return {
columns: [],
tableData: []
}
},
methods: {
beforeUpload (file) {
getArrayFromFile(file).then(data => {
let {columns, tableData} = getTableDataFromArray(data)
this.columns = columns
this.tableData = tableData
}).catch(() => {
this.$Notice.warning({
title: '只能上传Csv文件',
desc: '只能上传Csv文件请重新上传'
})
})
return false
}
}
}
</script>
<style>
.update-table-intro{
margin-top: 10px;
}
.code-high-line{
color: #2d8cf0;
}
</style>