Commit 6f0ebe3e authored by Junling Bu's avatar Junling Bu
Browse files

chore[litemall-admin]: 管理后台基于的vue-element-admin框架更新至3.9.3

parent cffdc561
......@@ -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
}
}
......@@ -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 {
.el-tooltip {
padding: 0 10px !important;
}
}
.el-submenu {
overflow: hidden;
&>.el-submenu__title {
padding-left: 10px !important;
.el-submenu__icon-arrow {
display: none;
}
}
}
.el-menu--collapse {
.el-submenu {
&>.el-submenu__title {
&>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-submenu {
&>.el-submenu__title {
padding-left: 10px!important;
&>span {
display: none;
}
.el-submenu__icon-arrow {
display: none;
.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;
}
}
.nest-menu {
.el-submenu__icon-arrow {
display: block!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;
}
span {
display: inline-block!important;
&.hideSidebar {
.sidebar-container {
transition-duration: 0.3s;
transform: translate3d(-180px, 0, 0);
}
}
}
.withoutAnimation {
.main-container,
.sidebar-container {
transition: none;
}
.nest-menu .el-submenu>.el-submenu__title,
.el-submenu .el-menu-item {
min-width: 180px!important;
background-color: $subMenuBg!important;
&:hover {
background-color: $menuHover!important;
}
}
.el-menu--vertical{
& >.el-menu{
.svg-icon{
margin-right: 16px;
}
.el-menu--collapse .el-menu .el-submenu{
min-width: 180px!important;
}
}
......@@ -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;
......
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)
}
/**
* 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)
}
// 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
}
......@@ -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 => {
return cleanArray(
Object.keys(json).map(key => {
if (json[key] === undefined) return ''
return encodeURIComponent(key) + '=' +
encodeURIComponent(json[key])
})).join('&')
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)) {
Object.keys(source).forEach(property => {
const sourceProperty = source[property]
if (typeof sourceProperty === 'object') {
target[property] = objectMerge(target[property], sourceProperty)
continue
}
} 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)) {
Object.keys(source).forEach(keys => {
if (source[keys] && typeof source[keys] === 'object') {
targetObj[keys] = source[keys].constructor === Array ? [] : {}
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)
}
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
}
}
......@@ -5,24 +5,28 @@ import { getToken } from '@/utils/auth'
// create an axios instance
const service = axios.create({
baseURL: process.env.BASE_API, // apibase_url
baseURL: process.env.BASE_API, // apibase_url
timeout: 5000 // request timeout
})
// request interceptor
service.interceptors.request.use(config => {
service.interceptors.request.use(
config => {
// Do something before request is sent
if (store.getters.token) {
config.headers['Admin-Token'] = getToken() // 让每个请求携带token-- ['Admin-Token']为自定义key 请根据实际情况自行修改
// 让每个请求携带token-- ['Admin-Token']为自定义key 请根据实际情况自行修改
config.headers['Admin-Token'] = getToken()
}
return config
}, error => {
},
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
......
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()
}
......@@ -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)
}
/* 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));
/* eslint-disable */
require('script-loader!file-saver');
require('script-loader!@/vendor/Blob');
import XLSX from 'xlsx'
function generateArray(table) {
......@@ -29,15 +28,24 @@ function generateArray(table) {
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}});
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);
if (colspan)
for (var k = 0; k < colspan - 1; ++k) outRow.push(null);
}
out.push(outRow);
}
......@@ -52,16 +60,30 @@ function datenum(v, date1904) {
function sheet_from_array_of_arrays(data, opts) {
var ws = {};
var range = {s: {c: 10000000, r: 10000000}, e: {c: 0, r: 0}};
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]};
var cell = {
v: data[R][C]
};
if (cell.v == null) continue;
var cell_ref = XLSX.utils.encode_cell({c: C, r: R});
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';
......@@ -69,8 +91,7 @@ function sheet_from_array_of_arrays(data, opts) {
cell.t = 'n';
cell.z = XLSX.SSF._table[14];
cell.v = datenum(cell.v);
}
else cell.t = 's';
} else cell.t = 's';
ws[cell_ref] = cell;
}
......@@ -101,7 +122,8 @@ export function export_table_to_excel(id) {
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'];
......@@ -111,32 +133,50 @@ export function export_table_to_excel(id) {
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) {
export function export_json_to_excel({
header,
data,
filename,
autoWidth = true,
bookType= 'xlsx'
} = {}) {
/* original data */
var data = jsonData;
data.unshift(th);
filename = filename || 'excel-list'
data = [...data]
data.unshift(header);
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);
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,14 +189,20 @@ 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;
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) {
......@@ -165,7 +211,7 @@ function formatJson(jsonSource, jsonFillter) {
}))
}
export function export_json_to_excel2(th, jsonSource, jsonFillter, defaultTitle) {
export function export_json_to_excel2(th, jsonSource, jsonFillter, name) {
const data = formatJson(jsonSource, jsonFillter)
export_json_to_excel(th, data, defaultTitle)
export_json_to_excel({header: th, data, filename: name})
}
\ No newline at end of file
/* 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('导出失败')
})
}
<template>
<el-row class="panel-group" :gutter="40">
<div class="dashboard-editor-container">
<el-row :gutter="40" class="panel-group">
<el-col :xs="12" :sm="12" :lg="6" class="card-panel-col">
<div class='card-panel' @click="handleSetLineChartData('newVisitis')">
<div class="card-panel" @click="handleSetLineChartData('newVisitis')">
<div class="card-panel-icon-wrapper icon-people">
<svg-icon icon-class="peoples" class-name="card-panel-icon" />
</div>
<div class="card-panel-description">
<div class="card-panel-text">用户数量</div>
<count-to class="card-panel-num" :startVal="0" :endVal="userTotal" :duration="2600"></count-to>
<count-to :start-val="0" :end-val="userTotal" :duration="2600" class="card-panel-num"/>
</div>
</div>
</el-col>
<el-col :xs="12" :sm="12" :lg="6" class="card-panel-col">
<div class="card-panel" @click="handleSetLineChartData('messages')">
<div class="card-panel-icon-wrapper icon-message">
<svg-icon icon-class="shoppingCard" class-name="card-panel-icon" />
<svg-icon icon-class="message" class-name="card-panel-icon" />
</div>
<div class="card-panel-description">
<div class="card-panel-text">商品数量</div>
<count-to class="card-panel-num" :startVal="0" :endVal="goodsTotal" :duration="3000"></count-to>
<count-to :start-val="0" :end-val="goodsTotal" :duration="3000" class="card-panel-num"/>
</div>
</div>
</el-col>
......@@ -29,7 +31,7 @@
</div>
<div class="card-panel-description">
<div class="card-panel-text">货品数量</div>
<count-to class="card-panel-num" :startVal="0" :endVal="productTotal" :duration="3200"></count-to>
<count-to :start-val="0" :end-val="productTotal" :duration="3200" class="card-panel-num"/>
</div>
</div>
</el-col>
......@@ -40,11 +42,12 @@
</div>
<div class="card-panel-description">
<div class="card-panel-text">订单数量</div>
<count-to class="card-panel-num" :startVal="0" :endVal="orderTotal" :duration="3600"></count-to>
<count-to :start-val="0" :end-val="orderTotal" :duration="3600" class="card-panel-num"/>
</div>
</div>
</el-col>
</el-row>
</div>
</template>
<script>
......@@ -63,11 +66,6 @@ export default {
orderTotal: 0
}
},
methods: {
handleSetLineChartData(type) {
this.$emit('handleSetLineChartData', type)
}
},
created() {
info(this.listQuery).then(response => {
this.userTotal = response.data.data.userTotal
......@@ -75,11 +73,26 @@ export default {
this.productTotal = response.data.data.productTotal
this.orderTotal = response.data.data.orderTotal
})
},
methods: {
handleSetLineChartData(type) {
this.$emit('handleSetLineChartData', type)
}
}
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
.dashboard-editor-container {
padding: 32px;
background-color: rgb(240, 242, 245);
.chart-wrapper {
background: #fff;
padding: 16px 16px 0;
margin-bottom: 32px;
}
}
.panel-group {
margin-top: 18px;
......
<template>
<div class="errPage-container">
<el-button @click="back" icon='arrow-left' class="pan-back-btn">返回</el-button>
<el-button icon="arrow-left" class="pan-back-btn" @click="back">返回</el-button>
<el-row>
<el-col :span="12">
<h1 class="text-jumbo text-ginormous">Oops!</h1>
gif来源<a href='https://zh.airbnb.com/' target='_blank'>airbnb</a> 页面
gif来源<a href="https://zh.airbnb.com/" target="_blank">airbnb</a> 页面
<h2>你没有权限去该页面</h2>
<h6>如有不满请联系你领导</h6>
<ul class="list-unstyled">
......@@ -13,15 +13,15 @@
<router-link to="/dashboard">回首页</router-link>
</li>
<li class="link-type"><a href="https://www.taobao.com/">随便看看</a></li>
<li><a @click.prevent="dialogVisible=true" href="#">点我看图</a></li>
<li><a href="#" @click.prevent="dialogVisible=true">点我看图</a></li>
</ul>
</el-col>
<el-col :span="12">
<img :src="errGif" width="313" height="428" alt="Girl has dropped her ice cream.">
</el-col>
</el-row>
<el-dialog title="随便看" :visible.sync="dialogVisible">
<img class="pan-img" :src="ewizardClap">
<el-dialog :visible.sync="dialogVisible" title="随便看">
<img :src="ewizardClap" class="pan-img">
</el-dialog>
</div>
</template>
......@@ -30,7 +30,7 @@
import errGif from '@/assets/401_images/401.gif'
export default {
name: 'page401',
name: 'Page401',
data() {
return {
errGif: errGif + '?' + +new Date(),
......@@ -53,10 +53,12 @@ export default {
<style rel="stylesheet/scss" lang="scss" scoped>
.errPage-container {
width: 800px;
max-width: 100%;
margin: 100px auto;
.pan-back-btn {
background: #008489;
color: #fff;
border: none!important;
}
.pan-gif {
margin: 0 auto;
......@@ -65,6 +67,7 @@ export default {
.pan-img {
display: block;
margin: 0 auto;
width: 100%;
}
.text-jumbo {
font-size: 60px;
......
<template>
<div style="background:#f0f2f5;margin-top: -20px;height:100%;">
<div class="wscn-http404-container">
<div class="wscn-http404">
<div class="pic-404">
<img class="pic-404__parent" :src="img_404" alt="404">
<img class="pic-404__child left" :src="img_404_cloud" alt="404">
<img class="pic-404__child mid" :src="img_404_cloud" alt="404">
<img class="pic-404__child right" :src="img_404_cloud" alt="404">
<img class="pic-404__parent" src="@/assets/404_images/404.png" alt="404">
<img class="pic-404__child left" src="@/assets/404_images/404_cloud.png" alt="404">
<img class="pic-404__child mid" src="@/assets/404_images/404_cloud.png" alt="404">
<img class="pic-404__child right" src="@/assets/404_images/404_cloud.png" alt="404">
</div>
<div class="bullshit">
<div class="bullshit__oops">OOPS!</div>
<div class="bullshit__info">版权所有
<a class='link-type' href='https://wallstreetcn.com' target='_blank'>华尔街见闻</a>
<a class="link-type" href="https://wallstreetcn.com" target="_blank">华尔街见闻</a>
</div>
<div class="bullshit__headline">{{ message }}</div>
<div class="bullshit__info">请检查您输入的网址是否正确,请点击以下按钮返回主页或者发送错误报告</div>
<a href="/" class="bullshit__return-home">返回首页</a>
<a href="" class="bullshit__return-home">返回首页</a>
</div>
</div>
</div>
</template>
<script>
import img_404 from '@/assets/404_images/404.png'
import img_404_cloud from '@/assets/404_images/404_cloud.png'
export default {
name: 'page404',
data() {
return {
img_404,
img_404_cloud
}
},
name: 'Page404',
computed: {
message() {
return '特朗普说这个页面你不能进......'
return '网管说这个页面你不能进......'
}
}
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
.wscn-http404-container{
transform: translate(-50%,-50%);
position: absolute;
top: 40%;
left: 50%;
}
.wscn-http404 {
position: relative;
width: 1200px;
margin: 20px auto 60px;
padding: 0 100px;
padding: 0 50px;
overflow: hidden;
.pic-404 {
position: relative;
float: left;
width: 600px;
padding: 150px 0;
overflow: hidden;
&__parent {
width: 100%;
......@@ -163,7 +159,7 @@ export default {
position: relative;
float: left;
width: 300px;
padding: 150px 0;
padding: 30px 0;
overflow: hidden;
&__oops {
font-size: 32px;
......@@ -179,7 +175,8 @@ export default {
&__headline {
font-size: 20px;
line-height: 24px;
color: #1482f0;
color: #222;
font-weight: bold;
opacity: 0;
margin-bottom: 10px;
animation-name: slideUp;
......
<template>
<div class="app-container calendar-list-container">
<div class="app-container">
<!-- 查询和其他操作 -->
<div class="filter-container">
<el-input clearable class="filter-item" style="width: 200px;" placeholder="请输入用户ID" v-model="listQuery.userId">
</el-input>
<el-input clearable class="filter-item" style="width: 200px;" placeholder="请输入商品ID" v-model="listQuery.valueId">
</el-input>
<el-input v-model="listQuery.userId" clearable class="filter-item" style="width: 200px;" placeholder="请输入用户ID"/>
<el-input v-model="listQuery.valueId" clearable class="filter-item" style="width: 200px;" placeholder="请输入商品ID"/>
<el-button class="filter-item" type="primary" icon="el-icon-search" @click="handleFilter">查找</el-button>
<el-button class="filter-item" type="primary" icon="el-icon-download" @click="handleDownload" :loading="downloadLoading">导出</el-button>
<el-button :loading="downloadLoading" class="filter-item" type="primary" icon="el-icon-download" @click="handleDownload">导出</el-button>
</div>
<!-- 查询结果 -->
<el-table size="small" :data="list" v-loading="listLoading" element-loading-text="正在查询中。。。" border fit highlight-current-row>
<el-table v-loading="listLoading" :data="list" size="small" element-loading-text="正在查询中。。。" border fit highlight-current-row>
<el-table-column align="center" label="用户ID" prop="userId">
</el-table-column>
<el-table-column align="center" label="用户ID" prop="userId"/>
<el-table-column align="center" label="商品ID" prop="valueId">
</el-table-column>
<el-table-column align="center" label="商品ID" prop="valueId"/>
<el-table-column align="center" label="打分" prop="star">
</el-table-column>
<el-table-column align="center" label="打分" prop="star"/>
<el-table-column align="center" label="评论内容" prop="content">
</el-table-column>
<el-table-column align="center" label="评论内容" prop="content"/>
<el-table-column align="center" label="评论图片" prop="picUrls">
<template slot-scope="scope">
<img v-for="item in scope.row.picUrls" :key="item" :src="item" width="40"/>
<img v-for="item in scope.row.picUrls" :key="item" :src="item" width="40">
</template>
</el-table-column>
<el-table-column align="center" label="时间" prop="addTime">
</el-table-column>
<el-table-column align="center" label="时间" prop="addTime"/>
<el-table-column align="center" label="操作" width="200" class-name="small-padding fixed-width">
<template slot-scope="scope">
......@@ -43,18 +36,13 @@
</el-table-column>
</el-table>
<!-- 分页 -->
<div class="pagination-container">
<el-pagination background @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page="listQuery.page"
:page-sizes="[10,20,30,50]" :page-size="listQuery.limit" layout="total, sizes, prev, pager, next, jumper" :total="total">
</el-pagination>
</div>
<pagination v-show="total>0" :total="total" :page.sync="listQuery.page" :limit.sync="listQuery.limit" @pagination="getList" />
<!-- 评论回复 -->
<el-dialog title="回复" :visible.sync="replyFormVisible">
<el-form ref="replyForm" :model="replyForm" status-icon label-position="left" label-width="100px" style='width: 400px; margin-left:50px;'>
<el-dialog :visible.sync="replyFormVisible" title="回复">
<el-form ref="replyForm" :model="replyForm" status-icon label-position="left" label-width="100px" style="width: 400px; margin-left:50px;">
<el-form-item label="回复内容" prop="content">
<el-input type="textarea" :autosize="{ minRows: 4, maxRows: 8}" v-model="replyForm.content"></el-input>
<el-input :autosize="{ minRows: 4, maxRows: 8}" v-model="replyForm.content" type="textarea"/>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
......@@ -69,9 +57,11 @@
<script>
import { listComment, deleteComment } from '@/api/comment'
import { replyComment } from '@/api/order'
import Pagination from '@/components/Pagination' // Secondary package based on el-pagination
export default {
name: 'Comment',
components: { Pagination },
data() {
return {
list: undefined,
......@@ -113,14 +103,6 @@ export default {
this.listQuery.page = 1
this.getList()
},
handleSizeChange(val) {
this.listQuery.limit = val
this.getList()
},
handleCurrentChange(val) {
this.listQuery.page = val
this.getList()
},
handleReply(row) {
this.replyForm = { commentId: row.id, content: '' }
this.replyFormVisible = true
......
<template>
<div class="app-container calendar-list-container">
<div class="app-container">
<el-card class="box-card">
<h3>商品介绍</h3>
<el-form :rules="rules" ref="goods" :model="goods" label-width="150px">
<el-form ref="goods" :rules="rules" :model="goods" label-width="150px">
<el-form-item label="商品编号" prop="goodsSn">
<el-input v-model="goods.goodsSn"></el-input>
<el-input v-model="goods.goodsSn"/>
</el-form-item>
<el-form-item label="商品名称" prop="name">
<el-input v-model="goods.name"></el-input>
<el-input v-model="goods.name"/>
</el-form-item>
<el-form-item label="专柜价格" prop="counterPrice">
<el-input v-model="goods.counterPrice" placeholder="0.00">
......@@ -40,105 +40,139 @@
</el-form-item>
<el-form-item label="商品图片">
<el-upload class="avatar-uploader" :action="uploadPath" list-type="picture-card" :show-file-list="false" :headers="headers"
accept=".jpg,.jpeg,.png,.gif" :on-success="uploadPicUrl">
<el-upload
:action="uploadPath"
:show-file-list="false"
:headers="headers"
:on-success="uploadPicUrl"
class="avatar-uploader"
list-type="picture-card"
accept=".jpg,.jpeg,.png,.gif">
<img v-if="goods.picUrl" :src="goods.picUrl" class="avatar">
<i v-else class="el-icon-plus avatar-uploader-icon"></i>
<i v-else class="el-icon-plus avatar-uploader-icon"/>
</el-upload>
</el-form-item>
<el-form-item label="宣传画廊">
<el-upload :action="uploadPath" :limit="5" multiple accept=".jpg,.jpeg,.png,.gif" list-type="picture-card" :headers="headers"
:on-exceed="uploadOverrun" :on-success="handleGalleryUrl" :on-remove="handleRemove">
<i class="el-icon-plus"></i>
<el-upload
:action="uploadPath"
:limit="5"
:headers="headers"
:on-exceed="uploadOverrun"
:on-success="handleGalleryUrl"
:on-remove="handleRemove"
multiple
accept=".jpg,.jpeg,.png,.gif"
list-type="picture-card">
<i class="el-icon-plus"/>
</el-upload>
</el-form-item>
<el-form-item label="商品单位">
<el-input v-model="goods.unit" placeholder="件 / 个 / 盒"></el-input>
<el-input v-model="goods.unit" placeholder="件 / 个 / 盒"/>
</el-form-item>
<el-form-item label="关键字">
<el-tag :key="tag" v-for="tag in keywords" closable type="primary" @close="handleClose(tag)">
{{tag}}
<el-tag v-for="tag in keywords" :key="tag" closable type="primary" @close="handleClose(tag)">
{{ tag }}
</el-tag>
<el-input class="input-new-keyword" v-if="newKeywordVisible" v-model="newKeyword" ref="newKeywordInput"
size="small" @keyup.enter.native="handleInputConfirm" @blur="handleInputConfirm">
</el-input>
<el-input
v-if="newKeywordVisible"
ref="newKeywordInput"
v-model="newKeyword"
class="input-new-keyword"
size="small"
@keyup.enter.native="handleInputConfirm"
@blur="handleInputConfirm"/>
<el-button v-else class="button-new-keyword" size="small" type="primary" @click="showInput">+ 增加</el-button>
</el-form-item>
<el-form-item label="所属分类">
<el-cascader expand-trigger="hover" :options="categoryList" @change="handleCategoryChange"></el-cascader>
<el-cascader :options="categoryList" expand-trigger="hover" @change="handleCategoryChange"/>
</el-form-item>
<el-form-item label="所属品牌商">
<el-select v-model="goods.brandId">
<el-option v-for="item in brandList" :key="item.value" :label="item.label" :value="item.value">
</el-option>
<el-option v-for="item in brandList" :key="item.value" :label="item.label" :value="item.value"/>
</el-select>
</el-form-item>
<el-form-item label="商品简介">
<el-input v-model="goods.brief"></el-input>
<el-input v-model="goods.brief"/>
</el-form-item>
<el-form-item label="商品详细介绍">
<editor :init="editorInit" v-model="goods.detail"></editor>
<editor :init="editorInit" v-model="goods.detail"/>
</el-form-item>
</el-form>
</el-card>
<el-card class="box-card">
<h3>商品规格</h3>
<el-row type="flex" align="middle" :gutter="20" style="padding:20px 0;">
<el-row :gutter="20" type="flex" align="middle" style="padding:20px 0;">
<el-col :span="10">
<el-radio-group v-model="multipleSpec" @change="specChanged">
<el-radio-button :label="false">默认标准规格</el-radio-button>
<el-radio-button :label="true">多规格支持</el-radio-button>
</el-radio-group>
</el-col>
<el-col :span="10" v-if="multipleSpec">
<el-button :plain="true" @click="handleSpecificationShow" type="primary">添加</el-button>
<el-col v-if="multipleSpec" :span="10">
<el-button :plain="true" type="primary" @click="handleSpecificationShow">添加</el-button>
</el-col>
</el-row>
<el-table :data="specifications">
<el-table-column property="specification" label="规格名"></el-table-column>
<el-table-column property="specification" label="规格名"/>
<el-table-column property="value" label="规格值">
<template slot-scope="scope">
<el-tag type="primary">
{{scope.row.value}}
{{ scope.row.value }}
</el-tag>
</template>
</el-table-column>
<el-table-column property="picUrl" label="规格图片">
<template slot-scope="scope">
<img :src="scope.row.picUrl" width="40" v-if="scope.row.picUrl"/>
<img v-if="scope.row.picUrl" :src="scope.row.picUrl" width="40">
</template>
</el-table-column>
<el-table-column align="center" label="操作" width="250" class-name="small-padding fixed-width"
v-if="multipleSpec">
<el-table-column
v-if="multipleSpec"
align="center"
label="操作"
width="250"
class-name="small-padding fixed-width">
<template slot-scope="scope">
<el-button type="danger" size="mini" @click="handleSpecificationDelete(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<el-dialog title="设置规格" :visible.sync="specVisiable">
<el-form :rules="rules" ref="specForm" :model="specForm" status-icon label-position="left" label-width="100px"
style='width: 400px; margin-left:50px;'>
<el-dialog :visible.sync="specVisiable" title="设置规格">
<el-form
ref="specForm"
:rules="rules"
:model="specForm"
status-icon
label-position="left"
label-width="100px"
style="width: 400px; margin-left:50px;">
<el-form-item label="规格名" prop="specification">
<el-input v-model="specForm.specification"></el-input>
<el-input v-model="specForm.specification"/>
</el-form-item>
<el-form-item label="规格值" prop="value">
<el-input v-model="specForm.value"></el-input>
<el-input v-model="specForm.value"/>
</el-form-item>
<el-form-item label="规格图片" prop="picUrl">
<el-upload class="avatar-uploader" :action='uploadPath' list-type="picture-card" :show-file-list="false" :headers="headers"
accept=".jpg,.jpeg,.png,.gif" :on-success="uploadSpecPicUrl">
<el-upload
:action="uploadPath"
:show-file-list="false"
:headers="headers"
:on-success="uploadSpecPicUrl"
class="avatar-uploader"
list-type="picture-card"
accept=".jpg,.jpeg,.png,.gif">
<img v-if="specForm.picUrl" :src="specForm.picUrl" class="avatar">
<i v-else class="el-icon-plus avatar-uploader-icon"></i>
<i v-else class="el-icon-plus avatar-uploader-icon"/>
</el-upload>
</el-form-item>
</el-form>
......@@ -154,18 +188,16 @@
<el-table :data="products">
<el-table-column property="value" label="货品规格">
<template slot-scope="scope">
<el-tag :key="tag" v-for="tag in scope.row.specifications">
{{tag}}
<el-tag v-for="tag in scope.row.specifications" :key="tag">
{{ tag }}
</el-tag>
</template>
</el-table-column>
<el-table-column property="price" width="100" label="货品售价">
</el-table-column>
<el-table-column property="number" width="100" label="货品数量">
</el-table-column>
<el-table-column property="price" width="100" label="货品售价"/>
<el-table-column property="number" width="100" label="货品数量"/>
<el-table-column property="url" width="100" label="货品图片">
<template slot-scope="scope">
<img :src="scope.row.url" width="40" v-if="scope.row.url"/>
<img v-if="scope.row.url" :src="scope.row.url" width="40">
</template>
</el-table-column>
<el-table-column align="center" label="操作" width="100" class-name="small-padding fixed-width">
......@@ -175,25 +207,36 @@
</el-table-column>
</el-table>
<el-dialog title="设置货品" :visible.sync="productVisiable">
<el-form ref="productForm" :model="productForm" status-icon label-position="left" label-width="100px"
style='width: 400px; margin-left:50px;'>
<el-dialog :visible.sync="productVisiable" title="设置货品">
<el-form
ref="productForm"
:model="productForm"
status-icon
label-position="left"
label-width="100px"
style="width: 400px; margin-left:50px;">
<el-form-item label="货品规格列" prop="specifications">
<el-tag :key="tag" v-for="tag in productForm.specifications">
{{tag}}
<el-tag v-for="tag in productForm.specifications" :key="tag">
{{ tag }}
</el-tag>
</el-form-item>
<el-form-item label="货品售价" prop="price">
<el-input v-model="productForm.price"></el-input>
<el-input v-model="productForm.price"/>
</el-form-item>
<el-form-item label="货品数量" prop="number">
<el-input v-model="productForm.number"></el-input>
<el-input v-model="productForm.number"/>
</el-form-item>
<el-form-item label="货品图片" prop="url">
<el-upload class="avatar-uploader" :action='uploadPath' list-type="picture-card" :show-file-list="false" :headers="headers"
accept=".jpg,.jpeg,.png,.gif" :on-success="uploadProductUrl">
<el-upload
:action="uploadPath"
:show-file-list="false"
:headers="headers"
:on-success="uploadProductUrl"
class="avatar-uploader"
list-type="picture-card"
accept=".jpg,.jpeg,.png,.gif">
<img v-if="productForm.url" :src="productForm.url" class="avatar">
<i v-else class="el-icon-plus avatar-uploader-icon"></i>
<i v-else class="el-icon-plus avatar-uploader-icon"/>
</el-upload>
</el-form-item>
</el-form>
......@@ -206,12 +249,10 @@
<el-card class="box-card">
<h3>商品参数</h3>
<el-button :plain="true" @click="handleAttributeShow" type="primary">添加</el-button>
<el-button :plain="true" type="primary" @click="handleAttributeShow">添加</el-button>
<el-table :data="attributes">
<el-table-column property="attribute" label="商品参数名称">
</el-table-column>
<el-table-column property="value" label="商品参数值">
</el-table-column>
<el-table-column property="attribute" label="商品参数名称"/>
<el-table-column property="value" label="商品参数值"/>
<el-table-column align="center" label="操作" width="100" class-name="small-padding fixed-width">
<template slot-scope="scope">
<el-button type="danger" size="mini" @click="handleAttributeDelete(scope.row)">删除</el-button>
......@@ -219,14 +260,19 @@
</el-table-column>
</el-table>
<el-dialog title="设置商品参数" :visible.sync="attributeVisiable">
<el-form ref="attributeForm" :model="attributeForm" status-icon label-position="left" label-width="100px"
style='width: 400px; margin-left:50px;'>
<el-dialog :visible.sync="attributeVisiable" title="设置商品参数">
<el-form
ref="attributeForm"
:model="attributeForm"
status-icon
label-position="left"
label-width="100px"
style="width: 400px; margin-left:50px;">
<el-form-item label="商品参数名称" prop="attribute">
<el-input v-model="attributeForm.attribute"></el-input>
<el-input v-model="attributeForm.attribute"/>
</el-form-item>
<el-form-item label="商品参数值" prop="value">
<el-input v-model="attributeForm.value"></el-input>
<el-input v-model="attributeForm.value"/>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
......@@ -238,7 +284,7 @@
<div class="op-container">
<el-button @click="handleCancel">取消</el-button>
<el-button @click="handlePublish" type="primary">上架</el-button>
<el-button type="primary" @click="handlePublish">上架</el-button>
</div>
</div>
......@@ -288,22 +334,15 @@
</style>
<script>
import { publishGoods, listCatAndBrand } from '@/api/goods'
import { createStorage, uploadPath } from '@/api/storage'
import Editor from '@tinymce/tinymce-vue'
import { MessageBox } from 'element-ui'
import { getToken } from '@/utils/auth'
import { publishGoods, listCatAndBrand } from '@/api/goods'
import { createStorage, uploadPath } from '@/api/storage'
import Editor from '@tinymce/tinymce-vue'
import { MessageBox } from 'element-ui'
import { getToken } from '@/utils/auth'
export default {
export default {
name: 'GoodsCreate',
components: { Editor },
computed: {
headers() {
return {
'Admin-Token': getToken()
}
}
},
data() {
return {
......@@ -345,6 +384,13 @@
}
}
},
computed: {
headers() {
return {
'Admin-Token': getToken()
}
}
},
created() {
this.init()
},
......@@ -563,5 +609,5 @@
this.attributes.splice(index, 1)
}
}
}
}
</script>
<template>
<div class="app-container calendar-list-container">
<div class="app-container">
<el-card class="box-card">
<h3>商品介绍</h3>
<el-form :rules="rules" ref="goods" :model="goods" label-width="150px">
<el-form ref="goods" :rules="rules" :model="goods" label-width="150px">
<el-form-item label="商品编号" prop="goodsSn">
<el-input v-model="goods.goodsSn"></el-input>
<el-input v-model="goods.goodsSn"/>
</el-form-item>
<el-form-item label="商品名称" prop="name">
<el-input v-model="goods.name"></el-input>
<el-input v-model="goods.name"/>
</el-form-item>
<el-form-item label="专柜价格" prop="counterPrice">
<el-input v-model="goods.counterPrice" placeholder="0.00">
......@@ -40,68 +40,66 @@
</el-form-item>
<el-form-item label="商品图片">
<el-upload class="avatar-uploader" :headers="headers" :action='uploadPath' list-type="picture-card" :show-file-list="false" accept=".jpg,.jpeg,.png,.gif" :on-success="uploadPicUrl">
<el-upload :headers="headers" :action="uploadPath" :show-file-list="false" :on-success="uploadPicUrl" class="avatar-uploader" list-type="picture-card" accept=".jpg,.jpeg,.png,.gif">
<img v-if="goods.picUrl" :src="goods.picUrl" class="avatar">
<i v-else class="el-icon-plus avatar-uploader-icon"></i>
<i v-else class="el-icon-plus avatar-uploader-icon"/>
</el-upload>
</el-form-item>
<el-form-item label="宣传画廊">
<el-upload :action='uploadPath' :headers="headers" :limit='5' multiple accept=".jpg,.jpeg,.png,.gif" :file-list="galleryFileList" list-type="picture-card" :on-exceed='uploadOverrun' :on-success="handleGalleryUrl" :on-remove="handleRemove">
<i class="el-icon-plus"></i>
<el-upload :action="uploadPath" :headers="headers" :limit="5" :file-list="galleryFileList" :on-exceed="uploadOverrun" :on-success="handleGalleryUrl" :on-remove="handleRemove" multiple accept=".jpg,.jpeg,.png,.gif" list-type="picture-card">
<i class="el-icon-plus"/>
</el-upload>
</el-form-item>
<el-form-item label="商品单位">
<el-input v-model="goods.unit" placeholder="件 / 个 / 盒"></el-input>
<el-input v-model="goods.unit" placeholder="件 / 个 / 盒"/>
</el-form-item>
<el-form-item label="关键字">
<el-tag :key="tag" v-for="tag in keywords" closable type="primary" @close="handleClose(tag)">
{{tag}}
<el-tag v-for="tag in keywords" :key="tag" closable type="primary" @close="handleClose(tag)">
{{ tag }}
</el-tag>
<el-input class="input-new-keyword" v-if="newKeywordVisible" v-model="newKeyword" ref="newKeywordInput" size="small" @keyup.enter.native="handleInputConfirm" @blur="handleInputConfirm">
</el-input>
<el-input v-if="newKeywordVisible" ref="newKeywordInput" v-model="newKeyword" class="input-new-keyword" size="small" @keyup.enter.native="handleInputConfirm" @blur="handleInputConfirm"/>
<el-button v-else class="button-new-keyword" size="small" type="primary" @click="showInput">+ 增加</el-button>
</el-form-item>
<el-form-item label="所属分类">
<el-cascader expand-trigger="hover" :options="categoryList" v-model="categoryIds" @change="handleCategoryChange"></el-cascader>
<el-cascader :options="categoryList" v-model="categoryIds" expand-trigger="hover" @change="handleCategoryChange"/>
</el-form-item>
<el-form-item label="所属品牌商">
<el-select v-model="goods.brandId">
<el-option v-for="item in brandList" :key="item.value" :label="item.label" :value="item.value">
</el-option>
<el-option v-for="item in brandList" :key="item.value" :label="item.label" :value="item.value"/>
</el-select>
</el-form-item>
<el-form-item label="商品简介">
<el-input v-model="goods.brief"></el-input>
<el-input v-model="goods.brief"/>
</el-form-item>
<el-form-item label="商品详细介绍">
<editor :init="editorInit" v-model="goods.detail"></editor>
<editor :init="editorInit" v-model="goods.detail"/>
</el-form-item>
</el-form>
</el-card>
<el-card class="box-card">
<h3>商品规格</h3>
<el-button :plain="true" @click="handleSpecificationShow" type="primary">添加</el-button>
<el-button :plain="true" type="primary" @click="handleSpecificationShow">添加</el-button>
<el-table :data="specifications">
<el-table-column property="specification" label="规格名" ></el-table-column>
<el-table-column property="specification" label="规格名" />
<el-table-column property="value" label="规格值" >
<template slot-scope="scope">
<el-tag type="primary">
{{scope.row.value}}
{{ scope.row.value }}
</el-tag>
</template>
</el-table-column>
<el-table-column property="picUrl" label="规格图片">
<template slot-scope="scope">
<img :src="scope.row.picUrl" width="40" v-if="scope.row.picUrl"/>
<img v-if="scope.row.picUrl" :src="scope.row.picUrl" width="40">
</template>
</el-table-column>
<el-table-column align="center" label="操作" width="250" class-name="small-padding fixed-width">
......@@ -111,18 +109,18 @@
</el-table-column>
</el-table>
<el-dialog title="设置规格" :visible.sync="specVisiable">
<el-form :rules="rules" ref="specForm" :model="specForm" status-icon label-position="left" label-width="100px" style='width: 400px; margin-left:50px;'>
<el-dialog :visible.sync="specVisiable" title="设置规格">
<el-form ref="specForm" :rules="rules" :model="specForm" status-icon label-position="left" label-width="100px" style="width: 400px; margin-left:50px;">
<el-form-item label="规格名" prop="specification">
<el-input v-model="specForm.specification"></el-input>
<el-input v-model="specForm.specification"/>
</el-form-item>
<el-form-item label="规格值" prop="value">
<el-input v-model="specForm.value"></el-input>
<el-input v-model="specForm.value"/>
</el-form-item>
<el-form-item label="规格图片" prop="picUrl">
<el-upload class="avatar-uploader" :headers="headers" :action='uploadPath' list-type="picture-card" :show-file-list="false" accept=".jpg,.jpeg,.png,.gif" :on-success="uploadSpecPicUrl">
<el-upload :headers="headers" :action="uploadPath" :show-file-list="false" :on-success="uploadSpecPicUrl" class="avatar-uploader" list-type="picture-card" accept=".jpg,.jpeg,.png,.gif">
<img v-if="specForm.picUrl" :src="specForm.picUrl" class="avatar">
<i v-else class="el-icon-plus avatar-uploader-icon"></i>
<i v-else class="el-icon-plus avatar-uploader-icon"/>
</el-upload>
</el-form-item>
</el-form>
......@@ -138,18 +136,16 @@
<el-table :data="products">
<el-table-column property="value" label="货品规格" >
<template slot-scope="scope">
<el-tag :key="tag" v-for="tag in scope.row.specifications">
{{tag}}
<el-tag v-for="tag in scope.row.specifications" :key="tag">
{{ tag }}
</el-tag>
</template>
</el-table-column>
<el-table-column property="price" width="100" label="货品售价">
</el-table-column>
<el-table-column property="number" width="100" label="货品数量">
</el-table-column>
<el-table-column property="price" width="100" label="货品售价"/>
<el-table-column property="number" width="100" label="货品数量"/>
<el-table-column property="url" width="100" label="货品图片">
<template slot-scope="scope">
<img :src="scope.row.url" width="40" v-if="scope.row.url"/>
<img v-if="scope.row.url" :src="scope.row.url" width="40">
</template>
</el-table-column>
<el-table-column align="center" label="操作" width="100" class-name="small-padding fixed-width">
......@@ -159,23 +155,23 @@
</el-table-column>
</el-table>
<el-dialog title="设置货品" :visible.sync="productVisiable">
<el-form ref="productForm" :model="productForm" status-icon label-position="left" label-width="100px" style='width: 400px; margin-left:50px;'>
<el-dialog :visible.sync="productVisiable" title="设置货品">
<el-form ref="productForm" :model="productForm" status-icon label-position="left" label-width="100px" style="width: 400px; margin-left:50px;">
<el-form-item label="货品规格列" prop="specifications">
<el-tag :key="tag" v-for="tag in productForm.specifications">
{{tag}}
<el-tag v-for="tag in productForm.specifications" :key="tag">
{{ tag }}
</el-tag>
</el-form-item>
<el-form-item label="货品售价" prop="price">
<el-input v-model="productForm.price"></el-input>
<el-input v-model="productForm.price"/>
</el-form-item>
<el-form-item label="货品数量" prop="number">
<el-input v-model="productForm.number"></el-input>
<el-input v-model="productForm.number"/>
</el-form-item>
<el-form-item label="货品图片" prop="url">
<el-upload class="avatar-uploader" :headers="headers" :action='uploadPath' list-type="picture-card" :show-file-list="false" accept=".jpg,.jpeg,.png,.gif" :on-success="uploadProductUrl">
<el-upload :headers="headers" :action="uploadPath" :show-file-list="false" :on-success="uploadProductUrl" class="avatar-uploader" list-type="picture-card" accept=".jpg,.jpeg,.png,.gif">
<img v-if="productForm.url" :src="productForm.url" class="avatar">
<i v-else class="el-icon-plus avatar-uploader-icon"></i>
<i v-else class="el-icon-plus avatar-uploader-icon"/>
</el-upload>
</el-form-item>
</el-form>
......@@ -188,12 +184,10 @@
<el-card class="box-card">
<h3>商品参数</h3>
<el-button :plain="true" @click="handleAttributeShow" type="primary">添加</el-button>
<el-button :plain="true" type="primary" @click="handleAttributeShow">添加</el-button>
<el-table :data="attributes">
<el-table-column property="attribute" label="商品参数名称">
</el-table-column>
<el-table-column property="value" label="商品参数值">
</el-table-column>
<el-table-column property="attribute" label="商品参数名称"/>
<el-table-column property="value" label="商品参数值"/>
<el-table-column align="center" label="操作" width="100" class-name="small-padding fixed-width">
<template slot-scope="scope">
<el-button type="danger" size="mini" @click="handleAttributeDelete(scope.row)">删除</el-button>
......@@ -201,13 +195,13 @@
</el-table-column>
</el-table>
<el-dialog title="设置商品参数" :visible.sync="attributeVisiable">
<el-form ref="attributeForm" :model="attributeForm" status-icon label-position="left" label-width="100px" style='width: 400px; margin-left:50px;'>
<el-dialog :visible.sync="attributeVisiable" title="设置商品参数">
<el-form ref="attributeForm" :model="attributeForm" status-icon label-position="left" label-width="100px" style="width: 400px; margin-left:50px;">
<el-form-item label="商品参数名称" prop="attribute">
<el-input v-model="attributeForm.attribute"></el-input>
<el-input v-model="attributeForm.attribute"/>
</el-form-item>
<el-form-item label="商品参数值" prop="value">
<el-input v-model="attributeForm.value"></el-input>
<el-input v-model="attributeForm.value"/>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
......@@ -219,47 +213,47 @@
<div class="op-container">
<el-button @click="handleCancel">取消</el-button>
<el-button @click="handleEdit" type="primary">更新商品</el-button>
<el-button type="primary" @click="handleEdit">更新商品</el-button>
</div>
</div>
</template>
<style>
.el-card {
.el-card {
margin-bottom: 10px;
}
.el-tag + .el-tag {
}
.el-tag + .el-tag {
margin-left: 10px;
}
.input-new-keyword {
}
.input-new-keyword {
width: 90px;
margin-left: 10px;
vertical-align: bottom;
}
.avatar-uploader .el-upload {
}
.avatar-uploader .el-upload {
border: 1px dashed #d9d9d9;
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
}
.avatar-uploader .el-upload:hover {
}
.avatar-uploader .el-upload:hover {
border-color: #20a0ff;
}
.avatar-uploader-icon {
}
.avatar-uploader-icon {
font-size: 28px;
color: #8c939d;
width: 120px;
height: 120px;
line-height: 120px;
text-align: center;
}
.avatar {
}
.avatar {
width: 120px;
height: 120px;
display: block;
}
}
</style>
<script>
......@@ -272,13 +266,6 @@ import { getToken } from '@/utils/auth'
export default {
name: 'GoodsEdit',
components: { Editor },
computed: {
headers() {
return {
'Admin-Token': getToken()
}
}
},
data() {
return {
uploadPath,
......@@ -294,32 +281,56 @@ export default {
specForm: { specification: '', value: '', picUrl: '' },
specifications: [{ specification: '规格', value: '标准', picUrl: '' }],
productVisiable: false,
productForm: { id: 0, specifications: [], price: 0.00, number: 0, url: '' },
products: [{ id: 0, specifications: ['标准'], price: 0.00, number: 0, url: '' }],
productForm: {
id: 0,
specifications: [],
price: 0.0,
number: 0,
url: ''
},
products: [
{ id: 0, specifications: ['标准'], price: 0.0, number: 0, url: '' }
],
attributeVisiable: false,
attributeForm: { attribute: '', value: '' },
attributes: [],
rules: {
goodsSn: [{ required: true, message: '商品编号不能为空', trigger: 'blur' }],
goodsSn: [
{ required: true, message: '商品编号不能为空', trigger: 'blur' }
],
name: [{ required: true, message: '商品名称不能为空', trigger: 'blur' }]
},
editorInit: {
language: 'zh_CN',
convert_urls: false,
plugins: ['advlist anchor autolink autosave code codesample colorpicker colorpicker contextmenu directionality emoticons fullscreen hr image imagetools importcss insertdatetime link lists media nonbreaking noneditable pagebreak paste preview print save searchreplace spellchecker tabfocus table template textcolor textpattern visualblocks visualchars wordcount'],
toolbar: ['searchreplace bold italic underline strikethrough alignleft aligncenter alignright outdent indent blockquote undo redo removeformat subscript superscript code codesample', 'hr bullist numlist link image charmap preview anchor pagebreak insertdatetime media table emoticons forecolor backcolor fullscreen'],
plugins: [
'advlist anchor autolink autosave code codesample colorpicker colorpicker contextmenu directionality emoticons fullscreen hr image imagetools importcss insertdatetime link lists media nonbreaking noneditable pagebreak paste preview print save searchreplace spellchecker tabfocus table template textcolor textpattern visualblocks visualchars wordcount'
],
toolbar: [
'searchreplace bold italic underline strikethrough alignleft aligncenter alignright outdent indent blockquote undo redo removeformat subscript superscript code codesample',
'hr bullist numlist link image charmap preview anchor pagebreak insertdatetime media table emoticons forecolor backcolor fullscreen'
],
images_upload_handler: function(blobInfo, success, failure) {
const formData = new FormData()
formData.append('file', blobInfo.blob())
createStorage(formData).then(res => {
createStorage(formData)
.then(res => {
success(res.data.data.url)
}).catch(() => {
})
.catch(() => {
failure('上传失败,请重新上传')
})
}
}
}
},
computed: {
headers() {
return {
'Admin-Token': getToken()
}
}
},
created() {
this.init()
},
......@@ -363,13 +374,15 @@ export default {
products: this.products,
attributes: this.attributes
}
editGoods(finalGoods).then(response => {
editGoods(finalGoods)
.then(response => {
this.$notify.success({
title: '成功',
message: '创建成功'
})
this.$router.push({ path: '/goods/list' })
}).catch(response => {
})
.catch(response => {
MessageBox.alert('业务错误:' + response.data.errmsg, '警告', {
confirmButtonText: '确定',
type: 'error'
......@@ -429,8 +442,12 @@ export default {
},
specChanged: function(label) {
if (label === false) {
this.specifications = [{ specification: '规格', value: '标准', picUrl: '' }]
this.products = [{ id: 0, specifications: ['标准'], price: 0.00, number: 0, url: '' }]
this.specifications = [
{ specification: '规格', value: '标准', picUrl: '' }
]
this.products = [
{ id: 0, specifications: ['标准'], price: 0.0, number: 0, url: '' }
]
} else {
this.specifications = []
this.products = []
......@@ -503,7 +520,13 @@ export default {
var z = specValues[x][combination[x]]
specifications.push(this.specifications[z].value)
}
products[productsIndex] = { id: productsIndex, specifications: specifications, price: 0.00, number: 0, url: '' }
products[productsIndex] = {
id: productsIndex,
specifications: specifications,
price: 0.0,
number: 0,
url: ''
}
productsIndex++
index++
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment