+
@@ -45,7 +45,8 @@ export default {
data() {
return {
visible: false,
- interval: null
+ interval: null,
+ isMoving: false
}
},
mounted() {
@@ -62,13 +63,16 @@ export default {
this.visible = window.pageYOffset > this.visibilityHeight
},
backToTop() {
+ if (this.isMoving) return
const start = window.pageYOffset
let i = 0
+ this.isMoving = true
this.interval = setInterval(() => {
const next = Math.floor(this.easeInOutQuad(10 * i, start, -start, 500))
if (next <= this.backPosition) {
window.scrollTo(0, this.backPosition)
clearInterval(this.interval)
+ this.isMoving = false
} else {
window.scrollTo(0, next)
}
diff --git a/litemall-admin/src/components/Breadcrumb/index.vue b/litemall-admin/src/components/Breadcrumb/index.vue
index 13c1a2ab..32a8fb69 100644
--- a/litemall-admin/src/components/Breadcrumb/index.vue
+++ b/litemall-admin/src/components/Breadcrumb/index.vue
@@ -1,20 +1,19 @@
-
- {{item.meta.title}}
- {{item.meta.title}}
+
+ {{ generateTitle(item.meta.title) }}
+ {{ generateTitle(item.meta.title) }}
diff --git a/litemall-admin/src/components/Charts/lineMarker.vue b/litemall-admin/src/components/Charts/lineMarker.vue
deleted file mode 100644
index e8dce6da..00000000
--- a/litemall-admin/src/components/Charts/lineMarker.vue
+++ /dev/null
@@ -1,225 +0,0 @@
-
-
-
-
-
diff --git a/litemall-admin/src/components/Charts/mixChart.vue b/litemall-admin/src/components/Charts/mixChart.vue
deleted file mode 100644
index 6454dd36..00000000
--- a/litemall-admin/src/components/Charts/mixChart.vue
+++ /dev/null
@@ -1,268 +0,0 @@
-
-
-
-
-
diff --git a/litemall-admin/src/components/Hamburger/index.vue b/litemall-admin/src/components/Hamburger/index.vue
index fb6aeacd..26472e23 100644
--- a/litemall-admin/src/components/Hamburger/index.vue
+++ b/litemall-admin/src/components/Hamburger/index.vue
@@ -1,20 +1,34 @@
+
+
+
diff --git a/litemall-admin/src/components/Pagination/index.vue b/litemall-admin/src/components/Pagination/index.vue
new file mode 100644
index 00000000..9698232b
--- /dev/null
+++ b/litemall-admin/src/components/Pagination/index.vue
@@ -0,0 +1,99 @@
+
+
+
+
+
+
+
diff --git a/litemall-admin/src/components/Screenfull/index.vue b/litemall-admin/src/components/Screenfull/index.vue
index 56de914e..4cdcb568 100644
--- a/litemall-admin/src/components/Screenfull/index.vue
+++ b/litemall-admin/src/components/Screenfull/index.vue
@@ -1,15 +1,28 @@
@@ -18,7 +31,7 @@
import screenfull from 'screenfull'
export default {
- name: 'screenfull',
+ name: 'Screenfull',
props: {
width: {
type: Number,
diff --git a/litemall-admin/src/components/ScrollBar/index.vue b/litemall-admin/src/components/ScrollBar/index.vue
deleted file mode 100644
index dc32f5c0..00000000
--- a/litemall-admin/src/components/ScrollBar/index.vue
+++ /dev/null
@@ -1,57 +0,0 @@
-
-
-
-
-
-
-
diff --git a/litemall-admin/src/components/ScrollPane/index.vue b/litemall-admin/src/components/ScrollPane/index.vue
index 1ce68c11..89998e80 100644
--- a/litemall-admin/src/components/ScrollPane/index.vue
+++ b/litemall-admin/src/components/ScrollPane/index.vue
@@ -1,16 +1,14 @@
-
+
+
+
+
+
+
diff --git a/litemall-admin/src/components/Sticky/index.vue b/litemall-admin/src/components/Sticky/index.vue
deleted file mode 100644
index 2c9657be..00000000
--- a/litemall-admin/src/components/Sticky/index.vue
+++ /dev/null
@@ -1,76 +0,0 @@
-
-
-
-
-
diff --git a/litemall-admin/src/components/SvgIcon/index.vue b/litemall-admin/src/components/SvgIcon/index.vue
index e331a27e..12a1f58d 100644
--- a/litemall-admin/src/components/SvgIcon/index.vue
+++ b/litemall-admin/src/components/SvgIcon/index.vue
@@ -1,19 +1,20 @@
+
+
diff --git a/litemall-admin/src/components/Upload/singleImage.vue b/litemall-admin/src/components/Upload/singleImage.vue
deleted file mode 100644
index c03127c3..00000000
--- a/litemall-admin/src/components/Upload/singleImage.vue
+++ /dev/null
@@ -1,128 +0,0 @@
-
-
-
-
- 将文件拖到此处,或点击上传
-
-
-
-
![]()
-
-
-
-
-
-
-
-
-
-
-
diff --git a/litemall-admin/src/components/Upload/singleImage2.vue b/litemall-admin/src/components/Upload/singleImage2.vue
deleted file mode 100644
index 85bec772..00000000
--- a/litemall-admin/src/components/Upload/singleImage2.vue
+++ /dev/null
@@ -1,123 +0,0 @@
-
-
-
-
- Drag或点击上传
-
-
-
-
![]()
-
-
-
-
-
-
-
-
-
-
-
diff --git a/litemall-admin/src/components/Upload/singleImage3.vue b/litemall-admin/src/components/Upload/singleImage3.vue
deleted file mode 100644
index 68f26fcc..00000000
--- a/litemall-admin/src/components/Upload/singleImage3.vue
+++ /dev/null
@@ -1,150 +0,0 @@
-
-
-
-
- 将文件拖到此处,或点击上传
-
-
-
-
![]()
-
-
-
-
-
-
-
-
![]()
-
-
-
-
-
-
-
-
-
-
-
diff --git a/litemall-admin/src/directive/clipboard/clipboard.js b/litemall-admin/src/directive/clipboard/clipboard.js
new file mode 100644
index 00000000..514aad20
--- /dev/null
+++ b/litemall-admin/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 should 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/litemall-admin/src/directive/clipboard/index.js b/litemall-admin/src/directive/clipboard/index.js
new file mode 100644
index 00000000..02c98163
--- /dev/null
+++ b/litemall-admin/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/litemall-admin/src/directive/permission/index.js b/litemall-admin/src/directive/permission/index.js
new file mode 100644
index 00000000..e5dadd36
--- /dev/null
+++ b/litemall-admin/src/directive/permission/index.js
@@ -0,0 +1,13 @@
+import permission from './permission'
+
+const install = function(Vue) {
+ Vue.directive('permission', permission)
+}
+
+if (window.Vue) {
+ window['permission'] = permission
+ Vue.use(install); // eslint-disable-line
+}
+
+permission.install = install
+export default permission
diff --git a/litemall-admin/src/directive/permission/permission.js b/litemall-admin/src/directive/permission/permission.js
new file mode 100644
index 00000000..17b85d79
--- /dev/null
+++ b/litemall-admin/src/directive/permission/permission.js
@@ -0,0 +1,23 @@
+
+import store from '@/store'
+
+export default{
+ inserted(el, binding, vnode) {
+ const { value } = binding
+ const roles = store.getters && store.getters.roles
+
+ if (value && value instanceof Array && value.length > 0) {
+ const permissionRoles = value
+
+ const hasPermission = roles.some(role => {
+ return permissionRoles.includes(role)
+ })
+
+ if (!hasPermission) {
+ el.parentNode && el.parentNode.removeChild(el)
+ }
+ } else {
+ throw new Error(`need roles! Like v-permission="['admin','editor']"`)
+ }
+ }
+}
diff --git a/litemall-admin/src/filters/index.js b/litemall-admin/src/filters/index.js
new file mode 100644
index 00000000..b164513a
--- /dev/null
+++ b/litemall-admin/src/filters/index.js
@@ -0,0 +1,42 @@
+// set function parseTime,formatTime to filter
+export { parseTime, formatTime } from '@/utils'
+
+function pluralize(time, label) {
+ if (time === 1) {
+ return time + label
+ }
+ return time + label + 's'
+}
+
+export function timeAgo(time) {
+ const between = Date.now() / 1000 - Number(time)
+ if (between < 3600) {
+ return pluralize(~~(between / 60), ' minute')
+ } else if (between < 86400) {
+ return pluralize(~~(between / 3600), ' hour')
+ } else {
+ return pluralize(~~(between / 86400), ' day')
+ }
+}
+
+/* 数字 格式化*/
+export function numberFormatter(num, digits) {
+ const si = [
+ { value: 1E18, symbol: 'E' },
+ { value: 1E15, symbol: 'P' },
+ { value: 1E12, symbol: 'T' },
+ { value: 1E9, symbol: 'G' },
+ { value: 1E6, symbol: 'M' },
+ { value: 1E3, symbol: 'k' }
+ ]
+ for (let i = 0; i < si.length; i++) {
+ if (num >= si[i].value) {
+ return (num / si[i].value + 0.1).toFixed(digits).replace(/\.0+$|(\.[0-9]*[1-9])0+$/, '$1') + si[i].symbol
+ }
+ }
+ return num.toString()
+}
+
+export function toThousandFilter(num) {
+ return (+num || 0).toString().replace(/^-?\d+/g, m => m.replace(/(?=(?!\b)(\d{3})+$)/g, ','))
+}
diff --git a/litemall-admin/src/icons/index.js b/litemall-admin/src/icons/index.js
index 14e2e130..c1c2e792 100644
--- a/litemall-admin/src/icons/index.js
+++ b/litemall-admin/src/icons/index.js
@@ -4,6 +4,6 @@ import SvgIcon from '@/components/SvgIcon'// svg组件
// register globally
Vue.component('svg-icon', SvgIcon)
-const requireAll = requireContext => requireContext.keys().map(requireContext)
const req = require.context('./svg', false, /\.svg$/)
+const requireAll = requireContext => requireContext.keys().map(requireContext)
requireAll(req)
diff --git a/litemall-admin/src/icons/svg/404.svg b/litemall-admin/src/icons/svg/404.svg
index bc5bc9fa..6df50190 100644
--- a/litemall-admin/src/icons/svg/404.svg
+++ b/litemall-admin/src/icons/svg/404.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/litemall-admin/src/icons/svg/bug.svg b/litemall-admin/src/icons/svg/bug.svg
index a12a9394..05a150dc 100644
--- a/litemall-admin/src/icons/svg/bug.svg
+++ b/litemall-admin/src/icons/svg/bug.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/litemall-admin/src/icons/svg/chart.svg b/litemall-admin/src/icons/svg/chart.svg
index b1b31336..27728fb0 100644
--- a/litemall-admin/src/icons/svg/chart.svg
+++ b/litemall-admin/src/icons/svg/chart.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/litemall-admin/src/icons/svg/clipboard.svg b/litemall-admin/src/icons/svg/clipboard.svg
index cf1c9b0c..90923ff6 100644
--- a/litemall-admin/src/icons/svg/clipboard.svg
+++ b/litemall-admin/src/icons/svg/clipboard.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/litemall-admin/src/icons/svg/component.svg b/litemall-admin/src/icons/svg/component.svg
index a8008c84..207ada34 100644
--- a/litemall-admin/src/icons/svg/component.svg
+++ b/litemall-admin/src/icons/svg/component.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/litemall-admin/src/icons/svg/dashboard.svg b/litemall-admin/src/icons/svg/dashboard.svg
index bee42507..5317d370 100644
--- a/litemall-admin/src/icons/svg/dashboard.svg
+++ b/litemall-admin/src/icons/svg/dashboard.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/litemall-admin/src/icons/svg/documentation.svg b/litemall-admin/src/icons/svg/documentation.svg
index 405c2ad7..70431228 100644
--- a/litemall-admin/src/icons/svg/documentation.svg
+++ b/litemall-admin/src/icons/svg/documentation.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/litemall-admin/src/icons/svg/drag.svg b/litemall-admin/src/icons/svg/drag.svg
index 819c8d50..4185d3ce 100644
--- a/litemall-admin/src/icons/svg/drag.svg
+++ b/litemall-admin/src/icons/svg/drag.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/litemall-admin/src/icons/svg/edit.svg b/litemall-admin/src/icons/svg/edit.svg
new file mode 100644
index 00000000..0306a867
--- /dev/null
+++ b/litemall-admin/src/icons/svg/edit.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/litemall-admin/src/icons/svg/email.svg b/litemall-admin/src/icons/svg/email.svg
index 8a87e147..055e5013 100644
--- a/litemall-admin/src/icons/svg/email.svg
+++ b/litemall-admin/src/icons/svg/email.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/litemall-admin/src/icons/svg/example.svg b/litemall-admin/src/icons/svg/example.svg
index 681422ea..46f42b53 100644
--- a/litemall-admin/src/icons/svg/example.svg
+++ b/litemall-admin/src/icons/svg/example.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/litemall-admin/src/icons/svg/excel.svg b/litemall-admin/src/icons/svg/excel.svg
index e5dd5cec..59d54b2c 100644
--- a/litemall-admin/src/icons/svg/excel.svg
+++ b/litemall-admin/src/icons/svg/excel.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/litemall-admin/src/icons/svg/eye.svg b/litemall-admin/src/icons/svg/eye.svg
index 194aa45c..16ed2d87 100644
--- a/litemall-admin/src/icons/svg/eye.svg
+++ b/litemall-admin/src/icons/svg/eye.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/litemall-admin/src/icons/svg/form.svg b/litemall-admin/src/icons/svg/form.svg
index 79716f06..dcbaa185 100644
--- a/litemall-admin/src/icons/svg/form.svg
+++ b/litemall-admin/src/icons/svg/form.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/litemall-admin/src/icons/svg/guide 2.svg b/litemall-admin/src/icons/svg/guide 2.svg
new file mode 100644
index 00000000..d053bd73
--- /dev/null
+++ b/litemall-admin/src/icons/svg/guide 2.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/litemall-admin/src/icons/svg/guide.svg b/litemall-admin/src/icons/svg/guide.svg
new file mode 100644
index 00000000..b2710017
--- /dev/null
+++ b/litemall-admin/src/icons/svg/guide.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/litemall-admin/src/icons/svg/icon.svg b/litemall-admin/src/icons/svg/icon.svg
index 906af96a..82be8eee 100644
--- a/litemall-admin/src/icons/svg/icon.svg
+++ b/litemall-admin/src/icons/svg/icon.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/litemall-admin/src/icons/svg/international.svg b/litemall-admin/src/icons/svg/international.svg
index 6912767d..e9b56eee 100644
--- a/litemall-admin/src/icons/svg/international.svg
+++ b/litemall-admin/src/icons/svg/international.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/litemall-admin/src/icons/svg/language.svg b/litemall-admin/src/icons/svg/language.svg
index 2baf7431..96d00288 100644
--- a/litemall-admin/src/icons/svg/language.svg
+++ b/litemall-admin/src/icons/svg/language.svg
@@ -1 +1 @@
-
+
\ No newline at end of file
diff --git a/litemall-admin/src/icons/svg/link.svg b/litemall-admin/src/icons/svg/link.svg
new file mode 100644
index 00000000..07090147
--- /dev/null
+++ b/litemall-admin/src/icons/svg/link.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/litemall-admin/src/icons/svg/list.svg b/litemall-admin/src/icons/svg/list.svg
new file mode 100644
index 00000000..20259edd
--- /dev/null
+++ b/litemall-admin/src/icons/svg/list.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/litemall-admin/src/icons/svg/lock.svg b/litemall-admin/src/icons/svg/lock.svg
index 37c60701..74fee543 100644
--- a/litemall-admin/src/icons/svg/lock.svg
+++ b/litemall-admin/src/icons/svg/lock.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/litemall-admin/src/icons/svg/message.svg b/litemall-admin/src/icons/svg/message.svg
index d807b002..14ca8172 100644
--- a/litemall-admin/src/icons/svg/message.svg
+++ b/litemall-admin/src/icons/svg/message.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/litemall-admin/src/icons/svg/money.svg b/litemall-admin/src/icons/svg/money.svg
index d4fcb9ca..c1580de1 100644
--- a/litemall-admin/src/icons/svg/money.svg
+++ b/litemall-admin/src/icons/svg/money.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/litemall-admin/src/icons/svg/nested.svg b/litemall-admin/src/icons/svg/nested.svg
new file mode 100644
index 00000000..06713a86
--- /dev/null
+++ b/litemall-admin/src/icons/svg/nested.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/litemall-admin/src/icons/svg/password.svg b/litemall-admin/src/icons/svg/password.svg
index 920b500b..e291d85d 100644
--- a/litemall-admin/src/icons/svg/password.svg
+++ b/litemall-admin/src/icons/svg/password.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/litemall-admin/src/icons/svg/people.svg b/litemall-admin/src/icons/svg/people.svg
index 3985ab51..2bd54aeb 100644
--- a/litemall-admin/src/icons/svg/people.svg
+++ b/litemall-admin/src/icons/svg/people.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/litemall-admin/src/icons/svg/peoples.svg b/litemall-admin/src/icons/svg/peoples.svg
index 2dccfcc0..2c911615 100644
--- a/litemall-admin/src/icons/svg/peoples.svg
+++ b/litemall-admin/src/icons/svg/peoples.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/litemall-admin/src/icons/svg/qq.svg b/litemall-admin/src/icons/svg/qq.svg
index 97aee717..ee13d4ec 100644
--- a/litemall-admin/src/icons/svg/qq.svg
+++ b/litemall-admin/src/icons/svg/qq.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/litemall-admin/src/icons/svg/shopping.svg b/litemall-admin/src/icons/svg/shopping.svg
new file mode 100644
index 00000000..87513e7c
--- /dev/null
+++ b/litemall-admin/src/icons/svg/shopping.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/litemall-admin/src/icons/svg/shoppingCard.svg b/litemall-admin/src/icons/svg/shoppingCard.svg
deleted file mode 100644
index cdebbdb4..00000000
--- a/litemall-admin/src/icons/svg/shoppingCard.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/litemall-admin/src/icons/svg/size.svg b/litemall-admin/src/icons/svg/size.svg
new file mode 100644
index 00000000..ddb25b8d
--- /dev/null
+++ b/litemall-admin/src/icons/svg/size.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/litemall-admin/src/icons/svg/star.svg b/litemall-admin/src/icons/svg/star.svg
index 685a301d..6cf86e66 100644
--- a/litemall-admin/src/icons/svg/star.svg
+++ b/litemall-admin/src/icons/svg/star.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/litemall-admin/src/icons/svg/tab.svg b/litemall-admin/src/icons/svg/tab.svg
index 17aa088b..b4b48e48 100644
--- a/litemall-admin/src/icons/svg/tab.svg
+++ b/litemall-admin/src/icons/svg/tab.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/litemall-admin/src/icons/svg/table.svg b/litemall-admin/src/icons/svg/table.svg
index 083bc8cc..dbfe5d64 100644
--- a/litemall-admin/src/icons/svg/table.svg
+++ b/litemall-admin/src/icons/svg/table.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/litemall-admin/src/icons/svg/theme.svg b/litemall-admin/src/icons/svg/theme.svg
index d5c2e5ad..5982a2f7 100644
--- a/litemall-admin/src/icons/svg/theme.svg
+++ b/litemall-admin/src/icons/svg/theme.svg
@@ -1 +1 @@
-
+
\ No newline at end of file
diff --git a/litemall-admin/src/icons/svg/tree.svg b/litemall-admin/src/icons/svg/tree.svg
new file mode 100644
index 00000000..dd4b7dd2
--- /dev/null
+++ b/litemall-admin/src/icons/svg/tree.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/litemall-admin/src/icons/svg/user.svg b/litemall-admin/src/icons/svg/user.svg
index 5971deeb..0ba0716a 100644
--- a/litemall-admin/src/icons/svg/user.svg
+++ b/litemall-admin/src/icons/svg/user.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/litemall-admin/src/icons/svg/wechat.svg b/litemall-admin/src/icons/svg/wechat.svg
index d88a64bc..9e18b717 100644
--- a/litemall-admin/src/icons/svg/wechat.svg
+++ b/litemall-admin/src/icons/svg/wechat.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/litemall-admin/src/icons/svg/zip.svg b/litemall-admin/src/icons/svg/zip.svg
index e9a9d012..f806fc48 100644
--- a/litemall-admin/src/icons/svg/zip.svg
+++ b/litemall-admin/src/icons/svg/zip.svg
@@ -1 +1 @@
-
+
\ No newline at end of file
diff --git a/litemall-admin/src/icons/svgo.yml b/litemall-admin/src/icons/svgo.yml
new file mode 100644
index 00000000..d11906ae
--- /dev/null
+++ b/litemall-admin/src/icons/svgo.yml
@@ -0,0 +1,22 @@
+# replace default config
+
+# multipass: true
+# full: true
+
+plugins:
+
+ # - name
+ #
+ # or:
+ # - name: false
+ # - name: true
+ #
+ # or:
+ # - name:
+ # param1: 1
+ # param2: 2
+
+- removeAttrs:
+ attrs:
+ - 'fill'
+ - 'fill-rule'
diff --git a/litemall-admin/src/lang/en.js b/litemall-admin/src/lang/en.js
new file mode 100644
index 00000000..930f0ad1
--- /dev/null
+++ b/litemall-admin/src/lang/en.js
@@ -0,0 +1,156 @@
+export default {
+ route: {
+ dashboard: 'Dashboard',
+ introduction: 'Introduction',
+ documentation: 'Documentation',
+ guide: 'Guide',
+ permission: 'Permission',
+ pagePermission: 'Page Permission',
+ directivePermission: 'Directive Permission',
+ icons: 'Icons',
+ components: 'Components',
+ componentIndex: 'Introduction',
+ tinymce: 'Tinymce',
+ markdown: 'Markdown',
+ jsonEditor: 'JSON Editor',
+ dndList: 'Dnd List',
+ splitPane: 'SplitPane',
+ avatarUpload: 'Avatar Upload',
+ dropzone: 'Dropzone',
+ sticky: 'Sticky',
+ countTo: 'CountTo',
+ componentMixin: 'Mixin',
+ backToTop: 'BackToTop',
+ dragDialog: 'Drag Dialog',
+ dragSelect: 'Drag Select',
+ dragKanban: 'Drag Kanban',
+ charts: 'Charts',
+ keyboardChart: 'Keyboard Chart',
+ lineChart: 'Line Chart',
+ mixChart: 'Mix Chart',
+ example: 'Example',
+ nested: 'Nested Routes',
+ menu1: 'Menu 1',
+ 'menu1-1': 'Menu 1-1',
+ 'menu1-2': 'Menu 1-2',
+ 'menu1-2-1': 'Menu 1-2-1',
+ 'menu1-2-2': 'Menu 1-2-2',
+ 'menu1-3': 'Menu 1-3',
+ menu2: 'Menu 2',
+ Table: 'Table',
+ dynamicTable: 'Dynamic Table',
+ dragTable: 'Drag Table',
+ inlineEditTable: 'Inline Edit',
+ complexTable: 'Complex Table',
+ treeTable: 'Tree Table',
+ customTreeTable: 'Custom TreeTable',
+ tab: 'Tab',
+ form: 'Form',
+ createArticle: 'Create Article',
+ editArticle: 'Edit Article',
+ articleList: 'Article List',
+ errorPages: 'Error Pages',
+ page401: '401',
+ page404: '404',
+ errorLog: 'Error Log',
+ excel: 'Excel',
+ exportExcel: 'Export Excel',
+ selectExcel: 'Export Selected',
+ uploadExcel: 'Upload Excel',
+ zip: 'Zip',
+ exportZip: 'Export Zip',
+ theme: 'Theme',
+ clipboardDemo: 'Clipboard',
+ i18n: 'I18n',
+ externalLink: 'External Link'
+ },
+ navbar: {
+ logOut: 'Log Out',
+ dashboard: 'Dashboard',
+ github: 'Github',
+ screenfull: 'Screenfull',
+ theme: 'Theme',
+ size: 'Global Size'
+ },
+ login: {
+ title: 'Login Form',
+ logIn: 'Log in',
+ username: 'Username',
+ password: 'Password',
+ any: 'any',
+ thirdparty: 'Or connect with',
+ thirdpartyTips: 'Can not be simulated on local, so please combine you own business simulation! ! !'
+ },
+ documentation: {
+ documentation: 'Documentation',
+ github: 'Github Repository'
+ },
+ permission: {
+ roles: 'Your roles',
+ switchRoles: 'Switch roles'
+ },
+ guide: {
+ description: 'The guide page is useful for some people who entered the project for the first time. You can briefly introduce the features of the project. Demo is based on ',
+ button: 'Show Guide'
+ },
+ components: {
+ documentation: 'Documentation',
+ tinymceTips: 'Rich text editor is a core part of management system, but at the same time is a place with lots of problems. In the process of selecting rich texts, I also walked a lot of detours. The common rich text editors in the market are basically used, and the finally chose Tinymce. See documentation for more detailed rich text editor comparisons and introductions.',
+ dropzoneTips: 'Because my business has special needs, and has to upload images to qiniu, so instead of a third party, I chose encapsulate it by myself. It is very simple, you can see the detail code in @/components/Dropzone.',
+ stickyTips: 'when the page is scrolled to the preset position will be sticky on the top.',
+ backToTopTips1: 'When the page is scrolled to the specified position, the Back to Top button appears in the lower right corner',
+ backToTopTips2: 'You can customize the style of the button, show / hide, height of appearance, height of the return. If you need a text prompt, you can use element-ui el-tooltip elements externally',
+ imageUploadTips: 'Since I was using only the vue@1 version, and it is not compatible with mockjs at the moment, I modified it myself, and if you are going to use it, it is better to use official version.'
+ },
+ table: {
+ dynamicTips1: 'Fixed header, sorted by header order',
+ dynamicTips2: 'Not fixed header, sorted by click order',
+ dragTips1: 'The default order',
+ dragTips2: 'The after dragging order',
+ title: 'Title',
+ importance: 'Imp',
+ type: 'Type',
+ remark: 'Remark',
+ search: 'Search',
+ add: 'Add',
+ export: 'Export',
+ reviewer: 'reviewer',
+ id: 'ID',
+ date: 'Date',
+ author: 'Author',
+ readings: 'Readings',
+ status: 'Status',
+ actions: 'Actions',
+ edit: 'Edit',
+ publish: 'Publish',
+ draft: 'Draft',
+ delete: 'Delete',
+ cancel: 'Cancel',
+ confirm: 'Confirm'
+ },
+ errorLog: {
+ tips: 'Please click the bug icon in the upper right corner',
+ description: 'Now the management system are basically the form of the spa, it enhances the user experience, but it also increases the possibility of page problems, a small negligence may lead to the entire page deadlock. Fortunately Vue provides a way to catch handling exceptions, where you can handle errors or report exceptions.',
+ documentation: 'Document introduction'
+ },
+ excel: {
+ export: 'Export',
+ selectedExport: 'Export Selected Items',
+ placeholder: 'Please enter the file name(default excel-list)'
+ },
+ zip: {
+ export: 'Export',
+ placeholder: 'Please enter the file name(default file)'
+ },
+ theme: {
+ change: 'Change Theme',
+ documentation: 'Theme documentation',
+ tips: 'Tips: It is different from the theme-pick on the navbar is two different skinning methods, each with different application scenarios. Refer to the documentation for details.'
+ },
+ tagsView: {
+ refresh: 'Refresh',
+ close: 'Close',
+ closeOthers: 'Close Others',
+ closeAll: 'Close All'
+ }
+}
diff --git a/litemall-admin/src/lang/index.js b/litemall-admin/src/lang/index.js
new file mode 100644
index 00000000..07a4a01f
--- /dev/null
+++ b/litemall-admin/src/lang/index.js
@@ -0,0 +1,30 @@
+import Vue from 'vue'
+import VueI18n from 'vue-i18n'
+import Cookies from 'js-cookie'
+import elementEnLocale from 'element-ui/lib/locale/lang/en' // element-ui lang
+import elementZhLocale from 'element-ui/lib/locale/lang/zh-CN'// element-ui lang
+import enLocale from './en'
+import zhLocale from './zh'
+
+Vue.use(VueI18n)
+
+const messages = {
+ en: {
+ ...enLocale,
+ ...elementEnLocale
+ },
+ zh: {
+ ...zhLocale,
+ ...elementZhLocale
+ }
+}
+
+const i18n = new VueI18n({
+ // set locale
+ // options: en | zh
+ locale: Cookies.get('language') || 'zh',
+ // set locale messages
+ messages
+})
+
+export default i18n
diff --git a/litemall-admin/src/lang/zh.js b/litemall-admin/src/lang/zh.js
new file mode 100644
index 00000000..46e96ccf
--- /dev/null
+++ b/litemall-admin/src/lang/zh.js
@@ -0,0 +1,156 @@
+export default {
+ route: {
+ dashboard: '首页',
+ introduction: '简述',
+ documentation: '文档',
+ guide: '引导页',
+ permission: '权限测试页',
+ pagePermission: '页面权限',
+ directivePermission: '指令权限',
+ icons: '图标',
+ components: '组件',
+ componentIndex: '介绍',
+ tinymce: '富文本编辑器',
+ markdown: 'Markdown',
+ jsonEditor: 'JSON编辑器',
+ dndList: '列表拖拽',
+ splitPane: 'Splitpane',
+ avatarUpload: '头像上传',
+ dropzone: 'Dropzone',
+ sticky: 'Sticky',
+ countTo: 'CountTo',
+ componentMixin: '小组件',
+ backToTop: '返回顶部',
+ dragDialog: '拖拽 Dialog',
+ dragSelect: '拖拽 Select',
+ dragKanban: '可拖拽看板',
+ charts: '图表',
+ keyboardChart: '键盘图表',
+ lineChart: '折线图',
+ mixChart: '混合图表',
+ example: '综合实例',
+ nested: '路由嵌套',
+ menu1: '菜单1',
+ 'menu1-1': '菜单1-1',
+ 'menu1-2': '菜单1-2',
+ 'menu1-2-1': '菜单1-2-1',
+ 'menu1-2-2': '菜单1-2-2',
+ 'menu1-3': '菜单1-3',
+ menu2: '菜单2',
+ Table: 'Table',
+ dynamicTable: '动态Table',
+ dragTable: '拖拽Table',
+ inlineEditTable: 'Table内编辑',
+ complexTable: '综合Table',
+ treeTable: '树形表格',
+ customTreeTable: '自定义树表',
+ tab: 'Tab',
+ form: '表单',
+ createArticle: '创建文章',
+ editArticle: '编辑文章',
+ articleList: '文章列表',
+ errorPages: '错误页面',
+ page401: '401',
+ page404: '404',
+ errorLog: '错误日志',
+ excel: 'Excel',
+ exportExcel: 'Export Excel',
+ selectExcel: 'Export Selected',
+ uploadExcel: 'Upload Excel',
+ zip: 'Zip',
+ exportZip: 'Export Zip',
+ theme: '换肤',
+ clipboardDemo: 'Clipboard',
+ i18n: '国际化',
+ externalLink: '外链'
+ },
+ navbar: {
+ logOut: '退出登录',
+ dashboard: '首页',
+ github: '项目地址',
+ screenfull: '全屏',
+ theme: '换肤',
+ size: '布局大小'
+ },
+ login: {
+ title: '系统登录',
+ logIn: '登录',
+ username: '账号',
+ password: '密码',
+ any: '随便填',
+ thirdparty: '第三方登录',
+ thirdpartyTips: '本地不能模拟,请结合自己业务进行模拟!!!'
+ },
+ documentation: {
+ documentation: '文档',
+ github: 'Github 地址'
+ },
+ permission: {
+ roles: '你的权限',
+ switchRoles: '切换权限'
+ },
+ guide: {
+ description: '引导页对于一些第一次进入项目的人很有用,你可以简单介绍下项目的功能。本 Demo 是基于',
+ button: '打开引导'
+ },
+ components: {
+ documentation: '文档',
+ tinymceTips: '富文本是管理后台一个核心的功能,但同时又是一个有很多坑的地方。在选择富文本的过程中我也走了不少的弯路,市面上常见的富文本都基本用过了,最终权衡了一下选择了Tinymce。更详细的富文本比较和介绍见',
+ dropzoneTips: '由于我司业务有特殊需求,而且要传七牛 所以没用第三方,选择了自己封装。代码非常的简单,具体代码你可以在这里看到 @/components/Dropzone',
+ stickyTips: '当页面滚动到预设的位置会吸附在顶部',
+ backToTopTips1: '页面滚动到指定位置会在右下角出现返回顶部按钮',
+ backToTopTips2: '可自定义按钮的样式、show/hide、出现的高度、返回的位置 如需文字提示,可在外部使用Element的el-tooltip元素',
+ imageUploadTips: '由于我在使用时它只有vue@1版本,而且和mockjs不兼容,所以自己改造了一下,如果大家要使用的话,优先还是使用官方版本。'
+ },
+ table: {
+ dynamicTips1: '固定表头, 按照表头顺序排序',
+ dynamicTips2: '不固定表头, 按照点击顺序排序',
+ dragTips1: '默认顺序',
+ dragTips2: '拖拽后顺序',
+ title: '标题',
+ importance: '重要性',
+ type: '类型',
+ remark: '点评',
+ search: '搜索',
+ add: '添加',
+ export: '导出',
+ reviewer: '审核人',
+ id: '序号',
+ date: '时间',
+ author: '作者',
+ readings: '阅读数',
+ status: '状态',
+ actions: '操作',
+ edit: '编辑',
+ publish: '发布',
+ draft: '草稿',
+ delete: '删除',
+ cancel: '取 消',
+ confirm: '确 定'
+ },
+ errorLog: {
+ tips: '请点击右上角bug小图标',
+ description: '现在的管理后台基本都是spa的形式了,它增强了用户体验,但同时也会增加页面出问题的可能性,可能一个小小的疏忽就导致整个页面的死锁。好在 Vue 官网提供了一个方法来捕获处理异常,你可以在其中进行错误处理或者异常上报。',
+ documentation: '文档介绍'
+ },
+ excel: {
+ export: '导出',
+ selectedExport: '导出已选择项',
+ placeholder: '请输入文件名(默认excel-list)'
+ },
+ zip: {
+ export: '导出',
+ placeholder: '请输入文件名(默认file)'
+ },
+ theme: {
+ change: '换肤',
+ documentation: '换肤文档',
+ tips: 'Tips: 它区别于 navbar 上的 theme-pick, 是两种不同的换肤方法,各自有不同的应用场景,具体请参考文档。'
+ },
+ tagsView: {
+ refresh: '刷新',
+ close: '关闭',
+ closeOthers: '关闭其它',
+ closeAll: '关闭所有'
+ }
+}
diff --git a/litemall-admin/src/main.js b/litemall-admin/src/main.js
index 0a4f40c1..49940983 100644
--- a/litemall-admin/src/main.js
+++ b/litemall-admin/src/main.js
@@ -1,6 +1,8 @@
import Vue from 'vue'
-import 'normalize.css/normalize.css'// A modern alternative to CSS resets
+import Cookies from 'js-cookie'
+
+import 'normalize.css/normalize.css' // A modern alternative to CSS resets
import Element from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
@@ -11,12 +13,20 @@ import App from './App'
import router from './router'
import store from './store'
+import i18n from './lang' // Internationalization
import './icons' // icon
import './permission' // permission control
-// import './mock' // simulation data
+
+import * as filters from './filters' // global filters
Vue.use(Element, {
- size: 'medium' // set element-ui default size
+ size: Cookies.get('size') || 'medium', // set element-ui default size
+ i18n: (key, value) => i18n.t(key, value)
+})
+
+// register global utility filters.
+Object.keys(filters).forEach(key => {
+ Vue.filter(key, filters[key])
})
Vue.config.productionTip = false
@@ -25,6 +35,6 @@ new Vue({
el: '#app',
router,
store,
- template: '
',
- components: { App }
+ i18n,
+ render: h => h(App)
})
diff --git a/litemall-admin/src/mock/index.js b/litemall-admin/src/mock/index.js
deleted file mode 100644
index d96477a0..00000000
--- a/litemall-admin/src/mock/index.js
+++ /dev/null
@@ -1,11 +0,0 @@
-import Mock from 'mockjs'
-import userAPI from './user'
-
-// 用户相关
-Mock.mock(/\/user\/list/, 'get', userAPI.getList)
-Mock.mock(/\/user\/create/, 'post', userAPI.createUser)
-Mock.mock(/\/user\/update/, 'post', userAPI.updateUser)
-Mock.mock(/\/user\/read/, 'get', userAPI.readUser)
-Mock.mock(/\/user\/delete/, 'post', userAPI.deleteUser)
-
-export default Mock
diff --git a/litemall-admin/src/mock/res.js b/litemall-admin/src/mock/res.js
deleted file mode 100644
index e04ad71f..00000000
--- a/litemall-admin/src/mock/res.js
+++ /dev/null
@@ -1,11 +0,0 @@
-export default {
- ok: data => ({
- errno: 0,
- errmsg: 'success',
- data: data
- }),
- fail: () => ({
- errno: -1,
- errmsg: 'fail'
- })
-}
diff --git a/litemall-admin/src/mock/user.js b/litemall-admin/src/mock/user.js
deleted file mode 100644
index 03d3cf61..00000000
--- a/litemall-admin/src/mock/user.js
+++ /dev/null
@@ -1,53 +0,0 @@
-import Mock from 'mockjs'
-import { param2Obj } from '@/utils'
-import resAPI from './res'
-
-const List = []
-const count = 100
-
-for (let i = 0; i < count; i++) {
- List.push(Mock.mock({
- 'id': '@increment',
- 'username': '@cname',
- 'mobile': '@string("number", 11)',
- 'age|10-30': 0,
- 'birthday': '@date("yyyy-MM-dd")',
- 'gender': '@integer(0, 2)',
- 'status|1': ['可用', '禁用', '删除']
- }))
-}
-
-export default {
- getList: config => {
- const { username, mobile, sort, page = 1, limit = 20 } = param2Obj(config.url)
-
- let mockList = List.filter(item => {
- if (username && item.username.indexOf(username) < 0) return false
- if (mobile && (item.mobile !== mobile)) return false
- return true
- })
-
- if (sort === '-id') {
- mockList = mockList.reverse()
- }
-
- const pageList = mockList.filter((item, index) => index < limit * page && index >= limit * (page - 1))
-
- return resAPI.ok({
- total: mockList.length,
- items: pageList
- })
- },
- createUser: () => {
- return resAPI.ok()
- },
- readUser: () => {
- return resAPI.ok()
- },
- updateUser: () => {
- return resAPI.ok()
- },
- deleteUser: () => {
- return resAPI.ok()
- }
-}
diff --git a/litemall-admin/src/permission.js b/litemall-admin/src/permission.js
index b6c6bf9e..8da24956 100644
--- a/litemall-admin/src/permission.js
+++ b/litemall-admin/src/permission.js
@@ -7,14 +7,14 @@ import { getToken } from '@/utils/auth' // getToken from cookie
NProgress.configure({ showSpinner: false })// NProgress Configuration
-// permissiom judge function
+// permission judge function
function hasPermission(roles, permissionRoles) {
if (roles.indexOf('admin') >= 0) return true // admin permission passed directly
if (!permissionRoles) return true
return roles.some(role => permissionRoles.indexOf(role) >= 0)
}
-const whiteList = ['/login']// no redirect whitelist
+const whiteList = ['/login', '/auth-redirect']// no redirect whitelist
router.beforeEach((to, from, next) => {
NProgress.start() // start progress bar
@@ -31,16 +31,16 @@ router.beforeEach((to, from, next) => {
router.addRoutes(store.getters.addRouters) // 动态添加可访问路由表
next({ ...to, replace: true }) // hack方法 确保addRoutes已完成 ,set the replace: true so the navigation will not leave a history record
})
- }).catch(() => {
+ }).catch((err) => {
store.dispatch('FedLogOut').then(() => {
- Message.error('验证失败,请输入正确的用户名和密码')
- next({ path: '/login' })
+ Message.error(err || 'Verification failed, please login again')
+ next({ path: '/' })
})
})
} else {
// 没有动态改变权限的需求可直接next() 删除下方权限判断 ↓
if (hasPermission(store.getters.roles, to.meta.roles)) {
- next()//
+ next()
} else {
next({ path: '/401', replace: true, query: { noGoBack: true }})
}
@@ -52,7 +52,7 @@ router.beforeEach((to, from, next) => {
if (whiteList.indexOf(to.path) !== -1) { // 在免登录白名单,直接进入
next()
} else {
- next('/login') // 否则全部重定向到登录页
+ next(`/login?redirect=${to.path}`) // 否则全部重定向到登录页
NProgress.done() // if current page is login will not trigger afterEach hook, so manually handle it
}
}
diff --git a/litemall-admin/src/router/_import_development.js b/litemall-admin/src/router/_import_development.js
deleted file mode 100644
index 7c8b5e6b..00000000
--- a/litemall-admin/src/router/_import_development.js
+++ /dev/null
@@ -1 +0,0 @@
-module.exports = file => require('@/views/' + file + '.vue').default // vue-loader at least v13.0.0+
diff --git a/litemall-admin/src/router/_import_production.js b/litemall-admin/src/router/_import_production.js
deleted file mode 100644
index 331acba4..00000000
--- a/litemall-admin/src/router/_import_production.js
+++ /dev/null
@@ -1 +0,0 @@
-module.exports = file => () => import('@/views/' + file + '.vue')
diff --git a/litemall-admin/src/router/index.js b/litemall-admin/src/router/index.js
index f4b88036..17c4a056 100644
--- a/litemall-admin/src/router/index.js
+++ b/litemall-admin/src/router/index.js
@@ -1,46 +1,73 @@
import Vue from 'vue'
import Router from 'vue-router'
-const _import = require('./_import_' + process.env.NODE_ENV)
-// in development-env not use lazy-loading, because lazy-loading too many pages will cause webpack hot update too slow. so only in production use lazy-loading;
-// detail: https://panjiachen.github.io/vue-element-admin-site/#/lazy-loading
Vue.use(Router)
/* Layout */
-import Layout from '../views/layout/Layout'
+import Layout from '@/views/layout/Layout'
-/** note: submenu only apppear when children.length>=1
-* detail see https://panjiachen.github.io/vue-element-admin-site/#/router-and-nav?id=sidebar
-**/
+/** note: Submenu only appear when children.length>=1
+ * detail see https://panjiachen.github.io/vue-element-admin-site/guide/essentials/router-and-nav.html
+ **/
/**
* hidden: true if `hidden:true` will not show in the sidebar(default is false)
* alwaysShow: true if set true, will always show the root menu, whatever its child routes length
* if not set alwaysShow, only more than one route under the children
* it will becomes nested mode, otherwise not show the root menu
-* redirect: noredirect if `redirect:noredirect` will no redirct in the breadcrumb
+* redirect: noredirect if `redirect:noredirect` will no redirect in the breadcrumb
* name:'router-name' the name is used by
(must set!!!)
* meta : {
roles: ['admin','editor'] will control the page roles (you can set multiple roles)
title: 'title' the name show in submenu and breadcrumb (recommend set)
icon: 'svg-name' the icon show in the sidebar,
- noCache: true if fasle ,the page will no be cached(default is false)
+ noCache: true if true ,the page will no be cached(default is false)
}
**/
export const constantRouterMap = [
- { path: '/login', component: _import('login/index'), hidden: true },
- { path: '/404', component: _import('error/404'), hidden: true },
- { path: '/401', component: _import('error/401'), hidden: true },
+ {
+ path: '/redirect',
+ component: Layout,
+ hidden: true,
+ children: [
+ {
+ path: '/redirect/:path*',
+ component: () => import('@/views/redirect/index')
+ }
+ ]
+ },
+ {
+ path: '/login',
+ component: () => import('@/views/login/index'),
+ hidden: true
+ },
+ {
+ path: '/auth-redirect',
+ component: () => import('@/views/login/authredirect'),
+ hidden: true
+ },
+ {
+ path: '/404',
+ component: () => import('@/views/errorPage/404'),
+ hidden: true
+ },
+ {
+ path: '/401',
+ component: () => import('@/views/errorPage/401'),
+ hidden: true
+ },
{
path: '',
component: Layout,
redirect: 'dashboard',
- children: [{
- path: 'dashboard',
- component: _import('dashboard/index'),
- name: 'dashboard',
- meta: { title: '首页', icon: 'dashboard', noCache: true }
- }]
+ children: [
+ {
+ path: 'dashboard',
+ component: () => import('@/views/dashboard/index'),
+ name: 'Dashboard',
+ meta: { title: 'dashboard', icon: 'dashboard', noCache: true }
+ }
+ ]
}
]
@@ -61,12 +88,42 @@ export const asyncRouterMap = [
icon: 'chart'
},
children: [
- { path: 'user', component: _import('user/user'), name: 'user', meta: { title: '会员管理', noCache: true }},
- { path: 'address', component: _import('user/address'), name: 'address', meta: { title: '收货地址', noCache: true }},
- { path: 'collect', component: _import('user/collect'), name: 'collect', meta: { title: '会员收藏', noCache: true }},
- { path: 'footprint', component: _import('user/footprint'), name: 'footprint', meta: { title: '会员足迹', noCache: true }},
- { path: 'history', component: _import('user/history'), name: 'history', meta: { title: '搜索历史', noCache: true }},
- { path: 'feedback', component: _import('user/feedback'), name: 'feedback', meta: { title: '意见反馈', noCache: true }}
+ {
+ path: 'user',
+ component: () => import('@/views/user/user'),
+ name: 'user',
+ meta: { title: '会员管理', noCache: true }
+ },
+ {
+ path: 'address',
+ component: () => import('@/views/user/address'),
+ name: 'address',
+ meta: { title: '收货地址', noCache: true }
+ },
+ {
+ path: 'collect',
+ component: () => import('@/views/user/collect'),
+ name: 'collect',
+ meta: { title: '会员收藏', noCache: true }
+ },
+ {
+ path: 'footprint',
+ component: () => import('@/views/user/footprint'),
+ name: 'footprint',
+ meta: { title: '会员足迹', noCache: true }
+ },
+ {
+ path: 'history',
+ component: () => import('@/views/user/history'),
+ name: 'history',
+ meta: { title: '搜索历史', noCache: true }
+ },
+ {
+ path: 'feedback',
+ component: () => import('@/views/user/feedback'),
+ name: 'feedback',
+ meta: { title: '意见反馈', noCache: true }
+ }
]
},
@@ -80,12 +137,42 @@ export const asyncRouterMap = [
icon: 'chart'
},
children: [
- { path: 'region', component: _import('mall/region'), name: 'region', meta: { title: '行政区域', noCache: true }},
- { path: 'brand', component: _import('mall/brand'), name: 'brand', meta: { title: '品牌制造商', noCache: true }},
- { path: 'category', component: _import('mall/category'), name: 'category', meta: { title: '商品类目', noCache: true }},
- { path: 'order', component: _import('mall/order'), name: 'order', meta: { title: '订单管理', noCache: true }},
- { path: 'issue', component: _import('mall/issue'), name: 'issue', meta: { title: '通用问题', noCache: true }},
- { path: 'keyword', component: _import('mall/keyword'), name: 'keyword', meta: { title: '关键词', noCache: true }}
+ {
+ path: 'region',
+ component: () => import('@/views/mall/region'),
+ name: 'region',
+ meta: { title: '行政区域', noCache: true }
+ },
+ {
+ path: 'brand',
+ component: () => import('@/views/mall/brand'),
+ name: 'brand',
+ meta: { title: '品牌制造商', noCache: true }
+ },
+ {
+ path: 'category',
+ component: () => import('@/views/mall/category'),
+ name: 'category',
+ meta: { title: '商品类目', noCache: true }
+ },
+ {
+ path: 'order',
+ component: () => import('@/views/mall/order'),
+ name: 'order',
+ meta: { title: '订单管理', noCache: true }
+ },
+ {
+ path: 'issue',
+ component: () => import('@/views/mall/issue'),
+ name: 'issue',
+ meta: { title: '通用问题', noCache: true }
+ },
+ {
+ path: 'keyword',
+ component: () => import('@/views/mall/keyword'),
+ name: 'keyword',
+ meta: { title: '关键词', noCache: true }
+ }
]
},
@@ -99,10 +186,31 @@ export const asyncRouterMap = [
icon: 'chart'
},
children: [
- { path: 'list', component: _import('goods/list'), name: 'goodsList', meta: { title: '商品列表', noCache: true }},
- { path: 'create', component: _import('goods/create'), name: 'goodsCreate', meta: { title: '商品上架', noCache: true }},
- { path: 'edit', component: _import('goods/edit'), name: 'goodsEdit', meta: { title: '商品编辑', noCache: true }, hidden: true },
- { path: 'comment', component: _import('goods/comment'), name: 'goodsComment', meta: { title: '商品评论', noCache: true }}
+ {
+ path: 'list',
+ component: () => import('@/views/goods/list'),
+ name: 'goodsList',
+ meta: { title: '商品列表', noCache: true }
+ },
+ {
+ path: 'create',
+ component: () => import('@/views/goods/create'),
+ name: 'goodsCreate',
+ meta: { title: '商品上架', noCache: true }
+ },
+ {
+ path: 'edit',
+ component: () => import('@/views/goods/edit'),
+ name: 'goodsEdit',
+ meta: { title: '商品编辑', noCache: true },
+ hidden: true
+ },
+ {
+ path: 'comment',
+ component: () => import('@/views/goods/comment'),
+ name: 'goodsComment',
+ meta: { title: '商品评论', noCache: true }
+ }
]
},
{
@@ -115,10 +223,30 @@ export const asyncRouterMap = [
icon: 'chart'
},
children: [
- { path: 'ad', component: _import('promotion/ad'), name: 'ad', meta: { title: '广告列表', noCache: true }},
- { path: 'topic', component: _import('promotion/topic'), name: 'topic', meta: { title: '专题管理', noCache: true }},
- { path: 'groupon-rule', component: _import('promotion/grouponRule'), name: 'grouponRule', meta: { title: '团购规则', noCache: true }},
- { path: 'groupon-activity', component: _import('promotion/grouponActivity'), name: 'grouponActivity', meta: { title: '团购活动', noCache: true }}
+ {
+ path: 'ad',
+ component: () => import('@/views/promotion/ad'),
+ name: 'ad',
+ meta: { title: '广告列表', noCache: true }
+ },
+ {
+ path: 'topic',
+ component: () => import('@/views/promotion/topic'),
+ name: 'topic',
+ meta: { title: '专题管理', noCache: true }
+ },
+ {
+ path: 'groupon-rule',
+ component: () => import('@/views/promotion/grouponRule'),
+ name: 'grouponRule',
+ meta: { title: '团购规则', noCache: true }
+ },
+ {
+ path: 'groupon-activity',
+ component: () => import('@/views/promotion/grouponActivity'),
+ name: 'grouponActivity',
+ meta: { title: '团购活动', noCache: true }
+ }
]
},
@@ -132,8 +260,18 @@ export const asyncRouterMap = [
icon: 'chart'
},
children: [
- { path: 'admin', component: _import('sys/admin'), name: 'admin', meta: { title: '管理员', noCache: true }},
- { path: 'os', component: _import('sys/os'), name: 'os', meta: { title: '对象存储', noCache: true }}
+ {
+ path: 'admin',
+ component: () => import('@/views/sys/admin'),
+ name: 'admin',
+ meta: { title: '管理员', noCache: true }
+ },
+ {
+ path: 'os',
+ component: () => import('@/views/sys/os'),
+ name: 'os',
+ meta: { title: '对象存储', noCache: true }
+ }
]
},
@@ -147,9 +285,64 @@ export const asyncRouterMap = [
icon: 'chart'
},
children: [
- { path: 'user', component: _import('stat/user'), name: 'statUser', meta: { title: '用户统计', noCache: true }},
- { path: 'order', component: _import('stat/order'), name: 'statOrder', meta: { title: '订单统计', noCache: true }},
- { path: 'goods', component: _import('stat/goods'), name: 'statGoods', meta: { title: '商品统计', noCache: true }}
+ {
+ path: 'user',
+ component: () => import('@/views/stat/user'),
+ name: 'statUser',
+ meta: { title: '用户统计', noCache: true }
+ },
+ {
+ path: 'order',
+ component: () => import('@/views/stat/order'),
+ name: 'statOrder',
+ meta: { title: '订单统计', noCache: true }
+ },
+ {
+ path: 'goods',
+ component: () => import('@/views/stat/goods'),
+ name: 'statGoods',
+ meta: { title: '商品统计', noCache: true }
+ }
+ ]
+ },
+ {
+ path: 'external-link',
+ component: Layout,
+ redirect: 'noredirect',
+ name: 'externalLink',
+ meta: {
+ title: '外链',
+ icon: 'link'
+ },
+ children: [
+ {
+ path: 'https://cloud.tencent.com/product/cos',
+ meta: { title: '腾讯云存储', icon: 'link' }
+ },
+ {
+ path: 'https://cloud.tencent.com/product/sms',
+ meta: { title: '腾讯云短信', icon: 'link' }
+ },
+ {
+ path: 'https://pay.weixin.qq.com/index.php/core/home/login',
+ meta: { title: '微信支付', icon: 'link' }
+ },
+ {
+ path: 'https://mpkf.weixin.qq.com/',
+ meta: { title: '小程序客服', icon: 'link' }
+ },
+ {
+ path: 'https://www.alibabacloud.com/zh/product/oss',
+ meta: { title: '阿里云存储', icon: 'link' }
+ },
+ {
+ path: 'https://www.qiniu.com/products/kodo',
+ meta: { title: '七牛云存储', icon: 'link' }
+ },
+ {
+ path: 'http://www.kdniao.com/api-track',
+ meta: { title: '快递鸟', icon: 'link' }
+ }
]
},
{
@@ -157,9 +350,15 @@ export const asyncRouterMap = [
component: Layout,
redirect: 'noredirect',
children: [
- { path: 'password', component: _import('profile/password'), name: 'password', meta: { title: '修改密码', noCache: true }}
+ {
+ path: 'password',
+ component: () => import('@/views/profile/password'),
+ name: 'password',
+ meta: { title: '修改密码', noCache: true }
+ }
],
hidden: true
},
+
{ path: '*', redirect: '/404', hidden: true }
]
diff --git a/litemall-admin/src/store/getters.js b/litemall-admin/src/store/getters.js
index 2227a606..0d05236e 100644
--- a/litemall-admin/src/store/getters.js
+++ b/litemall-admin/src/store/getters.js
@@ -1,6 +1,8 @@
const getters = {
sidebar: state => state.app.sidebar,
language: state => state.app.language,
+ size: state => state.app.size,
+ device: state => state.app.device,
visitedViews: state => state.tagsView.visitedViews,
cachedViews: state => state.tagsView.cachedViews,
token: state => state.user.token,
diff --git a/litemall-admin/src/store/modules/app.js b/litemall-admin/src/store/modules/app.js
index 1dc7940e..bc4fb478 100644
--- a/litemall-admin/src/store/modules/app.js
+++ b/litemall-admin/src/store/modules/app.js
@@ -3,9 +3,12 @@ import Cookies from 'js-cookie'
const app = {
state: {
sidebar: {
- opened: !+Cookies.get('sidebarStatus')
+ opened: !+Cookies.get('sidebarStatus'),
+ withoutAnimation: false
},
- language: Cookies.get('language') || 'zh'
+ device: 'desktop',
+ language: Cookies.get('language') || 'en',
+ size: Cookies.get('size') || 'medium'
},
mutations: {
TOGGLE_SIDEBAR: state => {
@@ -15,18 +18,40 @@ const app = {
Cookies.set('sidebarStatus', 0)
}
state.sidebar.opened = !state.sidebar.opened
+ state.sidebar.withoutAnimation = false
+ },
+ CLOSE_SIDEBAR: (state, withoutAnimation) => {
+ Cookies.set('sidebarStatus', 1)
+ state.sidebar.opened = false
+ state.sidebar.withoutAnimation = withoutAnimation
+ },
+ TOGGLE_DEVICE: (state, device) => {
+ state.device = device
},
SET_LANGUAGE: (state, language) => {
state.language = language
Cookies.set('language', language)
+ },
+ SET_SIZE: (state, size) => {
+ state.size = size
+ Cookies.set('size', size)
}
},
actions: {
toggleSideBar({ commit }) {
commit('TOGGLE_SIDEBAR')
},
+ closeSideBar({ commit }, { withoutAnimation }) {
+ commit('CLOSE_SIDEBAR', withoutAnimation)
+ },
+ toggleDevice({ commit }, device) {
+ commit('TOGGLE_DEVICE', device)
+ },
setLanguage({ commit }, language) {
commit('SET_LANGUAGE', language)
+ },
+ setSize({ commit }, size) {
+ commit('SET_SIZE', size)
}
}
}
diff --git a/litemall-admin/src/store/modules/permission.js b/litemall-admin/src/store/modules/permission.js
index ce1aafa6..97de701e 100644
--- a/litemall-admin/src/store/modules/permission.js
+++ b/litemall-admin/src/store/modules/permission.js
@@ -7,7 +7,7 @@ import { asyncRouterMap, constantRouterMap } from '@/router'
*/
function hasPermission(roles, route) {
if (route.meta && route.meta.roles) {
- return roles.some(role => route.meta.roles.indexOf(role) >= 0)
+ return roles.some(role => route.meta.roles.includes(role))
} else {
return true
}
@@ -15,20 +15,23 @@ function hasPermission(roles, route) {
/**
* 递归过滤异步路由表,返回符合用户角色权限的路由表
- * @param asyncRouterMap
+ * @param routes asyncRouterMap
* @param roles
*/
-function filterAsyncRouter(asyncRouterMap, roles) {
- const accessedRouters = asyncRouterMap.filter(route => {
- if (hasPermission(roles, route)) {
- if (route.children && route.children.length) {
- route.children = filterAsyncRouter(route.children, roles)
+function filterAsyncRouter(routes, roles) {
+ const res = []
+
+ routes.forEach(route => {
+ const tmp = { ...route }
+ if (hasPermission(roles, tmp)) {
+ if (tmp.children) {
+ tmp.children = filterAsyncRouter(tmp.children, roles)
}
- return true
+ res.push(tmp)
}
- return false
})
- return accessedRouters
+
+ return res
}
const permission = {
@@ -47,7 +50,7 @@ const permission = {
return new Promise(resolve => {
const { roles } = data
let accessedRouters
- if (roles.indexOf('admin') >= 0) {
+ if (roles.includes('admin')) {
accessedRouters = asyncRouterMap
} else {
accessedRouters = filterAsyncRouter(asyncRouterMap, roles)
diff --git a/litemall-admin/src/store/modules/tagsView.js b/litemall-admin/src/store/modules/tagsView.js
index 273d33bd..cbf9eeb7 100644
--- a/litemall-admin/src/store/modules/tagsView.js
+++ b/litemall-admin/src/store/modules/tagsView.js
@@ -4,24 +4,30 @@ const tagsView = {
cachedViews: []
},
mutations: {
- ADD_VISITED_VIEWS: (state, view) => {
+ ADD_VISITED_VIEW: (state, view) => {
if (state.visitedViews.some(v => v.path === view.path)) return
- state.visitedViews.push({
- name: view.name,
- path: view.path,
- title: view.meta.title || 'no-name'
- })
+ state.visitedViews.push(
+ Object.assign({}, view, {
+ title: view.meta.title || 'no-name'
+ })
+ )
+ },
+ ADD_CACHED_VIEW: (state, view) => {
+ if (state.cachedViews.includes(view.name)) return
if (!view.meta.noCache) {
state.cachedViews.push(view.name)
}
},
- DEL_VISITED_VIEWS: (state, view) => {
+
+ DEL_VISITED_VIEW: (state, view) => {
for (const [i, v] of state.visitedViews.entries()) {
if (v.path === view.path) {
state.visitedViews.splice(i, 1)
break
}
}
+ },
+ DEL_CACHED_VIEW: (state, view) => {
for (const i of state.cachedViews) {
if (i === view.name) {
const index = state.cachedViews.indexOf(i)
@@ -30,47 +36,125 @@ const tagsView = {
}
}
},
- DEL_OTHERS_VIEWS: (state, view) => {
+
+ DEL_OTHERS_VISITED_VIEWS: (state, view) => {
for (const [i, v] of state.visitedViews.entries()) {
if (v.path === view.path) {
state.visitedViews = state.visitedViews.slice(i, i + 1)
break
}
}
+ },
+ DEL_OTHERS_CACHED_VIEWS: (state, view) => {
for (const i of state.cachedViews) {
if (i === view.name) {
const index = state.cachedViews.indexOf(i)
- state.cachedViews = state.cachedViews.slice(index, i + 1)
+ state.cachedViews = state.cachedViews.slice(index, index + 1)
break
}
}
},
- DEL_ALL_VIEWS: (state) => {
+
+ DEL_ALL_VISITED_VIEWS: state => {
state.visitedViews = []
+ },
+ DEL_ALL_CACHED_VIEWS: state => {
state.cachedViews = []
+ },
+
+ UPDATE_VISITED_VIEW: (state, view) => {
+ for (let v of state.visitedViews) {
+ if (v.path === view.path) {
+ v = Object.assign(v, view)
+ break
+ }
+ }
}
+
},
actions: {
- addVisitedViews({ commit }, view) {
- commit('ADD_VISITED_VIEWS', view)
+ addView({ dispatch }, view) {
+ dispatch('addVisitedView', view)
+ dispatch('addCachedView', view)
},
- delVisitedViews({ commit, state }, view) {
- return new Promise((resolve) => {
- commit('DEL_VISITED_VIEWS', view)
+ addVisitedView({ commit }, view) {
+ commit('ADD_VISITED_VIEW', view)
+ },
+ addCachedView({ commit }, view) {
+ commit('ADD_CACHED_VIEW', view)
+ },
+
+ delView({ dispatch, state }, view) {
+ return new Promise(resolve => {
+ dispatch('delVisitedView', view)
+ dispatch('delCachedView', view)
+ resolve({
+ visitedViews: [...state.visitedViews],
+ cachedViews: [...state.cachedViews]
+ })
+ })
+ },
+ delVisitedView({ commit, state }, view) {
+ return new Promise(resolve => {
+ commit('DEL_VISITED_VIEW', view)
resolve([...state.visitedViews])
})
},
- delOthersViews({ commit, state }, view) {
- return new Promise((resolve) => {
- commit('DEL_OTHERS_VIEWS', view)
+ delCachedView({ commit, state }, view) {
+ return new Promise(resolve => {
+ commit('DEL_CACHED_VIEW', view)
+ resolve([...state.cachedViews])
+ })
+ },
+
+ delOthersViews({ dispatch, state }, view) {
+ return new Promise(resolve => {
+ dispatch('delOthersVisitedViews', view)
+ dispatch('delOthersCachedViews', view)
+ resolve({
+ visitedViews: [...state.visitedViews],
+ cachedViews: [...state.cachedViews]
+ })
+ })
+ },
+ delOthersVisitedViews({ commit, state }, view) {
+ return new Promise(resolve => {
+ commit('DEL_OTHERS_VISITED_VIEWS', view)
resolve([...state.visitedViews])
})
},
- delAllViews({ commit, state }) {
- return new Promise((resolve) => {
- commit('DEL_ALL_VIEWS')
+ delOthersCachedViews({ commit, state }, view) {
+ return new Promise(resolve => {
+ commit('DEL_OTHERS_CACHED_VIEWS', view)
+ resolve([...state.cachedViews])
+ })
+ },
+
+ delAllViews({ dispatch, state }, view) {
+ return new Promise(resolve => {
+ dispatch('delAllVisitedViews', view)
+ dispatch('delAllCachedViews', view)
+ resolve({
+ visitedViews: [...state.visitedViews],
+ cachedViews: [...state.cachedViews]
+ })
+ })
+ },
+ delAllVisitedViews({ commit, state }) {
+ return new Promise(resolve => {
+ commit('DEL_ALL_VISITED_VIEWS')
resolve([...state.visitedViews])
})
+ },
+ delAllCachedViews({ commit, state }) {
+ return new Promise(resolve => {
+ commit('DEL_ALL_CACHED_VIEWS')
+ resolve([...state.cachedViews])
+ })
+ },
+
+ updateVisitedView({ commit }, view) {
+ commit('UPDATE_VISITED_VIEW', view)
}
}
}
diff --git a/litemall-admin/src/store/modules/user.js b/litemall-admin/src/store/modules/user.js
index 5a8acdbe..67c1730d 100644
--- a/litemall-admin/src/store/modules/user.js
+++ b/litemall-admin/src/store/modules/user.js
@@ -64,7 +64,13 @@ const user = {
return new Promise((resolve, reject) => {
getUserInfo(state.token).then(response => {
const data = response.data.data
- commit('SET_ROLES', data.roles)
+
+ if (data.roles && data.roles.length > 0) { // 验证返回的roles是否是一个非空数组
+ commit('SET_ROLES', data.roles)
+ } else {
+ reject('getInfo: roles must be a non-null array !')
+ }
+
commit('SET_NAME', data.name)
commit('SET_AVATAR', data.avatar)
commit('SET_INTRODUCTION', data.introduction)
@@ -113,16 +119,17 @@ const user = {
},
// 动态修改权限
- ChangeRoles({ commit }, role) {
+ ChangeRoles({ commit, dispatch }, role) {
return new Promise(resolve => {
commit('SET_TOKEN', role)
setToken(role)
getUserInfo(role).then(response => {
- const data = response.data.data
+ const data = response.data
commit('SET_ROLES', data.roles)
commit('SET_NAME', data.name)
commit('SET_AVATAR', data.avatar)
commit('SET_INTRODUCTION', data.introduction)
+ dispatch('GenerateRoutes', data) // 动态修改权限后 重绘侧边菜单
resolve()
})
})
diff --git a/litemall-admin/src/styles/btn.scss b/litemall-admin/src/styles/btn.scss
index f3f75c16..0c5ee768 100644
--- a/litemall-admin/src/styles/btn.scss
+++ b/litemall-admin/src/styles/btn.scss
@@ -46,7 +46,6 @@
border-radius: 8px;
border: none;
outline: none;
- margin-right: 25px;
transition: 600ms ease all;
position: relative;
display: inline-block;
diff --git a/litemall-admin/src/styles/element-ui.scss b/litemall-admin/src/styles/element-ui.scss
index b6658af6..dc59591e 100644
--- a/litemall-admin/src/styles/element-ui.scss
+++ b/litemall-admin/src/styles/element-ui.scss
@@ -44,7 +44,7 @@
}
}
- //暂时性解决diolag 问题 https://github.com/ElemeFE/element/issues/2461
+ //暂时性解决dialog 问题 https://github.com/ElemeFE/element/issues/2461
.el-dialog {
transform: none;
left: 0;
@@ -52,6 +52,17 @@
margin: 0 auto;
}
+ //文章页textarea修改样式
+ .article-textarea {
+ textarea {
+ padding-right: 40px;
+ resize: none;
+ border: none;
+ border-radius: 0px;
+ border-bottom: 1px solid #bfcbd9;
+ }
+ }
+
//element ui upload
.upload-container {
.el-upload {
@@ -62,3 +73,10 @@
}
}
}
+
+//dropdown
+ .el-dropdown-menu{
+ a{
+ display: block
+ }
+}
diff --git a/litemall-admin/src/styles/sidebar.scss b/litemall-admin/src/styles/sidebar.scss
index 2fed2b1d..faa7366e 100644
--- a/litemall-admin/src/styles/sidebar.scss
+++ b/litemall-admin/src/styles/sidebar.scss
@@ -2,99 +2,132 @@
// 主体区域
.main-container {
min-height: 100%;
- transition: margin-left 0.28s;
+ transition: margin-left .28s;
margin-left: 180px;
- } // 侧边栏
+ position: relative;
+ }
+ // 侧边栏
.sidebar-container {
transition: width 0.28s;
- width: 180px!important;
+ width: 180px !important;
height: 100%;
position: fixed;
+ font-size: 0px;
top: 0;
bottom: 0;
left: 0;
z-index: 1001;
+ overflow: hidden;
+ //reset element-ui css
+ .horizontal-collapse-transition {
+ transition: 0s width ease-in-out, 0s padding-left ease-in-out, 0s padding-right ease-in-out;
+ }
+ .scrollbar-wrapper {
+ overflow-x: hidden!important;
+ .el-scrollbar__view {
+ height: 100%;
+ }
+ }
+ .el-scrollbar__bar.is-vertical{
+ right: 0px;
+ }
+ .is-horizontal {
+ display: none;
+ }
a {
display: inline-block;
width: 100%;
+ overflow: hidden;
}
.svg-icon {
margin-right: 16px;
}
.el-menu {
border: none;
- width: 100%;
+ height: 100%;
+ width: 100% !important;
+ }
+ .is-active > .el-submenu__title{
+ color: #f4f4f5!important;
}
}
.hideSidebar {
- .sidebar-container,.sidebar-container .el-menu {
- width: 36px!important;
- // overflow: inherit;
+ .sidebar-container {
+ width: 36px !important;
}
.main-container {
margin-left: 36px;
}
- }
- .hideSidebar {
.submenu-title-noDropdown {
- padding-left: 10px!important;
+ padding-left: 10px !important;
position: relative;
- span {
- height: 0;
- width: 0;
- overflow: hidden;
- visibility: hidden;
- transition: opacity .3s cubic-bezier(.55, 0, .1, 1);
- opacity: 0;
- display: inline-block;
- }
- &:hover {
- span {
- display: block;
- border-radius: 3px;
- z-index: 1002;
- width: 140px;
- height: 56px;
- visibility: visible;
- position: absolute;
- right: -145px;
- text-align: left;
- text-indent: 20px;
- top: 0px;
- background-color: $subMenuBg!important;
- opacity: 1;
- }
+ .el-tooltip {
+ padding: 0 10px !important;
}
}
.el-submenu {
+ overflow: hidden;
&>.el-submenu__title {
- padding-left: 10px!important;
- &>span {
- display: none;
- }
+ padding-left: 10px !important;
.el-submenu__icon-arrow {
display: none;
}
}
- .nest-menu {
- .el-submenu__icon-arrow {
- display: block!important;
- }
- span {
- display: inline-block!important;
+ }
+ .el-menu--collapse {
+ .el-submenu {
+ &>.el-submenu__title {
+ &>span {
+ height: 0;
+ width: 0;
+ overflow: hidden;
+ visibility: hidden;
+ display: inline-block;
+ }
}
}
}
}
- .nest-menu .el-submenu>.el-submenu__title,
- .el-submenu .el-menu-item {
- min-width: 180px!important;
- background-color: $subMenuBg!important;
+ .sidebar-container .nest-menu .el-submenu>.el-submenu__title,
+ .sidebar-container .el-submenu .el-menu-item {
+ min-width: 180px !important;
+ background-color: $subMenuBg !important;
&:hover {
- background-color: $menuHover!important;
+ background-color: $menuHover !important;
}
}
- .el-menu--collapse .el-menu .el-submenu{
- min-width: 180px!important;
+ .el-menu--collapse .el-menu .el-submenu {
+ min-width: 180px !important;
+ }
+
+ //适配移动端
+ .mobile {
+ .main-container {
+ margin-left: 0px;
+ }
+ .sidebar-container {
+ transition: transform .28s;
+ width: 180px !important;
+ }
+ &.hideSidebar {
+ .sidebar-container {
+ transition-duration: 0.3s;
+ transform: translate3d(-180px, 0, 0);
+ }
+ }
+ }
+ .withoutAnimation {
+ .main-container,
+ .sidebar-container {
+ transition: none;
+ }
+ }
+}
+
+.el-menu--vertical{
+ & >.el-menu{
+ .svg-icon{
+ margin-right: 16px;
+ }
}
}
diff --git a/litemall-admin/src/styles/transition.scss b/litemall-admin/src/styles/transition.scss
index 85c03286..04e16279 100644
--- a/litemall-admin/src/styles/transition.scss
+++ b/litemall-admin/src/styles/transition.scss
@@ -11,7 +11,21 @@
opacity: 0;
}
-/*fade*/
+/*fade-transform*/
+.fade-transform-leave-active,
+.fade-transform-enter-active {
+ transition: all .5s;
+}
+.fade-transform-enter {
+ opacity: 0;
+ transform: translateX(-30px);
+}
+.fade-transform-leave-to {
+ opacity: 0;
+ transform: translateX(30px);
+}
+
+/*breadcrumb transition*/
.breadcrumb-enter-active,
.breadcrumb-leave-active {
transition: all .5s;
diff --git a/litemall-admin/src/utils/clipboard.js b/litemall-admin/src/utils/clipboard.js
new file mode 100644
index 00000000..e916a44d
--- /dev/null
+++ b/litemall-admin/src/utils/clipboard.js
@@ -0,0 +1,36 @@
+import Vue from 'vue'
+import Clipboard from 'clipboard'
+
+function clipboardSuccess() {
+ Vue.prototype.$message({
+ message: 'Copy successfully',
+ type: 'success',
+ duration: 1500
+ })
+}
+
+function clipboardError() {
+ Vue.prototype.$message({
+ message: 'Copy failed',
+ 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/litemall-admin/src/utils/createUniqueString.js b/litemall-admin/src/utils/createUniqueString.js
new file mode 100644
index 00000000..611725c4
--- /dev/null
+++ b/litemall-admin/src/utils/createUniqueString.js
@@ -0,0 +1,8 @@
+/**
+ * Created by jiachenpan on 17/3/8.
+ */
+export default function createUniqueString() {
+ const timestamp = +new Date() + ''
+ const randomNum = parseInt((1 + Math.random()) * 65536) + ''
+ return (+(randomNum + timestamp)).toString(32)
+}
diff --git a/litemall-admin/src/utils/i18n.js b/litemall-admin/src/utils/i18n.js
new file mode 100644
index 00000000..76be5a4e
--- /dev/null
+++ b/litemall-admin/src/utils/i18n.js
@@ -0,0 +1,12 @@
+// translate router.meta.title, be used in breadcrumb sidebar tagsview
+export function generateTitle(title) {
+ const hasKey = this.$te('route.' + title)
+
+ if (hasKey) {
+ // $t :this method from vue-i18n, inject in @/lang/index.js
+ const translatedTitle = this.$t('route.' + title)
+
+ return translatedTitle
+ }
+ return title
+}
diff --git a/litemall-admin/src/utils/index.js b/litemall-admin/src/utils/index.js
index 2140fdff..0445827b 100644
--- a/litemall-admin/src/utils/index.js
+++ b/litemall-admin/src/utils/index.js
@@ -25,7 +25,8 @@ export function parseTime(time, cFormat) {
}
const time_str = format.replace(/{(y|m|d|h|i|s|a)+}/g, (result, key) => {
let value = formatObj[key]
- if (key === 'a') return ['一', '二', '三', '四', '五', '六', '日'][value - 1]
+ // Note: getDay() returns 0 on Sunday
+ if (key === 'a') { return ['日', '一', '二', '三', '四', '五', '六'][value ] }
if (result.length > 0 && value < 10) {
value = '0' + value
}
@@ -43,7 +44,8 @@ export function formatTime(time, option) {
if (diff < 30) {
return '刚刚'
- } else if (diff < 3600) { // less 1 hour
+ } else if (diff < 3600) {
+ // less 1 hour
return Math.ceil(diff / 60) + '分钟前'
} else if (diff < 3600 * 24) {
return Math.ceil(diff / 3600) + '小时前'
@@ -53,7 +55,17 @@ export function formatTime(time, option) {
if (option) {
return parseTime(time, option)
} else {
- return d.getMonth() + 1 + '月' + d.getDate() + '日' + d.getHours() + '时' + d.getMinutes() + '分'
+ return (
+ d.getMonth() +
+ 1 +
+ '月' +
+ d.getDate() +
+ '日' +
+ d.getHours() +
+ '时' +
+ d.getMinutes() +
+ '分'
+ )
}
}
@@ -81,9 +93,11 @@ export function getQueryObject(url) {
export function getByteLen(val) {
let len = 0
for (let i = 0; i < val.length; i++) {
- if (val[i].match(/[^\x00-\xff]/ig) != null) {
+ if (val[i].match(/[^\x00-\xff]/gi) != null) {
len += 1
- } else { len += 0.5 }
+ } else {
+ len += 0.5
+ }
}
return Math.floor(len)
}
@@ -100,11 +114,12 @@ export function cleanArray(actual) {
export function param(json) {
if (!json) return ''
- return cleanArray(Object.keys(json).map(key => {
- if (json[key] === undefined) return ''
- return encodeURIComponent(key) + '=' +
- encodeURIComponent(json[key])
- })).join('&')
+ return cleanArray(
+ Object.keys(json).map(key => {
+ if (json[key] === undefined) return ''
+ return encodeURIComponent(key) + '=' + encodeURIComponent(json[key])
+ })
+ ).join('&')
}
export function param2Obj(url) {
@@ -112,7 +127,14 @@ export function param2Obj(url) {
if (!search) {
return {}
}
- return JSON.parse('{"' + decodeURIComponent(search).replace(/"/g, '\\"').replace(/&/g, '","').replace(/=/g, '":"') + '"}')
+ return JSON.parse(
+ '{"' +
+ decodeURIComponent(search)
+ .replace(/"/g, '\\"')
+ .replace(/&/g, '","')
+ .replace(/=/g, '":"') +
+ '"}'
+ )
}
export function html2Text(val) {
@@ -131,31 +153,17 @@ export function objectMerge(target, source) {
if (Array.isArray(source)) {
return source.slice()
}
- for (const property in source) {
- if (source.hasOwnProperty(property)) {
- const sourceProperty = source[property]
- if (typeof sourceProperty === 'object') {
- target[property] = objectMerge(target[property], sourceProperty)
- continue
- }
+ Object.keys(source).forEach(property => {
+ const sourceProperty = source[property]
+ if (typeof sourceProperty === 'object') {
+ target[property] = objectMerge(target[property], sourceProperty)
+ } else {
target[property] = sourceProperty
}
- }
+ })
return target
}
-export function scrollTo(element, to, duration) {
- if (duration <= 0) return
- const difference = to - element.scrollTop
- const perTick = difference / duration * 10
- setTimeout(() => {
- console.log(new Date())
- element.scrollTop = element.scrollTop + perTick
- if (element.scrollTop === to) return
- scrollTo(element, to, duration - 10)
- }, 10)
-}
-
export function toggleClass(element, className) {
if (!element || !className) {
return
@@ -165,7 +173,9 @@ export function toggleClass(element, className) {
if (nameIndex === -1) {
classString += '' + className
} else {
- classString = classString.substr(0, nameIndex) + classString.substr(nameIndex + className.length)
+ classString =
+ classString.substr(0, nameIndex) +
+ classString.substr(nameIndex + className.length)
}
element.className = classString
}
@@ -179,7 +189,8 @@ export const pickerOptions = [
end.setTime(start.getTime())
picker.$emit('pick', [start, end])
}
- }, {
+ },
+ {
text: '最近一周',
onClick(picker) {
const end = new Date(new Date().toDateString())
@@ -187,7 +198,8 @@ export const pickerOptions = [
start.setTime(end.getTime() - 3600 * 1000 * 24 * 7)
picker.$emit('pick', [start, end])
}
- }, {
+ },
+ {
text: '最近一个月',
onClick(picker) {
const end = new Date(new Date().toDateString())
@@ -195,7 +207,8 @@ export const pickerOptions = [
start.setTime(start.getTime() - 3600 * 1000 * 24 * 30)
picker.$emit('pick', [start, end])
}
- }, {
+ },
+ {
text: '最近三个月',
onClick(picker) {
const end = new Date(new Date().toDateString())
@@ -203,7 +216,8 @@ export const pickerOptions = [
start.setTime(start.getTime() - 3600 * 1000 * 24 * 90)
picker.$emit('pick', [start, end])
}
- }]
+ }
+]
export function getTime(type) {
if (type === 'start') {
@@ -248,20 +262,30 @@ export function debounce(func, wait, immediate) {
}
}
+/**
+ * This is just a simple version of deep copy
+ * Has a lot of edge cases bug
+ * If you want to use a perfect deep copy, use lodash's _.cloneDeep
+ */
export function deepClone(source) {
if (!source && typeof source !== 'object') {
throw new Error('error arguments', 'shallowClone')
}
const targetObj = source.constructor === Array ? [] : {}
- for (const keys in source) {
- if (source.hasOwnProperty(keys)) {
- if (source[keys] && typeof source[keys] === 'object') {
- targetObj[keys] = source[keys].constructor === Array ? [] : {}
- targetObj[keys] = deepClone(source[keys])
- } else {
- targetObj[keys] = source[keys]
- }
+ Object.keys(source).forEach(keys => {
+ if (source[keys] && typeof source[keys] === 'object') {
+ targetObj[keys] = deepClone(source[keys])
+ } else {
+ targetObj[keys] = source[keys]
}
- }
+ })
return targetObj
}
+
+export function uniqueArr(arr) {
+ return Array.from(new Set(arr))
+}
+
+export function isExternal(path) {
+ return /^(https?:|mailto:|tel:)/.test(path)
+}
diff --git a/litemall-admin/src/utils/permission.js b/litemall-admin/src/utils/permission.js
new file mode 100644
index 00000000..221d3842
--- /dev/null
+++ b/litemall-admin/src/utils/permission.js
@@ -0,0 +1,25 @@
+import store from '@/store'
+
+/**
+ * @param {Array} value
+ * @returns {Boolean}
+ * @example see @/views/permission/directive.vue
+ */
+export default function checkPermission(value) {
+ if (value && value instanceof Array && value.length > 0) {
+ const roles = store.getters && store.getters.roles
+ const permissionRoles = value
+
+ const hasPermission = roles.some(role => {
+ return permissionRoles.includes(role)
+ })
+
+ if (!hasPermission) {
+ return false
+ }
+ return true
+ } else {
+ console.error(`need roles! Like v-permission="['admin','editor']"`)
+ return false
+ }
+}
diff --git a/litemall-admin/src/utils/request.js b/litemall-admin/src/utils/request.js
index 40d70412..3d567e1f 100644
--- a/litemall-admin/src/utils/request.js
+++ b/litemall-admin/src/utils/request.js
@@ -5,24 +5,28 @@ import { getToken } from '@/utils/auth'
// create an axios instance
const service = axios.create({
- baseURL: process.env.BASE_API, // api的base_url
+ baseURL: process.env.BASE_API, // api 的 base_url
timeout: 5000 // request timeout
})
// request interceptor
-service.interceptors.request.use(config => {
- // Do something before request is sent
- if (store.getters.token) {
- config.headers['Admin-Token'] = getToken() // 让每个请求携带token-- ['Admin-Token']为自定义key 请根据实际情况自行修改
+service.interceptors.request.use(
+ config => {
+ // Do something before request is sent
+ if (store.getters.token) {
+ // 让每个请求携带token-- ['Admin-Token']为自定义key 请根据实际情况自行修改
+ config.headers['Admin-Token'] = getToken()
+ }
+ return config
+ },
+ error => {
+ // Do something with request error
+ console.log(error) // for debug
+ Promise.reject(error)
}
- return config
-}, error => {
- // Do something with request error
- console.log(error) // for debug
- Promise.reject(error)
-})
+)
-// respone interceptor
+// response interceptor
service.interceptors.response.use(
response => {
const res = response.data
diff --git a/litemall-admin/src/utils/scrollTo.js b/litemall-admin/src/utils/scrollTo.js
new file mode 100644
index 00000000..8affede6
--- /dev/null
+++ b/litemall-admin/src/utils/scrollTo.js
@@ -0,0 +1,50 @@
+Math.easeInOutQuad = function(t, b, c, d) {
+ t /= d / 2
+ if (t < 1) {
+ return c / 2 * t * t + b
+ }
+ t--
+ return -c / 2 * (t * (t - 2) - 1) + b
+}
+
+// requestAnimationFrame for Smart Animating http://goo.gl/sx5sts
+var requestAnimFrame = (function() {
+ return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || function(callback) { window.setTimeout(callback, 1000 / 60) }
+})()
+
+// because it's so fucking difficult to detect the scrolling element, just move them all
+function move(amount) {
+ document.documentElement.scrollTop = amount
+ document.body.parentNode.scrollTop = amount
+ document.body.scrollTop = amount
+}
+
+function position() {
+ return document.documentElement.scrollTop || document.body.parentNode.scrollTop || document.body.scrollTop
+}
+
+export function scrollTo(to, duration, callback) {
+ const start = position()
+ const change = to - start
+ const increment = 20
+ let currentTime = 0
+ duration = (typeof (duration) === 'undefined') ? 500 : duration
+ var animateScroll = function() {
+ // increment the time
+ currentTime += increment
+ // find the value with the quadratic in-out easing function
+ var val = Math.easeInOutQuad(currentTime, start, change, duration)
+ // move the document.body
+ move(val)
+ // do the animation unless its over
+ if (currentTime < duration) {
+ requestAnimFrame(animateScroll)
+ } else {
+ if (callback && typeof (callback) === 'function') {
+ // the animation is done so lets callback
+ callback()
+ }
+ }
+ }
+ animateScroll()
+}
diff --git a/litemall-admin/src/utils/validate.js b/litemall-admin/src/utils/validate.js
index a0ca0497..ada0e7e2 100644
--- a/litemall-admin/src/utils/validate.js
+++ b/litemall-admin/src/utils/validate.js
@@ -2,6 +2,11 @@
* Created by jiachenpan on 16/11/18.
*/
+export function isvalidUsername(str) {
+ const valid_map = ['admin', 'editor']
+ return valid_map.indexOf(str.trim()) >= 0
+}
+
/* 合法uri*/
export function validateURL(textval) {
const urlregex = /^(https?|ftp):\/\/([a-zA-Z0-9.-]+(:[a-zA-Z0-9.&%$-]+)*@)*((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.(com|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{2}))(:[0-9]+)*(\/($|[a-zA-Z0-9.,?'\\+&%$#=~_-]+))*$/
@@ -21,7 +26,7 @@ export function validateUpperCase(str) {
}
/* 大小写字母*/
-export function validatAlphabets(str) {
+export function validateAlphabets(str) {
const reg = /^[A-Za-z]+$/
return reg.test(str)
}
@@ -35,4 +40,3 @@ export function validateEmail(email) {
const re = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
return re.test(email)
}
-
diff --git a/litemall-admin/src/vendor/Blob.js b/litemall-admin/src/vendor/Blob.js
deleted file mode 100644
index 26382ccd..00000000
--- a/litemall-admin/src/vendor/Blob.js
+++ /dev/null
@@ -1,179 +0,0 @@
-/* eslint-disable */
-/* Blob.js
- * A Blob implementation.
- * 2014-05-27
- *
- * By Eli Grey, http://eligrey.com
- * By Devin Samarin, https://github.com/eboyjr
- * License: X11/MIT
- * See LICENSE.md
- */
-
-/*global self, unescape */
-/*jslint bitwise: true, regexp: true, confusion: true, es5: true, vars: true, white: true,
- plusplus: true */
-
-/*! @source http://purl.eligrey.com/github/Blob.js/blob/master/Blob.js */
-
-(function (view) {
- "use strict";
-
- view.URL = view.URL || view.webkitURL;
-
- if (view.Blob && view.URL) {
- try {
- new Blob;
- return;
- } catch (e) {}
- }
-
- // Internally we use a BlobBuilder implementation to base Blob off of
- // in order to support older browsers that only have BlobBuilder
- var BlobBuilder = view.BlobBuilder || view.WebKitBlobBuilder || view.MozBlobBuilder || (function(view) {
- var
- get_class = function(object) {
- return Object.prototype.toString.call(object).match(/^\[object\s(.*)\]$/)[1];
- }
- , FakeBlobBuilder = function BlobBuilder() {
- this.data = [];
- }
- , FakeBlob = function Blob(data, type, encoding) {
- this.data = data;
- this.size = data.length;
- this.type = type;
- this.encoding = encoding;
- }
- , FBB_proto = FakeBlobBuilder.prototype
- , FB_proto = FakeBlob.prototype
- , FileReaderSync = view.FileReaderSync
- , FileException = function(type) {
- this.code = this[this.name = type];
- }
- , file_ex_codes = (
- "NOT_FOUND_ERR SECURITY_ERR ABORT_ERR NOT_READABLE_ERR ENCODING_ERR "
- + "NO_MODIFICATION_ALLOWED_ERR INVALID_STATE_ERR SYNTAX_ERR"
- ).split(" ")
- , file_ex_code = file_ex_codes.length
- , real_URL = view.URL || view.webkitURL || view
- , real_create_object_URL = real_URL.createObjectURL
- , real_revoke_object_URL = real_URL.revokeObjectURL
- , URL = real_URL
- , btoa = view.btoa
- , atob = view.atob
-
- , ArrayBuffer = view.ArrayBuffer
- , Uint8Array = view.Uint8Array
- ;
- FakeBlob.fake = FB_proto.fake = true;
- while (file_ex_code--) {
- FileException.prototype[file_ex_codes[file_ex_code]] = file_ex_code + 1;
- }
- if (!real_URL.createObjectURL) {
- URL = view.URL = {};
- }
- URL.createObjectURL = function(blob) {
- var
- type = blob.type
- , data_URI_header
- ;
- if (type === null) {
- type = "application/octet-stream";
- }
- if (blob instanceof FakeBlob) {
- data_URI_header = "data:" + type;
- if (blob.encoding === "base64") {
- return data_URI_header + ";base64," + blob.data;
- } else if (blob.encoding === "URI") {
- return data_URI_header + "," + decodeURIComponent(blob.data);
- } if (btoa) {
- return data_URI_header + ";base64," + btoa(blob.data);
- } else {
- return data_URI_header + "," + encodeURIComponent(blob.data);
- }
- } else if (real_create_object_URL) {
- return real_create_object_URL.call(real_URL, blob);
- }
- };
- URL.revokeObjectURL = function(object_URL) {
- if (object_URL.substring(0, 5) !== "data:" && real_revoke_object_URL) {
- real_revoke_object_URL.call(real_URL, object_URL);
- }
- };
- FBB_proto.append = function(data/*, endings*/) {
- var bb = this.data;
- // decode data to a binary string
- if (Uint8Array && (data instanceof ArrayBuffer || data instanceof Uint8Array)) {
- var
- str = ""
- , buf = new Uint8Array(data)
- , i = 0
- , buf_len = buf.length
- ;
- for (; i < buf_len; i++) {
- str += String.fromCharCode(buf[i]);
- }
- bb.push(str);
- } else if (get_class(data) === "Blob" || get_class(data) === "File") {
- if (FileReaderSync) {
- var fr = new FileReaderSync;
- bb.push(fr.readAsBinaryString(data));
- } else {
- // async FileReader won't work as BlobBuilder is sync
- throw new FileException("NOT_READABLE_ERR");
- }
- } else if (data instanceof FakeBlob) {
- if (data.encoding === "base64" && atob) {
- bb.push(atob(data.data));
- } else if (data.encoding === "URI") {
- bb.push(decodeURIComponent(data.data));
- } else if (data.encoding === "raw") {
- bb.push(data.data);
- }
- } else {
- if (typeof data !== "string") {
- data += ""; // convert unsupported types to strings
- }
- // decode UTF-16 to binary string
- bb.push(unescape(encodeURIComponent(data)));
- }
- };
- FBB_proto.getBlob = function(type) {
- if (!arguments.length) {
- type = null;
- }
- return new FakeBlob(this.data.join(""), type, "raw");
- };
- FBB_proto.toString = function() {
- return "[object BlobBuilder]";
- };
- FB_proto.slice = function(start, end, type) {
- var args = arguments.length;
- if (args < 3) {
- type = null;
- }
- return new FakeBlob(
- this.data.slice(start, args > 1 ? end : this.data.length)
- , type
- , this.encoding
- );
- };
- FB_proto.toString = function() {
- return "[object Blob]";
- };
- FB_proto.close = function() {
- this.size = this.data.length = 0;
- };
- return FakeBlobBuilder;
- }(view));
-
- view.Blob = function Blob(blobParts, options) {
- var type = options ? (options.type || "") : "";
- var builder = new BlobBuilder();
- if (blobParts) {
- for (var i = 0, len = blobParts.length; i < len; i++) {
- builder.append(blobParts[i]);
- }
- }
- return builder.getBlob(type);
- };
-}(typeof self !== "undefined" && self || typeof window !== "undefined" && window || this.content || this));
diff --git a/litemall-admin/src/vendor/Export2Excel.js b/litemall-admin/src/vendor/Export2Excel.js
index caca3aa6..a041a34c 100644
--- a/litemall-admin/src/vendor/Export2Excel.js
+++ b/litemall-admin/src/vendor/Export2Excel.js
@@ -1,142 +1,182 @@
/* eslint-disable */
require('script-loader!file-saver');
-require('script-loader!@/vendor/Blob');
import XLSX from 'xlsx'
function generateArray(table) {
- var out = [];
- var rows = table.querySelectorAll('tr');
- var ranges = [];
- for (var R = 0; R < rows.length; ++R) {
- var outRow = [];
- var row = rows[R];
- var columns = row.querySelectorAll('td');
- for (var C = 0; C < columns.length; ++C) {
- var cell = columns[C];
- var colspan = cell.getAttribute('colspan');
- var rowspan = cell.getAttribute('rowspan');
- var cellValue = cell.innerText;
- if (cellValue !== "" && cellValue == +cellValue) cellValue = +cellValue;
+ var out = [];
+ var rows = table.querySelectorAll('tr');
+ var ranges = [];
+ for (var R = 0; R < rows.length; ++R) {
+ var outRow = [];
+ var row = rows[R];
+ var columns = row.querySelectorAll('td');
+ for (var C = 0; C < columns.length; ++C) {
+ var cell = columns[C];
+ var colspan = cell.getAttribute('colspan');
+ var rowspan = cell.getAttribute('rowspan');
+ var cellValue = cell.innerText;
+ if (cellValue !== "" && cellValue == +cellValue) cellValue = +cellValue;
- //Skip ranges
- ranges.forEach(function (range) {
- if (R >= range.s.r && R <= range.e.r && outRow.length >= range.s.c && outRow.length <= range.e.c) {
- for (var i = 0; i <= range.e.c - range.s.c; ++i) outRow.push(null);
- }
- });
-
- //Handle Row Span
- if (rowspan || colspan) {
- rowspan = rowspan || 1;
- colspan = colspan || 1;
- ranges.push({s: {r: R, c: outRow.length}, e: {r: R + rowspan - 1, c: outRow.length + colspan - 1}});
- }
- ;
-
- //Handle Value
- outRow.push(cellValue !== "" ? cellValue : null);
-
- //Handle Colspan
- if (colspan) for (var k = 0; k < colspan - 1; ++k) outRow.push(null);
+ //Skip ranges
+ ranges.forEach(function (range) {
+ if (R >= range.s.r && R <= range.e.r && outRow.length >= range.s.c && outRow.length <= range.e.c) {
+ for (var i = 0; i <= range.e.c - range.s.c; ++i) outRow.push(null);
}
- out.push(outRow);
+ });
+
+ //Handle Row Span
+ if (rowspan || colspan) {
+ rowspan = rowspan || 1;
+ colspan = colspan || 1;
+ ranges.push({
+ s: {
+ r: R,
+ c: outRow.length
+ },
+ e: {
+ r: R + rowspan - 1,
+ c: outRow.length + colspan - 1
+ }
+ });
+ };
+
+ //Handle Value
+ outRow.push(cellValue !== "" ? cellValue : null);
+
+ //Handle Colspan
+ if (colspan)
+ for (var k = 0; k < colspan - 1; ++k) outRow.push(null);
}
- return [out, ranges];
+ out.push(outRow);
+ }
+ return [out, ranges];
};
function datenum(v, date1904) {
- if (date1904) v += 1462;
- var epoch = Date.parse(v);
- return (epoch - new Date(Date.UTC(1899, 11, 30))) / (24 * 60 * 60 * 1000);
+ if (date1904) v += 1462;
+ var epoch = Date.parse(v);
+ return (epoch - new Date(Date.UTC(1899, 11, 30))) / (24 * 60 * 60 * 1000);
}
function sheet_from_array_of_arrays(data, opts) {
- var ws = {};
- var range = {s: {c: 10000000, r: 10000000}, e: {c: 0, r: 0}};
- for (var R = 0; R != data.length; ++R) {
- for (var C = 0; C != data[R].length; ++C) {
- if (range.s.r > R) range.s.r = R;
- if (range.s.c > C) range.s.c = C;
- if (range.e.r < R) range.e.r = R;
- if (range.e.c < C) range.e.c = C;
- var cell = {v: data[R][C]};
- if (cell.v == null) continue;
- var cell_ref = XLSX.utils.encode_cell({c: C, r: R});
-
- if (typeof cell.v === 'number') cell.t = 'n';
- else if (typeof cell.v === 'boolean') cell.t = 'b';
- else if (cell.v instanceof Date) {
- cell.t = 'n';
- cell.z = XLSX.SSF._table[14];
- cell.v = datenum(cell.v);
- }
- else cell.t = 's';
-
- ws[cell_ref] = cell;
- }
+ var ws = {};
+ var range = {
+ s: {
+ c: 10000000,
+ r: 10000000
+ },
+ e: {
+ c: 0,
+ r: 0
}
- if (range.s.c < 10000000) ws['!ref'] = XLSX.utils.encode_range(range);
- return ws;
+ };
+ for (var R = 0; R != data.length; ++R) {
+ for (var C = 0; C != data[R].length; ++C) {
+ if (range.s.r > R) range.s.r = R;
+ if (range.s.c > C) range.s.c = C;
+ if (range.e.r < R) range.e.r = R;
+ if (range.e.c < C) range.e.c = C;
+ var cell = {
+ v: data[R][C]
+ };
+ if (cell.v == null) continue;
+ var cell_ref = XLSX.utils.encode_cell({
+ c: C,
+ r: R
+ });
+
+ if (typeof cell.v === 'number') cell.t = 'n';
+ else if (typeof cell.v === 'boolean') cell.t = 'b';
+ else if (cell.v instanceof Date) {
+ cell.t = 'n';
+ cell.z = XLSX.SSF._table[14];
+ cell.v = datenum(cell.v);
+ } else cell.t = 's';
+
+ ws[cell_ref] = cell;
+ }
+ }
+ if (range.s.c < 10000000) ws['!ref'] = XLSX.utils.encode_range(range);
+ return ws;
}
function Workbook() {
- if (!(this instanceof Workbook)) return new Workbook();
- this.SheetNames = [];
- this.Sheets = {};
+ if (!(this instanceof Workbook)) return new Workbook();
+ this.SheetNames = [];
+ this.Sheets = {};
}
function s2ab(s) {
- var buf = new ArrayBuffer(s.length);
- var view = new Uint8Array(buf);
- for (var i = 0; i != s.length; ++i) view[i] = s.charCodeAt(i) & 0xFF;
- return buf;
+ var buf = new ArrayBuffer(s.length);
+ var view = new Uint8Array(buf);
+ for (var i = 0; i != s.length; ++i) view[i] = s.charCodeAt(i) & 0xFF;
+ return buf;
}
export function export_table_to_excel(id) {
- var theTable = document.getElementById(id);
- var oo = generateArray(theTable);
- var ranges = oo[1];
+ var theTable = document.getElementById(id);
+ var oo = generateArray(theTable);
+ var ranges = oo[1];
- /* original data */
- var data = oo[0];
- var ws_name = "SheetJS";
+ /* original data */
+ var data = oo[0];
+ var ws_name = "SheetJS";
- var wb = new Workbook(), ws = sheet_from_array_of_arrays(data);
+ var wb = new Workbook(),
+ ws = sheet_from_array_of_arrays(data);
- /* add ranges to worksheet */
- // ws['!cols'] = ['apple', 'banan'];
- ws['!merges'] = ranges;
+ /* add ranges to worksheet */
+ // ws['!cols'] = ['apple', 'banan'];
+ ws['!merges'] = ranges;
- /* add worksheet to workbook */
- wb.SheetNames.push(ws_name);
- wb.Sheets[ws_name] = ws;
+ /* add worksheet to workbook */
+ wb.SheetNames.push(ws_name);
+ wb.Sheets[ws_name] = ws;
- var wbout = XLSX.write(wb, {bookType: 'xlsx', bookSST: false, type: 'binary'});
+ var wbout = XLSX.write(wb, {
+ bookType: 'xlsx',
+ bookSST: false,
+ type: 'binary'
+ });
- saveAs(new Blob([s2ab(wbout)], {type: "application/octet-stream"}), "test.xlsx")
+ saveAs(new Blob([s2ab(wbout)], {
+ type: "application/octet-stream"
+ }), "test.xlsx")
}
-export function export_json_to_excel(th, jsonData, defaultTitle) {
-
- /* original data */
-
- var data = jsonData;
- data.unshift(th);
- var ws_name = "SheetJS";
-
- var wb = new Workbook(), ws = sheet_from_array_of_arrays(data);
+export function export_json_to_excel({
+ header,
+ data,
+ filename,
+ autoWidth = true,
+ bookType= 'xlsx'
+} = {}) {
+ /* original data */
+ filename = filename || 'excel-list'
+ data = [...data]
+ data.unshift(header);
+ var ws_name = "SheetJS";
+ var wb = new Workbook(),
+ ws = sheet_from_array_of_arrays(data);
+ if (autoWidth) {
/*设置worksheet每列的最大宽度*/
const colWidth = data.map(row => row.map(val => {
/*先判断是否为null/undefined*/
if (val == null) {
- return {'wch': 10};
+ return {
+ 'wch': 10
+ };
}
/*再判断是否为中文*/
else if (val.toString().charCodeAt(0) > 255) {
- return {'wch': val.toString().length * 2};
+ return {
+ 'wch': val.toString().length * 2
+ };
} else {
- return {'wch': val.toString().length};
+ return {
+ 'wch': val.toString().length
+ };
}
}))
/*以第一行为初始值*/
@@ -149,23 +189,29 @@ export function export_json_to_excel(th, jsonData, defaultTitle) {
}
}
ws['!cols'] = result;
+ }
- /* add worksheet to workbook */
- wb.SheetNames.push(ws_name);
- wb.Sheets[ws_name] = ws;
+ /* add worksheet to workbook */
+ wb.SheetNames.push(ws_name);
+ wb.Sheets[ws_name] = ws;
- var wbout = XLSX.write(wb, {bookType: 'xlsx', bookSST: false, type: 'binary'});
- var title = defaultTitle || 'excel-list'
- saveAs(new Blob([s2ab(wbout)], {type: "application/octet-stream"}), title + ".xlsx")
+ var wbout = XLSX.write(wb, {
+ bookType: bookType,
+ bookSST: false,
+ type: 'binary'
+ });
+ saveAs(new Blob([s2ab(wbout)], {
+ type: "application/octet-stream"
+ }), `${filename}.${bookType}`);
}
function formatJson(jsonSource, jsonFillter) {
- return jsonSource.map(v => jsonFillter.map(j => {
- return v[j]
- }))
+ return jsonSource.map(v => jsonFillter.map(j => {
+ return v[j]
+ }))
}
-export function export_json_to_excel2(th, jsonSource, jsonFillter, defaultTitle) {
- const data = formatJson(jsonSource, jsonFillter)
- export_json_to_excel(th, data, defaultTitle)
+export function export_json_to_excel2(th, jsonSource, jsonFillter, name) {
+ const data = formatJson(jsonSource, jsonFillter)
+ export_json_to_excel({header: th, data, filename: name})
}
\ No newline at end of file
diff --git a/litemall-admin/src/vendor/Export2Zip.js b/litemall-admin/src/vendor/Export2Zip.js
new file mode 100644
index 00000000..f776465e
--- /dev/null
+++ b/litemall-admin/src/vendor/Export2Zip.js
@@ -0,0 +1,24 @@
+/* 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 || 'file'
+ const zip_name = zipName || 'file'
+ 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((blob) => {
+ saveAs(blob, `${zip_name}.zip`)
+ }, (err) => {
+ alert('导出失败')
+ })
+}
diff --git a/litemall-admin/src/views/dashboard/index.vue b/litemall-admin/src/views/dashboard/index.vue
index 876dee81..efc45254 100644
--- a/litemall-admin/src/views/dashboard/index.vue
+++ b/litemall-admin/src/views/dashboard/index.vue
@@ -1,50 +1,53 @@
-
-
-
-
-
+
+
+
+
+
-
-
-
-
-
-
+
+
+
-
-
-
-
-
-
-
-
-
+
+
+
diff --git a/litemall-admin/src/views/goods/edit.vue b/litemall-admin/src/views/goods/edit.vue
index 554ef1fa..7971ff94 100644
--- a/litemall-admin/src/views/goods/edit.vue
+++ b/litemall-admin/src/views/goods/edit.vue
@@ -1,14 +1,14 @@
-
+
diff --git a/litemall-admin/src/views/layout/components/AppMain.vue b/litemall-admin/src/views/layout/components/AppMain.vue
index 653d1810..b6a3378f 100644
--- a/litemall-admin/src/views/layout/components/AppMain.vue
+++ b/litemall-admin/src/views/layout/components/AppMain.vue
@@ -1,8 +1,8 @@
-
-
+
@@ -14,10 +14,21 @@ export default {
computed: {
cachedViews() {
return this.$store.state.tagsView.cachedViews
+ },
+ key() {
+ return this.$route.fullPath
}
- // key() {
- // return this.$route.name !== undefined ? this.$route.name + +new Date() : this.$route + +new Date()
- // }
}
}
+
+
+
diff --git a/litemall-admin/src/views/layout/components/Navbar.vue b/litemall-admin/src/views/layout/components/Navbar.vue
index 2381a850..17c7354c 100644
--- a/litemall-admin/src/views/layout/components/Navbar.vue
+++ b/litemall-admin/src/views/layout/components/Navbar.vue
@@ -1,33 +1,45 @@
-
-
+
+
-
+
-
+
diff --git a/litemall-admin/src/views/layout/components/Sidebar/Link.vue b/litemall-admin/src/views/layout/components/Sidebar/Link.vue
new file mode 100644
index 00000000..5d366f24
--- /dev/null
+++ b/litemall-admin/src/views/layout/components/Sidebar/Link.vue
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
+
+
diff --git a/litemall-admin/src/views/layout/components/Sidebar/SidebarItem.vue b/litemall-admin/src/views/layout/components/Sidebar/SidebarItem.vue
index 85365d1f..8a9b4396 100644
--- a/litemall-admin/src/views/layout/components/Sidebar/SidebarItem.vue
+++ b/litemall-admin/src/views/layout/components/Sidebar/SidebarItem.vue
@@ -1,49 +1,106 @@
-