提交 df6ccee4 authored 作者: 王鹏飞's avatar 王鹏飞

update

上级 e1798446
......@@ -9,6 +9,7 @@
"version": "3.0.14",
"license": "ISC",
"dependencies": {
"@tinymce/tinymce-vue": "^3.2.8",
"axios": "^0.21.1",
"blueimp-md5": "^2.18.0",
"core-js": "^3.12.1",
......@@ -1429,6 +1430,14 @@
"to-fast-properties": "^2.0.0"
}
},
"node_modules/@tinymce/tinymce-vue": {
"version": "3.2.8",
"resolved": "https://registry.npmjs.org/@tinymce/tinymce-vue/-/tinymce-vue-3.2.8.tgz",
"integrity": "sha512-jEz+NZ0g+FZFz273OEUWz9QkwPMyjc5AJYyxOgu51O1Y5UaJ/6IUddXTX6A20mwCleEv5ebwNYdalviafx4fnA==",
"peerDependencies": {
"vue": "^2.4.3"
}
},
"node_modules/@tootallnate/once": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz",
......@@ -14734,6 +14743,12 @@
"to-fast-properties": "^2.0.0"
}
},
"@tinymce/tinymce-vue": {
"version": "3.2.8",
"resolved": "https://registry.npmjs.org/@tinymce/tinymce-vue/-/tinymce-vue-3.2.8.tgz",
"integrity": "sha512-jEz+NZ0g+FZFz273OEUWz9QkwPMyjc5AJYyxOgu51O1Y5UaJ/6IUddXTX6A20mwCleEv5ebwNYdalviafx4fnA==",
"requires": {}
},
"@tootallnate/once": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz",
......
......@@ -69,6 +69,7 @@
"webpack-merge": "^4.2.2"
},
"dependencies": {
"@tinymce/tinymce-vue": "^3.2.8",
"axios": "^0.21.1",
"blueimp-md5": "^2.18.0",
"core-js": "^3.12.1",
......
......@@ -22,7 +22,7 @@ export function getSignature() {
// 图片上传
export function uploadFile(data) {
return httpRequest.post('https://webapp-pub.ezijing.com', data, {
return httpRequest.post('https://webapp-pub.oss-cn-beijing.aliyuncs.com', data, {
withCredentials: false,
headers: { 'Content-Type': 'multipart/form-data' }
})
......
<template>
<app-upload class="video-uploader" accept="video/*" :show-file-list="false" v-bind="$attrs" v-on="$listeners">
<img v-if="videoUrl" :src="`${videoUrl}?x-oss-process=video/snapshot,t_1,f_jpg,w_0,h_0,m_fast`" class="avatar" />
<img v-if="videoUrl" :src="`${videoUrl}?x-oss-process=video/snapshot,t_10,f_jpg,w_0,h_0,m_fast`" class="avatar" />
<i v-else class="el-icon-plus video-uploader-icon"></i>
</app-upload>
</template>
......
<template>
<div class="app-breadcrumb">
<el-breadcrumb>
<el-breadcrumb-item v-for="route in routes" :key="route.path">
<router-link :to="route.path">{{ route.meta.title }}</router-link>
</el-breadcrumb-item>
</el-breadcrumb>
</div>
</template>
<script>
export default {
data() {
return {}
},
computed: {
routes() {
return this.$route.matched
}
}
}
</script>
<style lang="scss">
.app-breadcrumb {
padding: 40px 0 32px;
.el-breadcrumb {
font-size: 24px;
font-weight: 400;
line-height: 1;
}
.el-breadcrumb__inner a {
font-weight: normal;
color: #5b91fd;
}
.router-link-active {
color: #1a1b1c;
}
}
</style>
<template>
<div>
<!-- bidirectional data binding(双向数据绑定) -->
<quill-editor class="editor" v-model="content" ref="myQuillEditor" :disabled="disabled" :options="editorOption" @blur="onEditorBlur($event)" @focus="onEditorFocus($event)" @change="onEditorChange" @ready="onEditorReady($event)"></quill-editor>
<!-- 文件上传input 将它隐藏-->
<el-upload class="upload-demo" action="https://up.qbox.me/" :before-upload='beforeUpload' :data="uploadData" :on-success='handleSuccess' :accept="accept" ref="upload" style="display:none">
<el-button size="small" type="primary" id="uploadInput" v-loading.fullscreen.lock="fullscreenLoading" element-loading-text="插入中,请稍候">点击上传</el-button>
</el-upload>
</div>
</template>
<script>
// require styles
import 'quill/dist/quill.core.css'
import 'quill/dist/quill.snow.css'
import 'quill/dist/quill.bubble.css'
import { quillEditor } from 'vue-quill-editor'
import { getToken } from '@/api/base'
import * as Quill from 'quill' // 引入编辑器
import md5 from 'md5'
let BlockEmbed = Quill.import('blots/block/embed')
// 图片
class CustomImageBlot extends BlockEmbed {
static create(value) {
let node = super.create()
let image = document.createElement('img')
image.setAttribute('src', value.url)
image.setAttribute('data-src', value.url)
image.setAttribute('data-width', value.width)
image.setAttribute('data-height', value.height)
node.appendChild(image)
return node
}
static value(node) {
let image = node.firstElementChild
return {
url: image.getAttribute('src'),
width: image.getAttribute('data-width'),
height: image.getAttribute('data-height')
}
}
}
CustomImageBlot.blotName = 'customImage'
CustomImageBlot.tagName = 'div'
CustomImageBlot.className = 'zaiart-image'
Quill.register(CustomImageBlot, true)
// 视频
class CustomVideoBlot extends BlockEmbed {
static create(value) {
let node = super.create()
let video = document.createElement('video')
video.setAttribute('src', value.url)
video.setAttribute('poster', value.poster)
video.setAttribute('data-width', value.width)
video.setAttribute('data-height', value.height)
video.setAttribute('controls', true)
video.setAttribute('webkit-playsinline', true)
video.setAttribute('playsinline', true)
node.appendChild(video)
return node
}
static value(node) {
let video = node.firstElementChild
return {
url: video.getAttribute('src'),
poster: video.getAttribute('poster'),
width: video.getAttribute('data-width'),
height: video.getAttribute('data-height')
}
}
}
CustomVideoBlot.blotName = 'customVideo'
CustomVideoBlot.tagName = 'div'
CustomVideoBlot.className = 'zaiart-video'
Quill.register(CustomVideoBlot, true)
export default {
props: ['value'],
components: { quillEditor },
data() {
return {
content: '',
editorOption: {},
image: {},
video: {},
uploadData: { token: '', key: '' },
uploadType: '', // 上传的文件类型(图片、视频)
accept: '', // 接受上传的文件类型
fullscreenLoading: false,
addRange: {
type: Array,
default: function() {
return []
}
},
disabled: true, // 为了修复异步添加数据,光标聚焦的问题
timer: null
}
},
computed: {
quill() {
return this.$refs.myQuillEditor.quill
}
},
watch: {
value: {
handler: function(newVal, oldVal) {
this.content = this.value
},
immediate: true
},
content: function(value) {
this.$emit('input', value)
}
},
methods: {
beforeUpload(file) {
var suffix = file.name.substr(file.name.lastIndexOf('.'))
let key =
'zaiart/cms/image/' + md5(file.name + new Date().getTime()) + suffix
return new Promise((resolve, reject) => {
getToken()
.then(response => {
this.uploadData.token = response.data.token
this.uploadData.key = key
resolve(true)
})
.catch(err => {
console.log(err)
reject(err)
})
})
},
handleSuccess(response, file) {
const domain = 'https://img.zai-art.com/'
let url = domain + response.key
if (this.uploadType === 'image') {
this.insertImage(url)
} else if (this.uploadType === 'video') {
this.insertVideo(url)
}
this.$refs['upload'].clearFiles() // 插入成功后清除input的内容
},
// 插入图片
insertImage(value) {
let img = new Image()
img.src = value
img.onload = () => {
let range = this.quill.getSelection(true)
let data = { url: img.src, width: img.width, height: img.height }
this.quill.insertEmbed(
range.index || 0,
'customImage',
data,
Quill.sources.USER
)
}
},
// 插入视频
insertVideo(value) {
let video = document.createElement('video')
video.src = value
video.onloadedmetadata = () => {
let range = this.quill.getSelection(true)
let data = {
url: video.src,
poster: '',
width: video.videoWidth,
height: video.videoHeight
}
this.quill.insertEmbed(
range.index || 0,
'customVideo',
data,
Quill.sources.USER
)
}
},
// 编辑器光标离开 将编辑器内容发射给父组件
onEditorBlur(quill) {
// this.$emit('getValue', this.content)
},
// 编辑器获得光标
onEditorFocus(quill) {
// editor.enable(true) // 实现达到上限字符可删除
},
// 编辑器文本发生变化
onEditorChange({ editor, html, text }) {
// let textLength = text.length
// if (textLength > 10000) {
// this.$message.error('最多输入10000个字符')
// editor.enable(false)
// }
// this.$emit('getValue', this.content)
},
// 清除编辑器内容
callMethod() {
this.content = ''
},
onEditorReady(quill) {
console.log('editor ready!', quill)
},
// 点击图片ICON触发事件
imageHandler(state) {
this.addRange = this.quill.getSelection()
this.uploadType = 'image'
// 上传文件类型
this.accept = 'image/*'
this.$nextTick(function() {
let fileInput = document.getElementById('uploadInput')
// 加一个触发事件
state && fileInput.click()
})
},
// 点击视频ICON触发事件
videoHandler(state) {
this.addRange = this.quill.getSelection()
this.uploadType = 'video'
// 上传文件类型
this.accept = 'video/*'
this.$nextTick(function() {
let fileInput = document.getElementById('uploadInput')
// 加一个触发事件
state && fileInput.click()
})
}
},
mounted() {
// 为图片ICON绑定事件 getModule 为编辑器的内部属性
this.quill.getModule('toolbar').addHandler('image', this.imageHandler)
// 为视频ICON绑定事件
this.quill.getModule('toolbar').addHandler('video', this.videoHandler)
// 取消禁用
this.timer = setTimeout(() => {
this.disabled = false
}, 1000)
},
beforeDestroy() {
clearTimeout(this.timer)
}
}
</script>
<style>
.editor {
line-height: 1;
}
.editor .ql-container {
height: 600px;
}
</style>
<template>
<el-form :model="ruleForm" :rules="rules" label-width="120px" ref="ruleForm" @submit.native.prevent>
<template v-for="(item, index) in option.formGroup">
<!--upload-->
<el-form-item :label="item.label" v-if="item.type === 'upload'" :prop="item.code" :key="index">
<el-upload class="avatar-uploader" action="https://jsonplaceholder.typicode.com/posts/" :show-file-list="false" :prop="item.code">
<img v-if="ruleForm[item.code]" :src="ruleForm[item.code]" class="avatar">
<i v-else class="el-icon-plus avatar-uploader-icon"></i>
</el-upload>
</el-form-item>
<!--input-->
<el-form-item :label="item.label" v-if="item.type === 'input'" :prop="item.code" :key="index">
<el-input v-model="ruleForm[item.code]" :disabled="item.disabled"></el-input>
</el-form-item>
<!--select-->
<el-form-item :label="item.label" v-if="item.type === 'select'" :prop="item.code" :key="index">
<el-select v-model="ruleForm[item.code]" style="width:100%" :disabled="item.disabled">
<template v-for="(option, i) in item.groups">
<el-option v-if="typeof option === 'string'" :label="option" :value="option" :key="i"></el-option>
<el-option v-else :label="option.label" :value="option.code" :key="i"></el-option>
</template>
</el-select>
</el-form-item>
<!--checkbox-->
<el-form-item :label="item.label" v-if="item.type === 'checkbox'" :prop="item.code" :key="index">
<el-checkbox-group v-model="ruleForm[item.code]">
<el-checkbox :label="value.code" v-for="(value, i) in item.groups" :key="i">{{value.name}}</el-checkbox>
</el-checkbox-group>
</el-form-item>
<!--radio-->
<el-form-item :label="item.label" v-if="item.type === 'radio'" :prop="item.code" :key="index">
<el-radio-group v-model="ruleForm[item.code]">
<el-radio :label="radio.value" v-for="(radio, i) in item.groups" :key="i">{{radio.label}}</el-radio>
</el-radio-group>
</el-form-item>
<!--textarea-->
<el-form-item :label="item.label" v-if="item.type === 'textarea'" :prop="item.code" :key="index">
<el-input type="textarea" v-model="ruleForm[item.code]" :disabled="item.disabled"></el-input>
</el-form-item>
<!--switch-->
<el-form-item :label="item.label" v-if="item.type === 'switch'" :prop="item.code" :key="index">
<el-switch v-model="ruleForm[item.code]" :on-value="1" :off-value="0"> </el-switch>
</el-form-item>
<!--cascader-->
<el-form-item :label="item.label" v-if="item.type === 'cascader'" :prop="item.code" :key="index">
<el-cascader v-model="ruleForm[item.code]" :options="item.options" :props="item.props" :show-all-levels="false" change-on-select></el-cascader>
</el-form-item>
<!--子集-->
<template v-if="item.child">
<template v-for="(child, key) in item.child">
<!--input-->
<el-form-item :label="child.label" v-if="child.type === 'input'" :prop="child.code" v-show="ruleForm[item.code] == key" :key="key">
<el-input v-model="ruleForm[child.code]"></el-input>
</el-form-item>
</template>
</template>
</template>
<el-form-item>
<el-button type="primary" @click="onSubmit(option.onSubmit)">确定</el-button>
<el-button @click="onCancel(option.onCancel)">取消</el-button>
</el-form-item>
</el-form>
</template>
<script>
export default {
name: 'Form',
props: ['option'],
data() {
return {
ruleForm: Object.assign({}, this.option.form),
rules: Object.assign({}, this.option.rules)
}
},
methods: {
/* 确定 */
onSubmit(callback) {
this.$refs.ruleForm.validate(valid => {
if (valid) {
callback && callback(this.ruleForm)
} else {
return false
}
})
},
/* 取消 */
onCancel(callback) {
callback && callback(this.ruleForm)
/* 清空表单 */
// this.resetData()
this.$router.back()
},
/* 设置数据 */
setData(data) {
this.ruleForm = Object.assign(this.ruleForm, data)
},
/* 重置数据 */
resetData(done) {
/* 清空表单 */
this.$refs.ruleForm.resetFields()
this.ruleForm = Object.assign({}, this.option.form)
}
}
}
</script>
<style>
.avatar-uploader .el-upload {
border: 1px dashed #d9d9d9;
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
}
.avatar-uploader .el-upload:hover {
border-color: #20a0ff;
}
.avatar-uploader-icon {
font-size: 28px;
color: #8c939d;
width: 178px;
height: 178px;
line-height: 178px;
text-align: center;
}
.avatar {
width: 178px;
height: 178px;
display: block;
}
</style>
......@@ -3,7 +3,7 @@
<header class="main-header">
<slot name="header">
<span class="logo">
<router-link to="/"></router-link>
<router-link to="/shop"></router-link>
</span>
<span class="title">{{ title }}</span>
<div class="user">
......
<template>
<div class="upload-wrapper">
<el-upload action="https://up.qbox.me/" :on-change="handleChange" :before-upload="beforeUpload" :on-success="handleSuccess" :on-remove="handleRemove" :data="data" :file-list="fileList">
<el-button size="small" type="primary">点击上传</el-button>
</el-upload>
</div>
</template>
<script>
import { getToken } from '@/api/base'
import md5 from 'md5'
export default {
name: 'UploadFile',
props: {
value: String
},
data() {
return {
data: { token: '', key: '' },
fileList: []
}
},
watch: {
value() {
if (this.value) {
this.fileList = [{ name: this.value }]
}
}
},
methods: {
beforeUpload(file) {
let key =
'zaiart/saas/file/' +
md5(file.name + new Date().getTime()) +
file.name.substr(file.name.lastIndexOf('.'))
return new Promise((resolve, reject) => {
getToken()
.then(response => {
this.data.token = response.data.token
this.data.key = key
resolve(true)
})
.catch(err => {
console.log(err)
reject(err)
})
})
},
handleChange(file, fileList) {
this.fileList = fileList.slice(-1)
},
handleSuccess(response, file) {
let url = 'https://img.zai-art.com/' + response.key
this.uploaded(url)
},
handleRemove(file) {
this.uploaded('')
},
uploaded(url) {
this.$emit('uploaded', url)
}
}
}
</script>
<template>
<div class="upload-wrapper">
<el-upload class="avatar-uploader" action="https://up.qbox.me/" type="drag" :show-file-list="false" :before-upload="beforeUpload" :on-success="handleSuccess" :data="data">
<div v-if="imageUrl" class="avatar"><img :src="imageUrl"></div>
<i v-else class="el-icon-plus avatar-uploader-icon"></i>
</el-upload>
</div>
</template>
<script>
import { getToken } from '@/api/base'
import md5 from 'md5'
export default {
name: 'UploadImage',
props: {
value: { type: String, immediate: true }
},
data() {
return {
data: { token: '', key: '' },
image: {}
}
},
computed: {
imageUrl() {
if (this.value) {
if (this.value.indexOf('http') !== -1) {
return this.value
} else {
return 'https://img.zai-art.com/' + this.value
}
} else {
return null
}
}
},
methods: {
beforeUpload(file) {
this.$emit('beforeUpload')
let key =
'zaiart/saas/image/' +
md5(file.name + new Date().getTime()) +
file.name.substr(file.name.lastIndexOf('.'))
return new Promise((resolve, reject) => {
getToken()
.then(response => {
this.data.token = response.data.token
this.data.key = key
resolve(true)
})
.catch(err => {
console.log(err)
reject(err)
})
})
},
handleSuccess(response, file) {
const domain = 'https://img.zai-art.com/'
let img = new Image()
img.src = domain + response.key
img.onload = () => {
this.image = { url: img.src, width: img.width, height: img.height }
this.uploaded(this.image)
}
},
uploaded(image) {
this.$emit('input', image.url)
this.$emit('uploaded', image)
}
}
}
</script>
<style>
.avatar-uploader .el-upload {
border: 1px dashed #d9d9d9;
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
}
.avatar-uploader .el-upload:hover {
border-color: #20a0ff;
}
.avatar-uploader-icon {
font-size: 28px;
color: #8c939d;
width: 178px;
height: 178px;
line-height: 178px;
text-align: center;
}
.avatar {
width: 178px;
height: 178px;
display: block;
}
.avatar img {
max-width: 100%;
max-height: 100%;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
</style>
<template>
<div class="upload-wrapper">
<el-upload
class="avatar-uploader"
action="https://up.qbox.me/"
list-type="picture-card"
:on-remove="handleRemove"
:before-upload="beforeUpload"
:on-success="handleSuccess"
:file-list="fileList2"
:data="data"
accept="video/*,image/*"
>
<i class="el-icon-plus"></i>
</el-upload>
</div>
</template>
<script>
import { getToken, fetchImage } from '@/api/base'
import md5 from 'md5'
export default {
domain: 'https://img.zai-art.com/',
props: {
value: {
type: Array,
default: function () {
return []
}
}
},
data() {
return {
data: { token: '', key: '' },
image: {},
video: {}
}
},
computed: {
fileList2() {
const results = []
for (let index = 0; index < this.value.length; index++) {
const photo = this.value[index]
const file = { name: '', url: photo.imageUrl }
results.push(file)
}
return results
}
},
methods: {
beforeUpload(file) {
var suffix = file.name.substr(file.name.lastIndexOf('.'))
const suffixList = ['.jpg', '.jpeg', '.png', '.gif', '.mp4']
if (!suffixList.includes(suffix)) {
this.$message.error('暂不支持此文件的格式!')
return false
}
const key = 'zaiart/saas/image/' + md5(file.name + new Date().getTime()) + suffix
return new Promise((resolve, reject) => {
getToken()
.then(response => {
this.data.token = response.data.token
this.data.key = key
resolve(true)
})
.catch(err => {
console.log(err)
reject(err)
})
})
},
handleSuccess(response, file) {
const domain = 'https://img.zai-art.com/'
if (
response.key.indexOf('jpg') !== -1 ||
response.key.indexOf('jpeg') !== -1 ||
response.key.indexOf('png') !== -1 ||
response.key.indexOf('gif') !== -1
) {
const img = document.createElement('img')
img.src = domain + response.key
img.onload = () => {
this.image = {
imageUrl: img.src,
imageWidth: img.width,
imageHeight: img.height,
resourceType: 0
}
this.uploaded(this.image)
}
} else if (response.key.indexOf('mp4') !== -1) {
var imgUrl1 = domain + response.key + '?vframe/jpg/offset/1'
var vidowUrl = domain + response.key
fetchImage({ url: imgUrl1 })
.then(response => {
var imgUrl2 = response.data.data.url
const img = document.createElement('img')
img.src = imgUrl2
img.onload = () => {
this.video = {
resourceUrl: vidowUrl,
resourceWidth: img.width,
resourceHeight: img.height,
resourceType: 1,
imageUrl: img.src,
imageWidth: img.width,
imageHeight: img.height
}
this.uploaded(this.video)
}
})
.catch(err => {
console.log(err)
})
} else {
this.$message.error('暂不支持此文件的格式!')
}
},
handleRemove(file, fileList) {
this.$emit('remove', file.url)
},
uploaded(file) {
this.$emit('uploaded', file)
}
}
}
</script>
......@@ -221,8 +221,9 @@ export default {
</script>
<style>
.sku-view {
border: 1px solid #e5e5e5;
padding: 10px;
background-color: #f9f9f9;
border-radius: 8px;
padding: 24px;
}
.sku-view table {
width: 100%;
......@@ -230,11 +231,11 @@ export default {
.sku-view th,
.sku-view td {
padding: 0 10px;
border: 1px solid #e5e5e5;
border: 1px solid #dcdfe6;
text-align: center;
}
.sku-view th {
background-color: #f2f2f2;
background-color: #f9f9f9;
font-weight: normal;
white-space: nowrap;
}
......
import { getSignature, uploadFile } from '@/api/base'
import md5 from 'blueimp-md5'
export default function(blobInfo, succFun, failFun) {
const file = blobInfo.blob()
getSignature()
.then(response => {
const prefix = 'upload/shop-admin/'
const fileName = file.name
const key = prefix + md5(fileName + new Date().getTime()) + fileName.substr(fileName.lastIndexOf('.'))
const { accessid, policy, signature, host } = response
const data = { key, OSSAccessKeyId: accessid, policy, signature, success_action_status: '200', file }
const fileUrl = `${host}/${key}`
uploadFile(data)
.then(() => {
succFun(fileUrl)
})
.catch(() => {
failFun('上传失败')
})
})
.catch(response => {
failFun('获取Signature失败')
})
}
<template>
<editor :init="init" v-bind="$attrs" v-on="$listeners" @onChange="onChange" @onBlur="onBlur" />
</template>
<script>
import Editor from '@tinymce/tinymce-vue'
import ImageUpload from './imageUpload'
export default {
components: {
editor: Editor
},
data() {
return {
init: {
min_height: 600,
max_height: 600,
menubar: false,
statusbar: false,
plugins: 'table autoresize charmap fullscreen hr lists link code preview quickbars',
toolbar:
'undo redo | fontsizeselect lineheight bold italic underline strikethrough forecolor backcolor | link quickimage image media table | align hangingindent indent outdent numlist bullist | charmap blockquote hr fullscreen | code preview',
// font_formats:
// '微软雅黑=Microsoft YaHei,Helvetica Neue,PingFang SC,sans-serif;苹果苹方=PingFang SC,Microsoft YaHei,sans-serif;宋体=simsun,serif;仿宋体=FangSong,serif;黑体=SimHei,sans-serif;Arial=arial,helvetica,sans-serif;Times New Roman',
fontsize_formats: '8px 10px 12px 14px 15px 16px 17px 18px 20px 24px',
lineheight_formats: '0.5 1 1.2 1.5 2',
images_upload_handler: ImageUpload,
automatic_uploads: true,
quickbars_insert_toolbar: false,
// style_formats: [{ title: '悬挂缩进', block: 'p', styles: { textIndent: '-2em', paddingLeft: '2em' } }],
content_style: 'img {max-width:100%;}'
}
}
},
methods: {
onChange(event, editor) {
this.dispatch('ElFormItem', 'el.form.change', editor.getContent())
},
onBlur(event, editor) {
this.dispatch('ElFormItem', 'el.form.blur', editor.getContent())
},
dispatch(componentName, eventName, params) {
var parent = this.$parent || this.$root
var name = parent.$options.componentName
while (parent && (!name || name !== componentName)) {
parent = parent.$parent
if (parent) {
name = parent.$options.componentName
}
}
if (parent) {
parent.$emit.apply(parent, [eventName].concat(params))
}
}
}
}
</script>
<style>
.tox .tox-tbtn--bespoke .tox-tbtn__select-label {
width: 4em !important;
}
</style>
......@@ -16,5 +16,6 @@
</head>
<body>
<div id="app"></div>
<script src="https://webapp-pub.ezijing.com/plugins/tinymce/tinymce.min.js"></script>
</body>
</html>
<template>
<div v-loading="loading" element-loading-text="拼命加载中">
<el-form :model="ruleForm" :rules="rules" label-position="top" ref="ruleForm" @submit.native.prevent>
<el-steps :active="stepActive" simple>
<el-step title="1.编辑基本信息"></el-step>
<el-step title="2.编辑商品详情"></el-step>
</el-steps>
<el-form
:model="ruleForm"
:rules="rules"
label-position="top"
ref="ruleForm"
@submit.native.prevent
v-show="stepActive === 1"
>
<app-card title="商品类型">
<el-radio-group v-model="ruleForm.category_id">
<el-radio :label="item.id" border v-for="item in categoryList" :key="item.id">{{ item.name }}</el-radio>
</el-radio-group>
</app-card>
<app-card title="基本信息">
<el-row :gutter="20">
<el-col :span="12">
......@@ -92,13 +102,25 @@
</el-radio-group>
</el-form-item>
</app-card>
<div class="form-footer">
<div class="inner">
<el-button type="primary" @click="onSubmit">保存并查看</el-button>
<el-button @click="onSubmit">下一步</el-button>
</el-form>
<app-card v-show="stepActive === 2">
<div class="goods-detail">
<div class="goods-preview">
<div class="goods-preview-hd">商品详情效果预览(移动端)</div>
<div class="goods-preview-html" v-html="ruleForm.spu_context"></div>
</div>
<div class="goods-editor">
<editor v-model="ruleForm.spu_context"></editor>
</div>
</div>
</el-form>
</app-card>
<div class="form-footer">
<div class="inner">
<el-button @click="prev" v-show="stepActive === 2">上一步</el-button>
<el-button type="primary" @click="submit()">保存并查看</el-button>
<el-button @click="next" v-show="stepActive === 1">下一步</el-button>
</div>
</div>
</div>
</template>
......@@ -110,15 +132,16 @@ import AppUploadVideo from '@/components/base/uploadVideo'
import Sku from '@/components/goods/sku'
import SkuView from '@/components/goods/skuView'
import Message from '@/components/goods/message'
import Editor from '@/components/tinymce'
// 接口
import { addGoods, updateGoods, getGoodsList } from '@/api/goods'
export default {
components: { AppCard, AppUpload, AppUploadVideo, Sku, SkuView, Message },
components: { AppCard, AppUpload, AppUploadVideo, Sku, SkuView, Message, Editor },
props: { isEdit: { type: Boolean, default: false } },
data() {
return {
loading: false, // 加载中
stepActive: 1,
categoryList: [{ id: '1', name: '虚拟商品' }], // 商品类型
ruleForm: {
category_id: '1', // 商品类型
......@@ -218,8 +241,20 @@ export default {
this.loading = false
})
},
// 上一步
prev() {
this.submit(() => {
this.stepActive = 1
})
},
// 下一步
next() {
this.submit(() => {
this.stepActive = 2
})
},
// 确定
onSubmit() {
submit(callback) {
// 基本信息表单校验
this.$refs.ruleForm
.validate()
......@@ -241,44 +276,29 @@ export default {
params.chart_oss = JSON.stringify(params.chart_oss)
params.spec = JSON.stringify(params.goodStockList)
params.app_button_text = JSON.stringify(params.messageList)
this.isEdit ? this.onEdit(params) : this.onAdd(params)
this.isEdit ? this.handleEdit(params, callback) : this.handleAdd(params, callback)
})
.catch(() => {
this.$message({ message: '请完善表单信息', type: 'error' })
})
},
// 取消
onCancel() {
// 返回上一页
this.$router.back()
},
// 成功回调
onSuccess(data) {
const redirectUri = this.$route.query.redirect_uri
if (redirectUri) {
this.$router.push({
path: decodeURIComponent(redirectUri),
query: { goods_id: data.id }
})
} else {
// 返回商品列表
this.$router.push('/goods')
}
handleSuccess(data) {
this.$message({ message: '保存商品成功', type: 'success' })
this.$router.push({ path: '/preview', query: { shop_id: this.shopId, id: data.spu_id } })
},
// 确定添加商品
onAdd(params) {
handleAdd(params, callback) {
addGoods(params).then(response => {
this.$message({ message: '保存商品成功', type: 'success' })
// 成功回调
this.onSuccess(response.data)
callback ? callback(response) : this.handleSuccess(response)
})
},
// 确定修改商品
onEdit(params) {
handleEdit(params, callback) {
updateGoods(params).then(response => {
this.$message({ message: '保存商品成功', type: 'success' })
// 成功回调
this.onSuccess(response.data)
callback ? callback(response) : this.handleSuccess(response)
})
},
onBuyButtonTextRadioChange(value) {
......@@ -294,6 +314,10 @@ export default {
</script>
<style lang="scss" scoped>
::v-deep .el-steps--simple {
margin-bottom: 20px;
background-color: #fff;
}
::v-deep .el-form--label-top .el-form-item__label {
padding: 0;
}
......@@ -315,7 +339,39 @@ export default {
align-items: center;
}
}
.goods-detail {
display: flex;
}
.goods-preview {
flex: 1;
overflow: hidden;
}
.goods-preview-hd {
background: #e5e5e5;
text-align: center;
height: 40px;
line-height: 40px;
color: #333;
}
.goods-preview-html {
height: 560px;
overflow-x: hidden;
overflow-y: auto;
padding: 14px;
border: 1px dashed #155bd4;
box-sizing: border-box;
}
::v-deep .goods-preview-html {
* {
max-width: 100% !important;
}
}
.goods-editor {
margin-left: 40px;
flex: 1;
overflow: hidden;
}
.buy-button-input {
display: inline-block;
width: 220px;
......
......@@ -80,7 +80,7 @@ export default {
{ label: '访问量', prop: 'page_view' },
{ label: '库存', prop: 'stock' },
{ label: '销量', prop: 'sales_volume' },
{ label: '创建时间', prop: 'create_time' },
{ label: '创建时间', prop: 'create_time', width: 180 },
{
label: '商品状态',
prop: 'status',
......@@ -89,7 +89,7 @@ export default {
return map[row.status]
}
},
{ label: '操作', slots: 'table-x' }
{ label: '操作', slots: 'table-x', width: 150 }
]
}
}
......
......@@ -58,7 +58,7 @@ export default {
httpRequest: getGroupList,
params: { shop_id: this.shopId, group_name: '' }
},
filters: [{ type: 'input', prop: 'group_name', placeholder: '搜索分组' }],
// filters: [{ type: 'input', prop: 'group_name', placeholder: '搜索分组' }],
columns: [
{ label: '分组名称', prop: 'group_name' },
{ label: '商品数', prop: 'spu_sum', width: 100 },
......
<template>
<div class="preview"></div>
</template>
<script>
export default {}
</script>
<style>
</style>
......@@ -103,5 +103,9 @@ export default [
meta: { title: '商品设置' }
}
]
},
{
path: '/preview',
component: () => import(/* webpackChunkName: "preview" */ '@/pages/preview')
}
]
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论