Compare commits
25 Commits
tinymce-ac
...
master
Author | SHA1 | Date |
---|---|---|
花裤衩 | 33a93a12b4 | |
花裤衩 | 1bc2e5c198 | |
花裤衩 | 9cc3fa732a | |
花裤衩 | 44e23d00ca | |
花裤衩 | 8fd08c9167 | |
花裤衩 | 5e7113935c | |
Kerollos Magdy | 46e11caafc | |
dyz | ea60478304 | |
crayymumu | fe22b3cd94 | |
花裤衩 | 7c06214190 | |
花裤衩 | 73a9ab0379 | |
花裤衩 | 585391228e | |
Dihak | ae2aa61880 | |
花裤衩 | f6d8204b0b | |
花裤衩 | 435db380d3 | |
qige2016 | 6d88db5c73 | |
花裤衩 | 1c943509f9 | |
花裤衩 | 4e7665c072 | |
花裤衩 | a87218e266 | |
Silentdoer | 7702b3d809 | |
MaYuanhai | a50180f653 | |
Cat73 | fc68f56d13 | |
花裤衩 | d3bd933a8e | |
花裤衩 | 0bf61aac53 | |
花裤衩 | 2ef49493c3 |
|
@ -3,12 +3,3 @@ ENV = 'development'
|
||||||
|
|
||||||
# base api
|
# base api
|
||||||
VUE_APP_BASE_API = '/dev-api'
|
VUE_APP_BASE_API = '/dev-api'
|
||||||
|
|
||||||
# vue-cli uses the VUE_CLI_BABEL_TRANSPILE_MODULES environment variable,
|
|
||||||
# to control whether the babel-plugin-dynamic-import-node plugin is enabled.
|
|
||||||
# It only does one thing by converting all import() to require().
|
|
||||||
# This configuration can significantly increase the speed of hot updates,
|
|
||||||
# when you have a large number of pages.
|
|
||||||
# Detail: https://github.com/vuejs/vue-cli/blob/dev/packages/@vue/babel-preset-app/index.js
|
|
||||||
|
|
||||||
VUE_CLI_BABEL_TRANSPILE_MODULES = true
|
|
||||||
|
|
|
@ -72,7 +72,13 @@ Entendiendo y aprendiendo esto pudiera ayudarle con su proyecto.
|
||||||
|
|
||||||
Sea un patrocinante y coloque su logo en nuestro LEEME en GitHub con un enlace directo a su sitio web. [[Se un Patrocinante]](https://www.patreon.com/panjiachen)
|
Sea un patrocinante y coloque su logo en nuestro LEEME en GitHub con un enlace directo a su sitio web. [[Se un Patrocinante]](https://www.patreon.com/panjiachen)
|
||||||
|
|
||||||
<a href="https://flatlogic.com/admin-dashboards?from=vue-element-admin"><img width="150px" src="https://wpimg.wallstcn.com/9c0b719b-5551-4c1e-b776-63994632d94a.png" /></a><p>Plantilla de Dashboard de administración hecha con Vue, React y Angular.</p>
|
### Akveo
|
||||||
|
<a href="https://store.akveo.com/products/vue-java-admin-dashboard-spring?utm_campaign=akveo_store-Vue-Vue_demo%2Fgithub&utm_source=vue_admin&utm_medium=referral&utm_content=github_banner"><img width="500px" src="https://raw.githubusercontent.com/PanJiaChen/vue-element-admin-site/master/docs/.vuepress/public/images/vue-java-banner.png" /></a><p>Get Java backend for Vue admin with 20% discount for 39$ use coupon code SWB0RAZPZR1M
|
||||||
|
</p>
|
||||||
|
|
||||||
|
### Flatlogic
|
||||||
|
|
||||||
|
<a href="https://flatlogic.com/admin-dashboards?from=vue-element-admin"><img width="150px" src="https://wpimg.wallstcn.com/9c0b719b-5551-4c1e-b776-63994632d94a.png" /></a><p>Admin Dashboard Templates made with Vue, React and Angular.</p>
|
||||||
|
|
||||||
## Características
|
## Características
|
||||||
|
|
||||||
|
|
|
@ -69,6 +69,12 @@
|
||||||
|
|
||||||
Become a sponsor and get your logo on our README on GitHub with a link to your site. [[Become a sponsor]](https://www.patreon.com/panjiachen)
|
Become a sponsor and get your logo on our README on GitHub with a link to your site. [[Become a sponsor]](https://www.patreon.com/panjiachen)
|
||||||
|
|
||||||
|
### Akveo
|
||||||
|
<a href="https://store.akveo.com/products/vue-java-admin-dashboard-spring?utm_campaign=akveo_store-Vue-Vue_demo%2Fgithub&utm_source=vue_admin&utm_medium=referral&utm_content=github_banner"><img width="500px" src="https://raw.githubusercontent.com/PanJiaChen/vue-element-admin-site/master/docs/.vuepress/public/images/vue-java-banner.png" /></a><p>Get Java backend for Vue admin with 20% discount for 39$ use coupon code SWB0RAZPZR1M
|
||||||
|
</p>
|
||||||
|
|
||||||
|
### Flatlogic
|
||||||
|
|
||||||
<a href="https://flatlogic.com/admin-dashboards?from=vue-element-admin"><img width="150px" src="https://wpimg.wallstcn.com/9c0b719b-5551-4c1e-b776-63994632d94a.png" /></a><p>Admin Dashboard Templates made with Vue, React and Angular.</p>
|
<a href="https://flatlogic.com/admin-dashboards?from=vue-element-admin"><img width="150px" src="https://wpimg.wallstcn.com/9c0b719b-5551-4c1e-b776-63994632d94a.png" /></a><p>Admin Dashboard Templates made with Vue, React and Angular.</p>
|
||||||
|
|
||||||
## 機能一覧
|
## 機能一覧
|
||||||
|
|
28
README.md
28
README.md
|
@ -28,6 +28,28 @@
|
||||||
|
|
||||||
English | [简体中文](./README.zh-CN.md) | [日本語](./README.ja.md) | [Spanish](./README.es.md)
|
English | [简体中文](./README.zh-CN.md) | [日本語](./README.ja.md) | [Spanish](./README.es.md)
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<b>SPONSORED BY</b>
|
||||||
|
</p>
|
||||||
|
<table align="center" cellspacing="0" cellpadding="0">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td align="center" valign="middle" width="250">
|
||||||
|
<a href="https://www.duohui.cn/?utm_source=vue-element-admin&utm_medium=web&utm_campaign=vue-element-admin_github" title="多会" target="_blank">
|
||||||
|
<img height="60px" src="https://qiniu.cdn.duohui.co/brand/duohui.png" title="多会 - 活动服务销售平台">
|
||||||
|
<p>活动服务销售平台</p>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td align="center" valign="middle" width="250">
|
||||||
|
<a href="https://youke.co/?utm_source=vue-element-admin&utm_medium=web&utm_campaign=vue-element-admin_github" title="有客" target="_blank">
|
||||||
|
<img height="60px" src="https://qiniu.cdn.duohui.co/brand/youke.png" title="有客 - 客户消息直达工作群">
|
||||||
|
<p>客户消息直达工作群</p>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
## Introduction
|
## Introduction
|
||||||
|
|
||||||
[vue-element-admin](https://panjiachen.github.io/vue-element-admin) is a production-ready front-end solution for admin interfaces. It is based on [vue](https://github.com/vuejs/vue) and uses the UI Toolkit [element-ui](https://github.com/ElemeFE/element).
|
[vue-element-admin](https://panjiachen.github.io/vue-element-admin) is a production-ready front-end solution for admin interfaces. It is based on [vue](https://github.com/vuejs/vue) and uses the UI Toolkit [element-ui](https://github.com/ElemeFE/element).
|
||||||
|
@ -72,6 +94,12 @@ Understanding and learning this knowledge in advance will greatly help the use o
|
||||||
|
|
||||||
Become a sponsor and get your logo on our README on GitHub with a link to your site. [[Become a sponsor]](https://www.patreon.com/panjiachen)
|
Become a sponsor and get your logo on our README on GitHub with a link to your site. [[Become a sponsor]](https://www.patreon.com/panjiachen)
|
||||||
|
|
||||||
|
### Akveo
|
||||||
|
<a href="https://store.akveo.com/products/vue-java-admin-dashboard-spring?utm_campaign=akveo_store-Vue-Vue_demo%2Fgithub&utm_source=vue_admin&utm_medium=referral&utm_content=github_banner"><img width="500px" src="https://raw.githubusercontent.com/PanJiaChen/vue-element-admin-site/master/docs/.vuepress/public/images/vue-java-banner.png" /></a><p>Get Java backend for Vue admin with 20% discount for 39$ use coupon code SWB0RAZPZR1M
|
||||||
|
</p>
|
||||||
|
|
||||||
|
### Flatlogic
|
||||||
|
|
||||||
<a href="https://flatlogic.com/admin-dashboards?from=vue-element-admin"><img width="150px" src="https://wpimg.wallstcn.com/9c0b719b-5551-4c1e-b776-63994632d94a.png" /></a><p>Admin Dashboard Templates made with Vue, React and Angular.</p>
|
<a href="https://flatlogic.com/admin-dashboards?from=vue-element-admin"><img width="150px" src="https://wpimg.wallstcn.com/9c0b719b-5551-4c1e-b776-63994632d94a.png" /></a><p>Admin Dashboard Templates made with Vue, React and Angular.</p>
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
|
@ -28,6 +28,28 @@
|
||||||
|
|
||||||
简体中文 | [English](./README.md) | [日本語](./README.ja.md) | [Spanish](./README.es.md)
|
简体中文 | [English](./README.md) | [日本語](./README.ja.md) | [Spanish](./README.es.md)
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<b>SPONSORED BY</b>
|
||||||
|
</p>
|
||||||
|
<table align="center" cellspacing="0" cellpadding="0">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td align="center" valign="middle" width="250">
|
||||||
|
<a href="https://www.duohui.cn/?utm_source=vue-element-admin&utm_medium=web&utm_campaign=vue-element-admin_github" title="多会" target="_blank">
|
||||||
|
<img height="60px" src="https://qiniu.cdn.duohui.co/brand/duohui.png" title="多会 - 活动服务销售平台">
|
||||||
|
<p>活动服务销售平台</p>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td align="center" valign="middle" width="250">
|
||||||
|
<a href="https://youke.co/?utm_source=vue-element-admin&utm_medium=web&utm_campaign=vue-element-admin_github" title="有客" target="_blank">
|
||||||
|
<img height="60px" src="https://qiniu.cdn.duohui.co/brand/youke.png" title="有客 - 客户消息直达工作群">
|
||||||
|
<p>客户消息直达工作群</p>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
## 简介
|
## 简介
|
||||||
|
|
||||||
[vue-element-admin](https://panjiachen.github.io/vue-element-admin) 是一个后台前端解决方案,它基于 [vue](https://github.com/vuejs/vue) 和 [element-ui](https://github.com/ElemeFE/element)实现。它使用了最新的前端技术栈,内置了 i18n 国际化解决方案,动态路由,权限验证,提炼了典型的业务模型,提供了丰富的功能组件,它可以帮助你快速搭建企业级中后台产品原型。相信不管你的需求是什么,本项目都能帮助到你。
|
[vue-element-admin](https://panjiachen.github.io/vue-element-admin) 是一个后台前端解决方案,它基于 [vue](https://github.com/vuejs/vue) 和 [element-ui](https://github.com/ElemeFE/element)实现。它使用了最新的前端技术栈,内置了 i18n 国际化解决方案,动态路由,权限验证,提炼了典型的业务模型,提供了丰富的功能组件,它可以帮助你快速搭建企业级中后台产品原型。相信不管你的需求是什么,本项目都能帮助到你。
|
||||||
|
@ -87,8 +109,14 @@
|
||||||
|
|
||||||
Become a sponsor and get your logo on our README on GitHub with a link to your site. [[Become a sponsor]](https://www.patreon.com/panjiachen)
|
Become a sponsor and get your logo on our README on GitHub with a link to your site. [[Become a sponsor]](https://www.patreon.com/panjiachen)
|
||||||
|
|
||||||
|
### Akveo
|
||||||
|
<a href="https://store.akveo.com/products/vue-java-admin-dashboard-spring?utm_campaign=akveo_store-Vue-Vue_demo%2Fgithub&utm_source=vue_admin&utm_medium=referral&utm_content=github_banner"><img width="500px" src="https://raw.githubusercontent.com/PanJiaChen/vue-element-admin-site/master/docs/.vuepress/public/images/vue-java-banner.png" /></a><p>Java 后端整合,可以使用优惠码:SWB0RAZPZR1M,获得20%的价格优化</p>
|
||||||
|
|
||||||
|
### Flatlogic
|
||||||
|
|
||||||
<a href="https://flatlogic.com/admin-dashboards?from=vue-element-admin"><img width="150px" src="https://wpimg.wallstcn.com/9c0b719b-5551-4c1e-b776-63994632d94a.png" /></a><p>Admin Dashboard Templates made with Vue, React and Angular.</p>
|
<a href="https://flatlogic.com/admin-dashboards?from=vue-element-admin"><img width="150px" src="https://wpimg.wallstcn.com/9c0b719b-5551-4c1e-b776-63994632d94a.png" /></a><p>Admin Dashboard Templates made with Vue, React and Angular.</p>
|
||||||
|
|
||||||
|
|
||||||
## 功能
|
## 功能
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
|
@ -1,5 +1,14 @@
|
||||||
module.exports = {
|
module.exports = {
|
||||||
presets: [
|
presets: [
|
||||||
'@vue/app'
|
// https://github.com/vuejs/vue-cli/tree/master/packages/@vue/babel-preset-app
|
||||||
]
|
'@vue/cli-plugin-babel/preset'
|
||||||
|
],
|
||||||
|
'env': {
|
||||||
|
'development': {
|
||||||
|
// babel-plugin-dynamic-import-node plugin only does one thing by converting all import() to require().
|
||||||
|
// This plugin can significantly increase the speed of hot updates, when you have a large number of pages.
|
||||||
|
// https://panjiachen.github.io/vue-element-admin-site/guide/advanced/lazy-loading.html
|
||||||
|
'plugins': ['dynamic-import-node']
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import Mock from 'mockjs'
|
const Mock = require('mockjs')
|
||||||
|
|
||||||
const List = []
|
const List = []
|
||||||
const count = 100
|
const count = 100
|
||||||
|
@ -27,7 +27,7 @@ for (let i = 0; i < count; i++) {
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
export default [
|
module.exports = [
|
||||||
{
|
{
|
||||||
url: '/vue-element-admin/article/list',
|
url: '/vue-element-admin/article/list',
|
||||||
type: 'get',
|
type: 'get',
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
import Mock from 'mockjs'
|
const Mock = require('mockjs')
|
||||||
import { param2Obj } from '../src/utils'
|
const { param2Obj } = require('./utils')
|
||||||
|
|
||||||
import user from './user'
|
const user = require('./user')
|
||||||
import role from './role'
|
const role = require('./role')
|
||||||
import article from './article'
|
const article = require('./article')
|
||||||
import search from './remote-search'
|
const search = require('./remote-search')
|
||||||
|
|
||||||
const mocks = [
|
const mocks = [
|
||||||
...user,
|
...user,
|
||||||
|
@ -16,7 +16,7 @@ const mocks = [
|
||||||
// for front mock
|
// for front mock
|
||||||
// please use it cautiously, it will redefine XMLHttpRequest,
|
// please use it cautiously, it will redefine XMLHttpRequest,
|
||||||
// which will cause many of your third-party libraries to be invalidated(like progress event).
|
// which will cause many of your third-party libraries to be invalidated(like progress event).
|
||||||
export function mockXHR() {
|
function mockXHR() {
|
||||||
// mock patch
|
// mock patch
|
||||||
// https://github.com/nuysoft/Mock/issues/300
|
// https://github.com/nuysoft/Mock/issues/300
|
||||||
Mock.XHR.prototype.proxy_send = Mock.XHR.prototype.send
|
Mock.XHR.prototype.proxy_send = Mock.XHR.prototype.send
|
||||||
|
@ -54,4 +54,7 @@ export function mockXHR() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default mocks
|
module.exports = {
|
||||||
|
mocks,
|
||||||
|
mockXHR
|
||||||
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ const mockDir = path.join(process.cwd(), 'mock')
|
||||||
|
|
||||||
function registerRoutes(app) {
|
function registerRoutes(app) {
|
||||||
let mockLastIndex
|
let mockLastIndex
|
||||||
const { default: mocks } = require('./index.js')
|
const { mocks } = require('./index.js')
|
||||||
const mocksForServer = mocks.map(route => {
|
const mocksForServer = mocks.map(route => {
|
||||||
return responseFake(route.url, route.type, route.response)
|
return responseFake(route.url, route.type, route.response)
|
||||||
})
|
})
|
||||||
|
@ -44,9 +44,6 @@ const responseFake = (url, type, respond) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = app => {
|
module.exports = app => {
|
||||||
// es6 polyfill
|
|
||||||
require('@babel/register')
|
|
||||||
|
|
||||||
// parse app.body
|
// parse app.body
|
||||||
// https://expressjs.com/en/4x/api.html#req.body
|
// https://expressjs.com/en/4x/api.html#req.body
|
||||||
app.use(bodyParser.json())
|
app.use(bodyParser.json())
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import Mock from 'mockjs'
|
const Mock = require('mockjs')
|
||||||
|
|
||||||
const NameList = []
|
const NameList = []
|
||||||
const count = 100
|
const count = 100
|
||||||
|
@ -10,7 +10,7 @@ for (let i = 0; i < count; i++) {
|
||||||
}
|
}
|
||||||
NameList.push({ name: 'mock-Pan' })
|
NameList.push({ name: 'mock-Pan' })
|
||||||
|
|
||||||
export default [
|
module.exports = [
|
||||||
// username search
|
// username search
|
||||||
{
|
{
|
||||||
url: '/vue-element-admin/search/user',
|
url: '/vue-element-admin/search/user',
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import Mock from 'mockjs'
|
const Mock = require('mockjs')
|
||||||
import { deepClone } from '../../src/utils/index.js'
|
const { deepClone } = require('../utils')
|
||||||
import { asyncRoutes, constantRoutes } from './routes.js'
|
const { asyncRoutes, constantRoutes } = require('./routes.js')
|
||||||
|
|
||||||
const routes = deepClone([...constantRoutes, ...asyncRoutes])
|
const routes = deepClone([...constantRoutes, ...asyncRoutes])
|
||||||
|
|
||||||
|
@ -35,7 +35,7 @@ const roles = [
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
export default [
|
module.exports = [
|
||||||
// mock get all routes form server
|
// mock get all routes form server
|
||||||
{
|
{
|
||||||
url: '/vue-element-admin/routes',
|
url: '/vue-element-admin/routes',
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
// Just a mock data
|
// Just a mock data
|
||||||
|
|
||||||
export const constantRoutes = [
|
const constantRoutes = [
|
||||||
{
|
{
|
||||||
path: '/redirect',
|
path: '/redirect',
|
||||||
component: 'layout/Layout',
|
component: 'layout/Layout',
|
||||||
|
@ -72,7 +72,7 @@ export const constantRoutes = [
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
export const asyncRoutes = [
|
const asyncRoutes = [
|
||||||
{
|
{
|
||||||
path: '/permission',
|
path: '/permission',
|
||||||
component: 'layout/Layout',
|
component: 'layout/Layout',
|
||||||
|
@ -523,3 +523,8 @@ export const asyncRoutes = [
|
||||||
|
|
||||||
{ path: '*', redirect: '/404', hidden: true }
|
{ path: '*', redirect: '/404', hidden: true }
|
||||||
]
|
]
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
constantRoutes,
|
||||||
|
asyncRoutes
|
||||||
|
}
|
||||||
|
|
|
@ -23,7 +23,7 @@ const users = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default [
|
module.exports = [
|
||||||
// user login
|
// user login
|
||||||
{
|
{
|
||||||
url: '/vue-element-admin/user/login',
|
url: '/vue-element-admin/user/login',
|
||||||
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
/**
|
||||||
|
* @param {string} url
|
||||||
|
* @returns {Object}
|
||||||
|
*/
|
||||||
|
function param2Obj(url) {
|
||||||
|
const search = decodeURIComponent(url.split('?')[1]).replace(/\+/g, ' ')
|
||||||
|
if (!search) {
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
const obj = {}
|
||||||
|
const searchArr = search.split('&')
|
||||||
|
searchArr.forEach(v => {
|
||||||
|
const index = v.indexOf('=')
|
||||||
|
if (index !== -1) {
|
||||||
|
const name = v.substring(0, index)
|
||||||
|
const val = v.substring(index + 1, v.length)
|
||||||
|
obj[name] = val
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return obj
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
* @param {Object} source
|
||||||
|
* @returns {Object}
|
||||||
|
*/
|
||||||
|
function deepClone(source) {
|
||||||
|
if (!source && typeof source !== 'object') {
|
||||||
|
throw new Error('error arguments', 'deepClone')
|
||||||
|
}
|
||||||
|
const targetObj = source.constructor === Array ? [] : {}
|
||||||
|
Object.keys(source).forEach(keys => {
|
||||||
|
if (source[keys] && typeof source[keys] === 'object') {
|
||||||
|
targetObj[keys] = deepClone(source[keys])
|
||||||
|
} else {
|
||||||
|
targetObj[keys] = source[keys]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return targetObj
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
param2Obj,
|
||||||
|
deepClone
|
||||||
|
}
|
107
package.json
107
package.json
|
@ -1,55 +1,28 @@
|
||||||
{
|
{
|
||||||
"name": "vue-element-admin",
|
"name": "vue-element-admin",
|
||||||
"version": "4.3.0",
|
"version": "4.4.0",
|
||||||
"description": "A magical vue admin. An out-of-box UI solution for enterprise applications. Newest development stack of vue. Lots of awesome features",
|
"description": "A magical vue admin. An out-of-box UI solution for enterprise applications. Newest development stack of vue. Lots of awesome features",
|
||||||
"author": "Pan <panfree23@gmail.com>",
|
"author": "Pan <panfree23@gmail.com>",
|
||||||
"license": "MIT",
|
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vue-cli-service serve",
|
"dev": "vue-cli-service serve",
|
||||||
|
"lint": "eslint --ext .js,.vue src",
|
||||||
"build:prod": "vue-cli-service build",
|
"build:prod": "vue-cli-service build",
|
||||||
"build:stage": "vue-cli-service build --mode staging",
|
"build:stage": "vue-cli-service build --mode staging",
|
||||||
"preview": "node build/index.js --preview",
|
"preview": "node build/index.js --preview",
|
||||||
"lint": "eslint --ext .js,.vue src",
|
"new": "plop",
|
||||||
"test:unit": "jest --clearCache && vue-cli-service test:unit",
|
|
||||||
"test:ci": "npm run lint && npm run test:unit",
|
|
||||||
"svgo": "svgo -f src/icons/svg --config=src/icons/svgo.yml",
|
"svgo": "svgo -f src/icons/svg --config=src/icons/svgo.yml",
|
||||||
"new": "plop"
|
"test:unit": "jest --clearCache && vue-cli-service test:unit",
|
||||||
},
|
"test:ci": "npm run lint && npm run test:unit"
|
||||||
"husky": {
|
|
||||||
"hooks": {
|
|
||||||
"pre-commit": "lint-staged"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"lint-staged": {
|
|
||||||
"src/**/*.{js,vue}": [
|
|
||||||
"eslint --fix",
|
|
||||||
"git add"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"keywords": [
|
|
||||||
"vue",
|
|
||||||
"admin",
|
|
||||||
"dashboard",
|
|
||||||
"element-ui",
|
|
||||||
"boilerplate",
|
|
||||||
"admin-template",
|
|
||||||
"management-system"
|
|
||||||
],
|
|
||||||
"repository": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "git+https://github.com/PanJiaChen/vue-element-admin.git"
|
|
||||||
},
|
|
||||||
"bugs": {
|
|
||||||
"url": "https://github.com/PanJiaChen/vue-element-admin/issues"
|
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"axios": "0.18.1",
|
"axios": "0.18.1",
|
||||||
"clipboard": "2.0.4",
|
"clipboard": "2.0.4",
|
||||||
"codemirror": "5.45.0",
|
"codemirror": "5.45.0",
|
||||||
|
"core-js": "3.6.5",
|
||||||
"driver.js": "0.9.5",
|
"driver.js": "0.9.5",
|
||||||
"dropzone": "5.5.1",
|
"dropzone": "5.5.1",
|
||||||
"echarts": "4.2.1",
|
"echarts": "4.2.1",
|
||||||
"element-ui": "2.13.0",
|
"element-ui": "2.13.2",
|
||||||
"file-saver": "2.0.1",
|
"file-saver": "2.0.1",
|
||||||
"fuse.js": "3.4.4",
|
"fuse.js": "3.4.4",
|
||||||
"js-cookie": "2.2.0",
|
"js-cookie": "2.2.0",
|
||||||
|
@ -60,7 +33,6 @@
|
||||||
"path-to-regexp": "2.4.0",
|
"path-to-regexp": "2.4.0",
|
||||||
"screenfull": "4.2.0",
|
"screenfull": "4.2.0",
|
||||||
"script-loader": "0.7.2",
|
"script-loader": "0.7.2",
|
||||||
"showdown": "1.9.0",
|
|
||||||
"sortablejs": "1.8.4",
|
"sortablejs": "1.8.4",
|
||||||
"tui-editor": "1.3.3",
|
"tui-editor": "1.3.3",
|
||||||
"vue": "2.6.10",
|
"vue": "2.6.10",
|
||||||
|
@ -72,43 +44,68 @@
|
||||||
"xlsx": "0.14.1"
|
"xlsx": "0.14.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "7.0.0",
|
"@vue/cli-plugin-babel": "4.4.4",
|
||||||
"@babel/register": "7.0.0",
|
"@vue/cli-plugin-eslint": "4.4.4",
|
||||||
"@vue/cli-plugin-babel": "3.5.3",
|
"@vue/cli-plugin-unit-jest": "4.4.4",
|
||||||
"@vue/cli-plugin-eslint": "^3.9.1",
|
"@vue/cli-service": "4.4.4",
|
||||||
"@vue/cli-plugin-unit-jest": "3.5.3",
|
|
||||||
"@vue/cli-service": "3.5.3",
|
|
||||||
"@vue/test-utils": "1.0.0-beta.29",
|
"@vue/test-utils": "1.0.0-beta.29",
|
||||||
"autoprefixer": "^9.5.1",
|
"autoprefixer": "9.5.1",
|
||||||
"babel-core": "7.0.0-bridge.0",
|
"babel-eslint": "10.1.0",
|
||||||
"babel-eslint": "10.0.1",
|
|
||||||
"babel-jest": "23.6.0",
|
"babel-jest": "23.6.0",
|
||||||
|
"babel-plugin-dynamic-import-node": "2.3.3",
|
||||||
"chalk": "2.4.2",
|
"chalk": "2.4.2",
|
||||||
"chokidar": "2.1.5",
|
"chokidar": "2.1.5",
|
||||||
"connect": "3.6.6",
|
"connect": "3.6.6",
|
||||||
"eslint": "5.15.3",
|
"eslint": "6.7.2",
|
||||||
"eslint-plugin-vue": "5.2.2",
|
"eslint-plugin-vue": "6.2.2",
|
||||||
"html-webpack-plugin": "3.2.0",
|
"html-webpack-plugin": "3.2.0",
|
||||||
"html2canvas": "^1.0.0-rc.5",
|
|
||||||
"husky": "1.3.1",
|
"husky": "1.3.1",
|
||||||
"lint-staged": "8.1.5",
|
"lint-staged": "8.1.5",
|
||||||
"mockjs": "1.0.1-beta3",
|
"mockjs": "1.0.1-beta3",
|
||||||
"plop": "2.3.0",
|
"plop": "2.3.0",
|
||||||
"runjs": "^4.3.2",
|
"runjs": "4.3.2",
|
||||||
"sass": "^1.26.2",
|
"sass": "1.26.2",
|
||||||
"sass-loader": "^7.1.0",
|
"sass-loader": "8.0.2",
|
||||||
"script-ext-html-webpack-plugin": "2.1.3",
|
"script-ext-html-webpack-plugin": "2.1.3",
|
||||||
"serve-static": "^1.13.2",
|
"serve-static": "1.13.2",
|
||||||
"svg-sprite-loader": "4.1.3",
|
"svg-sprite-loader": "4.1.3",
|
||||||
"svgo": "1.2.0",
|
"svgo": "1.2.0",
|
||||||
"vue-template-compiler": "2.6.10"
|
"vue-template-compiler": "2.6.10"
|
||||||
},
|
},
|
||||||
|
"browserslist": [
|
||||||
|
"> 1%",
|
||||||
|
"last 2 versions"
|
||||||
|
],
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://github.com/PanJiaChen/vue-element-admin/issues"
|
||||||
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=8.9",
|
"node": ">=8.9",
|
||||||
"npm": ">= 3.0.0"
|
"npm": ">= 3.0.0"
|
||||||
},
|
},
|
||||||
"browserslist": [
|
"keywords": [
|
||||||
"> 1%",
|
"vue",
|
||||||
"last 2 versions"
|
"admin",
|
||||||
]
|
"dashboard",
|
||||||
|
"element-ui",
|
||||||
|
"boilerplate",
|
||||||
|
"admin-template",
|
||||||
|
"management-system"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"lint-staged": {
|
||||||
|
"src/**/*.{js,vue}": [
|
||||||
|
"eslint --fix",
|
||||||
|
"git add"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"husky": {
|
||||||
|
"hooks": {
|
||||||
|
"pre-commit": "lint-staged"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git+https://github.com/PanJiaChen/vue-element-admin.git"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,2 @@
|
||||||
exports.notEmpty = name => {
|
exports.notEmpty = name => v =>
|
||||||
return v => {
|
!v || v.trim() === '' ? `${name} is required` : true
|
||||||
if (!v || v.trim === '') {
|
|
||||||
return `${name} is required`
|
|
||||||
} else {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -248,6 +248,7 @@ export default {
|
||||||
// 浏览器是否支持该控件
|
// 浏览器是否支持该控件
|
||||||
isSupported,
|
isSupported,
|
||||||
// 浏览器是否支持触屏事件
|
// 浏览器是否支持触屏事件
|
||||||
|
// eslint-disable-next-line no-prototype-builtins
|
||||||
isSupportTouch: document.hasOwnProperty('ontouchstart'),
|
isSupportTouch: document.hasOwnProperty('ontouchstart'),
|
||||||
// 步骤
|
// 步骤
|
||||||
step: 1, // 1选择文件 2剪裁 3上传
|
step: 1, // 1选择文件 2剪裁 3上传
|
||||||
|
|
|
@ -1,12 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="upload-container">
|
<div class="upload-container">
|
||||||
<el-button
|
<el-button :style="{background:color,borderColor:color}" icon="el-icon-upload" size="mini" type="primary" @click=" dialogVisible=true">
|
||||||
:style="{background:color,borderColor:color}"
|
|
||||||
icon="el-icon-upload"
|
|
||||||
size="mini"
|
|
||||||
type="primary"
|
|
||||||
@click=" dialogVisible=true"
|
|
||||||
>
|
|
||||||
upload
|
upload
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-dialog :visible.sync="dialogVisible">
|
<el-dialog :visible.sync="dialogVisible">
|
||||||
|
@ -21,20 +15,14 @@
|
||||||
action="https://httpbin.org/post"
|
action="https://httpbin.org/post"
|
||||||
list-type="picture-card"
|
list-type="picture-card"
|
||||||
>
|
>
|
||||||
<el-button
|
<el-button size="small" type="primary">
|
||||||
size="small"
|
|
||||||
type="primary"
|
|
||||||
>
|
|
||||||
Click upload
|
Click upload
|
||||||
</el-button>
|
</el-button>
|
||||||
</el-upload>
|
</el-upload>
|
||||||
<el-button @click="dialogVisible = false">
|
<el-button @click="dialogVisible = false">
|
||||||
Cancel
|
Cancel
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button
|
<el-button type="primary" @click="handleSubmit">
|
||||||
type="primary"
|
|
||||||
@click="handleSubmit"
|
|
||||||
>
|
|
||||||
Confirm
|
Confirm
|
||||||
</el-button>
|
</el-button>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
|
@ -61,16 +49,12 @@ export default {
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
checkAllSuccess() {
|
checkAllSuccess() {
|
||||||
return Object.keys(this.listObj).every(
|
return Object.keys(this.listObj).every(item => this.listObj[item].hasSuccess)
|
||||||
item => this.listObj[item].hasSuccess
|
|
||||||
)
|
|
||||||
},
|
},
|
||||||
handleSubmit() {
|
handleSubmit() {
|
||||||
const arr = Object.keys(this.listObj).map(v => this.listObj[v])
|
const arr = Object.keys(this.listObj).map(v => this.listObj[v])
|
||||||
if (!this.checkAllSuccess()) {
|
if (!this.checkAllSuccess()) {
|
||||||
this.$message(
|
this.$message('Please wait for all images to be uploaded successfully. If there is a network problem, please refresh the page and upload again!')
|
||||||
'Please wait for all images to be uploaded successfully. If there is a network problem, please refresh the page and upload again!'
|
|
||||||
)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
this.$emit('successCBK', arr)
|
this.$emit('successCBK', arr)
|
||||||
|
@ -108,12 +92,7 @@ export default {
|
||||||
const img = new Image()
|
const img = new Image()
|
||||||
img.src = _URL.createObjectURL(file)
|
img.src = _URL.createObjectURL(file)
|
||||||
img.onload = function() {
|
img.onload = function() {
|
||||||
_self.listObj[fileName] = {
|
_self.listObj[fileName] = { hasSuccess: false, uid: file.uid, width: this.width, height: this.height }
|
||||||
hasSuccess: false,
|
|
||||||
uid: file.uid,
|
|
||||||
width: this.width,
|
|
||||||
height: this.height
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
resolve(true)
|
resolve(true)
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,234 +0,0 @@
|
||||||
<template>
|
|
||||||
<div class="layout-container">
|
|
||||||
<div ref="list" class="layout-body">
|
|
||||||
<tinymce
|
|
||||||
v-model="content"
|
|
||||||
:menu="menu"
|
|
||||||
:height="height"
|
|
||||||
:new-event-list="eventList"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="util-component" style="display:none;">
|
|
||||||
<el-upload
|
|
||||||
:multiple="true"
|
|
||||||
:file-list="dataList.list"
|
|
||||||
:on-progress="uploadProgress"
|
|
||||||
:on-success="handleSuccess"
|
|
||||||
class="editor-slide-upload"
|
|
||||||
action="https://httpbin.org/post"
|
|
||||||
list-type="picture-card"
|
|
||||||
>
|
|
||||||
<el-button id="editorUploader" size="small" type="primary">
|
|
||||||
Click upload
|
|
||||||
</el-button>
|
|
||||||
</el-upload>
|
|
||||||
</div>
|
|
||||||
<div ref="imageWrapper" class="show-html" v-html="content" />
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import Tinymce from './index'
|
|
||||||
import html2canvas from 'html2canvas'
|
|
||||||
import { Loading } from 'element-ui'
|
|
||||||
export default {
|
|
||||||
name: 'TinymceDemo',
|
|
||||||
components: { Tinymce },
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
showPreviewDialog: false,
|
|
||||||
dataurl: '',
|
|
||||||
eventList: [
|
|
||||||
{
|
|
||||||
toolbarName: 'uploadImg',
|
|
||||||
tooltip: 'uploadImg',
|
|
||||||
text: 'uploadImg',
|
|
||||||
fn: (e, callback) => {
|
|
||||||
this.handleAdd('uploadImg', data => {
|
|
||||||
if (callback && typeof callback === 'function') {
|
|
||||||
callback(data)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// {
|
|
||||||
// toolbarName: 'uploadVideo',
|
|
||||||
// tooltip: 'uploadVideo',
|
|
||||||
// text: 'uploadVideo',
|
|
||||||
// fn: (e, callback) => {
|
|
||||||
// this.handleAdd('uploadVideo', data => {
|
|
||||||
// if (callback && typeof callback === 'function') {
|
|
||||||
// callback(data)
|
|
||||||
// }
|
|
||||||
// })
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
{
|
|
||||||
toolbarName: 'spreview',
|
|
||||||
tooltip: 'spreview',
|
|
||||||
text: 'spreview',
|
|
||||||
fn: (e, callback) => {
|
|
||||||
this.toImage()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
isUpload: false,
|
|
||||||
dataList: {
|
|
||||||
list: [
|
|
||||||
{
|
|
||||||
url: ''
|
|
||||||
}
|
|
||||||
],
|
|
||||||
type: 'image',
|
|
||||||
setDom: v => `<a>${v.dom}</a>`
|
|
||||||
},
|
|
||||||
height: 700,
|
|
||||||
menu: {
|
|
||||||
file: { title: '文件', items: 'newdocument' },
|
|
||||||
edit: {
|
|
||||||
title: '编辑',
|
|
||||||
items: 'undo redo | cut copy paste pastetext | selectall'
|
|
||||||
},
|
|
||||||
insert: { title: '插入', items: 'link media | image hr' },
|
|
||||||
view: { title: '查看', items: 'visualaid' },
|
|
||||||
format: {
|
|
||||||
title: '格式',
|
|
||||||
items:
|
|
||||||
'bold italic underline strikethrough superscript subscript | formats | removeformat'
|
|
||||||
},
|
|
||||||
table: {
|
|
||||||
title: '表格',
|
|
||||||
items: 'inserttable tableprops deletetable | cell row column'
|
|
||||||
},
|
|
||||||
tools: { title: '工具', items: 'spellchecker code' }
|
|
||||||
},
|
|
||||||
loadingInstance: null,
|
|
||||||
content: `<h1 style="text-align: center;">测试demo!</h1>
|
|
||||||
<p>嗨</p>
|
|
||||||
<img class="img" src="https://ali-image-test.dabanjia.com/image/20200508/1588911589606_5140%24ban.jpg" width="200"/>
|
|
||||||
<p>枕着幸福在梦中微笑,清零生活的压力烦恼,阳光透过窗与你拥抱,伸伸懒腰迎接周末来到,问候源于无法按捺的心跳,愿你周末乐逍遥,生活更美妙!
|
|
||||||
周末来到,让烦恼跑光,让忧愁找不到,跟寂寞说再见,跟压力说拜拜,跟快乐说你好,牵着幸福的手,携上平安,开开心心的过周末!
|
|
||||||
每个人,都有一个世界;每首歌,都有一个故事;每一周,都有一个周末;每个人,都要一个愿望;我的愿望很现实:周末清晨,搅黄你的美梦!周末快乐!
|
|
||||||
</p>
|
|
||||||
`
|
|
||||||
}
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
fileType() {
|
|
||||||
// 自定义配置上传组件支持的文件,需要对上传组件进行封装,制定文件规格
|
|
||||||
if (this.dataList.type === 'image') {
|
|
||||||
return 'png|jpe?g|gif|svg'
|
|
||||||
}
|
|
||||||
if (this.dataList.type === 'video') {
|
|
||||||
// 暂时只支持mp4
|
|
||||||
return 'mp4'
|
|
||||||
// return "mp4|webm|ogg|mp3|wav|flac|aac";
|
|
||||||
}
|
|
||||||
return ''
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
// 实时获取高度,并减去padding值
|
|
||||||
this.height = this.$refs.list.clientHeight - 32
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
toImage() {
|
|
||||||
var width = 330 // 获取dom 宽度
|
|
||||||
var height = this.$refs.imageWrapper.scrollHeight // 获取dom 高度
|
|
||||||
var canvas = document.createElement('canvas') // 创建一个canvas节点
|
|
||||||
var scale = 2 // 定义任意放大倍数 支持小数默认设置为2,确保图片清晰度
|
|
||||||
canvas.width = width * scale * scale // 定义canvas 宽度 * 缩放
|
|
||||||
canvas.height = height * scale * scale // 定义canvas高度 *缩放
|
|
||||||
canvas.getContext('2d').scale(scale, scale) // 获取context,设置scale
|
|
||||||
var opts = {
|
|
||||||
tainttest: true, // 检测每张图片都已经加载完成
|
|
||||||
scale: scale, // 添加的scale 参数
|
|
||||||
useCORS: true,
|
|
||||||
canvas: canvas, // 自定义 canvas
|
|
||||||
logging: true, // 日志开关
|
|
||||||
width: width, // dom 原始宽度
|
|
||||||
height: height // dom 原始高度
|
|
||||||
}
|
|
||||||
html2canvas(this.$refs.imageWrapper, opts).then(canvas => {
|
|
||||||
const dataurl = canvas.toDataURL('image/png')
|
|
||||||
this.dataurl = dataurl
|
|
||||||
if (this.dataurl !== '') {
|
|
||||||
console.log(this.dataurl, '生成base64')
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
error(e) {
|
|
||||||
console.log(e)
|
|
||||||
},
|
|
||||||
handleAdd(key, callback) {
|
|
||||||
this.dataList.list[0].url = ''
|
|
||||||
this.isUpload = false
|
|
||||||
switch (key) {
|
|
||||||
case 'uploadImg':
|
|
||||||
this.dataList.type = 'image'
|
|
||||||
document.getElementById('editorUploader').click()
|
|
||||||
|
|
||||||
break
|
|
||||||
case 'uploadVideo':
|
|
||||||
this.dataList.type = 'video'
|
|
||||||
document.getElementById('editorUploader').click()
|
|
||||||
|
|
||||||
break
|
|
||||||
// 批量操作,可自定义所有操作
|
|
||||||
case 'selectImg':
|
|
||||||
break
|
|
||||||
case 'selectVideo':
|
|
||||||
break
|
|
||||||
}
|
|
||||||
const unWatch = this.$watch('isUpload', val => {
|
|
||||||
if (val && callback && typeof callback === 'function') {
|
|
||||||
callback(this.dataList)
|
|
||||||
// 取消监听
|
|
||||||
unWatch()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
uploadProgress() {
|
|
||||||
this.loadingInstance = Loading.service({
|
|
||||||
lock: true,
|
|
||||||
text: '数据加载中,请稍后...',
|
|
||||||
background: 'rgba(0, 0, 0, 0.1)'
|
|
||||||
})
|
|
||||||
},
|
|
||||||
// 定义模板
|
|
||||||
handleSuccess(data, file) {
|
|
||||||
this.loadingInstance.close()
|
|
||||||
this.dataList.list[0].url = file.url
|
|
||||||
const { type } = this.dataList
|
|
||||||
if (type === 'image') {
|
|
||||||
this.dataList.setDom = v =>
|
|
||||||
`<img class="img-scale" src="${v.url}" width="200">`
|
|
||||||
}
|
|
||||||
if (type === 'video') {
|
|
||||||
// 自定义配置模板信息,宽高后期都可变成输入
|
|
||||||
this.dataList.setDom = v =>
|
|
||||||
`<p >
|
|
||||||
<video controls="controls" width="200" height="350" >
|
|
||||||
<source src="https://dev-saas.oss-cn-beijing.aliyuncs.com/056eb234fa9844b0b7ec3e13bd39faf7.mp4" type="video/mp4" />
|
|
||||||
<source src="${v.url}" type="video/mp4" />
|
|
||||||
</video>
|
|
||||||
|
|
||||||
</p>
|
|
||||||
`
|
|
||||||
}
|
|
||||||
this.isUpload = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
<style scoped>
|
|
||||||
.show-html {
|
|
||||||
position: fixed;
|
|
||||||
right: 40px;
|
|
||||||
top: 80px;
|
|
||||||
width: 330px;
|
|
||||||
padding: 16px;
|
|
||||||
background: #fff;
|
|
||||||
border: 1px solid goldenrod;
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,16 +1,8 @@
|
||||||
<template>
|
<template>
|
||||||
<div
|
<div :class="{fullscreen:fullscreen}" class="tinymce-container" :style="{width:containerWidth}">
|
||||||
:class="{ fullscreen: fullscreen }"
|
|
||||||
class="tinymce-container"
|
|
||||||
:style="{ width: containerWidth }"
|
|
||||||
>
|
|
||||||
<textarea :id="tinymceId" class="tinymce-textarea" />
|
<textarea :id="tinymceId" class="tinymce-textarea" />
|
||||||
<div class="editor-custom-btn-container">
|
<div class="editor-custom-btn-container">
|
||||||
<editorImage
|
<editorImage color="#1890ff" class="editor-upload-btn" @successCBK="imageSuccessCBK" />
|
||||||
color="#1890ff"
|
|
||||||
class="editor-upload-btn"
|
|
||||||
@successCBK="imageSuccessCBK"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -26,8 +18,7 @@ import toolbar from './toolbar'
|
||||||
import load from './dynamicLoadScript'
|
import load from './dynamicLoadScript'
|
||||||
|
|
||||||
// why use this cdn, detail see https://github.com/PanJiaChen/tinymce-all-in-one
|
// why use this cdn, detail see https://github.com/PanJiaChen/tinymce-all-in-one
|
||||||
const tinymceCDN =
|
const tinymceCDN = 'https://cdn.jsdelivr.net/npm/tinymce-all-in-one@4.9.3/tinymce.min.js'
|
||||||
'https://cdn.jsdelivr.net/npm/tinymce-all-in-one@4.9.3/tinymce.min.js'
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Tinymce',
|
name: 'Tinymce',
|
||||||
|
@ -36,11 +27,7 @@ export default {
|
||||||
id: {
|
id: {
|
||||||
type: String,
|
type: String,
|
||||||
default: function() {
|
default: function() {
|
||||||
return (
|
return 'vue-tinymce-' + +new Date() + ((Math.random() * 1000).toFixed(0) + '')
|
||||||
'vue-tinymce-' +
|
|
||||||
+new Date() +
|
|
||||||
((Math.random() * 1000).toFixed(0) + '')
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
value: {
|
value: {
|
||||||
|
@ -58,13 +45,6 @@ export default {
|
||||||
type: String,
|
type: String,
|
||||||
default: 'file edit insert view format table'
|
default: 'file edit insert view format table'
|
||||||
},
|
},
|
||||||
newEventList: {
|
|
||||||
type: Array,
|
|
||||||
required: false,
|
|
||||||
default() {
|
|
||||||
return []
|
|
||||||
}
|
|
||||||
},
|
|
||||||
height: {
|
height: {
|
||||||
type: [Number, String],
|
type: [Number, String],
|
||||||
required: false,
|
required: false,
|
||||||
|
@ -83,18 +63,17 @@ export default {
|
||||||
tinymceId: this.id,
|
tinymceId: this.id,
|
||||||
fullscreen: false,
|
fullscreen: false,
|
||||||
languageTypeList: {
|
languageTypeList: {
|
||||||
en: 'en',
|
'en': 'en',
|
||||||
zh: 'zh_CN',
|
'zh': 'zh_CN',
|
||||||
es: 'es_MX',
|
'es': 'es_MX',
|
||||||
ja: 'ja'
|
'ja': 'ja'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
containerWidth() {
|
containerWidth() {
|
||||||
const width = this.width
|
const width = this.width
|
||||||
if (/^[\d]+(\.[\d]+)?$/.test(width)) {
|
if (/^[\d]+(\.[\d]+)?$/.test(width)) { // matches `100`, `'100'`
|
||||||
// matches `100`, `'100'`
|
|
||||||
return `${width}px`
|
return `${width}px`
|
||||||
}
|
}
|
||||||
return width
|
return width
|
||||||
|
@ -104,8 +83,7 @@ export default {
|
||||||
value(val) {
|
value(val) {
|
||||||
if (!this.hasChange && this.hasInit) {
|
if (!this.hasChange && this.hasInit) {
|
||||||
this.$nextTick(() =>
|
this.$nextTick(() =>
|
||||||
window.tinymce.get(this.tinymceId).setContent(val || '')
|
window.tinymce.get(this.tinymceId).setContent(val || ''))
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -126,7 +104,7 @@ export default {
|
||||||
methods: {
|
methods: {
|
||||||
init() {
|
init() {
|
||||||
// dynamic load tinymce from cdn
|
// dynamic load tinymce from cdn
|
||||||
load(tinymceCDN, err => {
|
load(tinymceCDN, (err) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
this.$message.error(err.message)
|
this.$message.error(err.message)
|
||||||
return
|
return
|
||||||
|
@ -136,39 +114,13 @@ export default {
|
||||||
},
|
},
|
||||||
initTinymce() {
|
initTinymce() {
|
||||||
const _this = this
|
const _this = this
|
||||||
// 自定义事件列表
|
|
||||||
let newToolbar = []
|
|
||||||
let barStr = ''
|
|
||||||
const eventData = []
|
|
||||||
this.newEventList.map(itemData => {
|
|
||||||
const { toolbarName, tooltip, text, fn } = itemData
|
|
||||||
eventData.push({
|
|
||||||
data: {
|
|
||||||
tooltip,
|
|
||||||
text,
|
|
||||||
onclick: e => {
|
|
||||||
fn(e, data => {
|
|
||||||
// 当事件成功后触发回调函数
|
|
||||||
_this.dataSuccessCBK(data)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
name: toolbarName
|
|
||||||
})
|
|
||||||
barStr += toolbarName + ' '
|
|
||||||
})
|
|
||||||
if (this.toolbar.length > 0) {
|
|
||||||
newToolbar = this.toolbar.concat(barStr)
|
|
||||||
} else {
|
|
||||||
newToolbar = toolbar.concat(barStr)
|
|
||||||
}
|
|
||||||
window.tinymce.init({
|
window.tinymce.init({
|
||||||
selector: `#${this.tinymceId}`,
|
selector: `#${this.tinymceId}`,
|
||||||
language: this.languageTypeList['en'],
|
language: this.languageTypeList['en'],
|
||||||
height: this.height,
|
height: this.height,
|
||||||
body_class: 'panel-body ',
|
body_class: 'panel-body ',
|
||||||
object_resizing: false,
|
object_resizing: false,
|
||||||
toolbar: newToolbar,
|
toolbar: this.toolbar.length > 0 ? this.toolbar : toolbar,
|
||||||
menubar: this.menubar,
|
menubar: this.menubar,
|
||||||
plugins: plugins,
|
plugins: plugins,
|
||||||
end_container_on_empty_block: true,
|
end_container_on_empty_block: true,
|
||||||
|
@ -192,9 +144,8 @@ export default {
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
setup(editor) {
|
setup(editor) {
|
||||||
eventData.map(item => {
|
editor.on('FullscreenStateChanged', (e) => {
|
||||||
console.log('-----', item)
|
_this.fullscreen = e.state
|
||||||
editor.addButton(item.name, item.data)
|
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
// it will try to keep these URLs intact
|
// it will try to keep these URLs intact
|
||||||
|
@ -253,29 +204,7 @@ export default {
|
||||||
window.tinymce.get(this.tinymceId).getContent()
|
window.tinymce.get(this.tinymceId).getContent()
|
||||||
},
|
},
|
||||||
imageSuccessCBK(arr) {
|
imageSuccessCBK(arr) {
|
||||||
const _this = this
|
arr.forEach(v => window.tinymce.get(this.tinymceId).insertContent(`<img class="wscnph" src="${v.url}" >`))
|
||||||
arr.forEach(v => {
|
|
||||||
window.tinymce
|
|
||||||
.get(_this.tinymceId)
|
|
||||||
.insertContent(`<img class="wscnph" src="${v.url}" >`)
|
|
||||||
})
|
|
||||||
},
|
|
||||||
dataSuccessCBK(dataList) {
|
|
||||||
const { type, list, setDom } = dataList
|
|
||||||
let newDom = ''
|
|
||||||
console.log(dataList)
|
|
||||||
const self = this
|
|
||||||
if (type === 'image') {
|
|
||||||
list.forEach(v => {
|
|
||||||
newDom = setDom(v)
|
|
||||||
window.tinymce.get(self.tinymceId).insertContent(newDom)
|
|
||||||
})
|
|
||||||
} else if (type === 'video') {
|
|
||||||
list.forEach(v => {
|
|
||||||
newDom = setDom(v)
|
|
||||||
window.tinymce.get(self.tinymceId).insertContent(newDom)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
import store from '@/store'
|
import store from '@/store'
|
||||||
|
|
||||||
export default {
|
function checkPermission(el, binding) {
|
||||||
inserted(el, binding, vnode) {
|
const { value } = binding
|
||||||
const { value } = binding
|
const roles = store.getters && store.getters.roles
|
||||||
const roles = store.getters && store.getters.roles
|
|
||||||
|
|
||||||
if (value && value instanceof Array && value.length > 0) {
|
if (value && value instanceof Array) {
|
||||||
|
if (value.length > 0) {
|
||||||
const permissionRoles = value
|
const permissionRoles = value
|
||||||
|
|
||||||
const hasPermission = roles.some(role => {
|
const hasPermission = roles.some(role => {
|
||||||
|
@ -15,8 +15,17 @@ export default {
|
||||||
if (!hasPermission) {
|
if (!hasPermission) {
|
||||||
el.parentNode && el.parentNode.removeChild(el)
|
el.parentNode && el.parentNode.removeChild(el)
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
throw new Error(`need roles! Like v-permission="['admin','editor']"`)
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
throw new Error(`need roles! Like v-permission="['admin','editor']"`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
inserted(el, binding) {
|
||||||
|
checkPermission(el, binding)
|
||||||
|
},
|
||||||
|
update(el, binding) {
|
||||||
|
checkPermission(el, binding)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,11 @@ export default {
|
||||||
const vnodes = []
|
const vnodes = []
|
||||||
|
|
||||||
if (icon) {
|
if (icon) {
|
||||||
vnodes.push(<svg-icon icon-class={icon}/>)
|
if (icon.includes('el-icon')) {
|
||||||
|
vnodes.push(<i class={[icon, 'sub-el-icon']} />)
|
||||||
|
} else {
|
||||||
|
vnodes.push(<svg-icon icon-class={icon}/>)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (title) {
|
if (title) {
|
||||||
|
@ -27,3 +31,11 @@ export default {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.sub-el-icon {
|
||||||
|
color: currentColor;
|
||||||
|
width: 1em;
|
||||||
|
height: 1em;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
|
@ -25,7 +25,7 @@ import nestedRouter from './modules/nested'
|
||||||
* meta : {
|
* meta : {
|
||||||
roles: ['admin','editor'] control the page roles (you can set multiple roles)
|
roles: ['admin','editor'] control the page roles (you can set multiple roles)
|
||||||
title: 'title' the name show in sidebar and breadcrumb (recommend set)
|
title: 'title' the name show in sidebar and breadcrumb (recommend set)
|
||||||
icon: 'svg-name' the icon show in the sidebar
|
icon: 'svg-name'/'el-icon-x' the icon show in the sidebar
|
||||||
noCache: true if set true, the page will no be cached(default is false)
|
noCache: true if set true, the page will no be cached(default is false)
|
||||||
affix: true if set true, the tag will affix in the tags-view
|
affix: true if set true, the tag will affix in the tags-view
|
||||||
breadcrumb: false if set false, the item will hidden in breadcrumb(default is true)
|
breadcrumb: false if set false, the item will hidden in breadcrumb(default is true)
|
||||||
|
@ -197,7 +197,7 @@ export const asyncRoutes = [
|
||||||
name: 'Example',
|
name: 'Example',
|
||||||
meta: {
|
meta: {
|
||||||
title: 'Example',
|
title: 'Example',
|
||||||
icon: 'example'
|
icon: 'el-icon-s-help'
|
||||||
},
|
},
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
|
|
|
@ -13,6 +13,7 @@ const state = {
|
||||||
|
|
||||||
const mutations = {
|
const mutations = {
|
||||||
CHANGE_SETTING: (state, { key, value }) => {
|
CHANGE_SETTING: (state, { key, value }) => {
|
||||||
|
// eslint-disable-next-line no-prototype-builtins
|
||||||
if (state.hasOwnProperty(key)) {
|
if (state.hasOwnProperty(key)) {
|
||||||
state[key] = value
|
state[key] = value
|
||||||
}
|
}
|
||||||
|
|
|
@ -103,28 +103,23 @@ const actions = {
|
||||||
},
|
},
|
||||||
|
|
||||||
// dynamically modify permissions
|
// dynamically modify permissions
|
||||||
changeRoles({ commit, dispatch }, role) {
|
async changeRoles({ commit, dispatch }, role) {
|
||||||
return new Promise(async resolve => {
|
const token = role + '-token'
|
||||||
const token = role + '-token'
|
|
||||||
|
|
||||||
commit('SET_TOKEN', token)
|
commit('SET_TOKEN', token)
|
||||||
setToken(token)
|
setToken(token)
|
||||||
|
|
||||||
const { roles } = await dispatch('getInfo')
|
const { roles } = await dispatch('getInfo')
|
||||||
|
|
||||||
resetRouter()
|
resetRouter()
|
||||||
|
|
||||||
// generate accessible routes map based on roles
|
// generate accessible routes map based on roles
|
||||||
const accessRoutes = await dispatch('permission/generateRoutes', roles, { root: true })
|
const accessRoutes = await dispatch('permission/generateRoutes', roles, { root: true })
|
||||||
|
// dynamically add accessible routes
|
||||||
|
router.addRoutes(accessRoutes)
|
||||||
|
|
||||||
// dynamically add accessible routes
|
// reset visited views and cached views
|
||||||
router.addRoutes(accessRoutes)
|
dispatch('tagsView/delAllViews', null, { root: true })
|
||||||
|
|
||||||
// reset visited views and cached views
|
|
||||||
dispatch('tagsView/delAllViews', null, { root: true })
|
|
||||||
|
|
||||||
resolve()
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -57,6 +57,11 @@
|
||||||
margin-right: 16px;
|
margin-right: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.sub-el-icon {
|
||||||
|
margin-right: 12px;
|
||||||
|
margin-left: -2px;
|
||||||
|
}
|
||||||
|
|
||||||
.el-menu {
|
.el-menu {
|
||||||
border: none;
|
border: none;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
@ -105,6 +110,10 @@
|
||||||
.svg-icon {
|
.svg-icon {
|
||||||
margin-left: 20px;
|
margin-left: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.sub-el-icon {
|
||||||
|
margin-left: 19px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -118,6 +127,10 @@
|
||||||
margin-left: 20px;
|
margin-left: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.sub-el-icon {
|
||||||
|
margin-left: 19px;
|
||||||
|
}
|
||||||
|
|
||||||
.el-submenu__icon-arrow {
|
.el-submenu__icon-arrow {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
@ -178,6 +191,10 @@
|
||||||
.svg-icon {
|
.svg-icon {
|
||||||
margin-right: 16px;
|
margin-right: 16px;
|
||||||
}
|
}
|
||||||
|
.sub-el-icon {
|
||||||
|
margin-right: 12px;
|
||||||
|
margin-left: -2px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.nest-menu .el-submenu>.el-submenu__title,
|
.nest-menu .el-submenu>.el-submenu__title,
|
||||||
|
|
|
@ -162,19 +162,21 @@ export function param(json) {
|
||||||
* @returns {Object}
|
* @returns {Object}
|
||||||
*/
|
*/
|
||||||
export function param2Obj(url) {
|
export function param2Obj(url) {
|
||||||
const search = url.split('?')[1]
|
const search = decodeURIComponent(url.split('?')[1]).replace(/\+/g, ' ')
|
||||||
if (!search) {
|
if (!search) {
|
||||||
return {}
|
return {}
|
||||||
}
|
}
|
||||||
return JSON.parse(
|
const obj = {}
|
||||||
'{"' +
|
const searchArr = search.split('&')
|
||||||
decodeURIComponent(search)
|
searchArr.forEach(v => {
|
||||||
.replace(/"/g, '\\"')
|
const index = v.indexOf('=')
|
||||||
.replace(/&/g, '","')
|
if (index !== -1) {
|
||||||
.replace(/=/g, '":"')
|
const name = v.substring(0, index)
|
||||||
.replace(/\+/g, ' ') +
|
const val = v.substring(index + 1, v.length)
|
||||||
'"}'
|
obj[name] = val
|
||||||
)
|
}
|
||||||
|
})
|
||||||
|
return obj
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -13,11 +13,7 @@ export default function checkPermission(value) {
|
||||||
const hasPermission = roles.some(role => {
|
const hasPermission = roles.some(role => {
|
||||||
return permissionRoles.includes(role)
|
return permissionRoles.includes(role)
|
||||||
})
|
})
|
||||||
|
return hasPermission
|
||||||
if (!hasPermission) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
} else {
|
} else {
|
||||||
console.error(`need roles! Like v-permission="['admin','editor']"`)
|
console.error(`need roles! Like v-permission="['admin','editor']"`)
|
||||||
return false
|
return false
|
||||||
|
|
|
@ -1,12 +1,8 @@
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import {
|
import { MessageBox, Message } from 'element-ui'
|
||||||
Message,
|
|
||||||
MessageBox,
|
|
||||||
Loading
|
|
||||||
} from 'element-ui'
|
|
||||||
import store from '@/store'
|
import store from '@/store'
|
||||||
import { getToken } from '@/utils/auth'
|
import { getToken } from '@/utils/auth'
|
||||||
var loadingInstance = null
|
|
||||||
// create an axios instance
|
// create an axios instance
|
||||||
const service = axios.create({
|
const service = axios.create({
|
||||||
baseURL: process.env.VUE_APP_BASE_API, // url = base url + request url
|
baseURL: process.env.VUE_APP_BASE_API, // url = base url + request url
|
||||||
|
@ -25,12 +21,6 @@ service.interceptors.request.use(
|
||||||
// please modify it according to the actual situation
|
// please modify it according to the actual situation
|
||||||
config.headers['X-Token'] = getToken()
|
config.headers['X-Token'] = getToken()
|
||||||
}
|
}
|
||||||
console.log('------')
|
|
||||||
loadingInstance = Loading.service({
|
|
||||||
lock: true,
|
|
||||||
text: '数据加载中,请稍后...',
|
|
||||||
background: 'rgba(0, 0, 0, 0.1)'
|
|
||||||
})
|
|
||||||
return config
|
return config
|
||||||
},
|
},
|
||||||
error => {
|
error => {
|
||||||
|
@ -53,7 +43,6 @@ service.interceptors.response.use(
|
||||||
* You can also judge the status by HTTP Status Code
|
* You can also judge the status by HTTP Status Code
|
||||||
*/
|
*/
|
||||||
response => {
|
response => {
|
||||||
loadingInstance.close()
|
|
||||||
const res = response.data
|
const res = response.data
|
||||||
|
|
||||||
// if the custom code is not 20000, it is judged as an error.
|
// if the custom code is not 20000, it is judged as an error.
|
||||||
|
@ -83,7 +72,6 @@ service.interceptors.response.use(
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
error => {
|
error => {
|
||||||
loadingInstance.close()
|
|
||||||
console.log('err' + error) // for debug
|
console.log('err' + error) // for debug
|
||||||
Message({
|
Message({
|
||||||
message: error.message,
|
message: error.message,
|
||||||
|
|
|
@ -2,44 +2,34 @@
|
||||||
<div class="components-container">
|
<div class="components-container">
|
||||||
<aside>
|
<aside>
|
||||||
Rich text is a core feature of the management backend, but at the same time it is a place with lots of pits. In the process of selecting rich texts, I also took a lot of detours. The common rich texts on the market have been basically used, and I finally chose Tinymce. See the more detailed rich text comparison and introduction.
|
Rich text is a core feature of the management backend, but at the same time it is a place with lots of pits. In the process of selecting rich texts, I also took a lot of detours. The common rich texts on the market have been basically used, and I finally chose Tinymce. See the more detailed rich text comparison and introduction.
|
||||||
<a
|
<a target="_blank" class="link-type" href="https://panjiachen.github.io/vue-element-admin-site/feature/component/rich-editor.html">Documentation</a>
|
||||||
target="_blank"
|
|
||||||
class="link-type"
|
|
||||||
href="https://panjiachen.github.io/vue-element-admin-site/component/rich-editor.html"
|
|
||||||
>Documentation</a>
|
|
||||||
</aside>
|
</aside>
|
||||||
<div>
|
<div>
|
||||||
<tinymce
|
<tinymce v-model="content" :height="300" />
|
||||||
v-model="content"
|
|
||||||
:height="300"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div class="editor-content" v-html="content" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Tinymce from '@/components/Tinymce/example'
|
import Tinymce from '@/components/Tinymce'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'TinymceDemo',
|
name: 'TinymceDemo',
|
||||||
components: { Tinymce },
|
components: { Tinymce },
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
content: `<h1 style="text-align: center;">测试demo!</h1>
|
content:
|
||||||
<p>嗨</p>
|
`<h1 style="text-align: center;">Welcome to the TinyMCE demo!</h1><p style="text-align: center; font-size: 15px;"><img title="TinyMCE Logo" src="//www.tinymce.com/images/glyph-tinymce@2x.png" alt="TinyMCE Logo" width="110" height="97" /><ul>
|
||||||
<img class="img" src="https://ali-image-test.dabanjia.com/image/20200508/1588911589606_5140%24ban.jpg" width="200"/>
|
<li>Our <a href="//www.tinymce.com/docs/">documentation</a> is a great resource for learning how to configure TinyMCE.</li><li>Have a specific question? Visit the <a href="https://community.tinymce.com/forum/">Community Forum</a>.</li><li>We also offer enterprise grade support as part of <a href="https://tinymce.com/pricing">TinyMCE premium subscriptions</a>.</li>
|
||||||
<p>枕着幸福在梦中微笑,清零生活的压力烦恼,阳光透过窗与你拥抱,伸伸懒腰迎接周末来到,问候源于无法按捺的心跳,愿你周末乐逍遥,生活更美妙!
|
</ul>`
|
||||||
周末来到,让烦恼跑光,让忧愁找不到,跟寂寞说再见,跟压力说拜拜,跟快乐说你好,牵着幸福的手,携上平安,开开心心的过周末!
|
|
||||||
每个人,都有一个世界;每首歌,都有一个故事;每一周,都有一个周末;每个人,都要一个愿望;我的愿望很现实:周末清晨,搅黄你的美梦!周末快乐!
|
|
||||||
</p>
|
|
||||||
`
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.editor-content {
|
.editor-content{
|
||||||
margin-top: 20px;
|
margin-top: 20px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
<el-progress :percentage="18" />
|
<el-progress :percentage="18" />
|
||||||
</div>
|
</div>
|
||||||
<div class="progress-item">
|
<div class="progress-item">
|
||||||
<span>Css</span>
|
<span>CSS</span>
|
||||||
<el-progress :percentage="12" />
|
<el-progress :percentage="12" />
|
||||||
</div>
|
</div>
|
||||||
<div class="progress-item">
|
<div class="progress-item">
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="app-container documentation-container">
|
<div class="app-container documentation-container">
|
||||||
|
<a class="document-btn" target="_blank" href="https://store.akveo.com/products/vue-java-admin-dashboard-spring?utm_campaign=akveo_store-Vue-Vue_demo%2Fgithub&utm_source=vue_admin&utm_medium=referral&utm_content=demo_English_button">Java backend integration</a>
|
||||||
<a class="document-btn" target="_blank" href="https://panjiachen.github.io/vue-element-admin-site/">Documentation</a>
|
<a class="document-btn" target="_blank" href="https://panjiachen.github.io/vue-element-admin-site/">Documentation</a>
|
||||||
<a class="document-btn" target="_blank" href="https://github.com/PanJiaChen/vue-element-admin/">Github Repository</a>
|
<a class="document-btn" target="_blank" href="https://github.com/PanJiaChen/vue-element-admin/">Github Repository</a>
|
||||||
<a class="document-btn" target="_blank" href="https://panjiachen.gitee.io/vue-element-admin-site/zh/">国内文档</a>
|
<a class="document-btn" target="_blank" href="https://panjiachen.gitee.io/vue-element-admin-site/zh/">国内文档</a>
|
||||||
|
@ -37,7 +38,7 @@ export default {
|
||||||
margin: 50px;
|
margin: 50px;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
justify-content: space-evenly;
|
justify-content: flex-start;
|
||||||
|
|
||||||
.document-btn {
|
.document-btn {
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
|
@ -46,8 +47,8 @@ export default {
|
||||||
background: black;
|
background: black;
|
||||||
color: white;
|
color: white;
|
||||||
height: 60px;
|
height: 60px;
|
||||||
width: 200px;
|
padding: 0 16px;
|
||||||
margin-bottom: 16px;
|
margin: 16px;
|
||||||
line-height: 60px;
|
line-height: 60px;
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
|
|
@ -71,21 +71,21 @@ export default {
|
||||||
},
|
},
|
||||||
handleDownload() {
|
handleDownload() {
|
||||||
this.downloadLoading = true
|
this.downloadLoading = true
|
||||||
import('@/vendor/Export2Excel').then(excel => {
|
import('@/vendor/Export2Excel').then(excel => {
|
||||||
const multiHeader = [['Id', 'Main Information', '', '', 'Date']]
|
const multiHeader = [['Id', 'Main Information', '', '', 'Date']]
|
||||||
const header = ['', 'Title', 'Author', 'Readings', '']
|
const header = ['', 'Title', 'Author', 'Readings', '']
|
||||||
const filterVal = ['id', 'title', 'author', 'pageviews', 'display_time']
|
const filterVal = ['id', 'title', 'author', 'pageviews', 'display_time']
|
||||||
const list = this.list
|
const list = this.list
|
||||||
const data = this.formatJson(filterVal, list)
|
const data = this.formatJson(filterVal, list)
|
||||||
const merges = ['A1:A2', 'B1:D1', 'E1:E2']
|
const merges = ['A1:A2', 'B1:D1', 'E1:E2']
|
||||||
excel.export_json_to_excel({
|
excel.export_json_to_excel({
|
||||||
multiHeader,
|
multiHeader,
|
||||||
header,
|
header,
|
||||||
merges,
|
merges,
|
||||||
data
|
data
|
||||||
})
|
|
||||||
this.downloadLoading = false
|
|
||||||
})
|
})
|
||||||
|
this.downloadLoading = false
|
||||||
|
})
|
||||||
},
|
},
|
||||||
formatJson(filterVal, jsonData) {
|
formatJson(filterVal, jsonData) {
|
||||||
return jsonData.map(v => filterVal.map(j => {
|
return jsonData.map(v => filterVal.map(j => {
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
import { param2Obj } from '@/utils/index.js'
|
||||||
|
describe('Utils:param2Obj', () => {
|
||||||
|
const url = 'https://github.com/PanJiaChen/vue-element-admin?name=bill&age=29&sex=1&field=dGVzdA==&key=%E6%B5%8B%E8%AF%95'
|
||||||
|
|
||||||
|
it('param2Obj test', () => {
|
||||||
|
expect(param2Obj(url)).toEqual({
|
||||||
|
name: 'bill',
|
||||||
|
age: '29',
|
||||||
|
sex: '1',
|
||||||
|
field: window.btoa('test'),
|
||||||
|
key: '测试'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
|
@ -49,8 +49,20 @@ module.exports = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
chainWebpack(config) {
|
chainWebpack(config) {
|
||||||
config.plugins.delete('preload') // TODO: need test
|
// it can improve the speed of the first screen, it is recommended to turn on preload
|
||||||
config.plugins.delete('prefetch') // TODO: need test
|
// it can improve the speed of the first screen, it is recommended to turn on preload
|
||||||
|
config.plugin('preload').tap(() => [
|
||||||
|
{
|
||||||
|
rel: 'preload',
|
||||||
|
// to ignore runtime.js
|
||||||
|
// https://github.com/vuejs/vue-cli/blob/dev/packages/@vue/cli-service/lib/config/app.js#L171
|
||||||
|
fileBlacklist: [/\.map$/, /hot-update\.js$/, /runtime\..*\.js$/],
|
||||||
|
include: 'initial'
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
|
// when there are many pages, it will cause too many meaningless requests
|
||||||
|
config.plugins.delete('prefetch')
|
||||||
|
|
||||||
// set svg-sprite-loader
|
// set svg-sprite-loader
|
||||||
config.module
|
config.module
|
||||||
|
@ -69,17 +81,6 @@ module.exports = {
|
||||||
})
|
})
|
||||||
.end()
|
.end()
|
||||||
|
|
||||||
// set preserveWhitespace
|
|
||||||
config.module
|
|
||||||
.rule('vue')
|
|
||||||
.use('vue-loader')
|
|
||||||
.loader('vue-loader')
|
|
||||||
.tap(options => {
|
|
||||||
options.compilerOptions.preserveWhitespace = true
|
|
||||||
return options
|
|
||||||
})
|
|
||||||
.end()
|
|
||||||
|
|
||||||
config
|
config
|
||||||
.when(process.env.NODE_ENV !== 'development',
|
.when(process.env.NODE_ENV !== 'development',
|
||||||
config => {
|
config => {
|
||||||
|
@ -115,6 +116,7 @@ module.exports = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
// https:// webpack.js.org/configuration/optimization/#optimizationruntimechunk
|
||||||
config.optimization.runtimeChunk('single')
|
config.optimization.runtimeChunk('single')
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
Loading…
Reference in New Issue